I would like to backup a realm database file to an iCloud drive, like WhatsApp, I have some questions:
What is the best practice to do this?
I have a database located in a shared group folder to access it from extensions, how can I back it up? How can I show the progress bar of upload? Like WhatsApp for example?
If I put a realm file in a document folder it will be synced for each modify.
Are there some samples code that we can see?
Thanks for the help, have any ideas? links?
Just to clarify, this is a question about backing up a discrete Realm file itself to iCloud Drive, so that it would be visible in the iCloud Drive app. Not synchronizing the contents of the file to a CloudKit store.
If you leave the Realm file in the Documents directory, then if the user performs an iCloud or iTunes backup, the file will be backed up. All this means though is that if the user decides to upgrade to a new device and perform a restore using the old device's backup image, the Realm file will be restored then. If the user deletes the app from your old device before then, the iCloud backup will also be deleted.
If you want to export your Realm file so it can be permanently saved and accessed in iCloud Drive, you can export a copy of the Realm file to your app's iCloud ubiquity container. This is basically just another folder like the shared group's folder, but it's managed by iCloud. This folder sort of behaves like Dropbox in that anything you put in there is automatically synchronized.
The code would look something like this:
let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)
let realmArchiveURL = containerURL.appendPathComponent("MyArchivedRealm.realm")
let realm = try! Realm()
try! realm.writeCopy(toFile: realmArchiveURL)
This is a really basic example. The Apple documentation recommends you do this on a background thread since setting up the iCloud folder for the first time can create some time.
Updating this wouldn't happen automatically. You'll need to export a new copy of the Realm each time the user wants to perform a backup.
I have recently had the same requirements and I am able to achieve from below steps
Swift: 4+
Step:1
1.Setup Your cloudKit for your app with a Developer account
2. You can take reference: https://www.raywenderlich.com/1000-cloudkit-tutorial-getting-started
Step 2
- Add CloudKit Capabilities in your App
- Please check out the screenshot: https://prnt.sc/pdpda5
Step 3
- Check for cloud Enabled options for your iphone
// Return true if iCloud is enabled
func isCloudEnabled() -> Bool {
if DocumentsDirectory.iCloudDocumentsURL != nil { return true }
else { return false }
}
Step 4
- Setup the below variables for Local or iCloud Document directories
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")
}
Step:5
Below function is used for copyRealmFileToIcloudContainer
func uploadDatabaseToCloudDrive()
{
if(isCloudEnabled() == false)
{
self.iCloudSetupNotAvailable()
return
}
let fileManager = FileManager.default
self.checkForExistingDir()
let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents", isDirectory: true)
let iCloudDocumentToCheckURL = iCloudDocumentsURL?.appendingPathComponent("\(memberId)_default.realm", isDirectory: false)
let realmArchiveURL = iCloudDocumentToCheckURL//containerURL?.appendingPathComponent("MyArchivedRealm.realm")
if(fileManager.fileExists(atPath: realmArchiveURL?.path ?? ""))
{
do
{
try fileManager.removeItem(at: realmArchiveURL!)
print("REPLACE")
let realm = try! Realm()
try! realm.writeCopy(toFile: realmArchiveURL!)
}catch
{
print("ERR")
}
}
else
{
print("Need to store ")
let realm = try! Realm()
try! realm.writeCopy(toFile: realmArchiveURL!)
}
}
Step:6
- Once your realm file uploaded on the server , you can check this in your iPhone
- Steps
- 1.Go To Setting
- 2.Go To iCloud
- 3.Go To ManageStorage
- 4.You will see your application there
- 5.Tap on Application, you will able to see your realm file over there
Step:7
- Make Sure you have added the below lines in info.plist
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.example.app</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>iCloudDemoApp</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
#yonlau as per your request sharing answer for backup realm file , This is tested once and the realm data only have when they backup on iCloud.
func DownloadDatabaseFromICloud()
{
let fileManager = FileManager.default
// Browse your icloud container to find the file you want
if let icloudFolderURL = DocumentsDirectory.iCloudDocumentsURL,
let urls = try? fileManager.contentsOfDirectory(at: icloudFolderURL, includingPropertiesForKeys: nil, options: []) {
// Here select the file url you are interested in (for the exemple we take the first)
if let myURL = urls.first {
// We have our url
var lastPathComponent = myURL.lastPathComponent
if lastPathComponent.contains(".icloud") {
// Delete the "." which is at the beginning of the file name
lastPathComponent.removeFirst()
let folderPath = myURL.deletingLastPathComponent().path
let downloadedFilePath = folderPath + "/" + lastPathComponent.replacingOccurrences(of: ".icloud", with: "")
var isDownloaded = false
while !isDownloaded {
if fileManager.fileExists(atPath: downloadedFilePath) {
isDownloaded = true
print("REALM FILE SUCCESSFULLY DOWNLOADED")
self.copyFileToLocal()
}
else
{
// This simple code launch the download
do {
try fileManager.startDownloadingUbiquitousItem(at: myURL )
} catch {
print("Unexpected error: \(error).")
}
}
}
// Do what you want with your downloaded file at path contains in variable "downloadedFilePath"
}
}
}
}
2.Copy realm file from iCloud to Document directory
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")
//HERE ACCESSING DATA AVAILABLE IN REALM GET FROM ICLOUD
let realm = RealmManager()
let array = realm.FetchObjects(type: Mood.self)
print(array?.count)
} catch let error as NSError {
print("Failed to move file to local dir : \(error)")
}
}
}
}
You could take a look at this Github project by mikemac8888.
Basically you make your model objects conform to RealmCloudObject:
class Note: Object, RealmCloudObject {
...
}
You have to implement a mapping function :
func toRecord() -> CKRecord {
...
record["text"] = self.text
record["dateModified"] = self.dateModified
}
... and the reverse function used to create Realm records out of CloudKit records:
public func changeLocalRecord(...) throws {
...
realm.create(objectClass as! Object.Type,
value: ["id": id,
"text": text,
"dateModified": NSDate(),
"ckSystemFields": recordToLocalData(record)],
update: true)
...
}
The full documentation could be read at the link I provided, obviously.
Related
I have an iOS application that uses UIDocumentPickerController to present the user with a dialog where they can pick the location of a directory containing files for upload
This is working just fine in most cases but if they pick a directory that is located on their iCloud account, DocumentPickerViewController returns a URL that produces unusable file URLs when enumerating the iCloud directory
Here is how I set it up:
var documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypeFolder as String], in: .open)
documentPicker.delegate = self
sView.present(documentPicker, animated: true, completion: nil)
And here is how I enumerate the files in the iCloud directory that they have chosen:
NSFileCoordinator().coordinate(readingItemAt: url, error: &error) { (url) in
let access = url.startAccessingSecurityScopedResource()
var directoryContents: [URL]
do {
let keys : [URLResourceKey] = [.nameKey, .isDirectoryKey]
directoryContents = try FileManager.default.contentsOfDirectory(
at: url,
includingPropertiesForKeys: keys)
} catch {
print("error \(error))")
if access {
url.stopAccessingSecurityScopedResource()
}
return
}
if access {
url.stopAccessingSecurityScopedResource()
}
}
For each file, I pass it to FileManager to copy into the application's sandbox:
for fromURL in directoryContents {
var lastPath = fromURL.lastPathComponent
print("Lastpath is \(lastPath)")
let toURL: URL = documentURL.appendingPathComponent(lastPath)
print("fromURL - \(fromURL)")
print("toURL \(toURL)")
let fM = FileManager.default
if(fM.fileExists(atPath: toURL.path)) {
exists += 1
} else {
do {
let access = url.startAccessingSecurityScopedResource()
try fM.copyItem(at: fromURL, to: toURL)
if access {
url.stopAccessingSecurityScopedResource()
}
count += 1
} catch {
print("Error on file copy \(error)")
}
}
}
This works just fine if the directory is on, say, an external USB drive but if I point it to iCloud, this is what I get:
fromURL - file:///Users/greg/Library/Developer/CoreSimulator/Devices/FFDED20B-B142-4FC6-BA8F-C1DC193E2AB7/data/Library/Mobile%20Documents/com~apple~CloudDocs/Savvy/SavvyLink/SmallEngineDataRepo/.Flt0002_20180913P.csv.icloud
toURL file:///Users/greg/Library/Developer/CoreSimulator/Devices/FFDED20B-B142-4FC6-BA8F-C1DC193E2AB7/data/Containers/Data/Application/167AC69D-494C-4CB1-8A36-E230C5303F30/Documents/.Flt0002_20180913P.csv.icloud
Note the file names have a dot (.) prepended and a .iCloud postpended. If I look in my application's sandbox I see the files in the Documents directory but the contents of the files are NOT the contents of the files on iCloud but instead look to be themselves some kind of a URL
Thanks for any insight into what is going on here and what I am missing
What I expect to happen is to be able to take the URLs returned when enumerating the files in the iCloud directory and then pass those URLs to FileManager.copyItems to copy them into my application's sandbox
As I said, this works just fine for directories located on (say) USB drives but when used for directories on iCloud it results in copying in files with very different contents than what is on the original files in iCloud and with names changed to have a "." prepended and ".iCloud" postpended
Basically, I want to handle a case where any device got SyncError with type ClientResetError then, want my device to re-login to realm again. but as per documentation, we have to closeRealmSafely before I login to realm again, but I am not sure how to close realm safely.
I am going through the doc (https://docs.realm.io/sync/using-synced-realms/errors#client-reset) to handle client reset error and found it's very confusing . I want help to understand about the following code.
First there is no method available to closeRealmsafely. Please help me understand how can I close the realm safely?
How can I backup and when I will use it? Should I skip the reset error because in documentation it's mentions if the client reset process is not manually initiated, it will instead automatically take place after the next time the app is launched, upon first accessing the SyncManager singleton. It is the app’s responsibility to persist the location of the backup copy if needed, so that the backup copy can be found later."
Below is the error handler sample code from the doc.
let syncError = error as! SyncError
switch syncError.code {
case .clientResetError:
if let (path, clientResetToken) = syncError.clientResetInfo() {
closeRealmSafely()
saveBackupRealmPath(path)
SyncSession.immediatelyHandleError(clientResetToken)
}
default:
// Handle other errors...
()
}
}```
Finally we figured out how to handle the client reset error. We have taken following steps To avoid the data loss incase user is offline and came online and got reset error.
Save the local realm to another directory
Invalidate and nil the realm
Initiate realm manual reset - Call SyncSession.immediatelyHandleError with clientResetToken passed and it will delete the existing realm from directory
Show client reset alert - This will intimate user to relaunch the app.
On next launch realm creates a fresh realm from ROS.
After new realm connects, restore the realm records (if any) from the old realm saved in backup directory above.
Delete the backup realm(old realm) from directory.
switch syncError.code {
case .clientResetError:
if let (path, clientResetToken) = syncError.clientResetInfo() {
// taking backup
backUpRealm(realm: yourLocalRealm)
// making realm nil and invalidating
yourLocalRealm?.invalidate()
yourLocalRealm = nil
//Initiate realm manual reset - Call `SyncSession.immediatelyHandleError` with `clientResetToken` passed and it will delete the existing realm from directory
SyncSession.immediatelyHandleError(clientResetToken)
// can show alert to user to relaunch the app
showAlertforAppRelaunch()
}
default:
// Handle other errors...
()
}
}```
The back up realm code look like this:
func backUpRealm(realm: Realm?) {
do {
try realm?.writeCopy(toFile: backupUrl)
} catch {
print("Error backing up data")
}
}
After doing this backup will be available at backup path. On next launch device will connect and download a fresh realm from ROS so after device connects restore the realm records from the backup realm saved in the backup path.
The restore merge backup code will look like this. place the below method when realm connects after relauch.The ```restoredRealm`` is fresh downloaded realm on launch
func restoreAndMergeFromBackup(restoredRealm: Realm?) {
let realmBackUpFilePath = isRealmBackupExits()
// check if backup exists or not
if realmBackUpFilePath.exists {
let config = Realm.Configuration(
fileURL: URL(fileURLWithPath: realmBackUpFilePath.path),
readOnly: true)
let realm = try? Realm(configuration: config)
guard let backupRealm = realm else { return }
//Get your realm Objects
let objects = backupRealm.objects(YourRealmObject.self)
try? restoredRealm?.safeWrite {
for object in objects {
// taking local changes to the downloaded realm if it has
restoredRealm?.create(YourRealmObject.self, value: object, update: .modified)
}
self.removeRealmFiles(path: realmBackUpFilePath.path)
}
} else {
debug("backup realm does not exists")
}
}
private func isRealmBackupExits() -> (exists: Bool, path: String) {
let documentsPath = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let realmPathComponent = documentsPath.appendingPathComponent("your_backup.realm")
let filePath = realmPathComponent.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
return (true, filePath)
}
return (false, "")
}
private func removeRealmFiles(path: String) {
let realmURL = URL(fileURLWithPath: path)
let realmURLs = [
realmURL,
realmURL.appendingPathExtension("lock"),
realmURL.appendingPathExtension("realm"),
realmURL.appendingPathExtension("management")
]
for URL in realmURLs {
do {
try FileManager.default.removeItem(at: URL)
} catch {
debug("error while deleting realm urls")
}
}
}```
In our testing we have found that there is a backup made by realm automatically so we deleted it for safety purpose. the path argument you will get in the if let (path, clientResetToken) = syncError.clientResetInfo()
func removeAutoGeneratedRealmBackUp(path: String) {
do {
try FileManager.default.removeItem(at: URL(fileURLWithPath: path))
} catch {
debug("error while deleting realm backUp path \(path)")
}
}
I’m trying to get the names of all files and folders in an iCloud Drive directory:
import Foundation
let fileManager = FileManager.default
let directoryURL = URL(string: "folderPathHere")!
do {
let directoryContents = try fileManager.contentsOfDirectory(at: directoryURL, includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles])
for url in directoryContents {
let fileName = fileManager.displayName(atPath: url.absoluteString)
print(fileName)
}
} catch let error {
let directoryName = fileManager.displayName(atPath: directoryURL.absoluteString)
print("Couldnt get contents of \(directoryName): \(error.localizedDescription)")
}
It appears that any iCloud files that haven’t been downloaded to the device don’t return URLs.
I know I can check if a path contains a ubiquitous item when I already know the path with the code below (even if it isn’t downloaded):
fileManager.isUbiquitousItem(at: writePath)
Is there a way to get the URLs & names of those iCloud files without downloading them first?
The directory URL is a security-scoped URL constructed from bookmark data in case that makes any difference (omitted that code here for clarity).
Thanks
Found the answer. I was skipping hidden files with ".skipsHiddenFiles", but the non-downloaded files are actually hidden files, named: ".fileName.ext.iCloud".
Remove the skips hidden files option now works as expected.
You need to use a NSFileCoordinator to access the directory in iCloud Storage, and then normalize placeholder file names for items that haven't been downloaded yet:
let iCloudDirectoryURL = URL(...)
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
fileCoordinator.coordinate(
readingItemAt: iCloudDirectoryURL,
options: NSFileCoordinator.ReadingOptions(),
error: nil
) { readingURL in
do {
let contents = try FileManager.default.contentsOfDirectory(
at: readingURL, includingPropertiesForKeys: nil
)
for url in contents {
print("\(canonicalURL(url))")
}
} catch {
print("Error listing iCloud directory: '\(error)'")
}
}
func canonicalURL(_ url: URL) -> URL {
let prefix = "."
let suffix = ".icloud"
var fileName = url.lastPathComponent
if fileName.hasPrefix(prefix), fileName.hasSuffix(suffix) {
fileName.removeFirst(prefix.count)
fileName.removeLast(suffix.count)
var result = url.deletingLastPathComponent()
result.append(path: fileName)
return result
} else {
return url
}
}
I have implemented icloud drive using swift. it's code looking good as given in official document and uploading/downloading files with single device perfectly.
Now when going to download file on another device with same cloud user. its unable to find file in that device. now i have disabled/enabled icloud from device cloud settings after some time then tried again and its worked (now found file on that device).
so here some questions occurs regarding this below.
is icloud not sync files instantly between devices (if no, then how can we sync files on between device instantly)
is there any way to notify device for sync new created files on cloud ?
currently copied files to cloud document not listing on cloud.com. so how can we show/hide copied files on icloud ?
here are code for upload/download files :
copy file to cloud
if let cloudURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents") {
let file = cloudURL.appendingPathComponent("file.txt")
if (FileManager.default.fileExists(atPath: file.path, isDirectory: nil)) {
do{
try FileManager.default.removeItem(at: file)
}catch let error as NSError {
print("error",error)
}
}
let localDocumentsURL = DocumentsDirectory.localDocumentsURL.appendingPathComponent("file.txt")
do {
try FileManager.default.copyItem(at: localDocumentsURL, to: file)
} catch let error as NSError{
print("can not copy file",error)
}
}
download file from cloud directory
let fileManager = FileManager.default
if let cloudURL = fileManager.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents") {
let file = cloudURL.appendingPathComponent("file.txt")
do{
var downloded = false
try fileManager.startDownloadingUbiquitousItem(at: file)
while(!downloded){
if(fileManager.fileExists(atPath: file.path, isDirectory: nil)){
downloded = true
}
}
let filePath = DocumentsDirectory.localDocumentsURL.appendingPathComponent("file.txt")
try fileManager.copyItem(at: file, to: filePath)
}catch let error as NSError {
print("error",error)
}
}
I published my app for reviewing in Applestore and I got a rejection, which says :
"
On launch and content download, your app stores 1.4 GB on the user's iCloud, which does not comply with the iOS Data Storage Guidelines.
"
I pasted the full rejection text in the end of the question.
I am not aware of storing data to the ICLoud and I am not sure how they test my app. They suggested to look in the settings for the ICloud storage, what I have done, but there I can't see any ICloud storage used by my app, when installed :
Is there any automatic process storing data from documents folder to the cloud after some time or setting ?
Generally my app is
- downloading a zipfile from my server to be stored in the Document folder of my app.
- Then the app is unzipping the zipfile also in the documents folder.
When testing with IOS-Simulator I can check the content of the document folder with a shell and the content is there. (and not on the ICloud)
Can someone help me , what a developer has to do in this case.
I pasted the relevant code here, which shows how I am downloading the content and how I am unzipping the content.
class ViewController: UIViewController, NSURLSessionDelegate {
let sDataPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString as String; ///var/mobile/Containers/Data/Application/8897ED62-5BAE-4255-8459-F0F8381C171F/Documents/
download_file(sfrom,sToLocation: sDataPath);
func download_file(sURL: String, sToLocation: String) {
print("start downloading ...");
print("sURL : " + sURL + " sToLocation : " + sToLocation);
bytesDownloaded=0;
let downloadsessiondelegate = self;
downloadsessiondelegate.storePath=sToLocation;
downloadsessiondelegate.progressView=progressView;
downloadsessiondelegate.surl=sURL;
struct SessionProperties {
static let identifier : String! = "url_session_background_download"
}
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Hour, .Minute], fromDate: date)
let hour = String(components.hour)
let minutes = String(components.minute)
let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("session_" + hour + minutes)
let backgroundSession = NSURLSession(configuration: configuration, delegate: downloadsessiondelegate, delegateQueue: nil)
//myURLSession = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
let url = NSURLRequest(URL: NSURL(string: sURL)!)
//var downloadTask = backgroundSession.downloadTaskWithRequest(url)
let downloadTask = backgroundSession.downloadTaskWithRequest(url)
downloadTask.resume()
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location1: NSURL) {
//println("session \(session) has finished the download task \(downloadTask) of URL \(location1).")
// remove Session ID
//defaults.removeObjectForKey(<#defaultName: String#>)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.removeObjectForKey("SessionProperties_identifier" + "_" + surl)
var error: NSError?
let target=storePath;
var source : String = location1.absoluteString;
let filemgr = NSFileManager.defaultManager();
let modifiedsource = source.stringByReplacingOccurrencesOfString("file://", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
if (filemgr.fileExistsAtPath(target))
{
do {
try filemgr.removeItemAtPath(target)
} catch let error1 as NSError {
error = error1
}
//println("FILE AVAILABLE");
}
do {
try filemgr.moveItemAtPath(modifiedsource, toPath: target)
dispatch_async(dispatch_get_main_queue(),{
self.progressView.setProgress(0, animated: false);
//progressView.removeFromSuperview();
print("Move successful")
self.install_content();
})
//download_file_finished();
} catch var error1 as NSError {
error = error1
print("Moved failed with error: \(error!.localizedDescription)")
install_failed("Moved failed with error: \(error!.localizedDescription)");
}
}
func unzipFile(sZipFile: String, toDest: String, estimatedByte:Int){
SSZipArchive.unzipFileAtPath(sZipFile, toDestination: toDest, progressHandler: {
(entry, zipInfo, readByte, totalByte) -> Void in
//let itotalByte=totalByte;
let stufe=4096*1000;
if totalByte % stufe == 0 {
//println("readByte : \(totalByte)")
}
dispatch_async(dispatch_get_main_queue()) {
var percent:Float=Float(Float(totalByte)/Float(estimatedByte));
if percent>1.0{
percent=1.0;
}
let percent_int=Int(percent*100);
let percenttext:String=String(percent_int);
self.progressView.setProgress(percent, animated: false);
let sMessage=self.actualinstalldefinition.title;
let messagewithprozent=sMessage + " (" + percenttext + "%)";
self.progress_label_Titel.text = messagewithprozent;
if percent_int % 20 == 0 && self.lastpercent != percent_int{
let progressimage=self.get_random_progress_image();
self.progressImageView.image = progressimage;
self.lastpercent=percent_int;
}
}
}, completionHandler: { (path, success, error) -> Void in
if success {
// Loesche das ZipFile
let fileManager = NSFileManager.defaultManager()
var error: NSError?
if (fileManager.fileExistsAtPath(sZipFile)){
do {
try fileManager.removeItemAtPath(sZipFile)
} catch let error1 as NSError {
error = error1
} catch {
fatalError()
}
}
dispatch_async(dispatch_get_main_queue(),{
self.progressView.setProgress(0, animated: false);
//progressView.removeFromSuperview();
print("Move successful")
self.install_content();
})
} else {
//progressBar?.hidden = true
self.install_failed(error.localizedDescription);
print(error)
}
})
}
Complete rejection statement :
2.23 - Apps must follow the iOS Data Storage Guidelines or they will be rejected
2.23 Details
On launch and content download, your app stores 1.4 GB on the user's iCloud, which does not comply with the iOS Data Storage Guidelines.
Next Steps
Please verify that only the content that the user creates using your app, e.g., documents, new files, edits, etc. is backed up by iCloud as required by the iOS Data Storage Guidelines. Also, check that any temporary files used by your app are only stored in the /tmp directory; please remember to remove or delete the files stored in this location when it is determined they are no longer needed.
Data that can be recreated but must persist for proper functioning of your app - or because users expect it to be available for offline use - should be marked with the "do not back up" attribute. For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. For CFURLRef objects, use the corresponding kCRUFLIsExcludedFromBackupKey attribute.
Resources
To check how much data your app is storing:
- Install and launch your app
- Go to Settings > iCloud > Storage > Manage Storage
- Select your device
- If necessary, tap "Show all apps"
- Check your app's storage
For additional information on preventing files from being backed up to iCloud and iTunes, see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes.
If you have difficulty reproducing a reported issue, please try testing the workflow described in Technical Q&A QA1764: How to reproduce bugs reported against App Store submissions.
If you have code-level questions after utilizing the above resources, you may wish to consult with Apple Developer Technical Support. When the DTS engineer follows up with you, please be ready to provide:
- complete details of your rejection issue(s)
- screenshots
- steps to reproduce the issue(s)
- symbolicated crash logs - if your issue results in a crash log
Files placed in the Documents folder are automatically backed up to iCloud.
The backup process may not start instantly which is why you do not see your app's iCloud storage.
Use this code to exclude a file from the auto backup.
try! filePath.setResourceValue(true, forKey: NSURLIsExcludedFromBackupKey)
See the docs for more information.