Encoding Custom Types with Encodable In Swift 4

Photo by  Steve Johnson  on  Unsplash

This post is the third of a ten-part series called Exploring Codable and Core Data in Swift 4.

Encoding Custom Types Automatically

To be consistent with our previous posts in the series, we’re going to continue to use The Movie Database API for this series, because it will provide the best real-world example for using Codable in your own application(s). For instance, let’s start with a JSON object representing a favorited item in a user account’s favorites list—say, Favorite:

Action: Genre
   "media_type": "movie",
   "media_id": 500,
   "favorite": true

An account favorite model consists of a media_type, media_id, and favorite. Here’s an example of this model in Swift:

Action: Genre
    struct FavoritedMedia {
   let id: Int
   let type: String
   let isFavorited: Bool

As we said in the previous post, because Int, String and Bool already conform to Codable, by having FavoritedMedia conform to Encodable, we get an automatic conformance that satisfies all requirements of Encodable:

Action: Genre
    struct FavoritedMedia: Encodable {
   let id: Int
   let type: String
   let isFavorited: Bool
   // The Encodable method encode(to:) is implemented by default.

Encoding Custom Types Manually

In the example below, the Movie structure is extended to conform to the Decodable protocol by implementing its required function, encode(to:):

Action: Genre
    extension FavoritedMedia: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(type, forKey: .type)
        try container.encode(isFavorited, forKey: .isFavorited)

Now that we’ve gone over how to encode these values 1:1, let’s see how we can use the power of the Swift enum to make our logic a bit more safe. Next, we’ll convert our type property into a nested enum called FavoritedMedia.MediaType:

Action: Genre
    struct FavoritedMedia {
    let id: Int
    let type: MediaType
    let isFavorited: Bool

extension FavoritedMedia {
    enum MediaType: String {
        case movie
        case tv

extension FavoritedMedia: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(type.rawValue, forKey: .type)
        try container.encode(isFavorited, forKey: .isFavorited)

Decoding Custom Types with Decodable in Swift 4

Photo by  Steve Johnson  on  Unsplash

This post is the second of a ten-part series called Exploring Codable and Core Data in Swift 4.

Conforming to Codable

There are two ways of implementing Codable on your own types—automatic and manual declaration. Both of which are essential in understanding to proper serialize data to and/or from any Swift type. First, we’ll go over the fundamentals for Codable conformance:

  • Many standard library types conform to Codable by default, such as StringInt, and Double

  • A few Foundation types also conform, such as DateData, and URL

  • Collection and mappable types, such as Array,  Dictionary, and Optional automatically gain conformance to Codable when their contents also conform to Codable

  • When a custom type consists of properties which all conform to Codable, then the custom type automatically conforms as well with proper declaration

  • Custom Codable types may be defined as either structs or classes

Decoding Custom Types Automatically

Next, we’re going to use The Movie Database API for this series, because it will provide the best real-world example for using Codable in your own application(s). For instance, let’s start with a JSON object representing a movie genre—say, Action:

Action: Genre
   "id": 28,
   "name": "Action"

A genre model consists of an id and name. Here’s an example of this model in Swift:

Action: Genre
    struct Genre {
    let id: Int
    let name: String

As we said above, because Int and String already conform to Codable, by having Genre also conform, we get an automatic conformance that satisfies all requirements of Encodable and Decodable:

Action: Genre
    struct Genre: Codable {
    let id: Int
    let name: String
    // The Codable methods init(from:) and encode(to:) are implemented by default.

The next example is for a larger JSON object representing the movie, Aquaman. Let’s assume this movie object, for the time being, is a simplified data model—only representing the id, title, tagline, and genres.

Action: Genre
    struct Movie: Decodable {
    // Int and String both conform to Codable.
    let id: Int
    let title: String
    let tagline: String
    // The Decodable method init(from:) is implemented by default.

    // Movie is still decodable after adding an Array of Genre, which is also decodable.
    let genres: [Genre]

Coding Keys

In order to support manual decoding or encoding, Codable needs to know which data attributes should be transformed into Codable types, such as Int, Float, Double, Bool, String, etc.

Codable types can declare a nested enumeration called CodingKeys which conforms to the CodingKey protocol. When this enumeration is declared, the cases listed represent the properties which should be encoded or decoded from the external data to your codable type.

Properties can be ignored from encoding or decoding by omitting their key from the enumeration declaration.

Note: When omitting properties, a default value must be provided in the initializer to support automatic Codable conformance.

Because most languages have their own naming conventions, we can't assume that a given key from JSON should be the exact property name in its Swift representation. Thankfully, CodingKey also supports RawRepresentable which allows our properties to follow the Swift API Design Guidelines, while the raw value can represent the JSON key.

The example below uses alternative keys for our Movie model.

Action: Genre
    struct Movie: Decodable {
    // Int, String, Date, and Double all conform to Codable.
    let id: Int
    let title: String
    let tagline: String
    let releaseDate: Date
    let voteAverage: Double
    let voteCount: Int

    // Some JSON keys should be renamed to follow the API Design Guidelines.
    enum CodingKeys: String, CodingKey {
        case id
        case title       = "original_title"
        case tagline
        case releaseDate = "release_date"
        case voteAverage = "vote_average"
        case voteCount   = "vote_count"

Decoding Custom Types Manually

Even though codable types handle both encoding and decoding, we should be explicit about the functionality we would like to expose when implementing the protocol(s) on our own types. Therefore, if our custom type will only be used to decode values, we should conform to Decodable instead of Codable; we should conform to Encodable when we only need to encode values as well.

In the example below, the Movie structure is extended to conform to the Decodable protocol by implementing its required initializer, init(from:):

Action: Genre
    extension Movie: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(Int.self, forKey: .id)
        title = try values.decode(String.self, forKey: .title)
        tagline = try values.decode(String.self, forKey: .tagline)

        // We decode the value to String, then transform into the desired Date type.
        let releaseDateString = try values.decode(String.self, forKey: .releaseDate)
        releaseDate = releaseDateString)

        voteAverage = try values.decode(Double.self, forKey: .voteAverage)
        voteCount = try values.decode(Int.self, forKey: .voteCount)

Note: For more information about the container types used when customizing the encoding and decoding process, see KeyedDecodingContainerProtocol and UnkeyedDecodingContainer.

Getting Started with Codable in Swift 4

Photo by  Steve Johnson  on  Unsplash

This post is the first of a ten-part series called Exploring Codable and Core Data in Swift 4.

What is Codable?

Apple's Foundation framework provides two protocols for encoding and decoding objects in Objective-C: NSCoding and NSSecureCoding. The introduction of Swift 4 has brought us two new protocols to handle data encoding and decoding: Encodable and Decodable. Collectively, these protocols are known as Codable.

Codable is a type alias for the Encodable and Decodable protocols:

Codable Definition
    /// A type that can convert itself into and out of an external representation.
typealias Codable = Decodable & Encodable

Now, what exactly is an external representation anyway? These representations can consist of property lists, JSON, XML, protobuf, and other flat binary representations. For this series, we'll be using JSON because it's a common way of sending or receiving data over a network connections.

In our next post, titled Decoding Custom Types with Decodable (coming soon), we'll discover how to turn JSON objects into Decodable data models automatically and manually in Swift.

Stay tuned!

Exploring Codable and Core Data in Swift 4

Photo by  Andrea Casali  on  Unsplash

Photo by Andrea Casali on Unsplash

Recently, I had asked the iOS community on LinkedIn and Twitter if creating a framework which allowed Core Data to support Codable in Swift and Objective-C would be valuable:

Ever since Codable was introduced in Swift 4 / Xcode 9.0, I’ve been researching ways to combine the two services to provide an iOS framework to reduce their complexities, while improving maintenance and scalability. Most iOS applications these days are data-driven and receive their data from server-side API calls, process the response into their own data structures, and display that data to the User Interface.

Diagram displaying a common iOS application architecture when sending/receiving from a web server | © 2018 by PXPGraphics, Inc. All rights reserved.

To simplify this data flow, the data, caching, and persistence logic should be abstracted into their own layers within the application architecture — this is the purpose for the proposed framework, called Badger(?), which I hope to release in 2019. For example, some of the goals of the proposed framework include:

  • JSON serialization

  • Model validation

  • Object graph management

  • In-memory caching

  • On-disk persistence

  • Logging and error handling

Diagram displaying similarities and differences in responsibilities between Codable and Core Data | © 2018 by PXPGraphics, Inc. All rights reserved.

Due to the many responsibilities of this framework, I’ve decided to break down the components into this blog series, titled Exploring Codable and Core Data. To help lower the barrier of entry for readers, I’ve decided to focus on a few essential topics per post. The series will consist of the following topics:

  1. Getting Started with Codable

  2. Decoding Custom Types with Decodable (coming soon)

  3. Encoding Custom Types with Encodable (coming soon)

  4. Getting Started with Core Data (coming soon)

  5. Making Core Data Your Model Layer (coming soon)

  6. Creating Your Core Data Stack (coming soon)

  7. Saving Codable Models to Core Data (coming soon)

  8. Fetching Codable Models from Core Data (coming soon)

  9. Testing Codable Models (coming soon)

  10. Testing Core Data Models (coming soon)

Note: I strongly believe sharing knowledge this way helps readers from all levels of experience and backgrounds to learn and improve their skills, and help the community grow and build together. So any feedback, questions, or comments from you would be greatly appreciated. Thanks in advance, and stay tuned for updates!

Clean Architecture

Photo by  Jakub Gorajek  on  Unsplash

Photo by Jakub Gorajek on Unsplash


At its highest level, clean architecture is the blueprint for a modular system, which strictly follows the design principle called separation of concerns. More specifically, this style of architecture focuses on dividing software into layers, with the intention of simplifying development and maintenance of the system itself. When layers are well-separated, individual pieces can be reused, as well as developed and updated independently.

Declaring Rules of Separated Layers:

In order to take value from these layers of separation, each and every layer will conform to a small set of rules:

  1. They should be testable.
  2. They should be independent of a user interface.
  3. They should be independent of a database.
  4. They should be independent of interfaces to third-party dependencies.
  5. They should be respectful of boundaries.

Following Rules of Separated Layers:

Keep in mind that conforming to these architectural rules is not difficult (but does require some overhead). The adherence to these standards will set the system up with the ability to swap out tools as well as plug-and-play pieces with ease. In essence, this approach to engineering software can be summarized as such:

  • Coding is relatively easy; the real challenge is managing our code when new features are added.
  • Maintaining cleanliness is better than reactively cleaning up problems after they’ve spread throughout the system; code quality should be taken seriously.
  • Quickly rewriting problem code is usually not the answer; you may have solved the minor issue, but have ignored the architectural problem of the system.

As many great programmers and storytellers have quipped before us, slow and steady does win the race. This is a mindset that can easily be lost in the chaos of sprints and release weeks; in hindsight, the pressure to get things done quickly over getting things done correctly should strongly be reconsidered.

“The only way to go fast, is to go well.“ — Robert C. Martin


We have been tip-toeing through the cruft of a product that has worn many hats. There are few or no reasons to continue development in this manner—it is not a sustainable way of maintaining an active codebase. But there are a few habits we can start today that will help us reach our goal of migrating to a clean architecture:

  • We should take pride in our legacy code.
  • We should refactor our code constantly.
  • We should maintain a clean codebase.
  • We should focus on the product goals.

Taking Pride in our Legacy Code:

The term “legacy code” evokes negative connotations. In part because the process of becoming productive within a legacy codebase can be a nightmare. Legacy code is often characterized as obscure, undocumented, poorly structured, difficult to maintain, and overly-complicated.

Another definition of legacy code is logic that increases the technical debt of the system—this can be code from a decade ago, or code which was merged in a hurry yesterday due to time constraints. The evidence of working with a legacy codebase is highly visible—it can be identified by looking at the myriad of programming styles introduced by every developer who has ever worked on the project.

Note: The implementations used throughout the project aren’t bad, but were guided by a chain of ad-hoc decisions made prior to the project's current state. They may ~work~ but they may not ~work well together~.

For reference, here is a list of a handful of problems we’ve inherited from our legacy code:

  • Business logic coupled to enterprise entities, UI, persistence and database access.
  • Duplicated code, which may or may not provide identical functionality.
  • Lack of design consistency has become a melting pot of programming styles.
  • Many features and bug fixes have resulted from a reactionary approach.
  • Fractured data models allow for modifications to occur in one version of an entity, but not at a system level.
  • Spaghetti code, which makes maintenance, testing, and refactoring difficult for the team.

We should carefully analyze the troubled code in our codebase before making any crass decisions. Understanding why the design of the legacy code is failing our current needs is essential to fixing the root cause. Most problems associated with legacy code stem from the lack of a defined structure, architecture or API design.

“…[C]ode is never perfect… Neither architecture nor clean code insist on perfection, only on honesty and doing the best we can. To err is human; to forgive, divine.” — Robert C. Martin

Refactor our Code Constantly:

We should not try to solve every problem at once by refactoring the legacy code entirely. Without proper planning and design, this method will ensure a more fragile environment. We should tread lightly and think carefully before making drastic changes to the existing logic. We must try our best to adhere to the attributes of software quality:

  • Maintainability
  • Dependability
  • Efficiency
  • Usability

We should preserve the functionality our legacy code by choosing to add support before removing a line of code. This is key in any refactoring process, and we should be diligent in keeping the working behavior of the legacy code intact. Consider the following scenarios:

  1. The code works, and you know it works, but you need to reorganize it so you can can understand it in order to extend its functionality.

  2. The code works, and you know it works, but you want to refactor it although you have no allocated work associated with this code.

  3. The code doesn’t work, and its defects are known, however, you must touch the code because your allocated work requires some changes.

  4. The code doesn’t work, and its defects are known, and you want to refactor it although you have no allocated work associated with this code.

In scenario #1, you write tests against the code. If the tests fail, you have a bug in your test. Don’t change the code until you are confident you have enough tests to catch any regressions you may have introduced.

In scenario #2, just leave the code alone. Wait until the logic falls into scenario #1 before making any modifications.

In scenario #3, this is the most likely of scenarios. Try understanding the existing code as best you can, write tests which validate the functionality you’re certain of (based on what’s expected from your user stories), then iteratively fill in the knowledge gaps by reorganization.

At some point, you’ll have a handful of test cases (some correct, some incorrect), but they should all pass. Now, take the time to go through your tests and figure out which ones are actually incorrect—those will uncover the defects in the code that needs to be refactored. Once the bugs are discovered, correct the tests, then proceed to change the code until the test pass again.

In scenario #4, it’s difficult to suppress your urges, but it’s better off for the health of the system to leave unallocated work alone. The chances of you introducing a regression is high, since it’s a low focal point for the business. It seems like it’s helpful, but it’s moving in the wrong direction.

Again, the point is to achieve a common goal. If we’re randomly addressing and refactoring logic, we’ve deviated from the course. We must stay focused on reaching the goal line, but do so with a viable plan of attack.

Maintaining a Clean Codebase:

A clean codebase has many benefits for the company (and team), including:

  • Reduced complexity of the solutions required to address business needs.
  • Less development time required to add, remove, or modify features.
  • Debugging becomes easier for the team.
  • Lowered costs for development and quality assurance (QA).
  • Better support for an agile environment.

“Clean code always looks like it was written by someone who cares.” — Michael Feathers

Focusing on the Product Goals:

The architecture is about the structures that support the use cases of the application.

Remember, the architecture is not a framework, nor is it provided by a framework. Frameworks are tools, they are not your application; they should not dictate your application architecture. If external dependencies are abstracted away, or encapsulated, any newcomer to the team should be able to quickly identify the purpose of our system.

That being said, instead of focusing on the fundamentals of Clean Architecture alone, consider Clean Architecture as an example of fundamentals for each iOS application your company has to offer.

“You know you are working on clean code when each routine you read turns out to be pretty much what you expected. You can call it beautiful code when the code also makes it look like the language was made for the problem.” — Ward Cunningham

Creating these habits of a clean architecture will lead to a system that is:

  • Easier to understand.
  • Easier to write.
  • Easier to change.

Proposed Solution

In the introduction, we discussed the four rules of separated layers, which are the key to making this architecture successful. We’ll take some time now to explore those rules in depth:

  1. They should be testable. The business logic layer(s) can be tested without the UI, database, network, or any other external element.

  2. They should be independent of a user interface. The UI can be easily modified, without changing the rest of the application(s).

  3. They should be independent of a database. You can swap out Core Data or SQLite, for Realm, Firebase or something else entirely. Your business logic is not bound to the database or persistence layer(s).

  4. They should be independent of interfaces to third-party dependencies. The architecture does not depend on external libraries. This allows the team to use our own modules as tools, rather than having to adapt your application(s) to their limited constraints and rigid conventions.

  5. They should be respectful of boundaries. Each layer of software should be created to isolate concepts and/or technologies. They should isolate at the conceptual level (i.e., business logic should know nothing about network requests, transactions, or database) and/or at the technical level (i.e., handling failures, errors, and exceptions raised by the entity management code so they don’t find their way into the business logic).

This layer isolation should be bidirectional (i.e., the business logic should have no knowledge of how the database or UI is implemented, and vice versa).

Typically, data crossing the boundaries of another layer consist of simple data structures. We should prevent ourselves from passing entity objects across boundaries as this violates the rules of dependency. We can prevent exposing our entity objects by using view models and/or data transfer objects (DTOs). Both of these objects serve a similar purpose—encapsulating data for another layer of the application.This means that our view models and/or DTOs needed to cross these boundaries should belong within the inner layer (i.e., provide a view model for displaying UI metadata without exposing the underlying entity, provide a DTO to wrap auxiliary database operations like create, which may lack an identifier, or update, which requires an identifier).

“You will have to separate UI code from algorithmic code, so both will probably be easier to understand and maintain, maintaining algorithms won't break the UI, and vice versa.” — Matt Rickard

Prevent Code Smells

Furthermore, we should strive to prevent implementation details of types within a layer impacting any other system layers—this particular crossing of layer boundaries is considered a code smell.

“A code smell is a surface indication that usually corresponds to a deeper problem in the system.” — Martin Fowler

Advice for Maintaining a Clean Architecture

  • Depend on abstractions versus concrete types.
  • Use design patterns only to simplify logic.
  • Avoid tightly coupled code.
  • Design for loosely coupled interactions .
  • Maintain a clean and consistent code style.
  • Design with SOLID principles in mind.
  • Favor composition over inheritance.
  • Refactoring is your friend.
  • Testing is always your responsibility.

“Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write.” — Robert C. Martin

Detailed Design

The architecture will be composed of four fundamental layers. At the time of writing, these layers will be identified as:

  1. Enterprise Logic

    • Data models and entities live here.
    • The entities should be an abstract representation of our essential models of the business (i.e., user, document, annotation, etc.).
    • This layer contains critical business rules, but the rules are at the most generalized (platform agnostic) level.
    • This layer can be shared between many applications and should not be impacted by anything other than a modification to the critical business rules themselves.
    • It should not be impacted by the presentation logic or infrastructures such as the database.
  2. Application Logic

    • Business logic and use cases live here.
    • The data flow is managed in/out of the entities and depend on their critical business rules to achieve the use case (e.g., user manager, document manager, etc.).
    • This layer contains application specific business rules and domain specific constraints.
    • It should not impact the entities.
    • It should not be impacted by infrastructures (i.e., database, operating system, hardware).
  3. Presentation Logic

    • Presentation design patterns live here.
    • The data flow is converted from the application and/or domain specific to the UI.
    • This layer converts data from the data layers into use cases and presentation flows (e.g., MVC, MVVM, VIPER, MVP, Rx).
  4. Infrastructure Logic

    • External dependencies live here.
    • Most of this code was written elsewhere (e.g, Core Data, AsyncDisplayKit, Realm, ReactNative).
    • This layer is the infrastructure that supports the entire application.

Clean Architecture Model Diagrams:

The clean architecture model can be visualized in multiple ways. I’ve decided to provide two diagrams—the 2D pyramid, and the 3D sphere: