0

I've built an iOS app (TimeFinder) that uses plists to store user data, but I'm struggling to understand how I can support synchronizing users' data using iCloud.

I imagined that syncing plist data in this manner would be a reasonably common task, but I've scoured the web looking for answers, and all I can find are old Q&A threads that use Obj-C (One and Two). Those might be fine, but unfortunately I'm not yet familiar enough with Obj-C to figure out how to implement those strategies into my swift project. Even the Apple documentation on Document-based applications is entirely in obj-c and devoid of useful examples for my case.

In an attempt to get something going, I've managed to implement some code that copies the plist files from local storage to an iCloud Documents container, but it seems like this strategy might be a nightmare considering the potential for conflicts and data corruption.

Can someone give me some pointers on how to achieve my objective using Swift and UIDocument? Or should I just use the iCloud folder for storing my plists to begin with?

Here's one of the plists:

<plist version="1.0">
<array>
    <dict>
        <key>iconName</key>
        <string>Folder</string>
        <key>items</key>
        <array>
            <dict>
                <key>text</key>
                <string>Make handrail</string>
            </dict>
            <dict>
                <key>text</key>
                <string>Work out</string>
            </dict>
            <dict>
                <key>text</key>
                <string>Make kombucha </string>
            </dict>
        </array>
        <key>name</key>
        <string>Today</string>
    </dict>
</array>
</plist>

And here's an example of how I managed to at least copy the plists from the local directory to iCloud:

  func copyDocumentsToiCloudDirectory() {
      guard let localDocumentsURL = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: .userDomainMask).last else { return }
      
      guard let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents").appendingPathComponent("Data") else { return }
      
      var isDir:ObjCBool = false
      
      if FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: &isDir) {
          do {
              try FileManager.default.removeItem(at: iCloudDocumentsURL)
          }
          catch {
              //Error handling
              print("Error in remove item")
          }
      }
      
      do {
          try FileManager.default.copyItem(at: localDocumentsURL, to: iCloudDocumentsURL)
      }
      catch {
          //Error handling
          print("Error in copy item")
      }
  }
lukemmtt
  • 110
  • 2
  • 12

1 Answers1

1

Your file can be synced between devices via iCloud while kept in the app's own Documents directory. It's better to go this way, because your file format is plist, which does not mean anything to the users who might see it in their iCloud folder.

You can mark certain file as ubiquitous, by this method:

func setUbiquitous(_ flag: Bool, 
            itemAt url: URL, 
    destinationURL: URL) throws

More details here: https://developer.apple.com/documentation/foundation/filemanager/1413989-setubiquitous

There was no Swift when iCloud was introduced, but, to get the basic concept, you can check these WWDC 2011 slides https://download.developer.apple.com/wwdc_2011/adc_on_itunes__wwdc11_sessions__pdf/116_storing_documents_in_icloud_using_ios_5.pdf

Hint: when you see some ObjC APIs, search for them in the Apple documentation, and change the language there to Swift, it will show you how that same method looks like in Swift.

Eugene Dudnyk
  • 4,173
  • 1
  • 21
  • 41