Skip to content

πŸ—„ A Swift wrapper around the JSONbin.io API

License

Notifications You must be signed in to change notification settings

Fleuronic/Cubby

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

48 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Cubby

Release License Issues Downloads

Bitrise Codecov Codacy

Github

Cubby is a Swift wrapper around the JSONBin API that provides a simple interface for storing, categorizing, and fetching Swift structs in the cloud.

API Support

Cubby provides full support for both v2 and v3 of the JSONBin API.

Version 2.0

  • Bin
    • Create
    • Read
    • Update
    • Delete
  • Collection
    • Create
    • Read
  • Schema Doc
    • Create
    • Read
    • Update
  • Geolocation
    • Lookup
  • Experimental
    • Request Count

Version 3.0

  • Bin
    • Create
    • Read
    • Version Count
    • Update
    • Update Name
    • Update Privacy
    • Delete
    • Delete Versions
  • Collection
    • Create
    • Fetch in Collection
    • Fetch Uncategorized
    • Update Name
    • Add Schema Doc
    • Remove Schema Doc
  • Schema Doc
    • Create
    • Read
    • Update
    • Update Name
  • Other
    • List Usage Logs
    • Download Usage Log

Usage

Cubby uses specification protocols to indicate which endpoints each API version supports. Use an appropriate instantiation of JSONBin.V2.API or JSONBin.V3.API depending on the endpoint you want to call. (Note that version 2 of the JSONBin API was planned for deprecation on February 28, 2022.)

Bin

Create, read, update, or delete bins that represent Swift structs. Each bin represents a single Swift struct and has a privacy setting, an optional name, and an optional collection it belongs to.

public protocol JSONBinV2APIBinSpec: JSONBinV2APISpec {
    func createBin<Resource: Encodable>(named name: String?, with resource: Resource, inCollectionWith id: Collection.ID?, private: Bool?) -> Request<Creation<Resource>>
    func readBin<Resource: Decodable>(with id: ID, of type: Resource.Type, at version: Version?) -> Request<Resource>
    func updateBin<Resource: Encodable>(with id: ID, using resource: Resource, versioning: Bool?) -> Request<Update<Resource>>
    func deleteBin(with id: ID) -> Request<Deletion>
}

public protocol JSONBinV3APIBinSpec: JSONBinV3APISpec {
    func createBin<Resource: Encodable>(named name: String?, with resource: Resource, inCollectionWith id: Collection.ID?, private: Bool?) -> Request<Creation<Resource>>
    func readBin<Resource: Decodable>(with id: ID, of type: Resource.Type, at version: Version?, includingMetadata: Bool?, usingDotPath dotPath: String?) -> Request<Read<Resource>>
    func versionCount(ofBinWith id: ID) -> Request<VersionCount>
    func updateBin<Resource: Encodable>(with id: ID, using resource: Resource, versioning: Bool?) -> Request<Update<Resource>>
    func updateName(ofBinWith id: ID, toName name: String) -> Request<NameUpdate>
    func updatePrivacy(ofBinWith id: ID, toPrivate private: Bool) -> Request<PrivacyUpdate>
    func deleteBin(with id: ID) -> Request<Deletion>
    func deleteVersions(ofBinWith id: ID, preservingLatest: Bool?) -> Request<Deletion>
}
Creation Example
var apple = Company(name: "Apple", remoteWorkPolicy: .hybrid)

let request = api.createBin(named: "Apple Computer", with: apple)
let creation = try await request.returnedResource
let id = creation.metadata.id

print(creation.resource) // Company(name: "Apple", remoteWorkPolicy: .hybrid)
Read Example
let request = api.readBin(with: id, of type: Company.self, at: .number(1), includingMetadata: false)
let company = try await request.returnedResource

print(company) // Company(name: "Apple Computer", remoteWorkPolicy: .hybrid)
Update Example
apple.remoteWorkPolicy = .disallowed

let request = api.updateBin(with: id, using: apple)
let update = try await request.returnedResource

print(update.resource) // Company(name: "Apple", remoteWorkPolicy: .disallowed)
Update Name Example
let request = api.updateName(ofBinWith: id, toName: "Apple Inc.")
let update = try await request.returnedResource

print(update.resource) // Company(name: "Apple", remoteWorkPolicy: .disallowed)
Version Count Example
let request = api.versionCount(ofBinWith: id)
let versionCount = try await request.returnedResource.metadata.versionCount

print(versionCount) // 1
Deletion Versions Example
let request = api.deleteVersions(ofBinWithID: id, preservingLatest: true)
let deletion = try await request.returnedResource

print(deletion.message) // "Versions for the Bin are deleted successfully and latest version preserved on the base record."
Deletion Example
let request = api.deleteBin(with: id)
let deletion = try await request.returnedResource

print(deletion.message) // "Bin deleted successfully"

Collection

Create, fetch from, or update collections that contain your Swift structs. To ensure a collection only contains structs of the same type, attach a schema document (see below) representing that type to the collection. Once attached, attempting to create a bin in a collection of a different type (i.e., one that cannot be validated by the schema) will fail.

public protocol JSONBinV2APICollectionSpec: JSONBinV2APISpec {
    func createCollection(named name: String) -> Request<Creation>
    func updateCollection(with id: ID, using action: Action) -> Request<Update>
}

public protocol JSONBinV3APICollectionSpec: JSONBinV3APISpec {
    func createCollection(named name: String) -> Request<Creation>
    func fetchBins(inCollectionWith id: ID, sortedBy sortOrder: Fetch.SortOrder?) -> Request<[Fetch.Result]>
    func fetchUncategorizedBins(sortedBy sortOrder: Fetch.SortOrder?) -> Request<[Fetch.Result]>
    func updateName(ofCollectionWith id: ID, toName name: String) -> Request<NameUpdate>
    func addSchemaDoc(with id: SchemaDoc.ID, toCollectionWith collectionID: ID) -> Request<Addition>
    func removeSchemaDoc(fromCollectionWith id: ID) -> Request<Removal>
}
Creation Example
let request = api.createCollection(named: "WFH Companies")
let creation = try await request.returnedResource
let id = creation.metadata.id

print(creation.metadata.name) // "WFH Companies"
Fetch Example
let request = api.fetchBins(inCollectionWith: id)
let companies = try await request.returnedResource

print(companies) // [Company(name: "Twitter", remoteWorkPolicy: .allowed), Company(name: "GitHub", remoteWorkPolicy: .distributed)]
Update Name Example
let request = api.updateName(ofCollectionWith: id, toName: "Remote Companies")
let update = try await request.returnedResource

print(update.metadata.name) // "Remote Companies"
Add Schema Doc Example
let request = api.addSchemaDoc(with: schemaDocID, toCollectionWith: id)
request() // Adds a schema doc to the collection
Remove Schema Doc Example
let request = api.removeSchemaDoc(fromCollectionWith: id)
request() // Removes the schema doc from the collection

Schema Doc

Create, read, or update schema documents that can be attached to collections.

public protocol JSONBinV2APISchemaDocSpec: JSONBinV2APISpec {
    func createSchemaDoc<Resource>(for type: Resource.Type, named name: String) -> Request<Response<Resource>>
    func readSchemaDoc<Resource>(with id: ID, for type: Resource.Type) -> Request<Schema<Resource>>
    func updateSchemaDoc<Resource>(with id: ID, toSchemaFor type: Resource.Type) -> Request<Response<Resource>>
}

public protocol JSONBinV3APISchemaDocSpec: JSONBinV3APISpec {
    func createSchemaDoc<Resource>(for type: Resource.Type, named name: String) -> Request<Response<Resource>>
    func readSchemaDoc<Resource>(with id: ID, for type: Resource.Type) -> Request<Response<Resource>>
    func updateSchemaDoc<Resource>(with id: ID, toSchemaFor type: Resource.Type) -> Request<Update<Resource>>
    func updateName(ofSchemaDocWith id: ID, toName name: String) -> Request<NameUpdate>
}
Creation Example
extension Company: SchemaAdhering {
    static var description: String? {
        "A company has both a name and a remote work policy."
    }

    static var properties: [CodingKeys: SchemaType] {
        [
            .name: .string,
            .remoteWorkPolicy: .string
        ]
    }
}

let request = api.createSchemaDoc(for: Company.self, named: "Company Schema Doc")
let creation = try await request.returnedResource
let id = creation.metadata.id

print(creation.metadata.name) // "Company Schema Doc"
print(creation.schema.title) // "Company"
print(creation.schema.description) // "A company has both a name and a remote work policy."
print(creation.schema.properties) // [.name: .string, .remoteWorkPolicy: .string]
Read Example
let request = api.readSchemaDoc(with: id)
let read = try await request.returnedResource

print(read.metadata.name) // "Company Schema Doc"
print(read.schema.title) // "Company"
print(read.schema.description) // "A company has both a name and a remote work policy."
print(read.schema.properties) // [.name: .string, .remoteWorkPolicy: .string]
Update Example
extension Company: SchemaAdhering {
    static var description: String? {
        "A company has both a name, a remote work policy, and a number of employees."
    }

    static var properties: [CodingKeys: SchemaType] {
        [
            .name: .string,
            .remoteWorkPolicy: .string,
            .employeeCount: .integer
        ]
    }
}

let request = api.updateSchemaDoc(with: id, toSchemaFor: Company.self)
let update = request.returnedResource

print(update.metadata.name) // "Company Schema Doc"
print(update.schema.title) // "Company"
print(update.schema.description) // "A company has, a name, a remote work policy, and a number of employees."
print(update.schema.properties) // [.name: .string, .remoteWorkPolicy: .string, .employeeCount: .integer]
Update Name Example
let request = api.updateName(ofSchemaDocWith: id, toName: "Company Schema Document")
let update = try await request.returnedResource

print(update.metadata.name) // "Company Schema Document"

Geolocation

Look up geolocation data for an IP address.

public protocol JSONBinV2APIGeoIPSpec: JSONBinV2APISpec {
    func lookUpGeolocation(for ipAddress: IPAddress) -> Request<Lookup>
}
Look Up Geolocation Example
let ipAddress = IPAddress("141.158.45.225")
let request = api.lookUpGeolocation(for: ipAddress)
let lookupData = await request.returnedResource.data

print(lookupData.range) // 2375953408..<2375953919
print(lookupData.countryCode) // "US"
print(lookupData.regiounCode) // "PA"
print(lookupData.timeZone) // "America/New_York"
print(lookupData.city) // "Philadelphia"
print(lookupData.coordinates.latitude) // 39.934
print(lookupData.coordinates.longitude) // -75.16
print(lookupData.metroCode) // 504
print(lookupData.accuracyRadius) // 1

Experimental

Get the number of requests remaining for this account.

public protocol JSONBinV2APIExperimentalSpec: JSONBinV2APISpec {
    func requestCount() -> Request<Count>
}
Request Count Example
let request = api.requestCount()
let count = try await request.returnedResource

print(count.value) // 1000000

Other

List the usage logs for this account, or download a specific usage log.

public protocol JSONBinV3APIOtherSpec: JSONBinV3APISpec {
    func listUsageLogs() -> Request<UsageLog.List>
    func downloadUsageLog(named name: String) -> Request<UsageLog>
}
List Usage Logs Example
let ipAddress = IPAddress("141.158.45.225")
let request = api.listUsageLogs
let list = await request.returnedResource

print(list.logNames) // ["12-31-2022", "01-01-2022", "01-02-2022"]
Download Usage Log Example
let ipAddress = IPAddress("141.158.45.225")
let request = api.downloadUsageLog(named: "01-01-2022")
let usageLog = await request.returnedResource

print(usageLog.compressed) // ZIP data of log contents

Installation

Cubby is distributed using the Swift Package Manager. To install it into a project, simply add it as a dependency within your Package.swift manifest:

let package = Package(
    ...
    dependencies: [
        .package(url: "https://github.com/Fleuronic/Cubby", from: "1.0.0")
    ],
    ...
)

Then import Cubby wherever you’d like to use it:

import Cubby

For more information on how to use the Swift Package Manager, check out this article, or its official documentation.