Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to use iCloud Documents Storage

Tags:

ios

swift

icloud

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.

like image 669
Romain Avatar asked Nov 24 '15 06:11

Romain


People also ask

Is it safe to store documents in iCloud?

The iCloud has excellent and very transparent encryption protocols. They use end-to-end encryption for all data on the platform, whether it's in storage or in transit. Even when it's stored on third-party databases, those third parties are mandated by Apple to uphold a rigorous standard of safety.


2 Answers

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)")
            }
        }
    }
}



}
like image 190
Romain Avatar answered Nov 16 '22 03:11

Romain


for those who wants to use SWIFT 3: NOTE: Instead of moving the data I just do copy. But the destination path is cleared before copy data there..

class CloudDataManager {

    static let sharedInstance = CloudDataManager() // Singleton

    struct DocumentsDirectory {
        static let localDocumentsURL = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: .userDomainMask).last!
        static let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
    }


    // Return the Document directory (Cloud OR Local)
    // To do in a background thread

    func getDocumentDiretoryURL() -> URL {
        if 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: URL?) {
        let fileManager = FileManager.default
        let enumerator = fileManager.enumerator(atPath: url!.path)
        while let file = enumerator?.nextObject() as? String {

            do {
                try fileManager.removeItem(at: url!.appendingPathComponent(file))
                print("Files deleted")
            } catch let error as NSError {
                print("Failed deleting files : \(error)")
            }
        }
    }

    // Copy local files to iCloud
    // iCloud will be cleared before any operation
    // No data merging

    func copyFileToCloud() {
        if isCloudEnabled() {
            deleteFilesInDirectory(url: DocumentsDirectory.iCloudDocumentsURL!) // Clear all files in iCloud Doc Dir
            let fileManager = FileManager.default
            let enumerator = fileManager.enumerator(atPath: DocumentsDirectory.localDocumentsURL.path)
            while let file = enumerator?.nextObject() as? String {

                do {
                    try fileManager.copyItem(at: DocumentsDirectory.localDocumentsURL.appendingPathComponent(file), to: DocumentsDirectory.iCloudDocumentsURL!.appendingPathComponent(file))

                    print("Copied to iCloud")
                } catch let error as NSError {
                    print("Failed to move file to Cloud : \(error)")
                }
            }
        }
    }

    // Copy iCloud files to local directory
    // Local dir will be cleared
    // No data merging

    func copyFileToLocal() {
        if isCloudEnabled() {
            deleteFilesInDirectory(url: DocumentsDirectory.localDocumentsURL)
            let fileManager = FileManager.default
            let enumerator = fileManager.enumerator(atPath: DocumentsDirectory.iCloudDocumentsURL!.path)
            while let file = enumerator?.nextObject() as? String {

                do {
                    try fileManager.copyItem(at: DocumentsDirectory.iCloudDocumentsURL!.appendingPathComponent(file), to: DocumentsDirectory.localDocumentsURL.appendingPathComponent(file))

                    print("Moved to local dir")
                } catch let error as NSError {
                    print("Failed to move file to local dir : \(error)")
                }
            }
        }
    }



}
like image 45
Yaroslav Dukal Avatar answered Nov 16 '22 01:11

Yaroslav Dukal