Swift Write/Save/Move a Document File to Icloud Drive

Swift write/save/move a document file to iCloud drive

I had this problem. I followed the advice here and I found that my Info.plist key was not correct. Once I changed it to iCloud.MY_BUNDLE_IDENTIFIER (i.e. copy the string from the CFBundleIdentifier key higher in Info.plist) it all started working.

Removing the .com from your key may fix your issue.

Saving a file to icloud drive

You have to check and create "Documents" folder and also it's good practice to handle errors:

if let containerUrl = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents") {
if !FileManager.default.fileExists(atPath: containerUrl.path, isDirectory: nil) {
do {
try FileManager.default.createDirectory(at: containerUrl, withIntermediateDirectories: true, attributes: nil)
}
catch {
print(error.localizedDescription)
}
}

let fileUrl = containerUrl.appendingPathComponent("hello.txt")
do {
try "Hello iCloud!".write(to: fileUrl, atomically: true, encoding: .utf8)
}
catch {
print(error.localizedDescription)
}
}

Save PDF file to a user-defined default directory in iCloud Drive

Yes you can. Using UIDocumentPickerViewController you can ask for a directory, and you can save it in your app. This is explained in detail in Providing Access to Directories. iOS 13 and later.

See also What's New in File Management and Quick Look for sample code saving a directory URL and then reusing it to set the base directory for a subsequent call to UIDocumentPickerViewController

Failing to write to iCloud Drive

I've worked on an app that depends heavily on iCloud drive , Bottom line it's the worst . hard to debug unpredictable and poorly documented .

So i can give you a couple of options to try and see if it helps .
One of the things that could greatly help you is creating UIDocument SubClass , these can talk pretty well with iCloud Drive.

 class TextDoc:UIDocument{
var data:String?


override func contents(forType typeName: String) throws -> Any {
return data?.data(using: .utf8) ?? Data()
}

override func load(fromContents contents: Any, ofType typeName: String?) throws {
if let userContent = contents as? Data {
data = String(data: userContent, encoding: .utf8)
}
}

}


let folder = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Folder")

try? FileManager.default.createDirectory(atPath: folder?.path ?? "", withIntermediateDirectories: true, attributes: nil)

let fileURL = folder?.appendingPathComponent("iCloudDrive.txt")

let uiDoc = TextDoc(fileURL: fileURL!)
uidoc.save(to: url, for: .forOverwriting) { (success) in
print("save was \(success)")
}

If the saving was successful you can open it using open(completionHandler: <#T##((Bool) -> Void)?##((Bool) -> Void)?##(Bool) -> Void#>)

Also make sure you've iCloud Set correctly in developer.apple.com
Sample Image

iOS Swift 3 Copy File to iCloud Drive Programatically

One of the most useful articles when I was having this issue was at the following link:

Technical Q&A QA1893 Updating the metadata of iCloud containers for iCloud Drive

I also deleted and re-added my info.plist entries VERY CAREFULLY. That is what seemed to work. I originally copied and pasted the entries from an online resource. It seems that the keys were not exactly correct or contained some hidden characters which caused them not to be recognized.

Enabling Document Storage in iCloud Drive

Save iOS 8 Documents to iCloud Drive

Well, you've got me interested in this matter myself and as a result I've spent way to much time on this question, but now that I've got it working I hope it helps you as well!

File on my Mac

To see what actually happens in the background, you can have a look at ~/Library/Mobile Documents/, as this is the folder where the files eventually will show up. Another very cool utility is brctl, to monitor what happens on your mac after storing a file in the iCloud. Run brctl log --wait --shorten from a Terminal window to start the log.

First thing to do, after enabling the iCloud ability (with iCloud documents selected), is provide information for iCloud Drive Support (Enabling iCloud Drive Support). I also had to bump my bundle version before running the app again; took me some time to figure this out. Add the following to your info.plist:

<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.YOUR_BUNDLE_IDENTIFIER</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
<key>NSUbiquitousContainerName</key>
<string>iCloudDriveDemo</string>
</dict>
</dict>

Next up, the code:

- (IBAction)btnStoreTapped:(id)sender {
// Let's get the root directory for storing the file on iCloud Drive
[self rootDirectoryForICloud:^(NSURL *ubiquityURL) {
NSLog(@"1. ubiquityURL = %@", ubiquityURL);
if (ubiquityURL) {

// We also need the 'local' URL to the file we want to store
NSURL *localURL = [self localPathForResource:@"demo" ofType:@"pdf"];
NSLog(@"2. localURL = %@", localURL);

// Now, append the local filename to the ubiquityURL
ubiquityURL = [ubiquityURL URLByAppendingPathComponent:localURL.lastPathComponent];
NSLog(@"3. ubiquityURL = %@", ubiquityURL);

// And finish up the 'store' action
NSError *error;
if (![[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:localURL destinationURL:ubiquityURL error:&error]) {
NSLog(@"Error occurred: %@", error);
}
}
else {
NSLog(@"Could not retrieve a ubiquityURL");
}
}];
}

- (void)rootDirectoryForICloud:(void (^)(NSURL *))completionHandler {

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *rootDirectory = [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]URLByAppendingPathComponent:@"Documents"];

if (rootDirectory) {
if (![[NSFileManager defaultManager] fileExistsAtPath:rootDirectory.path isDirectory:nil]) {
NSLog(@"Create directory");
[[NSFileManager defaultManager] createDirectoryAtURL:rootDirectory withIntermediateDirectories:YES attributes:nil error:nil];
}
}

dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(rootDirectory);
});
});
}

- (NSURL *)localPathForResource:(NSString *)resource ofType:(NSString *)type {
NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *resourcePath = [[documentsDirectory stringByAppendingPathComponent:resource] stringByAppendingPathExtension:type];
return [NSURL fileURLWithPath:resourcePath];
}

I have a file called demo.pdf stored in the Documents folder, which I'll be 'uploading'.

I'll highlight some parts:

URLForUbiquityContainerIdentifier: provides the root directory for storing files, if you want to them to show up in de iCloud Drive on your Mac, then you need to store them in the Documents folder, so here we add that folder to the root:

NSURL *rootDirectory = [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]URLByAppendingPathComponent:@"Documents"];

You also need to add the file name to the URL, here I copy the filename from the localURL (which is demo.pdf):

// Now, append the local filename to the ubiquityURL
ubiquityURL = [ubiquityURL URLByAppendingPathComponent:localURL.lastPathComponent];

And that's basically it...

As a bonus, check out how you can provide an NSError pointer to get potential error information:

// And finish up the 'store' action
NSError *error;
if (![[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:localURL destinationURL:ubiquityURL error:&error]) {
NSLog(@"Error occurred: %@", error);
}


Related Topics



Leave a reply



Submit