ZeroDarkCloudDelegate

@protocol ZeroDarkCloudDelegate

The ZeroDarkCloudDelegate assists in push & pull operations, and facilitates communication about cloud changes.

  • When the PushManager is ready to upload a node, it uses this method to request the node’s data.

    You supply the node’s data, and the framework handles encrypting the data & uploading it to the cloud.

    In terms of size, the framework supports everything from empty (zero-length) nodes, to multi-gigabyte nodes. The ZDCData class helps to support this diversity by providing multiple ways in which you can reference the data that is to be encrypted & uploaded.

    For small nodes, you can simply serialize the data, and create a ZDCData holding those in-memory bytes. For large files, you can provide a reference via a fileURL.

    The framework also supports files that are stored to disk in an encrypted format via ZDCCryptoFile. For example, you can use the ZDCDiskManager to store files on disk in an encrypted manner. And files stored by the ZDCDiskManager can be easily uploaded by initializing a ZDCData instance from a ZDCCryptoFile returned from the DiskManager.

    Note

    The data that you return will be properly encrypted by the framework before uploading it to the cloud.

    Declaration

    Objective-C

    - (nonnull ZDCData *)dataForNode:(nonnull ZDCNode *)node
                              atPath:(nonnull ZDCTreesystemPath *)path
                         transaction:(id)transaction;

    Swift

    func data(for node: ZDCNode, at path: ZDCTreesystemPath, transaction: Any!) -> ZDCData

    Parameters

    node

    The node that is being uploaded.

    path

    The treesystem path of the node.

    transaction

    An atomic transaction in which its safe to read any needed information from the database.

    Return Value

    A ZDCData instance that wraps the data to be uploaded.

  • When the PushManager is ready to upload a node, it uses this method request the node’s optional metadata.

    You supply the node’s metadata, and the framework handles encrypting it & uploading it to the cloud.

    In terms of size, the framework supports everything from empty (zero-length) nodes, to multi-gigabyte nodes. If you’re storing large files in the cloud, you may want to include an additional metadata section. By doing so, you allow other devices to download the metadata independently from the (large) full node.

    For example, say the node is a movie file that’s over 100 MB. You could also include a small bit of metadata that includes the movie’s length, format, etc. This would allow other devices to download just the metadata and thumbnail, and then have everything needed to display the movie within your UI.

    (Remember, the server cannot read the content that you store in the cloud. So it’s impossible to ask the server to extract any information, such as metadata or a thumbnail.)

    Note

    The data that you return will be properly encrypted by the framework before uploading it to the cloud.

    Declaration

    Objective-C

    - (nullable ZDCData *)metadataForNode:(nonnull ZDCNode *)node
                                   atPath:(nonnull ZDCTreesystemPath *)path
                              transaction:(id)transaction;

    Swift

    func metadata(for node: ZDCNode, at path: ZDCTreesystemPath, transaction: Any!) -> ZDCData?

    Parameters

    node

    The node that is being uploaded.

    path

    The treesystem path of the node.

    transaction

    An atomic transaction in which its safe to read any needed information from the database.

    Return Value

    Either nil, or a ZDCData instance that wraps the (serialized) metadata to be included in the upload.

  • When the PushManager is ready to upload a node, it uses this method to request the node’s optional thumbnail.

    You supply the node’s thumbnail, and the framework handles encrypting it & uploading it to the cloud.

    In terms of size, the framework supports everything from empty (zero-length) nodes, to multi-gigabyte nodes. If you’re storing large files in the cloud, you may want to include an additional thumbnail section. By doing so, you allow other devices to download the thumbnail independently from the (large) full node.

    For example, say the node is a large image from a modern camera. You could also include a small thumbnail version of the image. This would allow other devices to download just the thumbnail, and then have everything needed to display the image within your UI. The app will then use less bandwidth & less disk storage for each image. The full version of the image can be downloaded on demand, as needed.

    (Remember, the server cannot read the content that you store in the cloud. So it’s impossible to ask the server to extract any information, such as metadata or a thumbnail.)

    Note

    The data that you return will be properly encrypted by the framework (as needed) before uploading it to the cloud.

    Declaration

    Objective-C

    - (nullable ZDCData *)thumbnailForNode:(nonnull ZDCNode *)node
                                    atPath:(nonnull ZDCTreesystemPath *)path
                               transaction:(id)transaction;

    Swift

    func thumbnail(for node: ZDCNode, at path: ZDCTreesystemPath, transaction: Any!) -> ZDCData?

    Parameters

    node

    The node that is being uploaded.

    path

    The treesystem path of the node.

    transaction

    An atomic transaction in which its safe to read any needed information from the database.

    Return Value

    Either nil, or a ZDCData instance that wraps the (serialized) thumbnail to be included in the upload.

  • This method is called by the framework after a node’s data has been pushed to the cloud.

    When this method is called, the ZDCNode instance has already been updated within the database. This method is being called within the same atomic transaction that modifies the node in the database.

    @path The location of the node within the treesystem.

    Declaration

    Objective-C

    - (void)didPushNodeData:(nonnull ZDCNode *)node
                     atPath:(nonnull ZDCTreesystemPath *)path
                transaction:(id)transaction;

    Swift

    func didPushNodeData(_ node: ZDCNode, at path: ZDCTreesystemPath, transaction: Any!)

    Parameters

    node

    The updated node.

    transaction

    The atomic transaction in which the node was modified in the database.

  • When the PushManager is ready to send an outgoing message, it uses this method to request the message data.

    You supply the message data, and the framework handles encrypting it & uploading it to the cloud.

    Declaration

    Objective-C

    - (nullable ZDCData *)dataForMessage:(nonnull ZDCNode *)message
                             transaction:(id)transaction;

    Swift

    func data(forMessage message: ZDCNode, transaction: Any!) -> ZDCData?

    Parameters

    message

    The outgoing message to be sent.

    transaction

    An atomic transaction in which its safe to read any needed information from the database.

    Return Value

    Either nil, or a ZDCData instance that wraps the message data to send.

  • This method is called by the framework after a message has been sent.

    When this method is called, the queued ZDCCloudOperation has already been deleted from the database. This method is being called within the same atomic transaction that modifies the the database.

    Declaration

    Objective-C

    - (void)didSendMessage:(nonnull ZDCNode *)message
               toRecipient:(nonnull ZDCUser *)recipient
               transaction:(id)transaction;

    Swift

    func didSendMessage(_ message: ZDCNode, toRecipient recipient: ZDCUser, transaction: Any!)

    Parameters

    message

    The message which was sent.

    transaction

    The atomic transaction in which the database was modified. This allows you to update your own objects within the same atomic transaction that removes the queued outgoing message.

  • This method is called by the framework when a new node has been discovered in the cloud.

    This method is NOT called if you directly create a new node (in your app, on this device). It’s only called if a new node has been detected in the cloud. In other words, you’re being notified about some change in the cloud that the framework discovered while syncing.

    The framework automatically fetches the treesystem information from the cloud. That is, the node’s name, permissions, and its location within the tree. However the framework does NOT automatically download the node’s data (the content your app creates). Instead the framework allows you to make those decisions, which allows you to optimize for your app. For example, you may choose not to download all content. Or perhaps download certain content on demand.

    When you’re ready to download the node’s content, the ZDCDownloadManager has multiple methods which allow you to download different things.

    To download the entire node data, you can use -[ZDCDownloadManager downloadNodeData:options:completionQueue:completionBlock:].

    Alternatively, you can choose just to download the node’s metadata or thumbnail via -[ZDCDownloadManager downloadNodeMeta:components:options:completionQueue:completionBlock:].

    Note

    Recall that the server cannot read the content that’s stored in the cloud. So the server has no idea if the content is an image. Which means it’s impossible for the server to generate a thumbnail of your content on the fly. If you want to enable the download of smaller thumbnails, you must include these with the upload, via -thumbnailForNode:atPath:transaction:.

    When this method is called, the ZDCNode instance has already been added to the database. This method is being called within the same atomic transaction that adds the node to the database.

    @path The location of the node within the treesystem.

    Declaration

    Objective-C

    - (void)didDiscoverNewNode:(nonnull ZDCNode *)node
                        atPath:(nonnull ZDCTreesystemPath *)path
                   transaction:(id)transaction;

    Swift

    func didDiscoverNewNode(_ node: ZDCNode, at path: ZDCTreesystemPath, transaction: Any!)

    Parameters

    node

    The newly discovered node.

    transaction

    The atomic transaction in which the node was added to the database.

  • This method is called by the framework when a node is discovered to have been modified in the cloud.

    This method is NOT called if you directly modify a node (in your app, on this device). It’s only called if a modified node has been detected in the cloud. In other words, you’re being notified about some change in the cloud that the framework discovered while syncing.

    There are two possibilities here.

    • Only the treesystem info was modified (filename, permissions, etc)
    • Only the node’s data was modified (the content generated by your app)

    When this method is called, the ZDCNode instance has already been updated within the database. This method is being called within the same atomic transaction that modifies the node in the database.

    Declaration

    Objective-C

    - (void)didDiscoverModifiedNode:(nonnull ZDCNode *)node
                         withChange:(ZDCNodeChange)change
                             atPath:(nonnull ZDCTreesystemPath *)path
                        transaction:(id)transaction;

    Swift

    func didDiscoverModifiedNode(_ node: ZDCNode, with change: ZDCNodeChange, at path: ZDCTreesystemPath, transaction: Any!)

    Parameters

    node

    The modified node.

    path

    The location of the node within the treesystem.

    transaction

    The atomic transaction in which the node was modified in the database.

  • Invoked when the system discovers that a node was moved and/or renamed.

    This method is NOT called if you directly move/rename a node (in your app, on this device). It’s only called if a moved/renamed node has been detected in the cloud. In other words, you’re being notified about some change in the cloud that the framework discovered while syncing.

    When this method is called, the node has already been updated, and the updates have been written to the database. This method is being called within the same atomic transaction that modified the node in the database.

    Declaration

    Objective-C

    - (void)didDiscoverMovedNode:(nonnull ZDCNode *)node
                            from:(nonnull ZDCTreesystemPath *)oldPath
                              to:(nonnull ZDCTreesystemPath *)newPath
                     transaction:(id)transaction;

    Swift

    func didDiscoverMovedNode(_ node: ZDCNode, from oldPath: ZDCTreesystemPath, to newPath: ZDCTreesystemPath, transaction: Any!)

    Parameters

    node

    The node that was moved and/or renamed.

    oldPath

    The treesystem path of the node before it was moved.

    newPath

    The treesystem path of the node after it was moved.

    transaction

    An active read-write transaction. This is the same transaction in which the node was just deleted. Its recommended you update your own objects within the same atomic transaction.

  • Invoked when the system discovers that a node has been deleted from the cloud.

    This method is NOT called if you directly delete a node (in your app, on this device). It’s only called if a deleted node has been detected in the cloud. In other words, you’re being notified about some change in the cloud that the framework discovered while syncing.

    When this method is called, the node has already been removed from the database. This method is being called within the same atomic transaction that removed the node from the database.

    Declaration

    Objective-C

    - (void)didDiscoverDeletedNode:(nonnull ZDCNode *)node
                            atPath:(nonnull ZDCTreesystemPath *)path
                         timestamp:(nullable NSDate *)timestamp
                       transaction:(id)transaction;

    Swift

    func didDiscoverDeletedNode(_ node: ZDCNode, at path: ZDCTreesystemPath, timestamp: Date?, transaction: Any!)

    Parameters

    node

    The node that was deleted.

    path

    The treesystem path of the node that was deleted.

    timestamp

    The date & time the delete was performed by the server. If the client is relatively up-to-date, we’ll have this information from the server. If not, this information won’t be available for us.

    transaction

    An active read-write transaction. This is the same transaction in which the node was just deleted. Its recommended you update your own objects within the same atomic transaction.

  • This (optional) method is called when:

    • the framework discovered that node X was deleted from the cloud
    • but the framework still has operations queued to modify children of node X

    The proper way to handle such a situation is highly dependent on both the app & specific node.

    Here’s what the framework does:

    • It calculates a list of clean top-level nodes that are children/ancestors of ‘node’.
    • It invokes this method to inform you of the situation
    • Afterwards it enumerates all the clean top-level nodes, and deletes them (if they still exist). For each deleted node, the didDiscoverDeletedNode:::: delegate method is invoked.

    The deleted dirty node is NOT automatically deleted from the database. Neither are any of the dirty ancestors, nor any parents that lead from ‘node’ to a dirty ancestor.

    When this delegate method is invoked, neither the node, nor any of its ancestors (clean or dirty) have been deleted from the database.

    Declaration

    Objective-C

    - (void)didDiscoverDeletedDirtyNode:(nonnull ZDCNode *)node
                         dirtyAncestors:
                             (nonnull NSArray<NSString *> *)dirtyAncestors
                              timestamp:(nullable NSDate *)timestamp
                            transaction:(id)transaction;

    Swift

    optional func didDiscoverDeletedDirtyNode(_ node: ZDCNode, dirtyAncestors: [String], timestamp: Date?, transaction: Any!)
  • Invoked when a conflict is detected.

    The framework can automatically recover from some conflicts, while other conflicts may require you to take action.

    For details on the types of conflicts, and how to deal with them, see ZDCNodeConflict.

    Declaration

    Objective-C

    - (void)didDiscoverConflict:(ZDCNodeConflict)conflict
                        forNode:(nonnull ZDCNode *)node
                         atPath:(nonnull ZDCTreesystemPath *)path
                    transaction:(id)transaction;

    Swift

    func didDiscoverConflict(_ conflict: ZDCNodeConflict, forNode node: ZDCNode, atPath path: ZDCTreesystemPath, transaction: Any!)

    Parameters

    conflict

    This enum value tells you which type of conflict you’re dealing with.

    node

    This is the local version of the node that is in conflict with the cloud.

    path

    The treesystem path of the node in conflict.

    transaction

    An active read-write transaction.

  • Background downloads can be tricky - and this delegate method is for an edge case.

    The edge case occurs when your app is terminated, but the background download ultimately completes, and the app is notified about the completed download AFTER a NEW app launch. In other words, the completionBlock you had initially installed to handle the download request no longer exists because the app has been restarted since then.

    It’s an edge case for sure, but also one that may not be too difficult to handle. This method, if implemented, allows you to handle this edge case.

    @note: This method is ONLY used on iOS. It will never be invoked on macOS. @note: This method is never invoked for failed downloads - only successful downloads where there’s actual data your app may be interested in.

    Declaration

    Objective-C

    - (void)didBackgroundDownloadNodeMeta:(nonnull ZDCNode *)node
                                   atPath:(nonnull ZDCTreesystemPath *)path
                           withComponents:(ZDCNodeMetaComponents)components
                                   header:(nonnull ZDCCloudDataInfo *)header
                                 metadata:(nullable NSData *)metadata
                                thumbnail:(nullable NSData *)thumbnail;

    Swift

    optional func didBackgroundDownloadNodeMeta(_ node: ZDCNode, at path: ZDCTreesystemPath, with components: ZDCNodeMetaComponents, header: ZDCCloudDataInfo, metadata: Data?, thumbnail: Data?)

    Parameters

    node

    The node for which the meta download was requested.

    path

    The treesystem path of the node.

    components

    The meta components that were requested.

    header

    The header information for the DATA file in the cloud. Recall that the DATA file stores the node’s contents (metadata, thumbnail & data).

    metadata

    If requested, and if the DATA file contains a metadata section, this value will be non-nil.

    thumbnail

    If requested, and if the DATA file contains a thumbanil section, this value will be non-nil.

  • Background downloads can be tricky - and this delegate method is for an edge case.

    The edge case occurs when your app is terminated, but the background download ultimately completes, and the app is notified about the completed download AFTER a NEW app launch. In other words, the completionBlock you had initially installed to handle the download request no longer exists because the app has been restarted since then.

    It’s an edge case for sure, but also one that may not be too difficult to handle. This method, if implemented, allows you to handle this edge case.

    @note: This method is ONLY used on iOS. It will never be invoked on macOS. @note: This method is never invoked for failed downloads - only successful downloads where there’s actual data your app may be interested in.

    Declaration

    Objective-C

    - (void)didBackgroundDownloadNodeData:(nonnull ZDCNode *)node
                                   atPath:(nonnull ZDCTreesystemPath *)path
                           withCryptoFile:(nonnull ZDCCryptoFile *)cryptoFile;

    Swift

    optional func didBackgroundDownloadNodeData(_ node: ZDCNode, at path: ZDCTreesystemPath, with cryptoFile: ZDCCryptoFile)

    Parameters

    node

    The node for which the data download was requested.

    path

    The treesystem path of the node.

    cryptoFile

    The cryptoFile provides everything you need to read an encrypted file.

  • The PullManager automatically downloads the tree hierarchy, sans data. That is, it downloads the node treesystem metadata (filename & permissions), but it doesn’t download the node data (e.g. serialized objects, files, etc).

    This is typically very fast, because the tree hierarchy information is small, and can be downloaded & decrypted very quickly. However, there are situations in which this can appear slow. In particular:

    • the user just logged into your app, AND
    • the user has thousands of nodes in the cloud, AND
    • the user is trying to access a particular branch of the tree right NOW

    And this optimization is designed for this use case. It allows you to influence the priority in which the PullManager downloads nodes.

    By default the PullManager uses the following simple algorithm:

    • prefer nodes that are more shallow
    • if nodes have the same depth, prefer nodes that were modified more recently

    For example, the node ‘/foo/bar’ is preferred over ‘/cow/moo/milk’ because a depth of 2 is less than a depth of 3.

    And if ‘/cow/moo/milk’ was modified yesterday, and ‘/dog/bark/leash’ was modified 2 months ago, then ‘/cow/moo/milk’ will be given priority.

    This simple algorithm usually results in the best experience for most tree designs. But app specific knowledge is always preferred.

    Declaration

    Objective-C

    - (nullable NSSet<NSString *> *)preferredNodeIDsForPullingRcrds;

    Swift

    optional func preferredNodeIDsForPullingRcrds() -> Set<String>?