Swift is already 3 years old, but still changing and improving a lot. Its unique features are starting to change the developers’ mindset, moving away from the old Objective-C concepts to some new ways of thinking with value types, protocols, generics, immutability. It’s a pitty and a lot of unlocked potential if you decide to go with Swift, but still bring the coding style learned from Objective-C, i.e. develop an Objective-C app in Swift. You are better off with Objective-C in those cases. In this post, I will explore some design considerations and decisions when doing a Swift app, with lessons learned from the Swift projects I’ve worked on as well as the great iOS community.
Value types over classes
This is probably the most common shift that is triggered by Swift’s features. As Dave Abrahams proudly proclaimed Swift as the first protocol oriented programming language, providing some pretty strong arguments why you should not use classes that much, developers started shifting towards the more lightweight value types like structs and enums, compensating the lost functionalities of classes with the awesome protocols in Swift. Although I strongly recommend watching this great talk, I will quickly sum up the arguments against classes, brought up by Abrahams’ alter ego Crusty.
The first one is the consequence of the fact that classes are reference types. The possibility of changing a value in one object, unexpectedly changes the value of another one (because they share the same memory address) is one common source of bugs, not only on iOS, but in software development in general. It’s best described with a short example:
var firstMobile: Developer = Developer("iOS") var secondMobile = mobile secondMobile.expertise = "Android" // now the iOS developer magically knows Android as well
This happens a lot, especially when the objects are passed around between screens or functions.
Class inheritance is too intrusive
The second argument is about the all the things that you might not need, but you get, just because you’ve decided to create abstract classes to keep the DRY principle alive. You need to initialise stored properties that you might never use in your class, have a lot of unused methods and so on. Also, by having abstractions on multiple levels, you are starting to lose track of all the methods and their overriding implementations. The classes seem to be too dependent on their superclasses. Also, you have that limitation of only subclassing one class and if you need functionality of multiple abstract classes, which one would you subclass?
Lost type relationships
Usually, with classes we are sometimes forced to downcast to a subclass to access the method we want to call – and this is the place where some important type relationship had been lost.
Having all this problems with classes in mind, it seems better to use value types like structs in combination with protocols. With protocols in Swift, you can do pretty much everything that you can do with classes. Something that sets Swift apart from other programming languages with similar features like protocols in Objective-C or interfaces in Java are protocol extensions. With protocol extensions, you can define default implementations of some methods, which other protocols inheriting from that protocol or structs implementing it will get those implementations for free. Another cool thing that can be done with protocol extensions is retroactive modelling – the ability to extend types for which you don’t have access with your functionality. Basically, if we want to replace some old implementation of our code with something that let’s say Apple provides in their SDK, if we are using protocols we can extend their implementation with our protocol, implement the required methods and just easily replace it in our code.
MVVM over MVC
The problem of massive view controllers doing all sorts of things in iOS development is already known for a long time. One of the patterns that tries to solve this problem is the Model-View-ViewModel pattern. The key difference between the two patterns is that in MVVM, the presentation logic (like transforming some values before sending them to the view for presentation / network requests etc.) is done in a separate type called ViewModel. There are few benefits with this change:
– the controllers are now much smaller, containing only the coordinating work that they should do.
– view models are independent of the controllers and can be plugged in anywhere where their functionality is needed.
– having the above in mind, it’s much easier to test the view models. Since they are now independent of the controller, they can be instantiated standalone in the unit tests.
MVVM works really good with some binding mechanism, which will update the view model and the view, when the model containing the data changes. And here’s where Functional Reactive Programming comes into the game. And now, with Swift having all of those great functional features like immutability, value types, first class functions, pure functions, function chaining and composition, currying, map, reduce and filter, going with FRP in Swift is a much easier decision. There are some really good libraries in Swift for FRP, like Reactive Cocoa, RXSwift and ReactKit, so MVVM + FRP over standard MVC might be a good option.
Avoiding Objective-C if possible
Although Apple has provided really good interoperability between Objective-C and Swift, still if you are using frameworks developed in Objective-C, you are a limited to only using the common features of the two languages in your Objective-C subclasses (in the Swift project). For example, you can’t use generics, nor protocol extensions, tuples, Swift enums and so on. That dependency also enforces the Objective-C coding style throughout the project, losing the chance to get the most of Swift’s most powerful features.
Of course, this is not always possible – let’s say you have a framework that you’ve developed in Objective-C for many years and it would not be that easy / feasible to start all over again just to get to use protocol extensions or generics. But at least, you should adjust your Objective-C framework as good as possible for Swift usage. For example, you should add nullability identifiers, so the variables are not just implicitily unwrapped in your Swift code. Also, you should adopt lightweight generics, so you know the types of objects in your Swift arrays.
Still, if you have a choice between sticking to legacy Objective-C framework and creating new one in Swift, I would go with the second one. It might be more expensive at the beginning, but it would be much easier for you in the future. Your app architecture will not be influenced by Objective-C concepts and you will be able to better explore and utilise the Swift concepts.
This was just a short overview of what one might stumble upon when starting their first Swift project. Of course, there are also many other things, factors and decisions that need to be made when starting out a Swift project, but I consider these three the most important ones. Especially the first one, value types with protocols is something really great and it would be a pitty if it’s overlooked in your Swift projects.