At Halodoc, our team is always exploring ways to enhance the user experience and deliver the best possible healthcare services. We are excited to share that we have started a transition, migrating from NSCoding to Codable for our iOS platform. This migration represents a crucial step forward in our continuous efforts to improve our app's performance, reliability, and efficiency.
In this blog post, we will take a closer look at the process of migrating from NSCoding to Codable and discuss the numerous advantages this change brings to our app and ours users. Additionally, we will also address the challenges and share the solutions to resolve issue during this migration.
NSCoding, while a reliable serialization mechanism, comes with certain limitations that can hinder the development process. One of the primary limitations is its tight coupling to the class implementation, requiring manual encoding and decoding methods to be implemented within the class itself. This can lead to code duplication and maintenance challenges, especially when dealing with complex data models. Additionally, NSCoding is limited to binary encoding, which can make it challenging to integrate with external systems or APIs that expect different data formats, such as JSON.
Another limitation of NSCoding is its lack of support for versioning and evolution of data models. This means that modifying the structure of the data model can break compatibility with existing encoded data. This becomes particularly problematic as apps evolve over time and require changes to the data model. Handling versioning and data model evolution manually can be error-prone and time-consuming. Therefore, to overcome these challenges and ensure a more flexible and efficient serialization approach, we recognize the importance of migrating to Codable, which provides better support for versioning and simplifies the handling of data model evolution.
By adopting Codable, it makes it easier to convert Swift objects into a format that can be stored or shared with other systems, like JSON or property lists, and vice versa, without the need to write custom serialization and deserialization code.
By using Codable, developers can automatically encode (convert to a structured format) and decode (convert back to Swift objects) their data without writing a lot of complex code. This saves time and effort. Here some points that why we are migrating to codable:
- Simplified Data Encoding and Decoding
Codable simplifies the process of encoding and decoding Swift objects, eliminating the need for manual implementation. By adopting Codable, you gain automatic support for encoding and decoding operations, reducing the amount of code you need to write.
- Time and Effort Savings
Migrating to Codable saves development time and effort. With Codable, you no longer need to write boilerplate code for serialization and deserialization, allowing you to focus on other critical aspects of your app.
- Type Safety and Compiler Support
Codable leverages Swift's type safety and compiler-generated code to ensure that the data is correctly mapped to Swift types during the conversion process. This enhances code reliability, reduces runtime errors, and improves overall efficiency.
- Improved Code Readability
Codable provides a clear and expressive way to define the encoding and decoding logic for your Swift types. By conforming to Codable protocols, your code becomes more readable, making it easier for you and other developers to understand and maintain the data conversion operations.
- Snake Case Support
With Codable, when used with snake case key strategies, seamlessly handles conversions between snake case JSON keys and Swift property names. This eliminates the need for manual mapping between different naming conventions, further simplifying the encoding and decoding process and enhancing code readability.
Need for Migration to Codable
Previously, our development process involved working with both NSCoding and Codable, which presented several pain points and complexities. The coexistence of these two encoding and decoding approaches introduced challenges such as:
- Encoding/Decoding Approaches
We had to handle NSCoding's manual implementation of encoding and decoding methods alongside Codable's automated serialization and deserialization. This resulted in inconsistent code patterns and increased complexity.
- Code Duplication
We often encountered duplicated encoding and decoding logic for the same data models, as NSCoding and Codable required separate implementations. This redundancy increased development effort, introduced the potential for errors, and made code maintenance more challenging.
- Compatibility Issues
NSCoding and Codable followed different naming conventions for keys. NSCoding relied on camel case, while Codable adhered to snake case or other conventions. The mixture of both approaches led to naming conflicts and data mapping issues, causing bugs and headaches during development.
- Increased Complexity
Managing both NSCoding and Codable simultaneously added complexity to our development process. We had to understand and handle two distinct encoding and decoding paradigms, increasing the learning curve and making code comprehension and maintenance more difficult.
Migration Process from NSCoding to Codable
Here we will provide a step-by-step overview of the migration process, covering each stage from the initial analysis and planning to the actual implementation, testing and also illustrating how we successfully transitioned from NSCoding to Codable.
- Analysis and Planning
It is important to analyze the existing NSCoding implementation and identify all the data models used through the application. This involves reviewing the codebase to understand how each data model is currently being encoded and decoded using NSCoding.
Check the compatibility of the data models with Codable. Make sure they are structs or classes and that their property types can be encoded and decoded using Codable.
And also consider any nested structs or classes within the data models and ensure they also conform to Codable. This ensures a seamless encoding and decoding process for the entire data hierarchy.
- Mapping Strategy
Design a mapping strategy to convert NSCoding properties to Codable equivalents. Determine how each all property will be encoded and decoded using Codable protocols.
- Update Data Models
Update each data model by adopting Codable protocols. Modify the model declarations to conform to Codable and implement the required encoding and decoding methods.
Here's an example of how a data model can be updated from NSCoding to Codable:
After migration (using Codable):
In the updated Codable version, the ProductList class has been transformed with conforming to the Codable protocol. The encode(with:) and init?(coder:) methods are no longer needed, as Codable automatically handles encoding and decoding based on the property names and types. The migration to Codable simplifies the code by removing the NSCoding-specific methods and replacing to the conventions provided by Codable.
During our migration process, we encountered crashes when trying to retrieve and store data using NSKeyedUnarchiver and NSKeyedArchiver.
This is the original code snippet retrieve the data using NSKeyedUnarchiver.
The crash occurs when using NSKeyedUnarchiver.unarchiveObject to decode a Codable class because Codable and NSCoding are different protocols with different requirements. NSKeyedUnarchiver is designed for NSCoding, while Codable uses JSONDecoder for decoding. In this case, we replace NSKeyedUnarchiver with JSONDecoder().decode.
This is the original code snippet archiving the data using NSKeyedArchiver.
The crash occurs when using NSKeyedArchiver.archivedData to encode a Codable class because Codable and NSCoding are different protocols with different requirements. NSKeyedArchiver is designed for NSCoding, while Codable uses JSONEncoder for decoding. In this case, we replace NSKeyedUnarchiver with JSONEncoder().encode.
Overall, the migration to Codable not only addresses the challenges of NSCoding but also empowered our developers with a more efficient, standardized, and future-proof approach to handling data serialization.
By adopting Codable, we have embraced a modern and standardized approach to encoding and decoding data, providing a more streamlined and efficient development experience. This migration enhances productivity, improves code quality, and sets the foundation for a scalable codebase.
Scalability, reliability, and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resumé at firstname.lastname@example.org.
Halodoc is the number 1 all-around Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke. We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 3500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allow patients to book a doctor appointment inside our application. We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek, and many more. We recently closed our Series C round and In total have raised around USD$180 million for our mission. Our team works tirelessly to make sure that we create the best healthcare solution personalised for all of our patient's needs, and are continuously on a path to simplifying healthcare for Indonesia.