I want to create pre-seed database at the beginning
The file is quite large (5mb).
I use copyItemAtPath to copy files, so do this method has completion?
How do i know when this process has been finished?
This code is enough:
do {
// copy files from main bundle to documents directory
print("copy")
try
NSFileManager.defaultManager().copyItemAtPath(sourcePath, toPath: destinationPath)
} catch let error as NSError {
// Catch fires here, with an NSError being thrown
print("error occurred, here are the details:\n \(error)")
}
where destinationPath can be for example:
NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, .UserDomainMask, true).first
From How to show the progress of copying a large file in iOS?
Run your copying process in a seperate thread (T1)
Run another thread (T2) which reads periodically (say every 100ms)
the destination file current_size.
Calculate the percentage to display progress: current_size / total_size
Related
I have a WKWebView app which i will like to clear a specific cache when image is uploaded .
When i upload an image, the name remains same but the old image will be override with new one, like old image is logo.png, new image will be renamed with logo.png also. But because of cache users will still be seeing the old image.
I have below line of code to clear cache once image has been uploaded and it working fine, but my only problem now is that all cached data is being cleared also.
Is there any way i can clear cache for just the image was uploaded, maybe by passing the image name?
Or just clear cache for images?
static func clearCache(){
let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
let fileManager = FileManager.default
do {
// Get the directory contents urls (including subfolders urls)
let directoryContents = try FileManager.default.contentsOfDirectory( at: cacheURL, includingPropertiesForKeys: nil, options: [])
for file in directoryContents {
print("CACHE = ", file)
do {
try fileManager.removeItem(at: file)
}
catch let error as NSError {
debugPrint("Ooops! Something went wrong: \(error)")
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
From the loop i got this
CACHE =
file:///private/var/mobile/Containers/Data/Application/BFEB788B-9EA4-4921-A902-230869CAC814/Library/Caches/com.myapp.ios.sellers/
CACHE =
file:///private/var/mobile/Containers/Data/Application/BFEB788B-9EA4-4921-A902-230869CAC814/Library/Caches/google-sdks-events/
CACHE =
file:///private/var/mobile/Containers/Data/Application/BFEB788B-9EA4-4921-A902-230869CAC814/Library/Caches/com.apple.WebKit.WebContent/
CACHE =
file:///private/var/mobile/Containers/Data/Application/BFEB788B-9EA4-4921-A902-230869CAC814/Library/Caches/WebKit/
It seems like you need to find the file you want to remove when you are in the for loop loop for files. Remove that specific image when the for loop hits. Right now that for loop removes all cache in directoryContents.
I wish to copy video file from Photo Library to my app's Documents directory and wish to be notified about completion. Here is what I do:
let videoAsset = fetchResult.object(at: indexPath.item)
print(videoAsset.description)
let options = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestAVAsset(forVideo: videoAsset, options: options) { [weak self] (avAsset, audioMix, info) in
if let avurlAsset = avAsset as? AVURLAsset {
let url = avurlAsset.url
let toUrl = //some Url
let fileManager = FileManager.default
do {
try fileManager.copyItem(at: url, to: toUrl)
} catch {
NSLog("Unable to copy file from \(url) to \(toUrl)")
}
}
}
Only problem with this approach is I have no way to be notified of completion of copyItem. What is the alternative to copyItem method (or altogether a different approach to above) that is atleast blocking till copy finishes? Is it possible to use FileHandle & read consecutive bytes and write to another file? Will that be synchronous enough?
EDIT: As pointed by Alex, copyItem is actually synchronous routine. On closer inspection, I see I sometimes get errors on copying. Not sure why the permission errors show up when it is app's Documents folder where I copy.
2018-08-27 20:30:07.485841+0530 MyProject[3577:1288452] Copying file...
2018-08-27 20:30:07.487880+0530 MyProject[3577:1288452] stat on /var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4: Operation not permitted
2018-08-27 20:30:07.512994+0530
MyProject[3577:1288452] Unable to copy file from file:///var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4 to file:///var/mobile/Containers/Data/Application/CC13FD5A-E4CF-42A1-931F-2F1FFE799C15/Documents/IMG-0027.mov, Error Domain=NSCocoaErrorDomain Code=513 "“IMG_7915.MP4” couldn’t be copied because you don’t have permission to access “Documents”." UserInfo=
{NSSourceFilePathErrorKey=/var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4, NSUserStringVariant=(
Copy
),
NSDestinationFilePath=/var/mobile/Containers/Data/Application/CC13FD5A-E4CF-42A1-931F-2F1FFE799C15/Documents/IMG-0027.mov, NSFilePath=/var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4, NSUnderlyingError=0x111c441c0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
Copyitem: Copies the item at the specified path to a new location synchronously.
Returns true if the item was copied successfully or the file manager’s delegate stopped the operation deliberately. Returns false if an error occurred.
This is a sync method so after it executed after catch without error then it means successful copied.
https://developer.apple.com/documentation/foundation/filemanager/1407903-copyitem
I'm storing files in iCloud as a means of backup. Uploading and downloading files seems to work fine, but if I delete the app on a device and re-install the app on that same device, I no longer see the files that were uploaded to iCloud (even from that device just before deleting the app). The ubiquityIdentityToken is the same from all installs. I'm not trying to sync among devices, just store and retrieve. I can see the files in \settings\icloud\manage storage\ but not by running this code:
func createListOfSQLBaseFilesIniCloudDocumentsDirectory() -> [String]{
let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
var iCloudBackupSQLFiles = [String]()
do{
let directoryContents = try FileManager.default.contentsOfDirectory(at: iCloudDocumentsURL!, includingPropertiesForKeys: nil, options: [])
for myFile in directoryContents {
if myFile.pathExtension == "sqlite" {
let fileWithExtension = myFile.lastPathComponent
//backupSQLFiles is an array of full file names - including the extension
iCloudBackupSQLFiles.append(fileWithExtension)
}//if myFile
}//for in
} catch let error as NSError {
print("Unresolved error \(error), \(error.userInfo)")
}//do catch
return iCloudBackupSQLFiles
}//createListOfSQLBaseFilesIniCloudDocumentsDirectory
Any guidance would be appreciated. Swift 3, iOS 10, Xcode 8
Hard to believe no one else has had this issue. Again, this is for simple file storage and retrieval, not syncing dynamically among devices.
The gist of the issue is that iCloud does not automatically sync cloud files down to a new device. Your code must do that. So if you remove an app from a device (but not from iCloud) and reinstall that same app, the app will not see prior iCloud files. You can add new ones and see them with the code above, but you are really just seeing the local ubiquitous container copy. To see previous items you need to perform a metadataQuery on iCloud, parse the filenames of the files of interest from the metadataQuery results and then run
startDownloadingUbiquitousItem(at:) on each file. For example, make an array of files in iCloud from the metadataQuery results and put this do-catch in a for-in loop.
let fileManager = FileManager.default
let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents", isDirectory: true)
let iCloudDocumentToCheckURL = iCloudDocumentsURL?.appendingPathComponent(whateverFileName, isDirectory: false)
do {
try fileManager.startDownloadingUbiquitousItem(at: iCloudDocumentToCheckURL!)
print("tested file: \(whateverFileName)")
} catch let error as NSError {
print("Unresolved error \(error), \(error.userInfo)")
}//do catch
In AppDelegate.swift, on first launch, the intent is to place some sample docs in the local Documents folder, or in the iCloud Documents folder if iCloud is enabled.
var templates = NSBundle.mainBundle().pathsForResourcesOfType(AppDelegate.myExtension, inDirectory: "Templates")
dispatch_async(appDelegateQueue) {
self.ubiquityURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)
if self.ubiquityURL != nil && templates.count != 0 {
// Move sample documents from Templates to iCloud directory on initial launch
for template in templates {
let tempurl = NSURL(fileURLWithPath: template)
let title = tempurl.URLByDeletingPathExtension?.lastPathComponent
let ubiquitousDestinationURL = self.ubiquityURL?.URLByAppendingPathComponent(title!).URLByAppendingPathExtension(AppDelegate.myExtension)
// let exists = NSFileManager().isUbiquitousItemAtURL(ubiquitousDestinationURL!)
do {
try NSFileManager.defaultManager().setUbiquitous(true, itemAtURL: tempurl, destinationURL: ubiquitousDestinationURL!)
}
catch let error as NSError {
print("Failed to move file \(title!) to iCloud: \(error)")
}
}
}
return
}
Before running this, I delete the app from the device and make sure no doc of that name is in iCloud. On first launch, without iCloud, the sample docs copy properly into the local Documents folder. With iCloud, this code runs, and the setUbiquitous call results in an error that says the file already exists. The commented call to isUbiquitousItemAtURL also returns true.
What might be making these calls register that a file exists that I'm pretty sure doesn't? Thank you!
The file already exists, so just replace it
The primary solution...in all the trial and error, I'd forgotten to put "Documents" back in the url. Should be:
let ubiquitousDestinationURL = self.ubiquityURL?.URLByAppendingPathComponent("Documents").URLByAppendingPathComponent(title!).URLByAppendingPathExtension(AppDelegate.myExtension)
Without that, wrote the file to the wrong directory, and so I couldn't see it by normal means.
I was wondering, if there is a different and faster solution to the following problem. I am downloading a file with NSURLSession. By default (I guess?) the downloaded file is stored in the tmp folder. Then I need to copy this file to the cache folder. At the moment I am using this code for my approach (in the didFinishDownloading function)
if let fileData = NSData(contentsOfURL: sourceUrl) {
fileData.writeToURL(destinationURL, atomically: true) // true
print(destinationURL.path!)
}
However, as my file is pretty large, this takes a while.
Is there a different option on copying this file to the cache folder?
Or is it possible to download a file directly to the Cache folder using the NSURLSession?
Instead of copying the file you can simply move it to the
desired location:
do {
try NSFileManager.defaultManager().moveItemAtURL(sourceURL, toURL: destinationURL)
} catch let err as NSError {
print(err.localizedDescription)
}
This would be much faster because only directory entries in the
file system are modified, but no data is actually copied.
Swift 3 update:
do {
try FileManager.default.moveItem(at: sourceURL, to: destinationURL)
} catch {
print(error.localizedDescription)
}