Download fails when on first use or app goes in background - ios

Alamofire 3.5.0, Swift2.2
I'm downloading ZIP files with the Alamofire download method, I've noticed that when I'm starting download process and apps goes background than the download fails with following error:
----------------------
error Optional(Error Domain=NSCocoaErrorDomain Code=4
"“CFNetworkDownload_pZ56nc.tmp” couldn’t be moved to “courses”
because either the former doesn't exist, or the folder containing
the latter doesn't exist." UserInfo=
{NSSourceFilePathErrorKey=
/private/var/mobile/Containers/Data/Application/[UUID]/tmp/CFNetworkDownload_pZ56nc.tmp,
NSUserStringVariant=(
Move
),
NSDestinationFilePath=
/var/mobile/Containers/Data/Application/[UUID]/Library/courses/course_302.zip,
NSFilePath=
/private/var/mobile/Containers/Data/Application/[UUID]/tmp/CFNetworkDownload_pZ56nc.tmp,
NSUnderlyingError=0x13f52f990 {Error Domain=NSPOSIXErrorDomain
Code=2 "No such file or directory"}})
----------------------
this is the code to download a file:
//...
var userLibraryPath:String = {
return NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)[0]
}()
//...
let _coursePath:NSURL = NSURL(string: "file://\(userLibraryPath)/)")!
//...
let zipURL = _coursePath.URLByAppendingPathComponent("course_\(courseId).zip")
//if file exists destroy it
if let zipPath = zipURL?.path where NSFileManager.defaultManager().fileExistsAtPath(zipPath) {
do {
try NSFileManager.defaultManager().removeItemAtPath(zipPath)
} catch let error as NSError {
print(error)
}
}
//
let downloadRequest = Alamofire.download(Router.download(courseId), destination: { (url:NSURL, urlResponse:NSHTTPURLResponse) -> NSURL in
//
return zipURL!
//
}).progress({ (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
//
let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
dispatch_async(GlobalMainQueue, {
self.notifyDownloadProgress(courseId, progress: progress)
})
}).response(completionHandler: { (request:NSURLRequest?, response:NSHTTPURLResponse?, data:NSData?, error:NSError?) in
self.removeFromQueue(courseId)
print("response")
print("----------------------")
print("error \(error)")
print("----------------------")
//here I would try to extract it
})
UPDATE I've just tested on iPhone 5 fresh install of the app and it doesn't have to go to background (e.g. via home button) to fail, it fails on the very first load (and any subsequent) untill after the app is killed and reopened.
Why is the "/private" bit added to the path? What am I doing wrong here?

And indeed it was a "No such file or directory" error.
When I've added:
//
let downloadRequest = Alamofire.download(Router.download(courseId), destination: { (url:NSURL, urlResponse:NSHTTPURLResponse) -> NSURL in
let course = zipURL!.URLByDeletingLastPathComponent!.path!
let fm = NSFileManager.defaultManager()
var isDir:ObjCBool = false
if(fm.fileExistsAtPath(path, isDirectory: &isDir) == false){
//doesnt exist
do {
try fm.createDirectoryAtPath(path, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
//
print(error)
}
}
return zipURL!
//
})

Related

Resolved URL bookmark (iCloud file) stops working after a few days: no such file or directory

I'm reading a file using UIDocumentPickerViewController:
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else {
return
}
openDocument(at: url) { result in
if let .success(data) = result {
save(url)
}
}
}
func openDocument(at url: URL, completion: (Result<Data, LoadError>) -> Void) {
if startAccessingSecurityScopedResource() {
defer { stopAccessingSecurityScopedResource() }
do {
let data = try Data(contentsOf: self)
return .success(data)
} catch {
return .failure(.genericError(error))
}
}
return .failure(.noAccess)
}
So after reading the document I make a copy of the bookmarkData of its URL:
func save(_ url: URL) {
NSFileCoordinator().coordinate(readingItemAt: url, error: &error) { coorindatedURL in
if coorindatedURL.startAccessingSecurityScopedResource() {
defer { coorindatedURL.stopAccessingSecurityScopedResource() }
do {
let bookmarkData = try coorindatedURL.bookmarkData()
// save bookmarkData with the url
} catch {
}
}
}
}
Then every time I read the document I read from saved bookmarkData:
var stale = false
if let url = try? URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &stale), stale == false {
if startAccessingSecurityScopedResource() {
defer { stopAccessingSecurityScopedResource() }
do {
let data = try Data(contentsOf: url)
} catch {
}
}
}
It works at first, and for 1 or 2 days, but after a few days I started getting this error:
Error Domain=NSCocoaErrorDomain Code=260 "The file FILENAME couldn’t be opened because there is no such file." UserInfo={NSFilePath=/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/PATH/TO/FILE.doc, NSUnderlyingError=0x2830cae80 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
I checked the file is still there in my iCloud folder, and the stale flag is false. So I'm not sure what the problem is?
Thanks!

Agora cannot record call with iOS Swift

I am following the doc here: https://docs.agora.io/en/Voice/rtc_recording_apple?platform=iOS and implementing a basic recording. This is my code:
func startRecording(){
let filename = getDocumentsDirectory().appendingPathComponent("\(APP_NAME)\(now()).WAV")
let str = String(describing: filename)
self.recordingPath = str
agoraKit?.startAudioRecording(str, quality: .high)
}
func stopRecording(){
agoraKit?.stopAudioRecording()
// get audio file
guard let audioUrl = URL(string: self.recordingPath) as? URL else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0 ) { [weak self] in
// getdata
do {
let myData = try Data(contentsOf: audioUrl)
print(myData.count, myData)
} catch {
print(error)
}
}
}
private func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
But I am getting error: The file “Whisper1608949569.WAV” couldn’t be opened because there is no such file
Full message:
file:///var/mobile/Containers/Data/Application/1F682ABD-153C-4DFD-BFF4-
02C1CE6F9A4C/Documents/Whisper1608949569.WAV
Error Domain=NSCocoaErrorDomain Code=260 "The file “Whisper1608949569.WAV” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/1F682ABD-153C-4DFD-BFF4-02C1CE6F9A4C/Documents/Whisper1608949569.WAV, NSUnderlyingError=0x281e33f60 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Am I not accessing the file correctly?
This is how I have initialized the agora client:
self.agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: AppID, delegate: self)
agoraKit?.delegate = self
agoraKit?.enableWebSdkInteroperability(true)
// sample loudest speaker every second
agoraKit?.enableAudioVolumeIndication(1000, smooth: 3, report_vad: true)
agoraKit?.enableAudio()
// config for livecast to start
agoraKit?.setChannelProfile(.liveBroadcasting)
// set framrate and HD/SD
agoraKit?.setVideoEncoderConfiguration( CONFIG_PRODUCTION )
//agoraKit?.setDefaultAudioRouteToSpeakerphone(true)
I just checked out the documentation referenced from the doc you're using and it says the method startAudioRecording(filepath, quality: quality) is now deprecated, and you should instead use this method with the additional sampleRate parameter:
https://docs.agora.io/en/Voice/API%20Reference/oc/Classes/AgoraRtcEngineKit.html#//api/name/startAudioRecording:sampleRate:quality:
Also check that the returned value of startAudioRecording and stopAudioRecording returns 0, meaning success.
If your channel name contains special characters (colons, slashes) recording will silently fail and no file will be produced.
It seems Agora uses the channel name when creating the temporary file.

iOS: How can app extension copy file to app Documents/ folder?

In the app extension is there a way to get file and copy it to Documents// folder?
I can get file with the code below. But how to copy it? I alway have an error
for item in self.extensionContext!.inputItems as! [NSExtensionItem] {
for provider in item.attachments! as! [NSItemProvider] {
provider.loadItem(forTypeIdentifier: provider.registeredTypeIdentifiers.first! as! String, options: nil, completionHandler: { (fileURL, error) in
if let fileURL = fileURL as? URL {
self.url = fileURL
// self.extensionOpenUrl(App.associatedDomain + fileURL.absoluteString)
}
})
}
}
copy by click:
let fileManager = FileManager.default
let pathForDocumentsDirectory = fileManager.containerURL(forSecurityApplicationGroupIdentifier: App.group)!.path
let fileURL = self.url!
let name = fileURL.lastPathComponent
let copiedPath = pathForDocumentsDirectory
do {
try fileManager.copyItem(atPath: fileURL.absoluteString, toPath: copiedPath)
if fileManager.fileExists(atPath: copiedPath) {
print("fileExists!!!")
}
} catch let error as NSError {
print("error in copyItemAtPath")
print(error.localizedDescription)
}
file url:
file:///var/mobile/Media/PhotoData/OutgoingTemp/3CEC8D4A-9B1B-468B-A919-7C70C9C522B3/IMG_5484.jpg
path to copy:
/private/var/mobile/Containers/Shared/AppGroup/D7D2317B-9C57-424D-9D2F-209C62BBFAE5/IMG_5484.jpg
error:
The file “IMG_5484.jpg” couldn’t be opened because you don’t have permission to view it.
You can't do that. Extensions can communicate with the main app only by leaving/modifying the files in the App Group space (which means also that you must create the App Group first in the developer portal and add proper entitlements).

Keep Downloaded File (Cache) For The Next Time Application Open

I download external pdf file using this Alamofire way.
The problem is, I would like to keep it for the next time user open the app. So the user does not need to download the pdf again.
I use the following method to download
let destination =
Alamofire.Request.suggestedDownloadDestination(directory: .CachesDirectory,
domain: .UserDomainMask);
Alamofire.download(.GET, urlString, destination: destination)
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
}
.response { request, response, _, error in
print("Downloaded to \(destination(NSURL(string: "")!, response!))");
}
The downloadedFilePath is something like this.
file:///var/mobile/Containers/Data/Application/4957AD15-947A-47D6-A126-EA06A5BCB099/Library/Caches/RewardMe-Presentation-at-NVIDIA-Auditorium.pdf
How do I keep the file for the next time my app launches?
I saved that path into NSUserDefaults the next time the app launches but the file is already gone.
There is a couple of functions add them into you code and call the copyFile where you are showing the path or after that.
func getPath(fileName: String) -> String {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
print("File Path Is : \(fileURL)")
return fileURL.path!
}
func copyFile(fileName: NSString , filePath : NSString) {
let dPath: String = getPath(fileName as String)
let fileManager = NSFileManager.defaultManager()
if !fileManager.fileExistsAtPath(dPath) {
let fromPath : NSURL = NSURL.fileURLWithPath(filePath);
var error : NSError?
do {
try fileManager.copyItemAtPath(fromPath.path!, toPath: dPath)
} catch let error1 as NSError {
error = error1
}
let alert: UIAlertView = UIAlertView()
if (error != nil) {
alert.title = "Error Occured"
alert.message = error?.localizedDescription
} else {
alert.title = "Successfully Copy"
alert.message = "Your File copy successfully"
}
alert.delegate = nil
alert.addButtonWithTitle("Ok")
alert.show()
}
}
copyFile two parameter one is file name for the file you want to save and second is where the file located.
And for checking what to do next time when application open. First get file path and check the file exist or not. If exist the d'not download the file otherwise download file.
let filePath: String = getPath("testFile.pdf")
let fileManager = NSFileManager.defaultManager()
if fileManager.fileExistsAtPath(dPath) {
//Code for using the file data
}
else {
//Download code for the file
}
Hope it helps :)
HTTP already has a caching mechanism, you could consider using it. Don't re-invent the wheel. Here's a way to do it.

iOS Dropbox get thumbnails images

I'm trying to load thumbnails images of a Dropbox account, but I got a bug. I create a local directory in the first method to put thumbnails, and I make a request to load them, but it doesn't works.
Here is my code :
// Create directory method
func createTempDirectory() -> String? {
let tempDirectoryTemplate = NSTemporaryDirectory()
tempDirectoryTemplate.stringByAppendingPathComponent("XXXXX")
let fileManager = NSFileManager.defaultManager()
var err: NSErrorPointer = nil
if fileManager.createDirectoryAtPath(tempDirectoryTemplate, withIntermediateDirectories: true, attributes: nil, error: err) {
return tempDirectoryTemplate
}
else {
return nil
}
}
// Get thumbnails images
func restClient(client: DBRestClient!, loadedMetadata metadata: DBMetadata!) {
temporaryDirectory = createTempDirectory()!
for file in metadata.contents {
if (file.thumbnailExists == true) {
client.loadThumbnail(file.path, ofSize: "s", intoPath: temporaryDirectory)
}
}
self.collectionView?.reloadData()
}
And this is the error :
[WARNING] DropboxSDK: error making request to /1/thumbnails/dropbox/star.jpg - (4) Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed. (Cocoa error 4.)
Thanks for your help !

Resources