MacroCodableKit

MacroCodableKit 增强了您在 Swift 中的 Codable 体验,利用宏生成精确高效的代码,零额外内存分配,这得益于纯(静态)函数的使用。它是一个全面的解决方案,提供对 AllOfOneOf 和可自定义的 CodingKeys 的支持,扩展了原生的 Codable 功能,以无缝地跟上 OpenAPI 规范。

目录

动机

使用属性包装器来实现 Codable 是另一种方法,它非常灵活,并且肯定会大大减少样板代码,但同时它也有其局限性

虽然有一个 提案 允许 @propertyWrappers 使用 let,我希望它最终能够完成

使用 来处理 Codable 可以实现零额外分配、不可变属性,并在一个工具中实现任何理想的 Codable 策略 - MacroCodableKit,它提供了来自 OpenAPI 规范的 OneOfAllOf 编码实现,并允许通过注解更改 CodingKeys

特性

... 以及更多!

用法

基础 - @Codable

使用 @Codable 注解一个结构体,如果在属性上没有额外的注解,它将生成默认的 Codable 一致性

注意 不要自己遵循 Codable 协议,这将阻止宏生成代码

@Codable
struct User {
    let birthday: Double
    let name: String
    let isVerified: Bool
}

让我们将 birthday 转换为 Date,更改 isVerified 的编码键,并将其默认设置为 false

注意 如果您不需要编码,则仅遵循 @Decodable

@Decodable
struct User {
    @ValueStrategy(ISO8601Default)
    let birthday: Date
    let name: String

    @CodingKey("is_verified")
    @DefaultValue(BoolFalse)
    let isVerified: Bool
}

// json: { "birthday": 1696291200.0, "name": "Mikhail" }
// is_verified is not specified, so the default value is "false" as specified by `@DefaultValue`

@AllOfCodable

@AllOfCodable 描述了 OpenAPI AllOf 关系

假设您有一个 SocialUser OpenAPI 规范,它继承自 User 并具有额外的属性

SocialUser:
  allOf:
    - $ref: '#components/schema/User'
    - type: object
      properties:
        username:
          type: string
        isPublic:
          type: boolean

在 Swift 代码中,可以使用 AllOfCodable 注解来实现

@AllOfCodable
struct SocialUser {
  struct Properties: Codable {
    let isPublic: Bool
    let username: String
  }
  let user: User
  let additionalProperties: Properties
}

@OneOfCodable

@OneOfCodable 描述了 OpenAPI OneOf 关系

注意 每个枚举案例中只期望有一个关联值

@OneOfCodable
enum PaymentMethod {
  case card(DebitCardPayload)
  case applePay(payload: ApplePayPayload)
  case sepa(_ payload: SepaPayload)
}
// valid jsons:
// { "card": { ... DebitCardPayload ... }
// { "applePay": { ... ApplePayPayload ... } }
// { "sepa": { ... SepaPayload ... } }

注解

@CodingKey

使用 @CodingKey(_ key: String) 注解属性,该键将用作解码和编码中的 CodingKey

struct User {
  @CodingKey("is_verified")
  let isVerified: Bool
}

@OmitCoding

使用 @OmitCoding() 注解跳过特定属性的编码

struct User {
  @OmitCoding()
  let isVerified: Bool
}

当您描述一个对象时,其中每个编码属性都是 http 请求体的一部分时,这可能很有用

@Encodable
struct Request {
  var endpoint: String { "/user/\(userID)/follow" }

  // We don't want to encode userID, since it's not part of the request body
  @OmitCoding
  let userID: String
  
  let isFollowing: Bool
}

@DefaultValue

使用 @DefaultValue<Provider: DefaultValueProvider>(_ type: Provider.Type) 在解码失败时提供默认值

警告 @DefaultValue(_:) 不影响编码

@Codable
struct User {
  @DefaultValue(BoolFalse) // property will be `false`, if value is absent or decoding fails
  let isVerified: Bool
}

内置预设

@ValueCodable

使用 @ValueStrategy<Strategy: ValueCodableStrategy>(_ strategy: Strategy.Type) 提供自定义映射

@Encodable
struct Upload {
    @ValueStrategy(Base64Data)
    let document: Data
}

可以与 DefaultValue 结合使用

@Decodable
struct Example {
    @ValueStrategy(SomeStringStrategy)
    @DefaultValue(EmptyString)
    let string: String
}

内置预设

@CustomCoding

@CustomCoding 注解允许为属性指定自定义的编码和解码策略。将其附加到属性并指定包含自定义编码/解码逻辑的类型。例如,@CustomCoding(SafeDecoding) 使用来自 CustomCodingDecodingCustomCodingEncodingsafeDecoding 函数,以便在编码和解码期间安全地处理数组和字典。

@Codable
struct TaggedPhotos: Equatable {
    @CustomCoding(SafeDecoding)
    var photos: [Photo]
}

@Codable
struct UserProfiles: Equatable {
    @CustomCoding(SafeDecoding)
    var profiles: [String: Profile]
}

// Corresponding JSON for TaggedPhotos with corrupted data
// {
//     "photos": [
//         { "url": "https://example.com/photo1.jpg", "tag": "vacation" },
//         { "url": "https://example.com/photo2.jpg", "tag": "family" },
//         "corruptedData"
//     ]
// }

// Corresponding JSON for UserProfiles with corrupted data
// {
//     "profiles": {
//         "john_doe": { "age": 25, "location": "NYC" },
//         "jane_doe": { "age": 28, "location": "LA" },
//         "corrupted_entry": "corruptedData"
//     }
// }

在上面的示例中,@CustomCoding(SafeDecoding) 将捕获并将无效解码引起的任何解码错误转发到 CustomCodingDecoding.errorHandler,从而允许其余数据安全地解码。

安装

Swift Package Manager

已知限制

致谢