Swift Portable Object Notation (SPON)

For iOS, tvOS and macOS
Action Data

Swift Portable Object Notation

The new Swift Portable Object Notation (SPON) data format that allows complex data models to be encoded in a portable text string that encodes not only property keys and data, but also includes type information about the encoded data.

The portable, human-readable string format encodes values with a single character type designator as follows:

  • % – Bool
  • ! – Int
  • $ – String
  • ^ – Float
  • & – Double
  • * – Embedded NSData or Data value

Additionally, embedded arrays will be in the @array[...] format and embedded dictionaries in the @obj:type<...> format.

Take the following sample objects:

import Foundation
import ActionUtilities
import ActionData

struct Address: Codable {
    var addr1 = ""
    var addr2 = ""
    var city = ""
    var state = ""
    var zip = ""
}

class Person: ADDataTable {
    
    static var tableName = "People"
    static var primaryKey = "id"
    static var primaryKeyType = ADDataTableKeyType.autoUUIDString
    
    var id = UUID().uuidString
    var firstName = ""
    var lastName = ""
    var addresses: [String:Address] = [:]
    
    required init() {
        
    }
    
    init(firstName: String, lastName:String, addresses: [String:Address] = [:]) {
        self.firstName = firstName
        self.lastName = lastName
        self.addresses = addresses
    }
}

class Group: ADDataTable {
    
    static var tableName = "Groups"
    static var primaryKey = "id"
    static var primaryKeyType = ADDataTableKeyType.autoUUIDString
    
    var id = UUID().uuidString
    var name = ""
    var people = ADCrossReference<Person>(name: "PeopleInGroup", leftKeyName: "groupID", rightKeyName: "personID")
    
    required init() {
        
    }
    
    init(name: String, people: [Person] = []) {
        self.name = name
        self.people.storage = people
    }
}

A populated Address struct above could be represented in SPON as:

@obj:Address<state$=`TX` city$=`Seabrook` addr1$=`25 Nasa Rd 1` zip$=`77586` addr2$=`Apt #123`>

 

ADSPONEncoder

Encodes a Codable or Encodable class into a ADRecord that can be written into a SQLite database using a ADSPONiteProvider. The result is a dictionary of key/value pairs representing the data currently stored in the class. This encoder will automatically handle URLs and Enums (if the Enum is value based and also marked Codable or Encodable).

Example:

import ActionUtilities
import ActionData

class Category: ADDataTable {

    enum CategoryType: String, Codable {
        case local
        case web
    }
    
    static var tableName = "Categories"
    static var primaryKey = "id"
    static var primaryKeyType: ADDataTableKeyType = .computedInt
    
    var id = 0
    var added = Date()
    var name = ""
    var description = ""
    var enabled = true
    var highlightColor = UIColor.white.toHex()
    var type: CategoryType = .local
    var icon: Data = UIImage().toData()
    
    required init() {
    
    }
}

let encoder = ADSPONEncoder()
let category = Category()
let data = try encoder.encode(category)

Remark:

To store UIColors in the record use the toHex() extension method and to store UIImages use the toData() extension method.

ADSPONDecoder

Decodes a Codable or Decodable class from a ADRecord read from a SQLite database using a ADSPONiteProvider. The result is an instance of the class with the properties set from the database record. This decoder will automatically handle URLs and Enums (if the Enum is value based and also marked Codable or Decodable).

Example:

import ActionUtilities
import ActionData

class Category: ADDataTable {

    enum CategoryType: String, Codable {
        case local
        case web
    }
    
    static var tableName = "Categories"
    static var primaryKey = "id"
    static var primaryKeyType: ADDataTableKeyType = .computedInt
    
    var id = 0
    var added = Date()
    var name = ""
    var description = ""
    var enabled = true
    var highlightColor = UIColor.white.toHex()
    var type: CategoryType = .local
    var icon: Data = UIImage().toData()
    
    required init() {
    
    }
}

let encoder = ADSPONEncoder()
let category = Category()
let data = try encoder.encode(category)

let decoder = ADSPONDecoder()
let category2 = try decoder.decode(Category.self, from: data)

Remark:

To retrieve UIColors in the record use the String.uiColor extension property and to retrieve UIImages use the String.uiImage extension property.

ADSPONProvider

The ADSPONProvider provides both light-weight, low-level access to data stored in a Swift Portable Object Notation (SPON) database and high-level access via a Object Relationship Management (ORM) model. Use provided functions to read and write data stored in a ADRecord format from and to the database using SQL statements directly.

Example:

let provider = ADSPONProvider.shared
let record = try provider.query("SELECT * FROM Categories WHERE id = ?", withParameters: [1])
print(record["name"])

Optionally, pass a class instance conforming to the ADDataTable protocol to the ADSPONProvider and it will automatically handle reading, writing and deleting data as required.

Example:

let addr1 = Address(addr1: "PO Box 1234", addr2: "", city: "Houston", state: "TX", zip: "77012")
let addr2 = Address(addr1: "25 Nasa Rd 1", addr2: "Apt #123", city: "Seabrook", state: "TX", zip: "77586")
 
let p1 = Person(firstName: "John", lastName: "Doe", addresses: ["home":addr1, "work":addr2])
let p2 = Person(firstName: "Sue", lastName: "Smith", addresses: ["home":addr1, "work":addr2])
 
let group = Group(name: "Employees", people: [p1, p2])
try provider.save(group)

Remark:

The ADSPONProvider will automatically create a SQL Table from a class instance if one does not already exist. In addition, ADSPONProvider contains routines to preregister or update the schema classes conforming to the ADDataTable protocol which will build or modify the database tables as required.