All Projects → outfoxx → PotentCodables

outfoxx / PotentCodables

Licence: MIT License
🧪 PotentCodables - A potent set of implementations and extensions to the Swift Codable system

Programming Languages

c
50402 projects - #5 most used programming language
swift
15916 projects
Makefile
30231 projects

Projects that are alternatives of or similar to PotentCodables

Userdefaultsstore
Why not use UserDefaults to store Codable objects 😉
Stars: ✭ 416 (+1200%)
Mutual labels:  tvos, watchos, codable
SeedTruck
Torrent management app for iOS, macOS, tvOS and watchOS made in SwiftUI 2. Same codebase for all platforms!
Stars: ✭ 25 (-21.87%)
Mutual labels:  tvos, watchos
stinsen
Coordinators in SwiftUI. Simple, powerful and elegant.
Stars: ✭ 563 (+1659.38%)
Mutual labels:  tvos, watchos
Apple-Platform-Security-Guides
Every Apple Platform Security Guide
Stars: ✭ 106 (+231.25%)
Mutual labels:  tvos, watchos
IrregularGradient
Create animated irregular gradients in SwiftUI.
Stars: ✭ 127 (+296.88%)
Mutual labels:  tvos, watchos
Dots
Lightweight Concurrent Networking Framework
Stars: ✭ 35 (+9.38%)
Mutual labels:  tvos, watchos
Johnny
Melodic Caching for Swift
Stars: ✭ 36 (+12.5%)
Mutual labels:  tvos, watchos
data-field
A SwiftUI view that wraps a text field to only accept specific data.
Stars: ✭ 13 (-59.37%)
Mutual labels:  tvos, watchos
Columbus
A feature-rich country picker for iOS, tvOS and watchOS.
Stars: ✭ 23 (-28.12%)
Mutual labels:  tvos, watchos
swift-standard-clients
Client declarations and live implementations for standard iOS managers
Stars: ✭ 28 (-12.5%)
Mutual labels:  tvos, watchos
OpenAPI-ObjectiveC
KKBOX Open API Developer SDK for iOS/macOS/watchOS/tvOS
Stars: ✭ 19 (-40.62%)
Mutual labels:  tvos, watchos
Jsonify
♨️A delightful JSON parsing framework.
Stars: ✭ 42 (+31.25%)
Mutual labels:  tvos, watchos
tracelog
TraceLog is a highly configurable, flexible, portable, and simple to use debug logging system for Swift and Objective-C applications running on Linux, macOS, iOS, watchOS, and tvOS.
Stars: ✭ 52 (+62.5%)
Mutual labels:  tvos, watchos
lisk-swift
Swift 4 library for Lisk - Including Local Signing for maximum security
Stars: ✭ 13 (-59.37%)
Mutual labels:  tvos, watchos
QuoteKit
A framework to use the free APIs provided by https://quotable.io
Stars: ✭ 17 (-46.87%)
Mutual labels:  tvos, watchos
SwiftRadix
Easily convert integers to binary/hex/octal strings and back again with clean functional syntax.
Stars: ✭ 34 (+6.25%)
Mutual labels:  tvos, watchos
BlockiesSwift
Unique blocky identicons generator for Swift
Stars: ✭ 53 (+65.63%)
Mutual labels:  tvos, watchos
KeyboardKitPro
KeyboardKit Pro extends KeyboardKit with pro features.
Stars: ✭ 42 (+31.25%)
Mutual labels:  tvos, watchos
Tesla-API
A iOS, macOS, watchOS and tvOS framework written in Swift to communicate with Teslas vehicle API
Stars: ✭ 32 (+0%)
Mutual labels:  tvos, watchos
Mechanica
A cross-platform library of Swift utils to ease your iOS | macOS | watchOS | tvOS and Linux development.
Stars: ✭ 27 (-15.62%)
Mutual labels:  tvos, watchos

🧪 PotentCodables

Build Status GitHub release (latest by date)

A potent set of implementations and extension to the Swift Codable system

Why?

The framework aims to solve three major pain points experienced with Swift's Codable system:

  • Allow decoding and/or encoding values of unknown structure (e.g. any encoded value)
  • Support polymorphic type encoding/decoding while still allowing Swift to implement Codable
  • Reduce the complexity and amount of code required to implement and test new serialization formats
  • Provide a library of fully featured implementations of popular serialization formats

Integration

Swift Package Manager

PotentCodables currently supports Swift Package Manager for project integration. Add a package dependency similar to the following:

  .package(url: "https://github.com/outfoxx/PotentCodables.git", from: "1.0.0")

The package provides multiple libraries corresponding to the core library and each format that is provided:

  • PotentCodables is the core library to be used by format implementors
  • PotentJSON provides JSON format support
  • PotentCBOR provide CBOR format support
  • PotentASN1 provides ASN.1 support
  • PotentYAML provides YAML 1.2 support

Usage

Using Encoders/Decoders

If your only goal is to use one of the provided implementations of a serialization format, not much information is needed beyond the name of the encoder/decoder pair that you are seeking to use. All of the implementations provided by the package are 100% compatible with Swift's Codable system and they all intentionally mimic the interface of Swift's native encoders & Decoder (e.g. Foundation.JSONEncoder and Foundation.JSONDecoder).

For example encoding to CBOR is essentially the same as encoding with Swift's standard JSONEncoder

let data = try CBOREncoder.default.encode(myValue)

Provided Formats

  • YAML - YAMLEncoder/YAMLDecoder or YAML.Encoder/YAML.Decoder A conformant YAML 1.2 implementation implemented via libfyaml. Due to the fact that JSON is a subset of YAML 1.2 the decoder can parse and decode YAML & JSON documents.

  • JSON - JSONEncoder/JSONDecoder or JSON.Encoder/JSON.Decoder A conformant JSON implementation that is a drop-in replacement for Swift's JSON encoder and decoder provided by Foundation. These implementations offer enhancements to what items can be encoded to (e.g. to/from Strings and to/from native value trees) and offer performance enhancements when using AnyValue.

  • CBOR - CBOREncoder/CBORDecoder or CBOR.Encoder/CBOR.Decoder A conformant implementation of the CBOR serialization format written in pure Swift.

  • ASN.1 - ASN1Encoder/ASN1Decoder or ASN1.Encoder/ASN1.Decoder A conformant implementation of the ASN.1 serialization format written in pure Swift. ASN.1's position based format can be very ambiguous, even so it is commonly used in situations that require absolute unambiguity. To overcome these issues the ASN1Encoder and ASN1Decoder require a schema be passed to their initializer that directs the encoding and/or decoding. More information is available here

  • AnyValue - AnyValueEncoder/AnyValueDecoder or AnyValue.Encoder/AnyValue.Decoder An in-memory transcoding implementation for working with unstructured values using AnyValue.

Extended Interfaces

All provided encoders and decoders come with an extended set of methods that allow different targets and sources when encoding and decoding.

Encoders provide these methods

// Encoding to value tree - Supported by all encoders
func encodeTree<T: Encodable>(_ value: T) throws -> Value

// Encoding to data - supported by text & binary format encoders
func encode<T: Encodable>(_ value: T) throws -> Data

// Encoding to string - supported by text format encoders
func encode<T: Encodable>(_ value: T) throws -> String  

Decoders provide these methods

// Decoding from a value tree - supported by all decoders
func decodeTree<T: Decodable>(_ type: T.Type, from value: Value) throws -> T
func decodeTreeIfPresent<T: Decodable>(_ type: T.Type, from value: Value) throws -> T?

// Decoding from data - supported by text & binary format decoders
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T
func decodeIfPresent<T: Decodable>(_ type: T.Type, from data: Data) throws -> T?

// Decoding from string - supported by text format encoders
func decode<T: Decodable>(_ type: T.Type, from data: String) throws -> T  
func decodeIfPresent<T: Decodable>(_ type: T.Type, from data: String) throws -> T?
Combine

When used on Apple platforms where the Combine framework is available, all the encoders conform to Combine's TopLevelEncoder and all decoders conform to its TopLevelDecoder.

Polymorphic Encoding/Decoding

Codable encoders and decoders are a great tool and very convenient. Unfortunately using them with polymorphic types is cumbersome, to say the least, and near impossible in a lot of other cases.

PotentCodables provides Ref and EmbeddedRef to make encoding/decoding polymorphic types very easy. The reference wrappers are designed to work with concrete type and protcols alike.

Ref is used to decode values that are "wrapped" with a type name. For example, given JSON similar to the following:

{ "@type" : "MyApp.Foo", "value" : { "name" : "A Value" } }

Ref can be used to decode a value with little extra code

protocol FooBar {
  var name: String { get }
}

struct Foo: FooBar, Codable {
  let name: String
}

struct Bar: FooBar, Codable {
  let count: Int
}

DefaultTypeIndex.setAllowedTypes([Foo.self, Bar.self]) // Authorize & map allowed types for polymorphic decoding

let val = try JSONDecoder.default.decode(Ref.self).as(FooBar.self)  // Decode ref and use the `as` utility to cast it or throw

To encode a value with the required structure and inserting the Swift type name simply use Ref.Value during encoding:

let data = try JSONEncoder.default.encode(Ref.Value(val))

EmbeddedRef, which includes EmbeddedRef.Value, is also provided and is used the exact same way as Ref. The difference is that EmbeddedRef embeds the type name along side the encoded values other keys. For example, the example JSON above would resemble the following with the key embedded:

{ "@type" : "MyApp.Foo", "name" : "A Value" }

EmbeddedRef requires the value it encodes to use a keyed-container. Unkeyed and single-value containers cannot be used with it, but they can be used with Ref.

The documentation for Ref and EmbeddedRef provide a lot of details on their usage as well as documentation of how to customize the keys used during encoding/decoding.

Type serialization & lookup

By default the type serialization & lookup mechanism (see Ref & DefaultTypeIndex code documentation) disallows all types to be decoded. This is to ensure that decoding is secure as only specific/authorized types can be decoded. Additionally, the index mechanism, by default, uses a type id that does not include the Swift module name so as to ensure stable type ids across modules, frameworks, and languages.

The means that you must explicity map allowed classes prior to using polymorphic decoding. This is done as simply as:

DefaultTypeIndex.setAllowedTypes([Foo.self, Bar.self])

This generates a type id for each type (Foo & Bar) and upates the map to allow only those types provided. Note that each call to setAllowedTypes overwrites the current set of allowed types and as such applications should register them in a single place.

Alternatively you can implement and provide a custom type index (see Ref, CustomRef & TypeIndex code documentation). If you have an alternate means of looking up types.

Allowed Types in Frameworks

The default type index is designed to be convenient and safe for simple applications. Unfortunately this means frameworks must use a custom type index to ensure the types it expects are registered and reduce the chance of inadvertantly creating security vulnerabilities.

Raw Value Container

Each decoder has an in memory representation known as the "tree" value. The great thing about tree values is that they hold the values in their exact serialized representation. For example, JSON tree values store numbers as a specialized JSON.Number that stores the exact number value as a string along with a number of other properties for helping the conversion of strings to integer or floating point numbers. Accessing this JSON.Number and reading the exact decimal value serialized in JSON is available from tree values.

The decoders support accessing the tree value using specializations of the protocol TreeValueDecodingContainer which extends the SingleValueDecodingContainer protocol.

Decoding JSON values using the TreeValueDecodingContainer as follows:

func init(from decoder: Decoder) throws {
let treeContainer = try decoder.singleValuedContainer() as! TreeValueDecodingContainer
self.jsonValue = try treeContainer.decodeTreeValue() as! JSON
}

Each tree value has the ability to "unwrap" itself (using it's unwrapped property) into it's the best available standard Swift type, returned as an Any. As an example, unwrappingthe the JSON value 123.456 result in a Swift Double.

Tree values are returned as an Any to allow easy support any possible tree value. For this reason the TreeValueDecodingContainer has a convenience method to access the unwrapped tree value without excessive casting.

Decoding unwrapped JSON values using the TreeValueDecodingContainer as follows:

func init(from decoder: Decoder) throws {
let treeContainer = try decoder.singleValuedContainer() as! TreeValueDecodingContainer
self.value = try treeContainer.decodeUnwrappedValue()
}

AnyValue - Unstructured Values

Sometimes it is necessary to decode values of any type or that can take on any structure; unfortunately Swift's Codable is not well suited for this purpose. PotentCodables provides AnyValue to fill the gap.

Using AnyValue is simple, just use it wherever you would normally use an Any. Since AnyValue supports Codable everything else works as normal including Swift's automatic codable generation.

struct Account : Codable {
  let name: String
  let data: AnyValue                  // `data` can store and scalar or complex value
  let dataDict: [String: AnyValue]    // `dataDict` is required to be a dictionary of name to any values
  let dataArray: [AnyValue]           // `dataArray` is required to be an array of any values
}

The example Account struct above has a data property that can take on any value supported by the codable system. For example when decoding from JSON, any value or tree of values (including null, bool, string, number, arrray or object) could be saved in the data property. Encoding the same Account value back to JSON will produce equivalent serialized JSON regardless of the contents of the data field.

AnyValue has lots of features to make building and using them natural in Swift, like "dynamic member lookup" to access fields of a AnyValue.dictionary. See the documentation for complete details.

Performance Although AnyValue is compatible with any conformant Codable encoder or decoder, PotentCodables decoders specifically have shortcuts to decode the proper values in a more performant fashion and should be used when possible.

More

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].