• Final WWDC Lab today with the CloudKit team! ☁️

    CloudKit


    Friday June 14, 2024
  • The macOS app is a helper tool for me to process data and manage its representation in CloudKit. But it’s currently stateless. It decodes JSON and writes directly to a public database – so I can reset the CloudKit developer environment as needed and quickly stand it back up with schemas and records.

    But as I started modeling a new metadata type that isn’t represented in the source JSON, I realized the Mac app needs a data store of it’s own.

    CloudKit


    Friday July 28, 2023
  • Maintaining the source schemas from each data source as its own record type has been a good choice. Now I have a small catalog of data from 3 places loaded into CloudKit. And a simple iOS app with a picker between 3 SwiftUI views – each tuned to its source. So each feels the same.

    Next I’ll model some common metadata concepts from across the 3 schemas as new CloudKit record types. I’ll expand the macOS writer app to run queries against the existing data catalog, create new CloudKit metadata records and build an object graph between content and tags using CKRecord.Reference. So the iOS reader can query against these common metadata records, and get back references to records from all 3 sources.

    CloudKit


    Wednesday July 26, 2023
  • I went down the path of trying to define a source agnostic schema, thinking I would transform all the incoming data to a common record type in the macOS app before loading it into CloudKit.

    Architecture diagram: records from 3 data sources are mapped to a unified agnostic schema and then pushed to CloudKit.

    But this adds some pressure to get the data normalization “right” on ingest. Now I’m exploring an alternative approach, where I maintain a 1:1 relationship between data source schemas and CloudKit record types.

    Architecture diagram: records from 3 data sources are each mapped to their own record type and then pushed to CloudKit, maintaining the schema of each source.

    CloudKit


    Monday July 24, 2023
  • I updated the macOS writer app to publish 25 actual records to CloudKit – and in turn, the iOS reader to fetch this new data. Each record includes a title and a remote imageURL. The titles appear right away, but the AsyncImage(url:) SwiftUI View has some noticeable latency when loading images. Tomorrow I’ll try saving them directly to CloudKit as a CKAsset when I create the records and see if this improves performance.

    Progress is tangible though. This morning the app only displayed temp strings. Tonight it’s showing actual content and it’s starting to feel like a real app.

    T minus 57-days…

    CloudKit


    Sunday July 23, 2023
  • Yesterday I realized my database had duplicate records, and that CloudKit doesn’t support unique constraints. So I added a check to make sure a record doesn’t exist before writing it in. My code evolved into a mix of bits from the documentation and a few phone-a-friends with ChatGPT. It worked. But how?

    Today I learned the fetch method on CKDatabase takes in a completion handler as a parameter, which gets called after the operation with the outcome of the fetch. The completion handler itself takes in a single Result parameter, which is a basic type from the Swift Standard Library.

    @frozen public enum Result<Success, Failure> where Failure : Error {
        case success(Success) 
        case failure(Failure)
    }
    

    Notice the <Success, Failure> in the enum’s declaration. It’s using Swift Generics as a placeholder to represent any Success or Failure value. In CloudKit, these are defined by the fetch method to return either a tuple of results, or an error if the request fails. So in the case of .success, I get back an array of matchResults. If this array .isEmpty, a write won’t create a duplicate record.

    CloudKit


    Sunday July 23, 2023
  • Architecture diagram: a macOS app writes data to CloudKit, an iOS app then reads this data from CloudKit

    (1) Made new CKContainer in the console.
    (2) Created macOS app with an iCloud entitlement to the new container.
    (3) Inflated the container’s public database with a schema and sample records.
    (4) Created separate iOS app with entitlement to same CloudKit container.
    (5) Fetched CKRecord objects from CloudKit and displayed results in SwiftUI.

    I’ve explored CloudKit as a compliment to Core Data before. But never as a standalone store like this. My main requirement was not using the console for schema and data tasks. I had reluctantly considered cktool cli or cktool.js with some node scripts, but neither of these felt great. A companion Mac app turned out to be a much nicer solution.

    Seeing the data appear on the iOS app for the first time was a good moment. I didn’t expect to get this far today!

    T-minus 58 days…

    CloudKit


    Saturday July 22, 2023
  • So to prevent writing duplicates into CloudKit, I run this CKQuery first, and if no matches come back, I create a new record.

    let predicate = NSPredicate(format: "%K == %@", recordField, recordValue)
    let query = CKQuery(recordType: recordType, predicate: predicate)
    

    CloudKit


    Saturday July 22, 2023
  • CloudKit doesn’t support unique constraints on schema fields. So running my setup code multiple times led to duplicate records in CloudKit (same data with different UUIDs). So I’m going to try using CKQuery to make sure a record doesn't already exist before creating it.

    CloudKit


    Saturday July 22, 2023
  • I didn’t see a CloudKit API in Swift to reset the development schema. So I made a SwiftUI button that runs a shell script of xcrun cktool reset-schema. But I couldn’t get it to work.

    Error executing command: Error Domain=NSCocoaErrorDomain Code=4 “The file “🧨.sh” doesn’t exist.”

    It does exist. But that’s ok. For now, I will click “Reset Environment…” in the CloudKit console and move on with my day.

    CloudKit


    Saturday July 22, 2023
  • I thought I’d have to define a CKRecord.RecordType along with a schema of field names and types before writing any data into CloudKit. But this is not necessary because a new RecordType is automatically created and modeled after the first instance of that particular type to be saved to the database.

    CloudKit


    Saturday July 22, 2023
  • First checkpoint. I have a shell of a Mac app with a CloudKit entitlement. It has a single button that creates a CKRecord object and saves it to the container’s public database. And in the CloudKit console, I see the new record type with schema defined along with the sample data!

    CloudKit


    Saturday July 22, 2023
  • I’m breaking ground on a companion Mac app today. It’s main job is to ingest JSON records, transform them to match a CloudKit schema and write them to a public CloudKit database.

    And my neighbors are jack hammering their patio, which is bringing some good build something ambiance.

    CloudKit


    Saturday July 22, 2023
  • I’m building a reader app for images. The content is all similar (in the same subject domain) – but coming from multiple sources, each with it’s own schema. Some is available via API, but some is only via data dump.

    Phase 1 is defining a common schema, fetching all the data, normalizing it into the shared schema, and then writing it all into a public CloudKit database.

    T-minus 59 days...

    CloudKit


    Friday July 21, 2023