APIの仕様により、JSONだけどIntの値がString型で返されたりします。例えばGitHubのNotification

[
  {
    "id": "1",
    "repository": {
    },
    "reason": "subscribed",
  }
]

このままだとCodableでdecode出来ないのでDecodable protocolに定義されているinitializerを定義してあげます。

struct Notification: Codable {
    enum CodingKeys: String, CodingKey {
        case id
        case reason
    }

    let id: Int

    let reason: String

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try (try values.decode(String.self, forKey: .id)).intValue()
        reason = try values.decode(String.self, forKey: .reason)
    }
}

extension String {
    func intValue() throws -> Int {
        guard let value = Int(self) else {
            throw CastError.stringToInt
        }
        return value
    }
}

enum CastError: Error {
    case stringToInt
}

extensionやErrorの定義がめんどくさければforced unwrappingで処理しちゃうことも出来ます。

id = Int(try values.decode(String.self, forKey: .id))!

SwiftyJSONやHimotokiに逃げようかと思いましたがCodableで戦えそうです。ただkeyの多いJSONだとCodingKeyの定義がめんどくさいですね。