An Objective-C to Swift transition

Guillermo Lechuga
Tech at PromoFarma by DocMorris
5 min readMar 12, 2019

I’ve been coding Objective-C for quite a few years, and felt really comfortable with it. I was happy putting my code into brackets. I even managed to have a good handle on all the retain and release stuff we were forced to use before Apple introduced ARC with iOS 5.0.

Maybe that is what caused me to postpone my debut as a Swift developer. Now that we’ve been migrating our project to Swift I see all the gains that I had been missing for such a long time. However not everything has been easy while having to combine both languages during the transition process. This post will cover some of the decisions taken and issues encountered during this process.

An iterative and incremental approach

Our PromoFarma iOS project was built with Objective-C under a VIPER architecture, which hides all the implementations behind protocols for the layer boundaries and interactions. There are a lot of resources talking about the many benefits of clean architectures, but I want to highlight one: layer isolation.

This layer isolation allowed us to plan the transition layer by layer. Converting each layer at a time is actually an iterative and incremental approach from the feature perspective. We were been able to face the issues one at a time and also test each feature with less changes to check during the process.

It should also be pointed out that, thanks to this approach, we kept releasing versions on a regular basis.

Swift at half its potential: Objective-C compliance

In order to follow the incremental process, we kept all the layer boundary protocols in Objective-C. This way, all the Objective-C code can remain basically intact. This, of course, meant that all the new Swift code needed to be Objective-C compliant and had to conform to its protocols.

Thus the first Swift implementation cannot take advantage of some of its features, like structures, tuples, generics or advanced enumerations. The good thing about that is that it helped us follow the initial migration plan.

To nil or not to nil

When coding in Objective-C one runs the risk of getting used to considering nil as a valid value to work with. Yes, when a value that shouldn’t be nil is nil, the app doesn’t crash. But, nor does it work as expected unless you deal with it.

One good thing about Swift is that it forces you to decide whether a value can be nil or not while coding. So, deciding the nullability of everything is something that made the whole migration a little slower than just translating one language into another. But, in the end, it turned into a more reliable app.

Bye bye Mantle & AFNetworking

Migration starts with the Entity layer since it is the simplest and less dependent one, and here came the first big issue. We were using Mantle, and it felt like keeping it in Swift would have been like going against the flow.

So, we decided to implement Swift’s built-in Codable protocol, and here came the second big issue. As we were using AFNetworking in our Data layer, the process of deserializing Codable objects wasn’t straightforward. Then we took the plunge and started migrating the Data layer with Alamofire.

It was a good decision in the mid term, but forced us to change slightly the incremental approach and migrate the Entity and Data layer both at the same time. It wasn’t also just a language translation, but some changes had to be made to each file.

Extensions… extensions everywhere

After these two first layers migration, the next ones have been straightforward. Not just because the Presenter, Interactor and Routing layers are quite simple, but also because the team was gaining confidence with the language itself.

Something notable is that, in particular in the Presenter Layer, we’ve been able to organize the code in an extension for each of the protocols it conformed, providing a lot of code readability and a better organization.

Hello generics

One thing I’ve always missed in Objective-C, from my experience as Android developer, has been the generic types. Well, here in the View Layer we decided to gift ourselves with the first one of the project.

class ViewController<ViewType: UIView>: UIViewController {    var _view: ViewType! {
return view as? ViewType
}
override func loadView() {
super.loadView()
view = ViewType.self.init()
}
}

It’s a really simple implementation, but it definitely made me love Swift if I hadn’t already at this point.

Protocol Oriented Programming

While migrating some common objects of the View Layer we realized that some classes could clearly be optimized if we used Protocol Oriented Programming (POP) instead of Object Oriented Programming (OOP). This is a huge paradigm change for someone who has always been programming in OOP.

Swift Protocols are similar to Objective-C’s and Java interfaces, but its key feature is that they can be extended with default implementations.

We’ll go in depth in future posts, but I’d like to mention two key benefits of POP over OOP.

First, one doesn’t have the temptation of creating a massive BaseClass with a lot of method implementations that most of its children will probably never use. Instead you create a protocol and its extension for every feature group, and every subtype conforms to those protocols.

Second, those protocol implementations can be conformed to by several types from different hierarchy trees. For example, our UICollectionViewCell and UITableViewCell subclasses share some implementations thanks to its protocol conformance.

protocol BaseViewCell {    static var cellIdentifier: String { get }}extension BaseViewCell {    static var cellIdentifier: String {
return String(describing: type(of: self)) + “_identifier”
}
}class ItemTableViewCell: UITableViewCell, BaseViewCell {}class ItemCollectionViewCell: UICollectionViewCell, BaseViewCell {}

Removing Objective-C protocol inheritance

After having migrated all the layers, only the old Objective-C protocols remained, but were conditioning a lot of Swift code. When we changed them to Swift protocols, we could finally get rid of those ugly NSNumber and Int32 (Swift translation for Objective-C’s int type). We could also remove some function overloads by using default attribute values on one single function.

What’s next

Now that the whole project has been migrated to Swift, we can start using more advanced features: structures, string enumerations, computed properties, generics, closures, functional programming, protocol orientation… Who misses code in brackets? Not me.

--

--