Alter is framework to make mapping Codable property and key easier.

Overview

Alter

Alter is framework to make mapping Codable property and key easier.

With Alter, you don't need to create CodingKey to manually mapping key and property.

Alter using propertyWrapper and reflection to achive key property mapping.

codebeat badge build test SwiftPM Compatible Version License Platform

Requirements

  • Swift 5.1 or higher
  • iOS 10.0 or higher

Installation

Cocoapods

Alter is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Alter', '~> 1.2.9'

Swift Package Manager from XCode

  • Add it using xcode menu File > Swift Package > Add Package Dependency
  • Add https://github.com/hainayanda/Alter.git as Swift Package url
  • Set rules at version, with Up to Next Major option and put 1.2.9 as its version
  • Click next and wait

Swift Package Manager from Package.swift

Add as your target dependency in Package.swift

dependencies: [
    .package(url: "https://github.com/hainayanda/Alter.git", .upToNextMajor(from: "1.2.9"))
]

Use it in your target as Alter

 .target(
    name: "MyModule",
    dependencies: ["Alter"]
)

Author

Nayanda Haberty, [email protected]

License

Alter is available under the MIT license. See the LICENSE file for more info.

Usage

Basic Usage

For example, if you need to map User JSON to Swift Object, you just need to create User class/struct and implement Alterable protocol and then mark all the property you need to map to JSON with @Mapped attributes.

struct User: Codable, Alterable {
    
    @Mapped
    var name: String = ""
    
    @Mapped
    var userName: String = ""
    
    @Mapped
    var age: Int = 0
}

or just use AlterCodable which is typealias of Alterable & Codable:

struct User: AlterCodable {
    
    @Mapped
    var name: String = ""
    
    @Mapped
    var userName: String = ""
    
    @Mapped
    var age: Int = 0
}

and then you can parse JSON to User Swift Object or vice versa like this

let user: User = getUserFromSomewhere()

//to JSON
let jsonObject: [String: Any] = try! user.toJSON()
let jsonString: String = try! user.toJSONString()
let jsonData: Data = try! user.toJSONData()

//from JSON
let userFromJSON: User = try! .from(json: jsonObject)
let userFromString: User = try! .from(jsonString: jsonString)
let userFromData: User = try! .from(jsonData: jsonData)

The Alterable actually just a simple protocol which will work with full functionality if paired with Codable. The only extendable function from Codable is that the Alterable will be use reflection to get all Mapped attributes and using it to do two way mapping.

public protocol Alterable

Since AlterCodable conform Codable, you could always do decode using codable decoder or encode using codable encoder just like Codable

let user: User = getUserFromSomewhere()
let propertyListData = try! PropertyListEncoder().encode(user)
let decodedPropertyList = try! PropertyListDecoder().decode(User.self, from: propertyListData)

let jsonData = try! JSONEncoder().encode(user)
let decodedJsonData = try! JSONDecoder().decode(User.self, from: jsonData)

The real power of Alterable is the mapping feature which eliminate the requirement of enumeration CodingKey when doing key mapping manually. If the property name of Decoded data is different with property in Swift object, then you can pass the name of that property at the attribute instead of creating CodingKey enumeration. Those properties then will be mapped using those key.

struct User: AlterCodable {
    
    @Mapped(key: "full_name")
    var fullName: String = ""
    
    @Mapped(key: "user_name")
    var userName: String = ""
    
    @Mapped
    var age: Int = 0
}

You could always do decode and encode manually by implement init(from:) throws and func encode(to:) throws. Alterable have some extensions to help you implement decode and encode manually

struct User: AlterCodable {
    
    @Mapped(key: "full_name")
    var fullName: String = ""
    
    @Mapped(key: "user_name")
    var userName: String = ""
    
    @Mapped
    var age: Int = 0
    
    var image: UIImage? = nil
    
    required init() {}
    
    init(from decoder: Decoder) throws {
        self.init()
        // this will automatically decode all Mapped properties and return container which you could use to decode property that not mapped
        let container = try decodeMappedProperties(from: decoder)
        // you could decode any type as long is Codable and passing String as a Key
        let base64Image: String = try container.decode(forKey: "image")
        if let imageData: Data = Data(base64Encoded: base64Image) {
            self.image = UIImage(data: imageData)
        }
    }
    
    func encode(to encoder: Encoder) throws {
        // this will automatically encode all Mapped properties and return container which you could use to encode property that not mapped
        var container = try encodeMappedProperties(to: encoder)
        if let base64Image = self.image.pngData()?.base64EncodedString() {
            // you could encode any type as long is Codable and passing String as a Key
            container.encode(value: base64Image, forKey: "address")
        }
    }
}

Manual Mapping

If you use non Codable type for property, or maybe you want to represent different data in Swift property other than real property, you could use @AlterMapped attribute instead of @Mapped and pass TypeAlterer as converter. With this method, you don't need to implement init(from:) throws and func encode(to:) throws manually.

struct User: AlterCodable {
    
    @Mapped(key: "full_name")
    var fullName: String = ""
    
    @Mapped(key: "user_name")
    var userName: String = ""
    
    @Mapped
    var age: Int = 0
    
    // manual mapping
    @AlterMapped(alterer: Base64ImageAlterer(format: .png))
    var image: UIImage = .init()
    
    // manual mapping with key
    @AlterMapped(key: "birth_date", alterer: StringDateAlterer(pattern: "dd-MM-yyyy"))
    var birthDate: Date = .distantPast
}

If your data type is optional, array or both, you can use optionally computed property or forArray computed property or even the combination of both, since the property is the extension of the TypeAlterer protocol. The order of the property call will affect the result of TypeAlterer type.

struct User: AlterCodable {
    
    @Mapped(key: "full_name")
    var fullName: String = ""
    
    @Mapped(key: "user_name")
    var userName: String = ""
    
    @Mapped
    var age: Int = 0
    
    @AlterMapped(key: "birth_date", alterer: StringDateAlterer(pattern: "dd-MM-yyyy"))
    var birthDate: Date = .distantPast
    
    // optional
    @AlterMapped(alterer: Base64ImageAlterer(format: .png).optionally)
    var image: UIImage? = nil
    
    // array
    @AlterMapped(key: "login_times", alterer: UnixLongDateAlterer().forArray)
    var loginTimes: [Date] = []
    
    // array optional
    @AlterMapped(key: "crashes_times", alterer: UnixLongDateAlterer().forArray.forOptional)
    var crashesTimes: [Date]? = nil
    
    // array of optional
    @AlterMapped(key: "some_times", alterer: UnixLongDateAlterer().forOptional.forArray)
    var someTimes: [Date?] = []
}

There are native TypeAlterer from Alter which you could use:

  • UnixLongDateAlterer which for converting Date into Int64 or vice versa
  • StringDateAlterer which for converting Date into patterned String or vice versa
  • Base64DataAlterer which for converting Data into Base64 String or vice versa
  • Base64ImageAlterer which for converting UIImage into Base64 String or vice versa

If you want to implement your own TypeAlterer, just create class or struct that implement TypeAlterer. Value is the property value type, AlteredValue is encoded value, should be implement Codable.

public struct MyOwnDataAlterer: TypeAlterer {
    public typealias Value = Data
    public typealias AlteredValue = String
    
    public init() { }
    
    public func alter(value: Data) -> String {
        value.base64EncodedString()
    }
    
    public func alterBack(value: String) -> Data {
        Data(base64Encoded: value) ?? .init()
    }
}

Nested Property

Alter can map nested property by using . example

instead of doing this:

struct Event: AlterCodable {
    
    @Mapped
    var title: String = ""
    
    @Mapped
    var ticket: Ticket = .init(price: 0)
    
    struct Ticket: AlterCodable {
        @Mapped
        var price: Double = 0
    }
}

you can skip Ticket object by using "ticket.price" key instead:

struct Event: AlterCodable {
    
    @Mapped
    var title: String = ""
    
    @Mapped(key: "ticket.price")
    var price: Double = 0
}

Mutability

In most case we don't want our model to be mutable, But since Alter need the property to be mutable so it could be assigned on object creation, you could just make the setter private.

struct User: AlterCodable {
    
    @Mapped(key: "full_name")
    private(set) var fullName: String = ""
    
    @Mapped(key: "user_name")
    private(set) var userName: String = ""
    
    @Mapped
    private(set) var age: Int = 0
}

There's some extras if you want to have mutable ability to treat Alterable as Dictionary. Any object that implement MutableAlterable protocol can be treated like Dictionary.

struct MutableUser: MutableAlterable {
    @Mapped(key: "user_name")
    var userName: String? = nil
    ...
    ...
    ...
}

or by using MutableAlterCodable which is typealias of MutableAlterable & Codable

struct MutableUser: MutableAlterCodable {
    @Mapped(key: "user_name")
    var userName: String? = nil
    ...
    ...
    ...
}

Then you could just treat it like dictionary

let user = MutableUser()
user[mappedKey: "user_name"] = "this is username"

// will print "this is username"
print(user.userName)

let userName: String = user[mappedKey: "user_name"] ?? ""

// will print "this is username"
print(userName)

The subscript can accept any type as long the type can be cast into property real type or altered type.

You might also like...
A fast, convenient and nonintrusive conversion framework between JSON and model. Your model class doesn't need to extend any base class. You don't need to modify any model file.

MJExtension A fast, convenient and nonintrusive conversion framework between JSON and model. 转换速度快、使用简单方便的字典转模型框架 📜 ✍🏻Release Notes: more details Co

ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects to and from JSON.

ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from J

This framework implements a strict JSON parser and generator in Objective-C.

SBJson 5 Chunk-based JSON parsing and generation in Objective-C. Overview SBJson's number one feature is stream/chunk-based operation. Feed the parser

Model framework for Cocoa and Cocoa Touch

Mantle Mantle makes it easy to write a simple model layer for your Cocoa or Cocoa Touch application. The Typical Model Object What's wrong with the wa

Magical Data Modeling Framework for JSON - allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS and tvOS apps.

JSONModel - Magical Data Modeling Framework for JSON JSONModel allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS

Magical Data Modeling Framework for JSON - allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS and tvOS apps.

JSONModel - Magical Data Modeling Framework for JSON JSONModel allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS

Swift/Obj-C HTTP framework with a focus on REST and JSON

Now Archived and Forked PMHTTP will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork of PHMT

Freddy - A reusable framework for parsing JSON in Swift.
Freddy - A reusable framework for parsing JSON in Swift.

Why Freddy? Parsing JSON elegantly and safely can be hard, but Freddy is here to help. Freddy is a reusable framework for parsing JSON in Swift. It ha

An iOS framework for creating JSON-based models. Written in Swift.
An iOS framework for creating JSON-based models. Written in Swift.

An iOS framework for creating JSON-based models. Written in Swift (because it totally rules!) Requirements iOS 8.0+ Xcode 7.3 Swift 2.2 Installation E

Releases(1.2.9)
Owner
Nayanda Haberty
Programmer. What else?
Nayanda Haberty
Reflection based (Dictionary, CKRecord, NSManagedObject, Realm, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift

EVReflection General information At this moment the master branch is tested with Swift 4.2 and 5.0 beta If you want to continue using EVReflection in

Edwin Vermeer 964 Dec 14, 2022
Decodable Simple and strict, yet powerful object mapping made possible by Swift 2's error handling.

Decodable Simple and strict, yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by Argo, but without a bizillion

Johannes Lund 1k Jul 15, 2022
Simple JSON Object mapping written in Swift

ObjectMapper ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from J

Tristan Himmelman 9k Jan 2, 2023
Library of Swiftui Views conforming to Codable, meaning we can produce JSON driven UI!

CodableView Library of Swiftui Views conforming to Codable, meaning we can produce JSON driven UI! Adding a CodableView Type Create View that conforms

Daniel Bolella 3 Apr 2, 2022
SwiftyJSON decoder for Codable

SwiftyJSONDecoder 功能 继承自 JSONDecoder,在标准库源码基础上做了改动,与其主要区别如下 使用 SwiftyJSON 解析数据,使用其类型兼容功能 废弃 nonConformingFloatDecodingStrategy 属性设置,Double 及 Float 默认解

null 2 Aug 10, 2022
Codable code is a Swift Package that allows you to convert JSON Strings into Swift structs

Codable code is a Swift Package that allows you to convert JSON Strings into Swift structs.

Julio Cesar Guzman Villanueva 2 Oct 6, 2022
Implement dynamic JSON decoding within the constraints of Swift's sound type system by working on top of Swift's Codable implementations.

DynamicCodableKit DynamicCodableKit helps you to implement dynamic JSON decoding within the constraints of Swift's sound type system by working on top

SwiftyLab 15 Oct 16, 2022
Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable

Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable. Elevate should no longer be used for

Nike Inc. 611 Oct 23, 2022
HandyJSON is a framework written in Swift which to make converting model objects to and from JSON easy on iOS.

HandyJSON To deal with crash on iOS 14 beta4 please try version 5.0.3-beta HandyJSON is a framework written in Swift which to make converting model ob

Alibaba 4.1k Dec 29, 2022
JSONHelper - ✌ Convert anything into anything in one operation; JSON data into class instances, hex strings into UIColor/NSColor, y/n strings to booleans, arrays and dictionaries of these; anything you can make sense of!

JSONHelper Convert anything into anything in one operation; hex strings into UIColor/NSColor, JSON strings into class instances, y/n strings to boolea

Baris Sencan 788 Jul 19, 2022