I want to support adding files to iCloud within my iOS Application. I want these files to be added to a folder on iCloud Drive (e.g. like the Numbers or Pages folders). I tried everything I found online about this subject but I did not manage to add a folder to iCloud Drive.
First, I added NSUbiquitousContainers to the info.plist
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.<xyz>.AppName</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>App Name</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
After this, I added the iCloud capability to my app:
Then, I added the following code to my ViewController:
if let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents") {
if (!FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: nil)) {
do {
try FileManager.default.createDirectory(at: iCloudDocumentsURL, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error.localizedDescription)
}
}
}
Note: after setting breakpoints I discovered that the code after if (!FileManager.default.fileExists(atPath: iCloudDocumentsURL.path, isDirectory: nil)) { never gets called. I temporarily removed this line, but this did not do anything.
Finaly, I added a file using the following code:
//Save text file to local directory
let file = "file.txt" //Text file to save to iCloud
let text = "sample text" //Text to write into the file
func saveToDirectory(){
guard let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileURL = directory.appendingPathComponent(file)
print(directory)
//Writing
do {
try text.write(to: fileURL, atomically: false, encoding: .utf8)
} catch {
/* error handling here */
print("Error in writing")
}
//Reading
do {
let getText = try String(contentsOf: fileURL, encoding: .utf8)
print(getText)
} catch {
/* error handling here */
print("Error in reading")
}
}
I do not see the app's folder, or the above file appear on my iCloud Drive. But when I download the apps container and look at it's contents I see the file listed under /AppData/Documents.
Did someone else got this? And/or does someone know how to solve this?
Thanks!
I also changed the Build/Version numbers of my app, but without any result
You should copy your file to iCloud container, you are writing it to app sandbox only.
First, create Documents folder on your iCloud container - if you store file in other folder, your app on another device will see the folder but it won't appear in Files app.
Then copy your file.txt into Documents folder on your iCloud container.
Related
I'm basically trying to save an audio file to an iPhone device, specifically in the Files app.
This is the code I'm using which works in the iOS Simulator:
if let audioUrl = URL(string: "https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_700KB.mp3") {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print(destinationUrl)
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path", destinationUrl)
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: audioUrl) { location, response, error in
guard let location = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: location, to: destinationUrl)
print("File moved to documents folder", destinationUrl)
} catch {
print(error)
}
}.resume()
}
}
this is the result in the simulator:
2022-04-08 15:48:17.605964+0200 TunnelPlay[3977:122650] [boringssl] boringssl_metrics_log_metric_block_invoke(153) Failed to log metrics
File moved to documents folder file:///Users/panashemuzamhindo/Library/Developer/CoreSimulator/Devices/A7F7DD88-1E0C-45CD-9783-F46E6644F98C/data/Containers/Data/Application/EDE8B3CC-C290-4369-8B88-4AE1717B9DB8/Documents/1645329769Sengkhathele(featit.caramel).mp3
Main Problem: When I try run this section in TestFlight, the app runs the save process but theres nothing in Files, maybe I'm looking for it in the wrong location? or I need some permissions?
Desired Outcome: I want to see the downloaded MP3 in the iPhone Files app.
And I did try to change the scheme to Release instead of Debug, but it still doesn't save the file.
If you want to be able to access the files in the iOS Files app, make sure to also enable LSSupportsOpeningDocumentsInPlace (in addition to enabling UIFileSharingEnabled)
LSSupportsOpeningDocumentsInPlace
A Boolean value indicating whether the app may open the original document from a file provider, rather than a copy of the document.
UIFileSharingEnabled
A Boolean value indicating whether the app shares files.
I want to transfer some files (sound, texts, etc.) from an iOS app to the files app. In addition, I want to put all these items into a folder which has the same name as my app - as it is the case with GarageBand or KeyNote, for example.
In Xcode, I did enable the iCloud Documents capability - I did also define a Container "iCloud.xxx.yyy" - see code below.
guard let fileURL = Bundle.main.url(forResource: "test", withExtension: "aiff") else { return }
guard let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: "iCloud.xxx.yyy") else { return }
if !FileManager.default.fileExists(atPath: containerURL.path) {
try FileManager.default.createDirectory(at: containerURL, withIntermediateDirectories: true, attributes: nil)
}
let backupFileURL = containerURL.appendingPathComponent("test.aiff")
if FileManager.default.fileExists(atPath: backupFileURL.path) {
try FileManager.default.removeItem(at: backupFileURL)
try FileManager.default.copyItem(at: fileURL, to: backupFileURL)
} else {
try FileManager.default.copyItem(at: fileURL, to: backupFileURL)
}
When I run my code, it seems to work - anyhow, I can't see nor folder representing my app name, nor "test.aiff" file in the files app. What is wrong with my approach?
You don't need to copy/move any file. What you need is to allow your app documents to be accessible from the other apps. Just go to your Info plist and allow "Supports Document Browser". All documents in your Documents directory will be automatically available there.
I have downloaded a pdf using code below. I am able to find the file in App Data Container, but from app data container my device needs Mac, x code or iTunes etc.
Can I give a distinction path to Documents OR another place to find the pdf in iPhone files? I have an option to open the file with iBook but it is not there.
My code to download the file is here:
func downloadFile(){
let url = "https://www.tutorialspoint.com/swift/swift_tutorial.pdf"
let fileName = "MyFile"
savePdf(urlString: url, fileName: fileName)
}
func savePdf(urlString:String, fileName:String) {
DispatchQueue.main.async {
let url = URL(string: urlString)
let pdfData = try? Data.init(contentsOf: url!)
let resourceDocPath = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
let pdfNameFromUrl = "YourAppName-\(fileName).pdf"
let actualPath = resourceDocPath.appendingPathComponent(pdfNameFromUrl)
do {
try pdfData?.write(to: actualPath, options: .atomic)
print("pdf successfully saved!")
//file is downloaded in app data container, I can find file from x code > devices > MyApp > download Container >This container has the file
} catch {
print("Pdf could not be saved")
}
}
}
Configure your app so that its files appear in the Files app by adding below lines to your Info.plist file.
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
OR
Just like below using Xcode
Note: Remember that you must be running iOS 11 or above.
I'm trying to access documents that I've put into my iCloud Documents folder on my Mac in an iOS app but I can't work out how to.
let manager = FileManager.default
let dir = manager.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
let files = try? FileManager.default.contentsOfDirectory(at: dir!, includingPropertiesForKeys: nil, options: [])
print(files) //(Optional([])
I have enabled iCloud Documents in my apps capabilities, and I know there are files in my Documents folder on my desktop..
I'm clearly missing something here..
Thanks in advance
Create iCloud Container for the iCloud
a. login to developer.apple.com
b. go to Identifiers and add iCloud Containers.
c. You can't delete iCloud Containers once created.
Create App ID that includes iCloud capabilities
a. Create App ID
b. Add iCloud support with explicit Bundle ID.
c. Create Provisioning Profile and download it to Xcode.
Configure the Capabilities in Xcode
a. Click and add iCloud support from "+ Capability"
b. For iCloud document storage, select iCloud Documents and select the Container you created.
c. Add NSUbiquitousContainers key in Info.plist
d. "MyApp" inside is what will display as foldername.
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.example.MyApp</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
<key>NSUbiquitousContainerName</key>
<string>MyApp</string>
</dict>
</dict>
Test Code Snippet
a. Try below quick and dirty test.
let driveURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
if let unwrappedFU = driveURL {
print("iCloud available")
let fileURL = driveURL!.appendingPathComponent("test.txt")
try? "Hello word".data(using: .utf8)?.write(to: fileURL)
} else {
print("iCloud not available")
}
Writing to your App Document Folder is different from iCloud document storage. You can make your App Document folder visible. By adding to Info.plist 2 additional property.
a. Add Support opening documents in place : YES
b. Supports opening documents in place : YES
c. Test using Tim's code snippet. https://www.youtube.com/watch?v=p1UNnsodJxc
let file = "\(UUID().uuidString).txt"
let contents = "Some text..."
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = dir.appendingPathComponent(file)
do {
try contents.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {
print("Error: \(error)")
}
NOTE: You will notice App Folder in the Files App in "On My iPhone" folder. That is App's Document folder you exposed to user. You will also notice your app folder in iCloud Drive folder. Those 2 folders are different. You should use your App Folder and use iCloud Drive folder as your export destination that way you don't have to deal with access control much.
CAVEAT: Oddly none of this worked including resigning into iCloud on the iPhone. Even though it was working on the simulator. It only started working when iPhone was rebooted in my case.
func clickFunction(){
let DOCUMENTS_DIRECTORY = "Documents"
if let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent(DOCUMENTS_DIRECTORY){
if let pathComponent = iCloudDocumentsURL.path as? String {
if (FileManager.default.fileExists(atPath: pathComponent, isDirectory: nil)) {
//Write it out the code of getting files
let fileManager = FileManager.default
let enumerator = fileManager.enumerator(atPath: DocumentsDirectory.iCloudDocumentsURL!.path!)
while let file = enumerator?.nextObject() as? String {
//this this the file or document path.
}
}
}
}
}
I'm working on an iOS Content Blocker, but I would like to have the user choose what lists they want to block (ads, tracks, adult sites etc.). I found that the app extension and the containing app's bundles are separate and have no access to each other's files, so a shared container is needed. I created an app group, but it seems like what I write there does not actually get written. What I am attempting to do is read a .json file from the bundle, and then write it to a sharedJson.json file that the content blocker extension can access.
func writeJsonToSharedJson(arrayOfStrings:[String]) -> Bool {
let composedString = arrayOfStrings.joined(separator: "\n")
let sharedJsonPath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.alexspear.BlockerTestGroup")?.appendingPathComponent("sharedJson.json")
//let sharedJsonPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("sharedJson.json")
do {
try composedString.write(to: sharedJsonPath!, atomically: true, encoding: .utf8)
}
catch {
print("Could not write to sharedJson.json\n")
return false
}
return verifyJsonWrite()
}
The result is that through the verifyJsonWrite() function, there is nothing there. Am I incorrect in assuming you can create a file in the app group container? I have also tried using FileManager's createFile function with the same result.