I recently changed my job and migrated to Swift 4.0. I actually felt it was a little easier with Swift 3.0 because all libraries were actively upgrading, but now they are all working on Swift 3.2 compatibility solutions. The different compatibility status of each library makes the migration more difficult.

For Codable migration, Moya + ObjectMapper is used in our project. For Swift, everyone should use the same JSON resolution plan. They all define the protocol, and the model complies with the protocol to provide JSON resolution methods.

If the JSON format is standard and the naming is consistent, changing Mappable to Codable is 99% of the migration work. But reality isn’t always that good, so leave the Mappable on hold and replace them later in Codable.

Ideally, the network-layer implementation is left untouched and compatibility is achieved by modifying the implementation of the functions that parse JSON. Moya-objectmapper implementation

extension Response {

    func mapObject<T: BaseMappable>(_ type: T.Type, context: MapContext? = nil) throws -> T {
        guard let object = Mapper<T>(context: context).map(JSONObject: try mapJSON()) else {
            throw MoyaError.jsonMapping(self)}return object
    }

    func mapArray<T: BaseMappable>(_ type: T.Type, context: MapContext? = nil) throws- > [T] {
        guard let array = try mapJSON() as? [[String : Any]] else {
            throw MoyaError.jsonMapping(self)}return Mapper<T>(context: context).mapArray(JSONArray: array)
    }
}Copy the code

To add Codable compatibility, it’s easy to override the two methods. Common projects don’t use context much, so define it this way:

// This should be thrown with MoyaError
extension Response {

    func mapObject<T>(_ type: T.Type, using decoder: JSONDecoder = .init(a)) throws -> T where T: Decodable {
        return try decoder.decode(T.self, from: data)
    }

    func mapArray<T>(_ type: T.Type, using decoder: JSONDecoder = .init(a)) throws- > [T] where T: Decodable {
        return try decoder.decode(Array<T>.self, from: data)
    }
}Copy the code

But our back-end interface typically wraps a layer around the data, storing status or count information, so we write a BaseResponse model:

struct BaseResponse<T: Mappable> :Mappable {

    var statusCode: Int
    var message: String
    var totalCount: Int
    var result: T?

    required init? (map: Map) {}func mapping(map: Map) {
        statusCode <- map["statusCode"]
        message    <- map["message"]
        totalCount <- map["totalCount"]
        result     <- map["result"]}}Copy the code

To make a BaseResponse compatible for Codable, we would have to make a BaseResponse compatible for Codable, and a T compatible for Codable. But making a T compatible for Codable and Mappable would be a defeat for us (though it wouldn’t take much work).

Ideally, if T is Codable, BaseResponse is Codable. Similarly, T complies with Mappable, and BaseResponse complies with Mappable:

struct BaseResponse<T> {

    var statusCode: Int
    var message: String
    var totalCount: Int
    var data: T?
}

extension BaseResponse: Mappable where T: Mappable {

    required init? (map: Map) {}func mapping(map: Map) {
        statusCode <- map["statusCode"]
        message    <- map["message"]
        totalCount <- map["totalCount"]
        data       <- map["data"]}}extension BaseResponse: Codable where T: Codable {}Copy the code

These functions are called Conditional Conformance, which literally translates to Conditional compliance, meaning that agreements are complied with when a condition is met. There are also various uses for this feature. For example, if the elements in an Array are Equtable, then the Array will also obey Equtable, which can be used to remove a lot of the same code in the abstract sense. Someone on Twitter even claimed to reduce the code in his project by 20% using this feature.

However, currently this function has not been implemented in Swift 4.0, but the corresponding Pull Request has been merged into the main branch two days ago, and it is likely that we can use ✌️ in the next version of Swift 4.1.

Note:

There’s another hurdle. Due to implementation issues, there’s no way to automatically generate parsing code for Codable in Extension, but you can do it manually. Swift team has opened a Pull Request to realize this function. However, due to the current lack of a good way to realize this function, the PR is turned off. It may take some time to realize this function.

If you think the article is good, you can follow my blog