CloudKit

  • Undocumented

    See more

    Declaration

    Objective-C

    @interface YapDatabaseCloudKitOptions : NSObject <NSCopying>
    
    /**
     * You can configure the extension to pre-filter all but a subset of collections.
     *
     * The primary motivation for this is to reduce the overhead when first setting up the extension.
     * For example, if you're only syncing objects from a single collection,
     * then you could specify that collection here. So when the extension first populates itself,
     * it will enumerate over just the allowedCollections, as opposed to enumerating over all collections.
     * And enumerating a small subset of the entire database during initial setup can improve speed,
     * especially with larger databases.
     *
     * In addition to reducing the overhead during initial setup,
     * the allowedCollections will pre-filter while you're making changes to the database.
     * So if you add a new object to the database, and the associated collection isn't in allowedCollections,
     * then the GetRecordBlock will never be invoked, and the extension will act as if the block returned nil.
     *
     * For all rows whose collection is in the allowedCollections, the extension acts normally.
     * So the GetRecordBlock would still be invoked as normal.
     *
     * The default value is nil.
     */
    @property (nonatomic, strong, readwrite, nullable) YapWhitelistBlacklist *allowedCollections;
    
    
    // Todo: Need ability to set default options for CKModifyRecordsOperation
    
    @end

    Swift

    class YapDatabaseCloudKitOptions : NSObject, NSCopying
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface YapDatabaseCloudKitTransaction : YapDatabaseExtensionTransaction
    
    /**
     * If the given recordID & databaseIdentifier are associated with a row in the database,
     * then this method will return YES, and set the collectionPtr/keyPtr with the collection/key of the associated row.
     * 
     * @param keyPtr (optional)
     *   If non-null, and this method returns YES, then the keyPtr will be set to the associated row's key.
     * 
     * @param collectionPtr (optional)
     *   If non-null, and this method returns YES, then the collectionPtr will be set to the associated row's collection.
     * 
     * @param recordID
     *   The CKRecordID to look for.
     * 
     * @param databaseIdentifier
     *   The identifying string for the CKDatabase.
     *   @see YapDatabaseCloudKitDatabaseIdentifierBlock.
     * 
     * @return
     *   YES if the given recordID & databaseIdentifier are associated with a row in the database.
     *   NO otherwise.
     * 
     *
     * Note:
     *   It's possible to associate multiple items in the database with a single CKRecord/databaseIdentifier.
     *   This is completely legal, and supported by YapDatabaseCloudKit extension.
     *   However, if you do this keep in mind that this method will only return 1 of the associated items.
     *   Further, which item it returns is not guaranteed, and may change between method invocations.
     *   So, in this particular case, you likely should be using 'collectionKeysForRecordID:databaseIdentifier:'.
     */
    - (BOOL)getKey:(NSString * _Nonnull * _Nullable)keyPtr collection:(NSString * _Nonnull * _Nullable)collectionPtr
                                                          forRecordID:(CKRecordID *)recordID
                                                   databaseIdentifier:(nullable NSString *)databaseIdentifier;
    
    /**
     * It's possible to associate multiple items in the database with a single CKRecord/databaseIdentifier.
     * This is completely legal, and supported by YapDatabaseCloudKit extension.
     * 
     * This method returns an array of YapCollectionKey objects,
     * each associated with the given recordID/databaseIdentifier.
     * 
     * @see YapCollectionKey
     */
    - (NSArray<YapCollectionKey *> *)collectionKeysForRecordID:(CKRecordID *)recordID
                                            databaseIdentifier:(nullable NSString *)databaseIdentifier;
    
    /**
     * If the given key/collection tuple is associated with a record,
     * then this method returns YES, and sets the recordIDPtr & databaseIdentifierPtr accordingly.
     * 
     * @param recordIDPtr (optional)
     *   If non-null, and this method returns YES, then the recordIDPtr will be set to the associated recordID.
     * 
     * @param databaseIdentifierPtr (optional)
     *   If non-null, and this method returns YES, then the databaseIdentifierPtr will be set to the associated value.
     *   Keep in mind that nil is a valid databaseIdentifier,
     *   and is generally used to signify the defaultContainer/privateCloudDatabase.
     *
     * @param key
     *   The key of the row in the database.
     * 
     * @param collection
     *   The collection of the row in the database.
     * 
     * @return
     *   YES if the given collection/key is associated with a CKRecord.
     *   NO otherwise.
     */
    - (BOOL)getRecordID:(CKRecordID * _Nonnull * _Nullable)recordIDPtr
     databaseIdentifier:(NSString * _Nonnull * _Nullable)databaseIdentifierPtr
                 forKey:(NSString *)key
           inCollection:(nullable NSString *)collection;
    
    /**
     * Returns a copy of the CKRcord for the given recordID/databaseIdentifier.
     * 
     * Keep in mind that YapDatabaseCloudKit stores ONLY the system fields of a CKRecord.
     * That is, it does NOT store any key/value pairs.
     * It only stores "system fields", which is the internal metadata that CloudKit uses to handle sync state.
     * 
     * So if you invoke this method from within a read-only transaction,
     * then you will receive a "base" CKRecord, which is really only useful for extracting "system field" metadata,
     * such as the 'recordChangeTag'.
     * 
     * If you invoke this method from within a read-write transaction,
     * then you will receive the "base" CKRecord, along with any modifications that have been made to the CKRecord
     * during the current read-write transaction.
     * 
     * Also keep in mind that you are receiving a copy of the record which YapDatabaseCloudKit is using internally.
     * If you intend to manually modify the CKRecord directly,
     * then you need to save those changes back into YapDatabaseCloudKit via 'saveRecord:databaseIdentifier'.
     * 
     * @see saveRecord:databaseIdentifier:
     */
    - (CKRecord *)recordForRecordID:(CKRecordID *)recordID databaseIdentifier:(nullable NSString *)databaseIdentifier;
    
    /**
     * Convenience method.
     * Combines the following two methods into a single call:
     * 
     * - getRecordID:databaseIdentifier:forKey:inCollection:
     * - recordForRecordID:databaseIdentifier:
     * 
     * @see recordForRecordID:databaseIdentifier:
     */
    - (CKRecord *)recordForKey:(NSString *)key inCollection:(nullable NSString *)collection;
    
    /**
     * High performance lookup method, if you only need to know if YapDatabaseCloudKit has a
     * record for the given recordID/databaseIdentifier.
     *
     * This method is much faster than invoking recordForRecordID:databaseIdentifier:,
     * if you don't actually need the record.
     * 
     * @return
     *   Whether or not YapDatabaseCloudKit is currently managing a record for the given recordID/databaseIdentifer.
     *   That is, whether or not there is currently one or more rows in the database attached to the CKRecord.
     */
    - (BOOL)containsRecordID:(CKRecordID *)recordID databaseIdentifier:(nullable NSString *)databaseIdentifier;
    
    /**
     * Use this method during CKFetchRecordChangesOperation.fetchRecordChangesCompletionBlock.
     * The values returned by this method will help you determine how to process each reported changedRecord.
     *
     * @param outRecordChangeTag
     *   If YapDatabaseRecord is managing a record for the given recordID/databaseIdentifier,
     *   this this will be set to the local record.recordChangeTag value.
     *   Remember that CloudKit tells us about changes that we made.
     *   It doesn't do so via push notification, but it still does when we use a CKFetchRecordChangesOperation.
     *   Thus its advantageous for us to ignore our own changes.
     *   This can be done by comparing the changedRecord.recordChangeTag vs outRecordChangeTag.
     *   If they're the same, then we already have this CKRecord (this change) in our system, and we can ignore it.
     *   
     *   Note: Sometimes during development, we may screw up some merge operations.
     *   This may happen when we're changing our data model(s) and record.
     *   If this happens, you can ignore the recordChangeTag,
     *   and force another merge by invoking mergeRecord:databaseIdentifier: again.
     * 
     * @param outPendingModifications
     *   Tells you if there are changes in the queue for the given recordID/databaseIdentifier.
     *   That is, whether or not this record has been modified, and we have modifications that are still
     *   pending upload to the CloudKit servers.
     *   If this value is YES, then you MUST invoke mergeRecord:databaseIdentifier:.
     *   
     *   Note: It's possible for this value to be YES, and for outRecordChangeTag to be nil.
     *   This may happen if the user modified a record, deleted it, and neither of these changes have hit the server yet.
     *   Thus YDBCK no longer actively manages the record, but it does have changes for it sitting in the queue.
     *   Failure to observe this value could result in an infinite loop:
     *   attempt upload, partial error, fetch changes, failure to invoke merge properly, attempt upload, partial error...
     *
     * @param outPendingDelete
     *   Tells you if there is a pending delete of the record in the queue.
     *   That is, if we deleted the item locally, and the delete operation is pending upload to the cloudKit server.
     *   If this value is YES, then you may not want to create a new database item for the record.
     */
    - (void)getRecordChangeTag:(NSString * _Nullable * _Nullable)outRecordChangeTag
       hasPendingModifications:(nullable BOOL *)outPendingModifications
              hasPendingDelete:(nullable BOOL *)outPendingDelete
                   forRecordID:(CKRecordID *)recordID
            databaseIdentifier:(nullable NSString *)databaseIdentifier;
    
    @end

    Swift

    class YapDatabaseCloudKitTransaction : YapDatabaseExtensionTransaction
  • The RecordHandler is the primary mechanism that is used to tell YapDatabaseCloudKit about CKRecord changes. That is, as you make changes to your own custom data model objects, you can use the RecordHandler block to tell YapDatabaseCloudKit about the changes that were made by handing it CKRecords.

    Here’s the general idea:

  • You update an object in the database via the normal setObject:forKey:inCollection method.
  • Since

    Since YapDatabaseCloudKit is an extension, it’s automatically notified that you modified an object.
  • YapDatabaseCloudKit then invokes the recordHandler, and passes you the modified object, along with an empty base CKRecord (if available), and asks you to set the proper values on the record.
  • Afterwards, the extension will check to see if it needs to upload the CKRecord (if it has changes), and handles the rest if it does.
  • For more information & sample code, please see the wiki: https://github.com/yapstudios/YapDatabase/wiki/YapDatabaseCloudKit#RecordHandlerBlock

    See more

    Declaration

    Objective-C

    @interface YapDatabaseCloudKitRecordHandler : NSObject

    Swift

    class YapDatabaseCloudKitRecordHandler : NSObject
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface YDBCKRecord : NSObject <NSCoding>
    
    /**
     * This method serializes just the "system fields" of the given record.
     * That is, it won't store any of the user-created key/value pairs.
     * It only stores the CloudKit specific stuff, such as the versioning info, syncing info, etc.
     */
    + (NSData *)serializeRecord:(CKRecord *)record;
    
    /**
     * Deserialize the given record data.
     *
     * If the record data came from [YDBCKRecord serializeRecord:],
     * then the returned record will only contain the "system fields".
     */
    + (CKRecord *)deserializeRecord:(NSData *)data;
    
    #pragma mark Instance
    
    - (instancetype)initWithRecord:(CKRecord *)record;
    
    @property (nonatomic, strong, readonly) CKRecord *record;
    
    @end

    Swift

    class YDBCKRecord : NSObject, NSCoding
  • This utility class is used by the YapDatabaseCloudKitRecordBlock. It provides metadata about the CKRecord.

    There are only 4 properties, which can be broken up into 2 sections:

    • Properties you can optionally SET (within the recordBlock):

      • databaseIdentifier
      • originalValues
    • Properties you need to CHECK (within the recordBlock):

      • keysToRestore
      • versionInfo
    See more

    Declaration

    Objective-C

    @interface YDBCKRecordInfo : NSObject

    Swift

    class YDBCKRecordInfo : NSObject
  • A change-set represents the set of changes that will be given to a CKModifyRecordsOperation.

    A change-set is automatically generated by a readWriteTransaction that makes one or more changes that result in modifications that need to be pushed to the CloudKit server.

    Changes are grouped by databaseIdentifier. So if a single readWriteTransaction makes several modifications that span multiple databaseIdentifiers, then the transaction will result in multiple generated change-sets.

    You are free to inspect the change-set, however, it is not possible to modify it.

    See more

    Declaration

    Objective-C

    @interface YDBCKChangeSet : NSObject

    Swift

    class YDBCKChangeSet : NSObject
  • This utility class is used by the YapDatabaseCloudKitMergeBlock.

    See more

    Declaration

    Objective-C

    @interface YDBCKMergeInfo : NSObject

    Swift

    class YDBCKMergeInfo : NSObject
  • Undocumented

    See more

    Declaration

    Objective-C

    @interface CKRecord (YapDatabaseCloudKit)
    
    /**
     * Returns a "sanitized" copy of the given record.
     * That is, a copy that ONLY includes the "system fields" of the record.
     * It will NOT contain any key/value pairs from the original record.
     */
    - (id)sanitizedCopy;
    
    /**
     * There was a bug in early versions of CloudKit:
     *
     * Calling [ckRecord copy] was completely broken.
     * This forced us to use a workaround.
     * 
     * The bug was fixed in iOS 9.
     */
    - (id)safeCopy;
    
    @end