Apple’s Use of Swift in iOS 13
blog.timac.org
blog.timac.org
For a while I was enamored of Haskell and its famed "if it compiles it works" saying, but I think I've realized that the features that contribute to that are: non-nullability and algebraic data types. Turns out I don't particularly like the extreme functional nature or (especially) the lazy aspect of it.
Rust is almost there, but then I'm dealing with lifetimes and working a little lower than I usually need. But I found I _love_ its exhaustiveness checking for enum variants.
The ML family is pretty much in my sweet spot, for some reason it doesn't really get the love that it seems it deserves, and last I heard Ocaml had an issue with being single threaded.
So that leaves Swift, with its non-nullability, algebraic data types, and exhaustive enum pattern matching. The only downside is its focus on Macs! I'm holding my breath, waiting for it to run just as smoothly on linux, and developing some developer cultural cachet and libraries for servers. I hope it does! I'm glad to see in this article that it's being used more and more at Apple.
And with SwiftUI, which looks pretty promising, I have to imagine it will only continue to grow in popularity.
Out of curiosity, when's the last time you worked with Rust? A lot of the lifetime's have been able to be elided in recent releases, especially in the 2018 edition. That's not to say that you don't still run into them, but in the middle of the road cases, it's much less frequent that you run into them unexpectedly.
I haven't been able to narrow down exactly what it is, but with Swift I can just put pencil to paper. With Rust I feel like I need to plan more, I dunno.
I do think my experience is colored by tooling, though
On the other hand, you can kind of make your own language within Swift because most of the keywords aren't required but allow some additive features. For example, some features help you write your code functionally and others support a declaritive style. It reminds me of what's going on with Babel and JavaScript, where babel extensions are like these language plugins you can mix and match to create your own language.
If you think of metaprogramming as a way to build your own language within a language, lisps and Ruby were kind of like pioneers of a build-your-own-language trend. I could see that as an interesting direction, I wonder if people have tried it before.
[1] https://medium.com/the-traveled-ios-developers-guide/swift-k...
Summary
Number of keywords: 310
PL/I made life ‘easier’ by not making keywords reserved, allowing you to use variable names that match the strings used to identify keywords, though, so you didn’t have to learn them all.> you can kind of make your own language within Swift
This is one of my favorite aspects of working with Swift. Because it's a relatively un-opinionated language with a diverse toolkit, and because of the powerful type/protocol system, it can really feel like you can carve out a DSL for each specific use-case.
"Progressive Disclosure" is an interesting idea for PL research, but IME Swift doesn't do a very good job at it. There's a million cases I've seen where people trying to do simple things run into a pandora's box of complexity (e.g., the infamous "Protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements"). There isn't really a "small subset" I've found that's usable for anything but the absolute simplest programs. Even the fundamental types like Int and String have a lot of not-entirely-hidden complexity in Swift.
> Because it's a relatively un-opinionated language
It seems awfully opinionated to me. There is a clear preferred way to write almost anything. Ask online about how to do things differently, and you'll be told to do it in a more "swifty" way.
> because of the powerful type/protocol system, it can really feel like you can carve out a DSL for each specific use-case
I'll have to see if the new features in the latest versions of Swift (used for SwiftUI) enable this more readily, but as of 4.2, anyway, writing DSLs in Swift is moderately limited and painful.
That's a big reason why I don't like it. I also can't stand func, let, and var. Because func and var are totally useless, and let is the wrong word (should be const at least).
There is also all the question marks and other confusing syntax.
OCaml is single-threaded, yes, but in practice that's not a big deal. There's a great concurrency library (Lwt) and the recently-added monadic (and applicative) bind syntax makes it look almost like sequential code. And if you really need a separate OS thread, there are ways to do that :-)
Last I checked, Swift still does concurrency mostly with Node-style callbacks.
But I do remember having to implement myself protocols for standard library types, most of them obviously belonged to the standard libraries, they even had theses protocols as examples in Apple's docs.
The language is truly great, but was not ready for primetime out of Cocoa apps.
Has anything changed?
I'd love to see a Rails like framework come to Swift. I don't think we'll ever see that.
For me personally, Swift is often fast enough for my needs as a compiled language, but it's so much easier to be productive in since it obviates many of the more tedious concerns of Rust.
Both languages play nicely with the C FFI so it's also possible to write high-level code in Swift and drop down into C or Rust for the performance sensitive parts.
Apple Swift developers are actively working Linux support, but I suppose it'll forever remain a second-tier platform to iOS & macOS. Kind of like C# to Windows, but Swift doesn't have its Miguel de Icaza to create its Mono on Linux.
I'm sure the Swift team is well aware of this cultural bottleneck, hence the current efforts to support Linux, but it remains a cultural chicken-and-egg problem.
https://discuss.ocaml.org/t/multicore-prerequisite-patches-a...
https://www.jetbrains.com/lp/devecosystem-2019/swift-objc/
How about HN devs?
We also have several legacy apps that are all ObjC, or 50% ObjC. There's not a ton of new feature development, but still updates.
If I had my druthers, I'd be Swift exclusive.
Same with Android, I would choose Kotlin before Java if it was my choice.
People still working with Objective C might be like I recently was: maintaining a sizable codebase which, unless Apple breaks something, porting to Swift is not justifiable to management.
You could do it with a while loop (if it exists in Swift) but I'm at a loss as to why you would remove a for loop from a programming language.
for i in 0..<10 {
print(i)
}As an also Lisp programmer, I find Swift's limited (and fixed!) set of control flow constructs downright ancient. They added for-each loops, but that's it. We had more "modern control flow" in the 1980's.
Besides, both of those examples are essentially non-local jumps, and I'm not sure I'd describe them as "modern".
In a sense, Swift already allows diversity in control flow constructs, via closures and the trailing closure syntax. It's just somewhat awkward, and not flexible enough to implement, say, most of Lisp's ITERATE library. That's packed full of exactly the kinds of control flow constructs that I have to write out by hand in Swift every day.
for (value, index) in arr.enumerated() { // Loop body }…how are you interacting with UIKit?!
I rarely see that annotation or class name in my codebases too.
A quick search through one of my apps shows that about 4% of functions are marked as @objc (and we're not using the old compatibility mode where more methods were implicitly @objc either).
By the way, what is painful in terms of project fragility?
It wasn't viable as v1, but now it's solid & clean.
Seems like it was a good opportunity to take a well understood language (C/C++/Obj-C) and after 30 years rebuild it ground-up into what it should be. Some constructs & workarounds just weren't going away without a clean-slate industry-wide fully-compatible restart.
Top problem now is getting developers to not force-unwrap optionals unless unless able to prove it won't cause a crash.
Crashes & exits are not acceptable in production code, and I’ve had to fix too many of them.
If a thing might be null/nothing but can't be used when it is, then you should write code that indicates your understanding of that case. Catching an error doesn't imply that your program should proceed, it communicates that you the programmer understood your system and anticipated its failure states. Proceeding or aborting is a secondary decision.
There are cases where the programmer cannot express this to the compiler without a bottom type.
Obviously in F# or Haskell or C#8 or Rust this isn't a problem. But what we are talking about is: how do you handle, at runtime, in a language such as C#7 or Java, an null parameter being passed to a method that must return a value based on its parameter, but was passed null? It's a choice between very bad options where the least bad is usually to throw an ArgumentNullException or similar. Because there is nothing better to do.
I don't disagree with this, but I can point out gobs of places in AppKit where Apple has arguably 'made an error' and I can't do anything about it. Or in third-party C libraries, which hold 99% of the functionality I ultimately need, and were not designed for having nice Swift programming interfaces.
I feel like everyone saying "never force unwrap!" must be writing pure-Swift standalone functions, with no dependencies, including the operating system. The API for writing items to the Mac clipboard still documents that it can throw exceptions, which are impossible to catch in Swift!
Right, hence why I'm suggesting that they can be useful if you don't put them everywhere and aren't using them as a band-aid to make your code compile :)
Is it a hard coded string that will never change? Force unwrapping is probably appropriate.
Is it based on user input? Then you should probably tell the user the string they entered isn't a URL.
But if you're creating it dynamically you should be guarding and throwing an error.
- abort and unwind the current action
- retry something
- log something
- abort the app explicitly
- fallback to a different value
- comment why this is a known-possible failure state
- present a message to the user
...
guard let data = notification.userInfo?["updatedItem"] as? Data else {
assertionFailure("Could not retrieve updated item")
return false
}
I am really being helpful here? If I see a crash on this line: let data = notification.userInfo?["updatedItem"] as! Data
I get essentially the same information, except it's done in a less verbose and easier-to-discover way. Whereas the first one is like adding this kind of useless comment: let x = 5 // Assign 5 to x1. ! used here is hard to spot, it's just few pixels of difference from ?.
2. assertionFailure is not the same as force unwrap, because it crashes only in debug mode. Ideally you would log it with some analytics so developers know if problem occured, but doing nothing is usually better than crashing the app.
Adding a ! suppresses an error from the compiler, it doesn't fix an error in your code!
At a given point, you can handle errors, but there's nothing you can do about them
It's good when languages (and developers) realize there are errors you should handle but there are errors you "shouldn't" because there's nothing you can do but bail out.
Even in those cases where you can't recover from a failure of those expectations, your code will be 1000x more legible if you demonstrate that you understand that the error could exist and what it might represent about the system as a whole. If you want to explicitly fire a fatal error after that because there's no other form of recovery, so be it.
Trying to capture all that in a "!" saves you a few LOC (or worse: minutes of reasoning) now but suggests somebody else might be pulling their hair out in frustration six months later. Please don't do that to someone.
Some crypto code is also still Objective-C due to easier linking, but that'll probably change soon enough.
I don't consider ObjC to be a bad language in the slightest; in fact, I'll offer the (probably contrarian) opinion that it's one of the greatest languages of the past few decades.
I've never used it, but have seen many code snippets in API documentations, and it just looks so...unnecessarily obfuscated and verbose, and with a syntax so wholly unique to Obj-C that my brain can't make sense of it like I would looking at, say, Java code from a C++ background.
- As malleable as JavaScript when you need it to be
- As typed as your favorite language when you need it to be
- Able to drop to much more arcane levels if you need to for certain performance-intensive places, without requiring some crazy setup (e.g, C/C++ are right there if you need them)
- ARC is IMO one of the best approaches to memory management out there
- The verbosity can be annoying at first, but it forces you to think hard about everything, and when I come back to Objective-C code years later, I've no issue remembering what the hell I was doing there
- People complain about brackets, but they just... don't matter. It's a syntax. You either deal with it or don't - you don't see me writing Lisp because I find the syntax annoying, which is fine.
- Message passing in ObjC is so optimized that it probably can't get much faster, and anyone acting like it's slow has a potentially skewed understanding of this
Swift is nicer for me in only two distinct ways:
- No more header files, because man was that annoying
- The stricter nil handling is overall better, if not a mental shift from some ObjC counterparts
The problem with Objc message passing is not the absolute execution time involved in passing a message. The problem is that the dynamism involved completely disables the compiler from performing any code-inlining. Typically inlining small functions is what enables the compiler to unlock further optimisations. From simple things such as merging duplicate loads from same address, to auto-vectorization.
These types of speedups just aren't necessary in that world. You'd drop to C or C++ if you needed it, which is trading convenience for complexity.
You could want it to be faster, but it's not going to materially show itself in common workloads.
And it shows, Swift gets slammed on the ixy paper by all tracing GC languages, spending an huge amount handling counters.
It makes sense for Swift though, because it is the easiest way to integrate with Objective-C, given the failure of its GC experience , having to deal with C semantics and non-GC enabled frameworks, that lead to constant crashes.
This this this. I love how it forces the verbosity. It makes it so easy to understand.
> - People complain about brackets, but they just... don't matter. It's a syntax. You either deal with it or don't - you don't see me writing Lisp because I find the syntax annoying, which is fine.
I really don't mind the brackets. If you structure your code right, is not even that big of a deal.
> - Message passing in ObjC is so optimized that it probably can't get much faster, and anyone acting like it's slow has a potentially skewed understanding of this
Not just this, but I love how you can send messages to nil and it doesn't crash, or you can define a method in code and it runs.
Then much later Apple added the . syntax, which while more familiar to a lot of modern developers kind of broke the cleanliness that the syntax used to have.
I absolutely agree. Its such a beautiful easy to read and code language. Its got its quirks for sure, but I absolutely love it.
I know Chris Lattner did something with TensorFlow though.
Is anyone into this?
I run a startup that utilizes CoreML pretty heavily in mobile apps, and I'm eager to eventually build out a web app with the backend entirely in Swift. Being able to do everything in one language is really enticing.
The ecosystem is indeed the greatest downside. A lot of the existing libraries are also polluted with UIKit code making them unusable on Linux. Just recently wanted to use an existing API client of some social network, but couldn't, because it contained UIKit code. Forking it and fleshing out the parts needed feels kinda wrong.
I'm not much of a server guy, so I might be wrong here, but dealing with strings seems like something a server is likely to need to deal with. Although, maybe the strings are short enough that it's not much of a problem, or maybe strings that are almost always ASCII are fast. Anyway, something to keep in mind.
Swift 5.0 and 5.1 each supposedly made some 10-20% gain in ARC performance compared to the previous version (4.2 and 5.0), so that may also help. I think Swift's performance can be very hit or miss but they (Apple) are making some good strides.
hasSuffix() doesn’t need UTF-8 to be performant, though. As long as both strings are both the same encoding, just go back len(suffix) bytes from the end of the string and memcmp to see if they are equal.
Kotlin would probably be a better idea, and if you really need some sort of perf boost, use rust or c++.
At any rate, I'd love to get back into it one of these days, but for now, PHP projects have paying clients, so that's still what I focus on. I would also like to see Swift get more "official" attention outside of Ubuntu; specifically I want it on the BSDs, though of course other Linux distros deserve it too. I'll gladly use Ubuntu if a client pays me to, though.
I’ve made a backend for my app that is essentially a middleware layer to an external API.
It’s entirely written in Swift, using the Vapor web framework, packaged up in a Docker container, and hosted on Google Cloud Run (which is essentially serverless for Docker containers).
Previously it was running on AWS Elastic Beanstalk, but the devops knowledge to get it running on Beanstalk was significantly more, and realistically beyond my capabilities. Towards the end there was an intermittent SSL issue on Beanstalk I couldn’t figure out how to fix.
Since moving to Cloud Run its been running extremely smoothly and is costing a fraction of the price.
It is in production, but the daily active users are measured in the thousands, not tens of thousands.
Which browser are you using? The site has been tested on a couple of platforms and different browsers. I can't reproduce such a redirecting problem.
Chrome 76.0.3809.132 (Official Build) (64-bit) (cohort: Stable), Windows 10.
That said, I guess I needed to update Chrome, so I just did to 77.0.3865.90 (Official Build) (64-bit) (cohort: Stable) , and same error.
Here's a "copy as cURL" for the request, maybe that can help?
curl 'https://blog.timac.org/2019/0926-state-of-swift-ios13/' -H 'authority: blog.timac.org' -H 'cache-control: max-age=0' -H 'upgrade-insecure-requests: 1' -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36' -H 'sec-fetch-mode: navigate' -H 'sec-fetch-user: ?1' -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3' -H 'sec-fetch-site: cross-site' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: en-US,en;q=0.9' --compressed
(As in not usable for development in the Apple ecosystem)
I don’t think swift will phase out C until a very long time. It still needs to sort its performance issues related to memory automatic reference counting and global lock. Which requires something close to rust borrow checker, but with an even more advanced technology to keep the syntax beginner friendly.