Japx

repository·master·Indexed 41 days ago

https://github.com/infinum/japx

A lightweight parser for the JSON:API specification that flattens complex structures into standard JSON dictionaries. It enables compatibility with Swift mapping tools like Codable, Unbox, Wrap, and ObjectMapper. Japx supports iOS 10.0+, macOS 10.12+, and Swift 5.0+, with available integrations for Alamofire, Moya, and RxSwift.

Tokens
2.1K
Snippets
7
Records
9
Agent score
50%

What's inside Japx

  1. What is Japx?

    master

    Japx is a lightweight JSON:API parser designed to flatten complex JSON:API structures into simple JSON and vice versa.

    It operates by transforming data between Dictionary objects. This allows you to use any object mapping tool you prefer, such as Codable, Unbox, Wrap, or ObjectMapper, on the resulting flattened data.

  2. Install Japx via CocoaPods

    master

    To install the core Japx library, add the following line to your Podfile:

    pod 'Japx'

    Japx also provides specialized subspecs for networking and reactive programming. You can install these by specifying the subspec name:

    • Japx/Alamofire: Integration with Alamofire
    • Japx/RxAlamofire: Integration with Alamofire and RxSwift
    • Japx/Moya: Integration with Moya
    • Japx/RxMoya: Integration with Moya and RxSwift
    • Japx/ObjC: Objective-C support

    Note: Regardless of which subspec you install, you should always use import Japx in your Swift code for the core functionality.

    platform :ios, '10.0'
    use_frameworks!
    
    target 'MyApp' do
      pod 'Japx/RxMoya'
    end
  3. Integrate Japx with Alamofire

    master

    Japx provides an extension for Alamofire to simplify decoding JSON:API responses directly from a DataRequest.

    Use the responseCodableJSONAPI(keyPath:completionHandler:) method. The keyPath argument allows you to specify which part of the JSON to extract (usually "data") so you can decode directly into your model without needing a wrapper struct.

    struct User: JapxCodable {
        let id: String
        let type: String
        let email: String
        let username: String
    }
    
    Alamofire
        .request(".../api/v1/users/login", method: .post, parameters: [...])
        .validate()
        .responseCodableJSONAPI(keyPath: "data", completionHandler: { (response: DataResponse<User>) in
            switch response.result {
            case .success(let user):
                print(user)
            case .failure(let error):
                print(error)
            }
        })
  4. Use Japx with Swift Codable

    master

    Japx provides a wrapper for Swift's Codable protocol. To use it, your models should conform to JapxCodable (which requires type and id properties).

    Because JSON:API responses often contain top-level meta or links keys, it is recommended to create a generic JapxResponse<T> wrapper to capture the data payload.

    Implementation Pattern:

    1. Define a wrapper struct for the response.
    2. Define your model conforming to JapxCodable.
    3. Use JapxDecoder().decode(_:from:) to parse the data.

    Note: JapxCodable is a typealias/convenience that includes JapxDecodable and JapxEncodable protocols.

    struct JapxResponse<T: Codable>: Codable {
        let data: T
    }
    
    struct User: JapxCodable {
        let id: String
        let type: String
        let email: String
        let username: String
    }
    
    let userResponse: JapxResponse<User> = try JapxDecoder().decode(JapxResponse<User>.self, from: data)
    let user: User = userResponse.data
  5. Integrate Japx with Alamofire and RxSwift

    master

    For reactive programming, Japx provides an extension for Alamofire's .rx property.

    Use rx.responseCodableJSONAPI(keyPath:) on a DataRequest to return a Single containing your decoded model. This is useful when chaining network requests with other reactive operations.

    let loginModel: LoginModel = ...
    let executeLogin: ([String: Any]) throws -> Single<User> = {
        return Alamofire
            .request(".../api/v1/users/login", method: .post, parameters: $0)
            .validate()
            .rx.responseCodableJSONAPI(keyPath: "data")
    }
    
    return Single.just(loginModel)
            .map { try JapxEncoder().encode($0) }
            .flatMap(executeLogin)
  6. Install Japx via Swift Package Manager

    master

    Add Japx as a dependency in your Package.swift file using the following URL and version requirement:

    .package(url: "https://github.com/infinum/Japx.git", .upToNextMajor(from: "4.0.0"))

    Selecting Products

    Depending on your requirements, you can choose specific products from the Japx package:

    RequirementProduct NameImport Module
    Pure JapxJapximport Japx
    AlamofireJapxAlamofireimport JapxAlamofire
    Alamofire + RxSwiftJapxRxAlamofireimport JapxRxAlamofire
    MoyaJapxMoyaimport JapxMoya
    Moya + RxSwiftJapxRxMoyaimport JapxRxMoya
    let package = Package(
        name: "YourDependency",
        products: [
            .library(name: "YourDependency", targets: ["YourDependency"])
        ],
        dependencies: [
            .package(url: "https://github.com/infinum/Japx.git", .upToNextMajor(from: "4.0.0")),
        ],
        targets: [
            .target(
                name: "YourDependency",
                dependencies: [.product(name: "Japx", package: "Japx")]
            )
        ]
    )
  7. Flatten a JSON:API object using JapxKit.Decoder.jsonObject(withJSONAPIObject:)

    master

    Use JapxKit.Decoder.jsonObject(withJSONAPIObject:) to convert a standard JSON:API object into a flattened dictionary. This process moves all properties from the attributes object to the root of the data object, making it easier to work with simple key-value pairs.

    Input Example:

    {
        "data": {
            "id": "1",
            "type": "users",
            "attributes": {
                "email": "john@infinum.co",
                "username": "john"
            }
        }
    }

    Output Example:

    {
        "data": {
            "email": "john@infinum.co",
            "id": "1",
            "username": "john",
            "type": "users"
        }
    }
    let jsonApiObject: [String: Any] = ...
    let simpleObject: [String: Any]
    
    do {
        simpleObject = try JapxKit.Decoder.jsonObject(withJSONAPIObject: jsonApiObject)
    } catch {
        print(error)
    }
  8. Parse relationships and included objects using includeList

    master

    To parse nested relationships and objects found in the included array, use JapxKit.Decoder.jsonObject(with:includeList:). You provide an includeList string representing the relationship path (e.g., "author.article.author") to tell the parser which nested objects to match and flatten into the main data tree.

    Example Usage:

    let includeList: String = "author.article.author"
    let jsonApiObject: [String: Any] = ...
    let recursiveObject: [String: Any] = try JapxKit.Decoder.jsonObject(with: jsonApiObject, includeList: includeList)