This article could have been about how we converted our current project to Swift, then eventually had to convert 15k lines of Swift back to Objective-C again. But it’s not going tell that story. – The main reasons are outlined in this article anyway: by Swift 1.0, compile times for a project like ours was exceeding a minute and for editing, Xcode 6.0 was thoroughly broken. With no known date when this would be fixed there wasn’t really much of a choice.
No, this article is going to talk the experience of taking the project to Swift and then back to Objective-C – what was better in Swift and what improved going back to Objective-C.
First I’ll offer a brief overview of what Swift brought to the table, then a subjective take on the “feel” of the code. After that I’ll share some evaluation of how much the various new features helped. Finally I’ll talk about how the code converted back to Objective-C and what I missed (or didn’t miss) when being back in Objective-C.
A brief summary of Swift
The most honest description of Swift is probably “a much improved upon C++ with streamlined syntax and a strong Cocoa interoperability-layer”. This is not ObjC 3.0 or a derivative. It has some features inspired by ObjC, such as the extensions, that’s all.
Compared to ObjC/C++, Swift offers the following:
- Optionals instead of NULL/nil
- Structs/enums like “almost” objects
- No direct pointer manipulation except for in C interop
- Simplified import/include
- Strict “safe” constructors
- Some native constructs for immutability (let/var, mutability modifiers for structs)
- Constants may be objects / results of functions
- Deep type inference
- Reified generics
- No header files
- Overload on return types
- Simple and streamlined closures / function pointer syntax
- And a few more minor features
Compared to ObjC, Swift prefers inlining, direct function calls, vtable dispatch and only as last option: ObjC message passing.
Looks like *, feels like Java
When Swift was released, everyone was quick to point out that the language was almost like their own favourite language. Swift is very obviously a “modern” language in that it is familiar in syntax to currently popular languages – not having to declare the type of variables is an obvious thing to mention. The similarities aren’t altogether deceiving, but they hide some problems with the language – I’ll talk about that later.
I’ve mostly worked in ObjC/Java/PHP/Ruby the last few years, compared to those languages Swift is distinctly most similar to Java. It’s easy to visually parse the code, except for the few syntax features which force you to pollute your variable declarations with code (didSet/willSet, lazy):
// What one might think ivar declarations will look like:
class CustomizeCharacterLogic : ScreenLogic {
var request : RegisterGameRequest
var sharedCreateCharacterData : SharedCreateCharacterData
var customizeCharacterSceen : CustomizeCharacterScreen
var name: String
let filteredCharacterSet : NSCharacterSet
/* ... implementation ... */
}
// What they really end up looking like:
class CustomizeCharacterLogic : ScreenLogic {
var request : RegisterGameRequest?
var sharedCreateCharacterData : SharedCreateCharacterData! = nil
var customizeCharacterSceen : CustomizeCharacterScreen {
get { return screen as CustomizeCharacterScreen } }
var name: String = "" { didSet { name = sanitizeName(name) } }
let filteredCharacterSet: NSCharacterSet = {
let characterSet = NSMutableCharacterSet.letterCharacterSet()
characterSet.addCharactersInString("- ")
characterSet.invert()
return characterSet
}()
/* ... implementation ... */
}
Despite that, my feeling is that Swift’s syntax is a major win compared to ObjC, especially for refactoring when you need to scan a lot of code quickly.
That said, Swift tempts with features that can obfuscate your code a whole lot worse than your typical C macro. I’m talking about custom operators, return type overloads, auto-closures and generics.
Of these, I think return type overloads are the most devious, because they look so straightforward and you’re likely to use them fairly often for type conversion. I haven’t tested it in practice, but it feels like one of those features you’ll curse a lot when you return to code you haven’t used for a long time.
For those not familiar with the feature, it’s essentially the ability to do this:
func doSomething(value: Int) -> Double { return Double(value) * 2.0 }
func doSomething(value: Int) -> Int { return value * 2 }
let foo : Int = doSomething(1); // 2
let bar : Double = doSomething(1); // 2.0
This is all very neat but is best used when the functions invoked are purely functional and well named. Here’s an extremely pathological example, which illustrates the extremes of obfuscation you can reach:
func doSomething() -> Bool {
return startGame()
}
func doSomething() -> Screen {
return nextRegistrationScreen()
}
// The magic of type inference at work:
if (!doSomething()) {
doSomething().open(); // Assuming Screen has a "open()" method.
}
The improvements in practice – Optionals
Let’s talk optionals first. Optionals are popping up all over the place, you can even find it in Java these days. For imperative languages like Java and C++ the main use is to force the developer to really think about the null case. For functional languages on the other hand it’s a fairly essential feature, as it allows short-circuiting a chain of function calls.
ObjC hold a slightly unique position in that unlike most other languages, invocation of nil is harmless. This allows us to avoid the typical Java code:
if (delegate != nil)
{
delegate.notifySomethingChanged(this);
}
And simply write:
[delegate notifySomethingChanged:self];
With the syntax enhancements to Swift’s optionals, we get something just as succinct as ObjC in the simplest case:
delegate?.notifySomethingChanged(self)
The advantage here is that Swift forces this to be explicit, which is a good thing. You can’t do the equivalent to “messaging to nil” without documenting it in the source.
In order to work with optional in more complicated cases we’ll typically unwrap the value using if-let:
if let foo = foo {
/* ... do stuff with foo ... */
} else {
/* ... handle foo is missing ... */
}
return
The problem we get here is that we can’t use guard clauses effectively. To illustrate the concept, this is the same code with guard clauses in Objective-C
if (!foo) {
/* ... handle foo is missing ... */
return;
}
/* ... do stuff with foo ... */
return;
This boils down to wether you use guard clauses or like nesting. I personally find almost no redeeming feature of the latter. In my opinion, guard clauses signals the intent and the main path of the code in a superior manner. In theory we can do the same in Swift, but not without losing the safe unwrap, by invoking the force unwrap operator “!”:
if foo == nil {
/* ... handle foo is missing */
return
}
let nonNilFoo = foo! // Force unwrap
/* ... do stuff with nonNilFoo ... */
return
The difference between the Swift and the Objective-C version is that the Swift version will crash if we forgot the nil check (Swift’s version of NullPointerException?), whereas the ObjC is version is more likely to just act a bit weird. Although being notified is nice, the irrecoverable crash isn’t suitable for all occasions.
Syntax improvements will make the Optionals more palatable, but right now their potential to create live crashes and obfuscate the code outweigh their use in making sure that the code is correct.
Object-like structs/enums
This is one of the best features of the language. Whereas before we would do something like:
CGContextRef c = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(c, kCGInterpolationHigh);
– Swift allows us to add methods to CGContext in order to get this – much easier to remember (and code complete):
let c = CGContext.currentUIGraphicsContext c.interpolationQuality = kCGInterpolationHigh
Furthermore, allowing operator overloading makes it easy to model structs as primitives. For example, it is trivial to write extensions that would allow the following:
let p1 = CGPoint(x: 1, y: 100) let p2 = CGPoint(x: 100, y: 100) let midpoint = (p1 + p2) / 2
It’s certainly possible to do the same thing in ObjC functionality-wise – but it’s more verbose and certainly not elegant:
CGPoint p1 = CGPointMake(1, 100); CGPoint p2 = CGPointMake(100, 100); CGPoint midpoint = CGPointDivide((CGPointAdd(p1, p2), 2);
Making value types in Swift is rewarding and fun.
Strict constructor initialization
This is another feature which seemed like a slam-dunk win. The contorted if ((self = [super init])) { } return self; is something that makes grown men cry, and is probably the single biggest reason why people use fewer classes in ObjC compared to languages like Java and C++. (The ObjC initializer also happens to extremely powerful, but that’s another story)
Swift loves its initializers. The Swift book dedicates 50 pages to explaining the different rules on initialization. The most important rule is that Swift mandates that all variables is to be initialized before calling super.init, and no instance function may be called until after super is called.
So, what do you do if:
- Your variables should be initialized with data that depends on instance variables defined in the parent?
- Your variables need to reference the instance itself in its constructor?
Two methods appear: using lazy variables or optionals. So either:
class Foobar : Bar {
lazy var baz = Baz(delegate: self)
var name : String
init() {
name = "Foo"
super.init()
}
}
Or:
class Foobar : Bar {
lazy var baz: Baz? // There is also the unsafe "Baz!"
var name : String
init()
{
name = "Foo"
super.init()
baz = Baz(delegate: self)
}
}
Both of those have deep impacts on your code. The “lazy” version force you move your initialization from the initializer to the variable declarations. This makes the code less readable, but it also make the actual initialization order less predictable.
The “optional” version require you to unwrap the variable baz every time you use it, even though the actual design of the class intends the variable never to be reassigned or nil!
There is a variant of the “optional” where the variables are declared with “!“, which avoids the need to explicitly unwrap the variables everywhere. Still, since the variable is both declared mutable and nullable, nothing prevents someone to accidentally set it to nil somewhere without the compiler giving you any warnings whatsoever.
Regardless of which strategy you choose, you’ll likely to end up with a lot of variable initialization in your variable declarations – something which seems like a rather unnecessary mess.
In practice, what’s won in simplifying the constructor compared to ObjC is lost and more in having to cluster code before and after super.init(). Initialization might not do anything unsafe, but the complexity introduced by the rules create plenty of other bugs instead.
Immutability everywhere
ObjC does not really support immutability, except for having mutable and immutable versions of many classes. In contrast, Swift follows the trend of many modern languages of making immutability more or less the default.
However, the constructor safety often makes it surprisingly difficult to make instance variables immutable. This is likely to improve in later iterations of the language, but as it stands the language sometimes conspire against your efforts to make things immutable.
This is a common paradox in the language: the language wants to encourage a certain type of coding, but some other rule in the language makes it difficult or downright impossible.
Generics for the win?
Or not.
The implementation of generics in Swift so far has been intensely bug ridden, incomplete and full of performance issues. It might seem odd that the generics is both ambitious, and yet so oddly restricted feature-wise.
I’m not going to go into details about the many things that is either missing or broken in the current implementation. The less said about it, the better.
Closures & functions
Swift uses closures a lot. It also have great trailing syntax for it, making it look (but not work) like a normal block of code.
Unfortunately, target/selector is largely discarded to the scrap pile – except when interfacing with Cocoa. The reason this is unfortunate is because stored closures aren’t scriptable and puts all memory management into the hands of the user of a function, rather than on the implementor.
The difference is that for:
- (void)setUpdateTarget:(id)target action:(SEL)action;
You can be pretty sure that you won’t create any retain cycle by setting an object as a target, whereas with this:
func setUpdateCallback(callback : ()-> Void
You WILL need to make sure that any references to other objects are weakly retained – or suffer the consequent retain cycles.
Since implementing the former deliberately has been made difficult in Swift, we’re stuck with the latter even when it isn’t the best choice. It doesn’t help that Swift – unlike ObjC – has no way to warn on an incorrectly typed selector either.
ObjC -> Swift -> ObjC
It’s time to talk a bit about how converting the code worked in practice.
First round: ObjC -> Swift
The first thing you notice is that conversion is dead slow. Type declarations are in different places, enums need to access raw values, strict initializer orderings and so on.
It’s not really difficult, just takes a lot of time, and you need to work from outside in since an Objective-C class can’t inherit from a Swift class, even if the Swift class is inheriting from NSObject.
This means that you can’t really leverage most Swift features until you’ve converted a fairly large portions of code.
Adding extensions to structs like CGRect and friends helps UI coding brevity and clarity, but this is largely offset by the need for explicit CGFloat-casts on integer variables everywhere.
Losing the .h file is a welcome change, but the downside is that unless you go amok declaring methods “private”, your class is exposing its entire interface to the rest of the project.
The improved syntax on closures is nice, but using closures for stored actions (such as a callback on tapping a button) requires [unowned self] or [weak self] and the resulting code isn’t significantly smaller or easier to read.
A major loss was losing macros, especially since Raw Enums is missing a lot of functionality which might have been fixed with X-macros or similar.
The worst part was working with dynamic data structures. This was simply not practical to do in Swift. The best way here turned out to be converting all the dynamic data to classes and structs. Writing a lightweight class or struct is quicker than with Objective-C, so it was not as bad as it might sound.
Swift -> Objective-C
This was easier than I had expected, given that I used quite a bit of functionality that’s improved in Swift.
In order to make this a bit less onerous, I used macros to handle a few cases that typically cause quite a bit of boilerplate on ObjC, such as non-static class constants and the aforementioned lightweight container classes and structs.
The complexity of variable initialization of objects went away entirely, including quite a bit of code that avoided unnecessary optionals in Swift.
Without the enforced immutability / not nil of Swift, the benefit of doing everything in the initializers didn’t have as much benefit and I could move things to setup methods, often avoiding redefining the init method except where it really made sense to do. In general, the laxness of ObjC allowed more straightforward and simple solutions for object construction and initialization.
But not everything got better: the ease of refactoring went down, and the extra header file is another weight to lug around (even though I like the smaller interface declared by the ObjC header files).
Most importantly for our project, we got our fast compile times and reliable runtime performance back.
And back to Swift?
Will we ever go back to Swift? For this project, the answer is obviously “no”, but what about the future? Nothing says that Swift can’t be a great language eventually, right?
Swift’s problematic runtime
One of the initial claims was that Swift was going to give you much faster runtime performance than Objective-C. Looking at the runtime model, it was obvious that the language would be able to inline or at worst virtually dispatch code. Therefore, the performance ought to be around the speed of C or C++. Imagine people’s surprise when micro-benchmarks showed that the language would run something like 100 to 1000 times slower than Objective-C!
It turns out that Swift made a tiny change to ARC (automatic ref counting) emits. In practice this would emit an extra retain-release pairs for method dispatches in an unoptimized build. The change was to allow Swift to eventually be more efficient with ARC than Objective C – as long as the optimizer ran anyway! As people quickly discovered, -Onone definitely didn’t have an optimizer pass to fix this. In fact, even when the compiler optimized the code, it could not always determine that the +1/-1 pairs weren’t needed. Ouch.
There are indications that with ARC, Swift’ll actually go back to the Objective C way of doing things – which at least has predictable runtime performance.
Aside from that issue, Swift has traditionally been very inconsistent in how and when to inline code. Tiny changes can sometimes grant magnitudes of faster code in a quite unpredictable manner. Swift runtime performance is really a mixed bag. From C performance to something speeds that even Java 1.0.2 would be deeply ashamed of.
It’s not the speed itself that’s the problem, but rather the unpredictability. And yes, this will improve – but when will it be solid enough to rely on? Objective-C is not fastest, but it’s guaranteed to perform exactly the same no matter how the other library is linked.
Swift’s static typing all the way down
An imagined next-gen ObjC language could have had Strongtalk-like static typing with generics, much of the structs-as-objects and simplified syntax of Swift, but with dynamic dispatch at the bottom.
Such a language would not have run into the same performance and feature issues as Swift, because it would leverage what already exists within the Objective-C runtime.
For an example of how ObjC-with-an-easier-syntax would look like, look no further than Eero:
Efforts like this shows that a new statically typed language was not the inevitable choice, making the choice to go for a statically typed language look like a unnecessary gamble than anything else.
My experience shows that the significant amount of effort you have to go through in order to satisfy the strict rules set by Swift mostly nullifies the gains in reliability. The language tries to cater to a functional paradigm, yet oddly does not offer the toolset to do so correctly.
Because of its static typing and type inference, it’s dependent on all those features being complete and correct, unlike a pluggable static type system on top of a dynamic language. This makes the incomplete state of 1.0 much worse as there are – by design – few ways to work around the static type system where it is broken.
With a thin layer over ObjC, we would have kept the highly modular and well understood performance model of ObjC+C, instead of struggling with the growth pains of a new language.
Swift also largely ignores the rich heritage of ObjC, which has made possible a vast array of technologies that are hard to duplicate well in statically typed languages.
Swift is a jack of all trades and master of none. Proponents of the language keeps claiming that eventually it will be great, while we could have had a language that was great today if they hadn’t been set on writing such a different language from Objective-C.
[Discuss at Hacker News or reddit]

Some additional reading for a meaningful Swift vs ObjC discussion: http://blog.jaredsinclair.com/post/98402624705/beyond-objective-c-beyond-swift and http://owensd.io/2014/09/24/swift-experiences.html
LikeLike
Thanks for your terrific review. I very much appreciate your perspective and poignant criticisms. It is rare to get programming language critique from somebody who clearly groks value types, immutability, functional approaches, and all that is good & bad about them (and vs. other paradigms), and all that goes into making them usable. Or not.
Also interesting to note how what you’ve written in places applies to many other popular languages. Those made by people who did not have a strong grasp of programming language theory, and those by folks who made a deal with the devil instead of using their mad plt skillz.
LikeLike
Nice overview. I’m looking forward to more. Very informative.
LikeLike
Great analysis here, and I have to agree that an ObjC 3.0 would have been a much better approach.
You mention that you get more classes with (e.g.) C++ as though that’s a good thing, but why would anyone want more classes?
I can always tell the C++-background ObjC programmers because they have a hundred classes most of which do almost nothing except make it difficult to find things and forcing you to switch between dozens of files all the time when working on one aspect of the app🙂
Strongly agree with the guard clauses comment. A Swift app I just worked on has one section parsing JSON with 12 levels of nesting, all with if let = on each line until the body is then processed. “Arrow Anti-Pattern” I’m sure someone called it.
LikeLike
Many classes are typically not needed in ObjC, but there’s also a temptation in ObjC not to add a class – even when it’s the right thing to do – just because it is so painful to do so.
LikeLike
Right.
Maybe I notice that less due to snippets and class templates, I don’t believe I hold back from creating classes whenever I want to.
The Swift init process you’ve described here is v painful though.
LikeLike
With the rise of ARC and properties, the objc init is fairly painless now; the if () isn’t needed at all, as call on nil will just do nothing.
– (instancetype) init
{
self = [super init];
self.myProperty = 123; // equivalent to [self setMyProperty:123], do nothing on nil
return self;
}
Will do what it is intended to do🙂
(and no, you shouldn’t have side effect in setter/getter, that is plain wrong, so using them in init should always be ok)
LikeLiked by 1 person
For better refactoring and easier class creation, drop XCode and use AppCode instead. I never notice .h files anymore because AppCode manages those for me. That’s two of Swifts benefits deleted by just using a proper IDE🙂
As for the other benefits, I have a hard time seeing them in my daily work.
For example, I perfectly understand the motivation behind wanting to get rid of nil pointers, but I’ll take an NPE over the horrid ! ? mess in Swift any day of the week. In fact I will venture a guess that a very large percentage of programmers will just try various combinations of ? and ! until the compiler shuts up, and then be happy with that. I don’t think it’s going to end up creating better code, only harder to read code.
LikeLiked by 2 people
Check this out: http://iswift.org – yep, an Objective-C to Swift automated converter.
It’s a Mac app but you can sure try it online, for free (http://iswift.org/try). Started as a personal project. But it has proven to be rather useful. So, here you are!
Looking forward to your feedback!🙂
LikeLike