How and why Anghami is transitioning to Swift
Since its launch in 2014, Swift has, rightfully, gathered lots of enthusiasm from the iOS development community. By now it’s clear and widely admitted that new projects are to be written in Swift. But what about projects already written in Objective-C? While transitioning to Swift is easy for small projects, it’s a daunting task for big codebases like Anghami’s iOS app.
Started in 2011, Anghami grew to acquire millions of users and its team worked to implement hundreds of new features to quench the thirst of its users. Today, Anghami’s iOS codebase consists of more than 1,000 classes — without counting its dependencies.
Why Anghami is moving to Swift
When Apple announced Swift 4 at WWDC 2017, we decided that it’s time to start transitioning the project to Swift for multiple reasons.
- The Swift programming language makes it a better and stabler candidate than Objective-C. This is an entirely different subject, you can read on why Swift is better here and here.
- The majority of new libraries and dependencies are written Swift. Similarly, libraries written in Objective-C that were once widely used are now deprecated and abandoned.
- After 4 years of development, Swift reached a certain maturity and is less likely to introduce breaking changes like Swift 2 and 3 previously did.
- Objective-C might or might not die. But if it does, you don’t want to die with it or lose one year rewriting your project.
- Most importantly, we’re building Anghami for the long-term and have big projects for it. Big projects require skilled and motivated people who, in ten years from today, will be more proficient in Swift than in Objective-C. By keeping our codebase in Objective-C only we would be falling behind technically and competitively in the hiring market.
How we’re gradually transitioning
A journey of a thousand classes begins with a single class. Anghami is a dynamic platform, updates are shipped on average every ten days with lots of new features.
Anghami iOS project = (iOS app + iMessage app + WatchOS app + Apple TV app + Carplay app) x 2*
*One “Release” target and one “Beta” target
Stopping development for 1 year to rewrite the app was not an option because our users don’t care whether the app is written in Objective-C, Swift, BASIC or COBOL. We needed a way to transition while continuing to ship our updates. We decided to proceed with three (seemingly) simple rules:
- Write new features / classes in Swift.
- When a class needs to be refactored, it’s refactored in Swift.
- New app extensions (WatchOS, iMessage…) are written in Swift.
On September 22nd 2017, the first Swift class — born from a refactor (Rule #2 above) — made its way in Anghami’s iOS project. Today, about 8% of the project is in Swift.
Integration Part 1: Mixing Swift & Objective-C classes
On the morning of September 20th 2017, our product manager Salim Batlouni had great ambitions for a feature that has been unused for a long time — Anghami’s sleep timer. To satisfy this ambition, we needed to refactor this class. It was time, Swift was on its way.
Transitioning to Swift requires Objective-C classes to be made available to Swift classes and vice-versa. This is how it’s done:
- Inside your Objective-C project, create a Swift class.
- When prompted “Would You like to configure an Objective-C bridging header” press on “Create Bridging Header”. Your Bridging Header will appear in XCode’s file navigator as “#TargetName#-Bridging-Header.h.
A Bridging Header is a header files that exposes your Objective-C classes to Swift. For example, if we want to use our Song object in Swift, we’ll need to import Song.h in the Bridging Header.
3. In your target’s project file > Build Settings > Define Module, change the value to YES.
4. Now, to use Swift classes in Objective-C: In build settings, search for “Product Module Name”. Write this name down, let’s say “Anghami”.
5. Finally, go your Objective-C class and import #ProductModuleName#-Swift.h. Example: Anghami-Swift.h. Once imported, you can use your Swift objects in Objective-C.
And we’re all set! Or that’s what we thought… Big projects are always different. Since we have one “Release” and one “Beta” target, Beta did not recognize the “Anghami-Swift.h” header file because its “Product Module Name” was not “Anghami” but “Anghami_Beta”.
So we repeated steps #3 to #5 for our Beta target. To avoid duplicates imports for Anghami-Swift.h and Anghami_Beta-Swift.h along their “if” conditions, we created a new header file that would take care of importing the correct header for each target.
6. One last note: To expose a Swift function to Objective-C, it needs to be preceded by “@objc”.
Note: XCode’s autocomplete deals really badly with mixed Objective-C and Swift code. Newly added declarations in Objective-C are not recognized by Swift (and vice-versa) throwing errors when we try to use them. Once we build the project, the errors disappear and the new declarations are detected by Xcode’s autocomplete.
The project finally built on Release and Beta with the new Swift-implemented feature and was shipped to the App Store. Our seven-years-old codebase felt young again and the users were happy.
Integration Part 2: Mixing Swift and Objective-C libraries
Anghami’s CTO, Elie Habib, had a plan in mind that would indirectly require us to implement Swift in all aspects of the app. On October 6th 2017, a meeting was held at Anghami’s offices to pave the way for Sockets that would improve interactions between Anghami’s apps and servers. This would allow us to implement a new generation of features and make the app experience even smoother. We settled on using SocketIO. Its iOS client is completely written in Swift. No problem, we already implemented Swift! But again, big projects are always different.
We usually use CocoaPods to integrate libraries. We added SocketIO to CocoaPods. Since it was in Swift and Swift does not support static libraries, we had to add “use_frameworks!” so CocoaPods can correctly install and bundle the library as a framework into the project. We ran “pod install” to download SocketIO, which worked.
However, when building the project, it failed. Realm.h, which is used throughout the app to manage data and also installed using CocoaPods, was not found. Fixing this issue caused other Objective-C libraries installed with CocoaPod to fail. It turned out that the issue was that “use_frameworks!” that we just added was breaking some Objective-C libraries. It became clear that going forward we’ll need a different dependency manager for Swift. For this purpose we chose Carthage.
Once Carthage was setup and our Cartfile ready, a simple install command (carthage bootstrap — platform iOS) downloaded the Swift SocketIO framework along its Starscream dependency (Also in Swift).
At this point the project successfully built and we integrated sockets in different parts of the app. As dictated by Rule #1 for new features, all our sockets classes were written in Swift. That was Anghami’s first major feature written in Swift.
Integration Part 3: Rewriting existing classes
Part of moving an existing Objective-C project to Swift is rewriting existing classes in Swift. However, there is one limitation to consider:
Swift classes cannot be subclassed by Objective-C classes but Objective-C classes can be subclassed by Swift classes.
Most projects, specially big ones, heavily rely on subclassing. That means that to rewrite existing classes in Swift, we’ll have to work our way from the subclass up to the superclass.
Example: If you have two Objective-C classes: Song (Superclass) and CachedSong (Subclass of Song) you’ll need to rewrite CachedSong in Swift first then rewrite Song.
Anghami’s Swift foundation has been built. Our objective is to move the whole project to Swift over the next few years while continuing to ship great features to our millions of users on a weekly basis.