Uploading a video to S3 with swift The operation couldn’t be completed. (Cocoa error 260.)" - swift

I am trying to upload a video file to Amazon S3
I constantly get an error 260:
Error in uploading the video: Error
Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be
completed. (Cocoa error 260.)
I read somewhere that amazon does not support asset library - is it true? and if so what do you suggest
Thanks Eran
func saveVideoToS3 () {
var uploadRequest: AWSS3TransferManagerUploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest.bucket = "BucketName"
uploadRequest.key = "KeyText"
//Move video file to the application folder so it can be read
var savedVideoURLToBeUsed = NSUserDefaults.standardUserDefaults().objectForKey("ThisIsTheVideoIWantToUse") as! String
println("Video saved in Store: \(savedVideoURLToBeUsed)")
var url: NSURL = NSURL(fileURLWithPath: savedVideoURLToBeUsed)!
uploadRequest.body = url
//uploadRequest.body = NSURL(fileURLWithPath: "file:///\(url)")
println("URL: \(url)")
let transferManager: AWSS3TransferManager = AWSS3TransferManager.defaultS3TransferManager()
transferManager.upload(uploadRequest).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (AWSTask) -> AnyObject! in
//Handle errors
if AWSTask.error != nil {
println("Error in uploading the video: \(AWSTask.error)")
if AWSTask.error.code == 1001 {
self.saveVideoToS3()
}
// Retrive information important for later downloading
} else {
println("Video upload successful..")
var uploadResult: AnyObject! = AWSTask.result
println("Upload result: \(uploadResult)")
//Delete file from the application folder
}
return nil
})
}

Cocoa error 260 is a NSFileReadNoSuchFileError, meaning the path you specified is not valid (file is just not where you say it is), so it probably has nothing with S3 itself. There are three things why this is happening that come to my mind:
you did not use .synchronize() when saving the key to user settings
your file URL contains invalid characters
you did not write the file into filesystem properly
iOS8 Breaking change
Also please note that as of iOS8, due to changes how application work with their assigned sandboxes, you can't save absolute URL to file because next time you open application, it will be different.
Beginning in iOS 8, the Documents and Library directories are no
longer siblings of your application's bundle.
I am using two quick convenience functions that I wrote to get file from cache directory:
func cachePathWithFileName(fileName : String) -> String {
let cacheDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
return cacheDirectoryPath.stringByAppendingPathComponent(fileName)
}
and documents directory:
func documentsPathWithFileName(fileName : String) -> String {
let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] as! String
return documentsDirectoryPath.stringByAppendingPathComponent(fileName)
}
Hopefully some of this tips help!

Related

After upgrading to Xcode 11 URL's for Bundle resource doesn't work anymore

I upgraded Xcode to Version 11.0 (11A420a) and part of code doesn't work as before. I want to copy folder from Bundle to TemporaryDirectory.
func makeHtmlFile (type: InvoiceType?) -> URL {
let identifier = Bundle.main.bundleIdentifier!
let cacheDirectory = NSTemporaryDirectory() + "\(identifier)/Website/"
let cacheURL = URL(fileURLWithPath: cacheDirectory )
let websiteUrl = URL(fileReferenceLiteralResourceName: "Website")
do {
try FileManager().copyItem(at: websiteUrl, to: cacheURL)
} catch let error {
print ("Copy \(error)\n\tWebsite: \(websiteUrl)\n\tCache:\(cacheURL)")
}
...
}
In Xcode 10 this part of code could find folder /Website inside bundle and temporary directory in /var/folders/...., but after upgrade prints an error:
Copy Error Domain=NSCocoaErrorDomain Code=4 "The file “Website” doesn’t exist."
Paths displayed in error message exists, I can go there by copying them from an error message and pasting in Finder -> Go -> Go to Folder. Does somebody know what was changed after upgrade?
OK, I solved problem at least for myself. Instead of using NSTemporaryDirectory() I used FileManager().temporaryDirectory for temporary files URL and
let tempDirectory = FileManager().temporaryDirectory
if let websiteURL = Bundle.main.url(forResource: "Website", withExtension: nil) {
let cacheURL = tempDirectory.appendingPathComponent(websiteURL.lastPathComponent, isDirectory: true)
...
}
Seems to work as before upgrade.

How do I write to a local file in Swift/XCTest?

My ultimate question is about saving a screenshot from an AppleTV application using XCTest and Swift4 (running on a MacBook paired to the TV device), but I'm having trouble even writing a simple text string to a local file. If I can get this simple file-save working, I'm hoping I can resolve the screenshot issue. (Apologies for making this look like two questions but they appear to be related and resulted from my troubleshooting efforts.)
First, here's what I'm trying to do with a screenshot, based on sample code I found somewhere online:
let appshot = XCUIApplication().windows.firstMatch.screenshot()
let shotpath = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0].appendingPathComponent("appshot.png")
let shotpathUrl = URL(string: "file://\(shotpath)")
print("Saving to: \(shotpath)")
do {
try appshot.pngRepresentation.write(to: shotpathUrl!)
} catch {
print("Failed saving screenshot due to \(error)")
}
This gives me the following output:
Saving to: file:///var/mobile/Containers/Data/Application/77D52C66-353B-4029-97D5-48E6BAE35C92/Downloads/appshot.png
Failed saving screenshot due to Error Domain=NSCocoaErrorDomain Code=4 "The file “appshot.png” doesn’t exist." UserInfo={NSFilePath=///var/mobile/Containers/Data/Application/77D52C66-353B-4029-97D5-48E6BAE35C92/Downloads/appshot.png, NSUnderlyingError=0x1c405bc60 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Of course, the file doesn't exist because that's the file I'm trying to create. But /var/mobile doesn't exist on my laptop either -- it looks like the path FileManager is building may exist on the AppleTV device, but I want it on my laptop where my test script is executing.
So I backed out to a much more simple case, and even this is giving me problems:
let str = "This is a test"
let path = "file:///Users/haljor/foo.txt"
let pathUrl = URL(string: path)!
print("Path: \(path)")
print("URL: \(pathUrl)")
do {
try str.write(to: pathUrl, atomically: true, encoding: .utf8)
} catch {
print("Caught error writing to \(pathUrl): \(error)")
}
And here's the output:
Path: file:///Users/haljor/foo.txt
URL: file:///Users/haljor/foo.txt
Caught error writing to file:///Users/haljor/foo.txt: Error Domain=NSCocoaErrorDomain Code=4 "The folder “foo.txt” doesn’t exist." UserInfo={NSURL=file:///Users/haljor/foo.txt, NSUserStringVariant=Folder, NSUnderlyingError=0x1c40553f0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Here, it looks like it's trying to write to a folder at the path I specified, not a file. Clearly there's something I'm not understanding in each of these cases.
I don't really have a preference for whether I use a fully-specified path or something using FileManager -- it just needs to land somewhere on my laptop (not the TV device). What am I missing?
You can add an attachment to the test case and save it to disk too. The problem was that Downloads folder may not exist in the container yet. The best way to handle this is via init-once property:
var downloadsFolder: URL = {
let fm = FileManager.default
let folder = fm.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
var isDirectory: ObjCBool = false
if !(fm.fileExists(atPath: folder.path, isDirectory: &isDirectory) && isDirectory.boolValue) {
try! fm.createDirectory(at: folder, withIntermediateDirectories: false, attributes: nil)
}
return folder
}()
func test() {
let appshot = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: appshot)
attachment.lifetime = .keepAlways
self.add(attachment)
// Save to container
let url = downloadsFolder.appendingPathComponent("appshot.png")
try! appshot.pngRepresentation.write(to: url)
}
If you want to view the attachment, right-click on the test case, select Jump to Report and expand the tree. You will see the screenshot eventually:

How to Use SwiftyDropbox's "destination" with a Download

In reviewing the SwiftyDropbox tutorial in the v2 Dropbox API, it shows how to perform a download:
// Download a file
let destination : (NSURL, NSHTTPURLResponse) -> NSURL = { temporaryURL, response in
let fileManager = NSFileManager.defaultManager()
let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
// generate a unique name for this file in case we've seen it before
let UUID = NSUUID().UUIDString
let pathComponent = "\(UUID)-\(response.suggestedFilename!)"
return directoryURL.URLByAppendingPathComponent(pathComponent)
}
client.files.download(path: "/MyFile.db", destination: destination).response { response, error in
if let (metadata, url) = response {
print("*** Download file ***")
let data = NSData(contentsOfURL: url)
print("Downloaded file name: \(metadata.name)")
print("Downloaded file url: \(url)")
print("Downloaded file data: \(data)")
} else {
print(error!)
}
}
I'm unclear what's going on with the destination part. Why do I need to generate a random string for the filename?
When I try to specify my own filename, the download doesn't seem to work:
let destination : (NSURL, NSHTTPURLResponse) -> NSURL = { temporaryURL, response in
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
return directoryURL.URLByAppendingPathComponent("MyFile.db")
}
I want to download a file from Dropbox named MyFile.db and I want to put it in my device's documents directory with the name MyFile.db and overwrite it if it's already there.
How can I do that?
When you say it doesn't seem to work, I expect you mean you get an error like this:
Error Domain=NSCocoaErrorDomain Code=516 "“CFNetworkDownload_bPYhu1.tmp” couldn’t be moved to “Documents” because an item with the same name already exists." UserInfo={NSSourceFilePathErrorKey=..., NSUserStringVariant=(
Move
), NSDestinationFilePath=..., NSUnderlyingError=0x7fda0a67cea0 {Error Domain=NSPOSIXErrorDomain Code=17 "File exists"}}
SwiftyDropbox, by virtue of using AlamoFire, doesn't currently let you overwrite files using the download function.
Specifically, SwiftyDropbox calls download in AlamoFire, and AlamoFire calls NSFileManager.moveItemAtURL. The documentation for NSFileManager.moveItemAtURL says:
If an item with the same name already exists at dstURL, this method aborts the move attempt and returns an appropriate error.
So, it seems like it's just being cautious, and making it hard for your app to accidentally overwrite (ad potentially lose) data. If you definitely know you want to overwrite a particular file, you'll need to do so explicitly, after the Dropbox API call. We'll consider this a feature request though.
Update: SwiftyDropbox now offers the ability to overwrite the files directly as of version 3.1.0, e.g., using download(path:rev:overwrite:destination:).

Swift write/save/move a document file to iCloud drive

I've been trying for over two days to write a file to iCloud drive. I have tried writing a simple text file directly, locally then moving it, using UIDocumentMenuViewController, etc. I'm not getting any errors with my code and stepping through debugger, it looks successful, but when I check to see if the file exists or at least the iCloud directory, there is nothing there. I tried on both the simulator and my iPhone, triggering iCloud synching, and everything else I can think of.
My main goal is to simply write a text file to the iCloud drive, which later will be "numbers" file
I have set up my plist file and my entitlements:
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.paul.c.$(PRODUCT_NAME:rfc1034identifier)</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>myCloudTest</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
I have also bumped up by bundle version as stated at: Save iOS 8 Documents to iCloud Drive
I have tried dozens of tutorials with no luck. My latest code is based off of this sample: https://medium.com/ios-os-x-development/icloud-drive-documents-1a46b5706fe1
Here is my code:
#IBAction func ExportFile(sender: AnyObject) {
var error:NSError?
let iCloudDocumentsURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)?.URLByAppendingPathComponent("myCloudTest")
//is iCloud working?
if iCloudDocumentsURL != nil {
//Create the Directory if it doesn't exist
if (!NSFileManager.defaultManager().fileExistsAtPath(iCloudDocumentsURL!.path!, isDirectory: nil)) {
//This gets skipped after initial run saying directory exists, but still don't see it on iCloud
NSFileManager.defaultManager().createDirectoryAtURL(iCloudDocumentsURL!, withIntermediateDirectories: true, attributes: nil, error: nil)
}
} else {
println("iCloud is NOT working!")
// return
}
if ((error) != nil) {
println("Error creating iCloud DIR")
}
//Set up directorys
let localDocumentsURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: .UserDomainMask).last as! NSURL
//Add txt file to my local folder
let myTextString = NSString(string: "HELLO WORLD")
let myLocalFile = localDocumentsURL.URLByAppendingPathComponent("myTextFile.txt")
let written = myTextString.writeToURL(myLocalFile, atomically: true, encoding: NSUTF8StringEncoding, error: &error)
if ((error) != nil){
println("Error saving to local DIR")
}
//If file exists on iCloud remove it
var isDir:ObjCBool = false
if (NSFileManager.defaultManager().fileExistsAtPath(iCloudDocumentsURL!.path!, isDirectory: &isDir)) {
NSFileManager.defaultManager().removeItemAtURL(iCloudDocumentsURL!, error: &error)
}
//copy from my local to iCloud
if (error == nil && !NSFileManager.defaultManager().copyItemAtURL(localDocumentsURL, toURL: iCloudDocumentsURL!, error: &error)) {
println(error?.localizedDescription);
}
Thank You for taking time for this.
Cheers,
Paul
I ran some code on my iphone after the code above:
var error:NSError?
let iCloudDocumentsURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil) //?.URLByAppendingPathComponent("myCloudTest")
var fileManager: NSFileManager = NSFileManager()
var fileList: NSArray = fileManager.contentsOfDirectoryAtURL(iCloudDocumentsURL!, includingPropertiesForKeys: nil, options: nil, error: &error)!
var filesStr: NSMutableString = NSMutableString(string: "Files in iCloud folder \n")
for s in fileList {
println(s)
}
and it prints out the path to my text file:
file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~paul~c~myApp/MyTextFile.txt
My file is there, I just can't see it on 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.
FWIW:
I also found out that the name of the project within the bundle ID is important.
My project bundle ID was something like the following: aaa-bbb-ccc-ddd
I could not get the iCloud working.
Then I renamed it to: aaa-bbb.ccc-ddd
It started working.
I believe I've found a way to get everything back in sync without constantly having to "bump" my bundle number. I've tried this multiple times while making changes within the "capabilities" area of key-value storage/iCloud Documents/CloudKit and it seems to work each time.
Sign out of iCloud on your Mac
Sign out of iCloud on your Simulator
Sign back into iCloud on your Mac
Sign back into iCloud on your Simulator
Do a clean build from XCode (Shift-Cmd-K)
This appears to reset the synchronization of the folder structures when you're App is writing to your iCloud Documents directory - without having to touch your bundle number. It takes a little longer to do it, but I'm a little OCD and kinda prefer my initial App launch to start with a 1!
You need to do 2 things
Do what #rick Andrews said: "Once I changed it to iCloud.MY_BUNDLE_IDENTIFIER (i.e. copy the string from the CFBundleIdentifier key higher in Info.plist)"
Store your filed inside the containers subfolder Documents
struct iCloudStore {
public var containerUrl: URL! {
return fileManager.url(forUbiquityContainerIdentifier: nil)!
}
public var documents: URL! {
return containerUrl.appendingPathComponent("Documents", isDirectory: true)
}
private let fileManager: FileManager = FileManager.default
func store(url: URL) {
// move ulr into the documents folder as a file
let fileID = "\(UUID().uuidString).<#extension#>"
let icloudFile = documents.appendingPathComponent(fileID, isDirectory: false)
try fileManager.copyItem(at: url, to: icloudFile)
}
}
Maybe there's a rule for container's name.
I tried the following names.
○:iSheet
×:sheetFiles
×:com.myname.sheetFiles

Alamofire not saving file to disk

I have a video file on S3 that I am trying to save to disk. However, if the file already exists on disk, I want to overwrite it. I wrote this function to download the file but it never saves the file. I can see the progress % increasing. But, how do I access the resulting file and save it to disk?
var finalPath: NSURL?
Alamofire.download(.GET, s3Url) { temporaryURL, response in
let fileManager = NSFileManager.defaultManager()
if let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {
let pathComponent = response.suggestedFilename
finalPath = directoryURL.URLByAppendingPathComponent(pathComponent!)
println(finalPath)
//remove the file if it exists
if fileManager.fileExistsAtPath(finalPath!.absoluteString!) {
println("file exists on disk, removing..")
fileManager.removeItemAtPath(finalPath!.absoluteString!, error: nil)
}
return finalPath!
}
return temporaryURL
}
.validate()
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
let progress = (Double(totalBytesRead) / Double(totalBytesExpectedToRead)) * 100
println(String(format: "%.2f", progress))
}
.response { request, response, data, error in
println(request)
println(response)
if let mediaData = data {
println("saving file to disk")
mediaData.writeToURL(finalPath!, atomically: true)
}
}
Normally I would use the example provided in the docs, but It fails if the file already exists. ie:
let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
So, how can I download the file, overwrite it if it exists and record the path that the file is written to to my coreData database?
You need to delete the file first. Alamofire only tries to move the file from the temp location to the final location that you provide in the destination closure.
You can create an extension on Alamofire.DownloadRequest to provide options for how to download the file where you can use the option DownloadRequest.DownloadOptions.removePreviousFile.
for details on how to do that see my answer to this question.