Writing custom tools with Swift
paul-samuels.com
paul-samuels.com
Swift tools might be a good choice for use cases where you need to integrate with an existing Swift project, or if you need lower level APIs.
In this example, a Bash script would have been almost done by the time you worked out the `curl | jq` command. But in other similar cases I would suggest Python Requests, which will take perhaps 10% as much code and avoid issues of Linux compatibility and mistakes like forgetting to call `resume()` on your download task or `exit()` in some branch (there are five calls just to keep the program from looping forever).
That said, I think this blog post is very informative and well-made for a beginner interested in talking to a web-based JSON API from Swift.
The obvious question, of course, is: why bother? The answer is that this code runs at about the same speed as C. So if you're doing data munging of a big file, this is a really interesting approach. For data scientists, I think there is a lot of opportunity here.
I like the idea of swift and I want to like it, but everytime I look at APIs it seems they dropped the ball.
Without the libraries, documentation, maturity, and major support.
But yes, if the plan is to run it locally on a mac, then your objection certainly hold.
That's more than anything that can be said for Crystal documentation.
And bad package management or not (I don't see much problem with it -- and you have several options to chose from, cocoa pods, carthage, etc), there are tons more libraries than Crystal has again.
> And bad package management or not (I don't see much problem with it -- and you have several options to chose from, cocoa pods, carthage, etc)
That's the problem. It should be SPM, period. But few libs use that, and some still don't work on linux and you don't realize until you try to build. Carthage is slow, hard to integrate with xcode project and CLI. CocoaPods, I guess it was super useful before swift, but now it seems like it can only work with xcode projects.
Well, you don't need to do any of those.
You could make a synchronous web request.
And you could decode JSON into a representation, instead of pre-creating a struct with the exact result.
JSON doesn't represent class hierarchies, but plain nested JS objects. And the types it supports are just dictionary, array, string, boolean, and float.
So having all the ceremony in Swift (or similar in Go, etc) to parse into predefined structs is not really idiomatic to JSON.
You can have all of those in the generic representation -- and this is exactly how any dynamic language does it (JS, Python, PHP, etc.) -- and any statically typed language when it wants to read arbitrary JSON.
A dictionary is a key/value map, period. If you want to use that, because you don't want to do the encode/decode ceremony, then you are doing stringly-typed code in a statically typed language. This is not bad per-se, as you say dynamic languages always do this, but you should recognize what you are doing.
It's not stringy-typed since all the scalars have the proper type as they did on the "wire". In JSON you have string, booleans, floats, objects, arrays, and null.
And JSON is not a general serialization format (e.g. all object keys are strings, all numeric values are floats), so this "encode/decode" ceremony just introduces a layer that isn't there to begin with (and would fail without custom decoders for things like dates and so on).
In general the decode is really a mapping for convenience and schema validation, than an actual decode of what the JSON contains (since JSON knows nothing about those structs as structs).
it is stringly typed when you do foo[“bar”][“baz”] etc. foo.bar.baz would be typesafe.
https://gist.github.com/josephg/469ddbe71b7c515247b0db801325...
The reality is you always have to do more work to interact with JSON data from a static language because you need to tell the compiler what type everything will be. The benefit you get from doing that can pay off in larger projects, but for quick and dirty scripts its a lot of wasted effort.
It also has the advantage that, if later this solution needs to grow past a simple command line tool, I can just grab the Swift code from the script, put it in an Xcode project, and make a Mac app out of it.
And in the domains where shell is a good choice, it's a far far better choice than Swift. Maybe that will change, but I doubt it.
I've implemented a couple of medium-small tools over the last two years and as an exercise did them in both shell and Swift. The shell versions are simpler, more maintainable, and more robust than the Swift versions. Swift's verbose "correctness" is not an asset in the domain(s) where scripting is useful. Gluing programs together is one. Frankly, text processing is another. An invocation of sed with a simple regex becomes a monstrosity when translated to Swift. The less code you write, the less you have to maintain.
This is because Swift’s regex support is overly verbose, being lifted straight from Objective-C. It’s not an inherent problem when the language.
- The official libraries that ship with Swift
- If you create a full-blown Swift package manager project and compile an executable. But this doesn't sound like a script anymore
- If you use John Sundell's Marathon [https://github.com/JohnSundell/Marathon] however it adds additional complexity around the writing of scripts
I.e. none of the solutions are as simple as writing a bash script
Edit: Just saw that Max Howell (creator of Homebrew) released this today https://github.com/mxcl/swift-sh This absolutely solves the issues. Fantastic.
The problem is of course that as the complexity of the script grows, the absolute dreadfulness of actual programming in the shell rears its ugly head. I haven't really found a good compromise here myself.
Swift gives the programmer a lot of flexibility in overriding operators, though, so one could probably make a decent DSL that approximates shell syntax.
And while not relevant in this particular case, native binaries are fast, and launch quickly as well.
This is not possible yet, unfortunately: https://bugs.swift.org/browse/SR-648
I don't think it's trying to be a "jack of all trades", sure Bash can do plenty but when you reach few hundred lines of Bash and start to fight against the syntax to do simple things well...
The shebang line is there to be used, you can use sh, bash, etc. but there are other citizen like perl, php, python, etc. so why not swift or anything else if it help you build quickly command-line tools?
If all you care about is ticking a box in the shortest amount of time, I've found that small scripts are best done in bash whereas longer "scripts" would benefit from a safer language utilized in a JIT compilation paradigm (shebang for Swift or Haskell, `go run`, etc).