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
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!
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
Swap Rootviewcontroller With Animation
Use Reserved Keyword a Enum Case
Should Conditional Compilation Be Used to Cope With Difference in Cgfloat on Different Architectures
Handling Multiple Gesturerecognizers
Swift - Could Not Cast Value of Type '_Nscfstring' to 'Nsdictionary'
Swiftui iOS 14 Widget Countdown
How to Add Material to Modelentity Programatically in Realitykit
Generating Random Numbers With Swift
Get Description of Emoji Character
Swift: How to Detect Linear Type Barcodes
Error When Trying to Save Image in Nsuserdefaults Using Swift
How to Animate a Model's Rotation in Realitykit