Cubby is a Swift wrapper around the JSONBin API that provides a simple interface for storing, categorizing, and fetching Swift structs in the cloud.
Cubby provides full support for both v2 and v3 of the JSONBin API.
- Bin
- Create
- Read
- Update
- Delete
- Collection
- Create
- Read
- Schema Doc
- Create
- Read
- Update
- Geolocation
- Lookup
- Experimental
- Request Count
- 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
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.)
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: company)
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"
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
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"
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
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
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
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.