5/26/2023 0 Comments Obj c to swift converterPhase 1: Vanilla ConversionĪs mentioned before, the first conversion step we would take is to simply transcribe each file to a Swift version, preserving types, superclasses, and frameworks. As a result, the project already had a well-abstracted network layer: different functionalities were split into their own managers, for example, user registration and invoice payments. This application is service-centric, meaning that the client’s API layer determines the majority of the functionality, while little is possible ‘offline’. Once we were happy that all was working as it did before, we did a ‘Swiftify-ing pass’, refactoring code to use modern features, as well as filtering out all the leftover Objective-C relics for example, by converting classes to structs where possible, decoupling objects from `NSObject`, and removing all annotations. However, we didn’t want to introduce an additional source of bugs during the conversion process, and decided to first convert the project to a ‘Swift copy of the Objective-C implementation’, and make sure that regression tests pass. While converting, it can be very tempting to refactor functionality in parallel in order to use Swift’s modern features, such as enums with associated values or property observers, since these ultimately reduce lines of code and improve readability. Aside from that, we were using the Xcode’s Assistant editor to transcribe each line to Swift by hand (it’s quicker than you’d think, and it really boosts your Swift API knowledge!). Swiftify allows free conversion of a few lines at a time, which was perfect since we already decided not to tackle the entire project in one go. Since there isn’t a magical Xcode conversion tool (yet?), we decided that we could use some help with the more mundane conversion tasks, such as view controller outlets and control flow. We settled on a combination of ‘per-layer’ and ‘per-functionality’ conversion, since the thought of doing it all in one shot and dealing with days of compiler issues seemed potentially detrimental to our health and sanity. But where do you start? Is it best to convert everything at once and then solve the integration issues, or is it better to convert one piece at a time while ensuring the project compiles and runs between stretches? So the decision was made: we wanted to bring the project up-to-date by converting all the Objective-C code, and replacing old frameworks along the way if possible. For a project that was going to be in active development for the next years, we really wanted to rid ourselves of annotations and `NSObject` subclasses if not for purity, then just for peace of mind. Not only that, but the compiler insisted on overriding the `initWithCoder` initializers, causing a boilerplate mess. For example, we were using Overcoat for network and parsing (a wrapper that uses AFNetworking and Mantle), meaning that new API models still had to subclass `MTLModel` and use `NSObject`-backed field properties structs and native Swift types were not an option. The crux comes down to when you want to use Swift’s modern features for functionalities that depend on existing ObjC systems. The urgency to convert the project would not have been as aggravating had Xcode done this job correctly. The autocomplete is useless at best, and the compiler red-marks all bridged code as ‘unknown receiver / no visible until you compile and run, making it a challenge to narrow down syntax errors or get the function signatures correct. While bridging is a quick way to interoperate between the two languages, it is unfortunately let down by numerous bugs in Xcode. Seems simple enough, but you can already see the inconveniences. Since most Swift developers came off Objective-C, working with both in the same project is often common practice: use the provided bridging headers, annotate Swift methods and properties with and ensure that you use ObjC-compatible types. Although this is initially satisfactory, it became apparent that this project would not remain sustainable throughout the next few years. Integration with older functionalities was done via the various bridging technologies provided by Xcode. Once Swift 2.0 was out, new functionalities were implemented in the new language. Although excited to explore and develop in this new language, we felt that it would be best to wait for the next iteration - such that our developers could get accustomed to it, and for Apple to iron out any early adopter issues. A long-standing client of ours, a commercial market leader, has had their business-critical application in active development since 2014, which you’ll remember is when Swift first made its appearance.
0 Comments
Leave a Reply. |