User Account Setup

There are two kinds of ZeroDark.cloud users: The ones you own are represented by ZDCLocalUser objects on your device. Other users, whom you might wish to share content to, are represented by ZDCUser objects. ZDCLocalUser is a subclass of ZDCUser and both kinds of objects can be kept persistent in the ZDC internal YapDatabase. The major difference between the two is that the ZDCLocalUser has a copy of the private key while the ZDCUser only has the public key part.

The actual data you sync with ZeroDark.cloud is stored in a user's assigned AWS S3 bucket protected by modern public key encryption. Since this encryption is performed by the ZeroDark.cloud framework automatically before any of that data ever leaves your device, only your devices and those people you explicitly share with, have access.

When you create an ZeroDark.cloud user account for the first time a private/public key pair is automatically generated on your device. Your public key and an encrypted version of your private key is uploaded to our server. The key that protects your private key is called an Access Key and it never leaves your device without your explicit permission. We never get a copy of it. So it is very important that you make a backup of the Access Key. The encryption section provides an overview of how all this works. For deeper technical details of how our cryptography works please refer to the cryptography section

You might also consider the option of allowing your app to support more than one ZDCLocalUser. In our development history we have found that as an app gets popular customers often want to use it for both their personal as well as office needs. In this case it is best to allow them to segregate the cloud data.

The ZDCUser objects come into play when you decide to share your app's objects. The management of these users is documented in the Sharing Section.


Creating or Signing-In to a User Account

Before your app can start working with synced data, you need to create a ZDCLocalUser object. While you have the flexibility to do this programmatically yourself, we highly recommend that you use the API provided by the ZDCManager. It handles all the user interaction for you, including both creating new users as well as signing in to an existing user.

let setupVC = 
    ZDCManager.uiTools().accountSetupViewController(withInitialViewController:nil,
                                            canDismissWithoutNewAccount:true)
        { ( localUserID:String?, completedActivation:Bool, shouldBackupAccessKey:Bool) in
            //  .. do something with new localUserID
        }

// push the setupVC


Accessing a ZDCLocalUser object

You can use the localUserID returned by accountSetupViewController to access the actual ZDCLocalUser object from the ZDC database you created earlier.

    var localUser: ZDCLocalUser? = nil
        ZDCManager.uiDatabaseConnection() .read { (transaction) in

            localUser = transaction.object(forKey: localUserID!, 
                                     inCollection: kZDCCollection_Users) as? ZDCLocalUser
        }

        if let localUser = localUser {
      print(user.displayName)
    }
Accessing the User Avatar

ZeroDarkCloud user accounts have an avatar image associated with them. This avatar originates from the social media or Storm4 account they use to login to ZeroDarkCloud. There is no guaranteed that a copy of the avatar will always be on the device, so we created imageManager APIs to access the avatar. The fetchUserAvatar is what most applications will need to use. This API will first check if there is a copy in the in memory cache and return it if possible, else it attempt to asynchronously load it from disk, as a last resort it will attempt to downloaded from the cloud or the social media site whichever is appropriate and cache the copy for later use.

Since these operations can occur asynchronously, we return the results with blocks or swift closures. In the case of fetchUserAvatar there is a preFetchBlock and a postFetchBlock. The preFetchBlock is always invoked, synchronously before the function returns. It only returns an image if there's a match in the cache that can immediately be used. The preFetchBlock also provides a willFetch Boolean parameter that will be FALSE if the the postFetchBlock will NOT be invoked. In the case that a an image can be provided, the postFetchBlock will be invoked after the image has been read from disk or downloaded from the cloud.

The imageManager also provides a defaultUserAvatar function to simply the cases where no avatar was available.

The following code snippet demonstrates fetching an avatar for ZDCLocalUser or ZDCUser object.

@IBOutlet public var userAvatar : UIImageView!
// assume user is ZDCLocalUser or ZDCUser object.

// fallback to default if no image found.
    let defaultImage = {
            return ZDCManager.imageManager().defaultUserAvatar()
    }

// handle image found in cache
    let preFetch = {(image: UIImage?, willFetch: Bool) in
             userAvatar.image = image ?? defaultImage()
    }

// image fetched from disk or cloud
    let postFetch = {(image: UIImage?, error: Error?) in
         userAvatar.image = image ?? defaultImage()
    }

    ZDCManager.imageManager().fetchUserAvatar(user,
                                                preFetch: preFetch, 
                                                postFetch: postFetch)

Continuing the Activation Process

There are times when the user activation can not be entirely completed in one pass. For example when an existing user signs in, but doesn't have their Access Key handy. In these cases the uiTools manager provides an API to resume the setup process.

let setupVC = ZDCManager.uiTools().accountResumeSetup(forLocalUserID: localUserID)
            { ( localUserID:String?, completedActivation:Bool, shouldBackupAccessKey:Bool) in
            //  .. do something with new localUserID
        }

// push the setupVC


Managing a User's Account Settings

ZDCLocalUser can have a number of social identities associated with them, these are handy when you want to add the ability to share data with other users. It makes it easier to lookup and verify that the user you share with is the same as you might have made contact with from outside your app. For those who have more security critical applications, even more so is the need to verify the account public key is authentic.

The ZeroDarkCloud uiTools provides simple interface to make it possible for the user to perform several user managment operations.

Screenshot

This view can be pushed using the following code snippet:

ZDCManager.uiTools().pushSettings(forLocalUserID: localUserID,
                                                     with: self.navigationController! )

In addition you can chose to present some of the views à la carte .

For example the Access Key backup view.

Screenshot

ZDCManager.uiTools().pushBackupAccessForLocalUserID(forLocalUserID: localUserID,
                                                     with: self.navigationController! )

Or the Social ID Management view

Screenshot

ZDCManager.uiTools().pushSocialIdMgmtWithUserID(forLocalUserID: localUserID,
                                                     with: self.navigationController! )
Allowing access to the Camera and Photo Library

On iOS and macOS you may wish to allow the user to scan a access key with the camera or save or retrieve the access key to the photo library. Since Apple considers this privacy-sensitive data they have added additional security and you are required to define and provide a usage description by adding the NSCameraUsageDescription and NSPhotoLibraryUsageDescription entries to your app's Info.plist file.

Screenshot

Removing a User Account from a device

Lastly, your app might want to allow a user to remove a ZDCLocalUser instance from the device database. This is fairly simple, although you might want to keep a few things in mind. Before allowing the user to remove its ZDCLocalUser instance it is considered good practice to check if the user has performed a backup of the Access Key. Otherwise the user can create a condition where they are no longer able to access their data in the cloud. You can easily determine this by checking the hasBackedUpAccessCode property of the ZDCLocalUser object. This property will be set if the user was cloned from another device or the user successfully completed a key backup with the ZeroDarkCloud uiTools as described above.

    var localUser: ZDCLocalUser? = nil
    ZDCManager.uiDatabaseConnection() .read { (transaction) in

            localUser = transaction.object(forKey: localUserID!, 
                                     inCollection: kZDCCollection_Users) as? ZDCLocalUser
        }

        if let localUser = localUser {

            if(!localUser.hasBackedUpAccessCode){           
                // warn user that before allowing delete
            }
      }

Once you are certain that this is what you want to do, removing the user can be accomplished by calling the deleteLocalUser function in the localUserManager. You need to do this within a YapDatabase readwrire connection block as demonstated below. Note that you will need to remove any objects you created related to that user and upon completion update the user interface to reflect that the user was deleted.