Search code examples
iosswifticloud

Best way to use iCloud Documents Storage


I am currently using local storage in my iOS App. The user data is stored in the Document Directory and now I am planning to use iCloud Documents storage instead.

Here is how I intend to do it :

  1. Checking if iCloud is available on the device

  2. If yes, use URLForUbiquityContainerIdentifier to get the iCloud container URL

  3. Save new files and documents to this new URL

For that I am using this code that will return the URL of the document folder (iCloud or local)

class CloudDataManager {

    class func getDocumentDiretoryURL() -> NSURL {
        let localDocumentsURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: .UserDomainMask).last! as NSURL
        let iCloudDocumentsURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)?.URLByAppendingPathComponent("Documents")

        if userDefault.boolForKey("useCloud") && iCloudDocumentsURL != nil  {
            return iCloudDocumentsURL!
        } else {
            return localDocumentsURL
        }
    }
}

Is it the best practice? I am worried problems will occur if one day iCloud isn't available so the local directory will be used instead of the cloud container and will be empty. Thank you.


Solution

  • Thanks to the comment above and with further readings, I've find a way to solve my problem.

    Here is how I decided to do it:

    • iCloud will be activated by default (if possible)
    • The user can use an UISwitch to disable/enable iCloud in the App
    • When the user disable iCloud, all the iCloud files will be transferred locally
    • When the user enable iCloud, all the local files will be transferred in the iCloud Ubiquity container
    • No data merging

    Like this data will not be lost.

    I guess almost everyone will use iCloud and everything will be transparent and painless. Anyway the files I sync are pretty small so it should work fine (so far it does).

    I have 5 simples methods:

    1. Method to check if iCloud is available
    2. Method to return the Document URL according to user choice (iCloud OR Local)
    3. Method to delete all files in a Directory (files used by the app)
    4. Method to move files from local dir to iCloud container
    5. Method to move fies from iCloud container to local dir

    Here is my class that handle the issue

    class CloudDataManager {
    
    static let sharedInstance = CloudDataManager() // Singleton
    
    struct DocumentsDirectory {
        static let localDocumentsURL: NSURL? = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: .UserDomainMask).last! as NSURL
       static let iCloudDocumentsURL: NSURL? = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)?.URLByAppendingPathComponent("Documents")
    
    }
    
    
    // Return the Document directory (Cloud OR Local)
    // To do in a background thread
    
    func getDocumentDiretoryURL() -> NSURL {
        print(DocumentsDirectory.iCloudDocumentsURL)
        print(DocumentsDirectory.localDocumentsURL)
        if userDefault.boolForKey("useCloud") && isCloudEnabled()  {
            return DocumentsDirectory.iCloudDocumentsURL!
        } else {
            return DocumentsDirectory.localDocumentsURL!
        }
    }
    
    // Return true if iCloud is enabled
    
    func isCloudEnabled() -> Bool {
        if DocumentsDirectory.iCloudDocumentsURL != nil { return true }
        else { return false }
    }
    
    // Delete All files at URL
    
    func deleteFilesInDirectory(url: NSURL?) {
        let fileManager = NSFileManager.defaultManager()
        let enumerator = fileManager.enumeratorAtPath(url!.path!)
        while let file = enumerator?.nextObject() as? String {
    
            do {
                try fileManager.removeItemAtURL(url!.URLByAppendingPathComponent(file))
                print("Files deleted")
            } catch let error as NSError {
                print("Failed deleting files : \(error)")
            }
        }
    }
    
    // Move local files to iCloud
    // iCloud will be cleared before any operation
    // No data merging
    
    func moveFileToCloud() {
        if isCloudEnabled() {
            deleteFilesInDirectory(DocumentsDirectory.iCloudDocumentsURL!) // Clear destination
            let fileManager = NSFileManager.defaultManager()
            let enumerator = fileManager.enumeratorAtPath(DocumentsDirectory.localDocumentsURL!.path!)
            while let file = enumerator?.nextObject() as? String {
    
                do {
                    try fileManager.setUbiquitous(true,
                        itemAtURL: DocumentsDirectory.localDocumentsURL!.URLByAppendingPathComponent(file),
                        destinationURL: DocumentsDirectory.iCloudDocumentsURL!.URLByAppendingPathComponent(file))
                    print("Moved to iCloud")
                } catch let error as NSError {
                    print("Failed to move file to Cloud : \(error)")
                }
            }
        }
    }
    
    // Move iCloud files to local directory
    // Local dir will be cleared
    // No data merging
    
    func moveFileToLocal() {
        if isCloudEnabled() {
            deleteFilesInDirectory(DocumentsDirectory.localDocumentsURL!)
            let fileManager = NSFileManager.defaultManager()
            let enumerator = fileManager.enumeratorAtPath(DocumentsDirectory.iCloudDocumentsURL!.path!)
            while let file = enumerator?.nextObject() as? String {
    
                do {
                    try fileManager.setUbiquitous(false,
                        itemAtURL: DocumentsDirectory.iCloudDocumentsURL!.URLByAppendingPathComponent(file),
                        destinationURL: DocumentsDirectory.localDocumentsURL!.URLByAppendingPathComponent(file))
                    print("Moved to local dir")
                } catch let error as NSError {
                    print("Failed to move file to local dir : \(error)")
                }
            }
        }
    }
    
    
    
    }