Simple Swift file download with URL - xcode

So I have the URL as string (in this case a JPG but would like a general procedure for any file type if possible) and I have the file path as string where I want to save the file.
What would be the fastest way to get this implemented?
Please keep in mind this is for OSX command line application. I tried few sample codes found here, mostly using UIImage but I get error:"Use of unresolved identifier", adding "import UIKit" gets me error:"No such Module". Please help!
import Foundation
let myURLstring = "http://www.safety.vanderbilt.edu/images/staff/Bob-Wheaton.jpg"
let myFilePathString = "/Volumes/HD/Staff Pictures/Bob-VEHS.jpg"
---> ABOVE IS THE ORIGINAL QUESTION <---
---> BELOW IS NEW IMPROVED CODE: WORKING <---
import Foundation
let myURLstring = "http://www.safety.vanderbilt.edu/images/staff/Bob-Wheaton.jpg"
let myFilePathString = "/Volumes/HD/Staff Pictures/Bob-VEHS.jpg"
let url = NSURL(string: myURLstring)
let imageDataFromURL = NSData(contentsOfURL: url)
let fileManager = NSFileManager.defaultManager()
fileManager.createFileAtPath(myFilePathString, contents: imageDataFromURL, attributes: nil)

If you're writing for OS X, you'll use NSImage instead of UIImage. You'll need import Cocoa for that - UIKit is for iOS, Cocoa is for the Mac.
NSData has an initializer that takes a NSURL, and another that takes a file path, so you can load the data either way.
if let url = NSURL(string: myURLstring) {
let imageDataFromURL = NSData(contentsOfURL: url)
}
let imageDataFromFile = NSData(contentsOfFile: myFilePathString)

With Swift 4, the code will be:
if let url = URL(string: myURLstring) {
let imageDataFromURL = try Data(contentsOf: url)
}

Related

How can I save a CKAsset in Swift 2?

Swift 2
I am trying to save a small asset to CloudKit but not having much luck finding example code. The record is "Table1" and the asset is in the application bundle. Any tips would be helpful. iOS9 Swift 2.
let audioPath = NSBundle.mainBundle().pathForResource("song-1", ofType: "mp3")!
let itemRecord:CKRecord = CKRecord(recordType: "Table1")
db.saveRecord(itemRecord) { (record:CKRecord?, error:NSError?) -> Void in
You first have to create a CKAsset object. You could do that with something like:
let audioPath = NSBundle.mainBundle().pathForResource("song-1", ofType: "mp3")!
var file: CKAsset? = CKAsset(fileURL: NSURL(fileURLWithPath: audioPath))
After that you can just add it to your CKRecord using:
itemRecord.setValue(file, forKey: "AudioFile")

NSURLDownload: Assertion failed ([path isAbsolutePath]) troubles

I'm trying to download a file off the internet and place it in the application name directory under the Application Support directory and I keep getting a
Assertion failed: ([path isAbsolutePath]), function -[NSURLDownload setDestination:allowOverwrite:], file /SourceCache/CFNetwork/CFNetwork-720.5.7/Foundation/NSURLDownload.mm, line 370.
Here's the code that I wrote:
var imageRequest = NSURLRequest(URL: self.source)
var imageDownload = NSURLDownload(request: imageRequest, delegate:self)
var error: NSError? = NSError()
/* does path exist */
let directoryPath = self.destination.stringByDeletingLastPathComponent
let fileMgr = NSFileManager();
fileMgr.createDirectoryAtPath(directoryPath, withIntermediateDirectories: true, attributes: nil, error: &error)
imageDownload.setDestination(self.destination, allowOverwrite: true);
When I step through the code everything looks correct. self.source is
(https:/remoteDomain.com/img/downloadimage.jpg)
a NSURL
self.destination is the full path in my system (file:/Users/ryan/Library/Application%20Support/AppName/downloadimage.jpg)
Any Ideas?
To answer the question to your specific topic:
The error message says that your path is invalid. The right way to create the path for your image is the following:
let fileManager = NSFileManager.defaultManager()
var folder = "~/Library/Application Support/[APPNAME]/someFolder" as NSString
folder = folder.stringByExpandingTildeInPath
if fileManager.fileExistsAtPath(folder as String) == false {
do {
try fileManager.createDirectoryAtPath(folder as String, withIntermediateDirectories: true, attributes: nil)
}
catch {
//Deal with the error
}
}
BUT
#jtbandes is right. You should use NSURLSessionDownloadTask to download your files.
It is part of the Foundation.framework, which is available on OS X, iOS and watchOS.
The reason to use it is that Apple keeps updating this Api to meet the latest standards. For example, you don't need to worry about IPv4 or IPv6 etc. This avoids crashes and weird behavior in your app.
This is how you use it (Swift):
var imageRequest = NSURLRequest(URL: self.source)
let session = NSURLSession.sharedSession()
let downloadTask = session.downloadTaskWithRequest(imageRequest) { (url: NSURL?, response: NSURLResponse?, error: NSError?) -> Void in
//Work with data
}
downloadTask.resume()
Note that url is the path to the downloaded image.

Paste gif image into NSPasteboard

I am developing osx application. I want to paste an Gif image into pasteboard. How can I do that?
What I have is
NSImage
NSPasteboard
What I want to do is to paste that image into pasteboard. I am able to paste PNG image but what I need is to paste GIF image.
My existing code
let imageURL = imageObject.imageURL!
let fileName = imageURL.lastPathComponent
var saveURL = NSURL(string: "file://" + NSTemporaryDirectory())
saveURL = saveURL?.URLByAppendingPathComponent(fileName!)
// I have data now
let data = NSData(contentsOfURL: imageURL)
pasteboard.declareTypes([NSTIFFPboardType], owner: nil)
pasteboard.setData(data!, forType: "com.compuserve.gif")
About 10 years ago same one asked How do I put a GIF onto an NSPasteboard?
in an Apple discussion group and here is my answer. Although 10 years old and the NSPasteboard methods changed since that time my answer still works. I confess: my advice is a bit dirty.
Okay I got it working after spending 6 hours searching online.
Basically I have to download the image file and paste it as a filename.
let imageURL = imageObject.imageURL!
let fileName = imageURL.lastPathComponent
var saveURL = NSURL(string: "file://" + NSTemporaryDirectory())
saveURL = saveURL?.URLByAppendingPathComponent(fileName!)
let data = NSData(contentsOfURL: imageURL)
data?.writeToURL(saveURL!, atomically: true)
pasteboard.declareTypes([NSFilenamesPboardType], owner: nil)
pasteboard.writeObjects([saveURL!])
I managed to get this working using the following code:
let pb = NSPasteboard.general
do {
let fileType = try NSWorkspace.shared.type(ofFile: localPath.path)
let pasteboardType = NSPasteboard.PasteboardType.init(fileType)
pb.declareTypes([pasteboardType], owner: nil)
pb.writeObjects([localPath as NSURL])
} catch {
return
}

How can I download an mp4 straight to my hard drive in Swift?

I'm using this code to download an mp4 file:
func downloadImageFile() {
let myURLstring = getImageURLCM()
let myFilePathString = "/Users/jack/Desktop/Comics/"+getTitle()+".mp4"
let url = NSURL(string: myURLstring)
let dataFromURL = NSData(contentsOfURL: url!)
let fileManager = NSFileManager.defaultManager()
fileManager.createFileAtPath(myFilePathString, contents: dataFromURL, attributes: nil)
}
But I've noticed that the file actually gets loaded up into my RAM first, before the NSFileManager saves it to my hard drive (based on the Xcode debug session). That's tolerable for smaller files, but most of the files I want to download with this will be at least 1GB.
My main question is: how to make this more RAM friendly?
I've also noticed that I get the spinning wheel of death until the download is finished, so if advice on fixing that would be appreciated as well.
You would be better to go with the system managed download in NSURLSession, specifically NSURLDownloadTask. This way you don't have to worry about memory management of large downloads. From NSURLSession swift file
/*
* download task convenience methods. When a download successfully
* completes, the NSURL will point to a file that must be read or
* copied during the invocation of the completion routine. The file
* will be removed automatically.
*/
func downloadTaskWithURL(url: NSURL, completionHandler: (NSURL?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDownloadTask?
Example of Use below - copy and paste into new Swift Playground:
import UIKit
import XCPlayground
func downloadFile(filePath: String) {
let url = NSURL(string: filePath)
if let unwrappedURL = url {
let downloadTask = NSURLSession.sharedSession().downloadTaskWithURL(unwrappedURL) { (urlToCompletedFile, reponse, error) -> Void in
// unwrap error if present
if let unwrappedError = error {
print(unwrappedError)
}
else {
if let unwrappedURLToCachedCompletedFile = urlToCompletedFile {
print(unwrappedURLToCachedCompletedFile)
// Copy this file to your destinationURL with
//NSFileManager.defaultManager().copyItemAtURL
}
}
}
downloadTask?.resume()
}
}
downloadFile("http://devstreaming.apple.com/videos/wwdc/2015/711y6zlz0ll/711/711_networking_with_nsurlsession.pdf?dl=1")
XCPSetExecutionShouldContinueIndefinitely()
Simple Example on github here - https://github.com/serendipityapps/NSURLSessionDownloadTaskExample
dataFromURL.writeToFile(myFilePathString, atomically: true)
This is the snippet I use, it writes the loaded data into the file at the given path.

Load files in xcode unit tests

I have an Xcode 5 unit test project and some test xml files related to it. I've tried a bunch of approaches but I can't seem to load the xml files.
I've tried the following which does not work
NSData* nsData = [NSData dataWithContentsOfFile:#"TestResource/TestData.xml"];
NSString* fileString = [NSString stringWithContentsOfFile:#"TestData.xml" encoding:NSUTF8StringEncoding error:&error];
Also if I try to preview all the bundles using [NSBundle allBundles] the unit test bundle does not appear in the list?
I tried to create a separate resource bundle and I can't seem to locate it programmatically although it does get built and deployed.
What am I doing wrong ?
When running tests the application bundle is still the main bundle. You need to use unit tests bundle.
Objective C:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:#"TestData" ofType:#"xml"];
NSData *xmlData = [NSData dataWithContentsOfFile:path];
Swift 2:
let bundle = NSBundle(forClass: self.dynamicType)
let path = bundle.pathForResource("TestData", ofType: "xml")!
let xmlData = NSData(contentsOfFile: path)
Swift 3 and up:
let bundle = Bundle(for: type(of: self))
let path = bundle.path(forResource: "TestData", ofType: "xml")!
let xmlData = NSData(contentsOfFile: path)
With swift Swift 3 the syntax self.dynamicType has been deprecated, use this instead
let testBundle = Bundle(for: type(of: self))
guard let ressourceURL = testBundle.url(forResource: "TestData", ofType: "xml") else {
// file does not exist
return
}
do {
let ressourceData = try Data(contentsOf: ressourceURL)
} catch let error {
// some error occurred when reading the file
}
or
guard let ressourceURL = testBundle.url(forResource: "TestData", withExtension: "xml")
As stated in this answer:
When the unit test harness runs your code, your unit test bundle is NOT the main bundle. Even though you are running tests, not your application, your application bundle is still the main bundle.
If you use following code, then your code will search the bundle that your unit test class is in, and everything will be fine.
Objective C:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:#"TestData" ofType:#"xml"];
NSData *xmlData = [NSData dataWithContentsOfFile:path];
Swift:
let bundle = NSBundle(forClass: self.dynamicType)
if let path = bundle.pathForResource("TestData", ofType: "xml")
{
let xmlData = NSData(contentsOfFile: path)
}
Swift 4+
I found none of the answers here up–to date or using a URL.
First add the following function to your testClass:
/// getFile. Opens a file in the current bundle and return as data
/// - Parameters:
/// - name: fileName
/// - withExtension: extension name, i.e. "json"
/// - Returns: Data of the contents of the file on nil if not found
static func getFile(_ name: String, withExtension: String) -> Data? {
guard let url = Bundle(for: Self.self)
.url(forResource: name, withExtension: withExtension) else { return nil }
guard let data = try? Data(contentsOf: url) else { return nil }
return data
}
Then you can call it in your test class like this:
func test() throws {
let xmlData = Self.getFile("TestData", withExtension: "xml")
XCTAssertNotNil(xmlData, "File not found")
}
Note: Apple recommends that URL's should always be used for resources (not paths) for security reasons.
Relative paths are relative to the current working directory. By default, that's / — the root directory. It's looking for that folder at the root level of your startup disk.
The correct way to get a resource that's within your bundle is to ask your bundle for it.
In an application, you'd get the bundle using [NSBundle mainBundle]. I don't know if that works in a test case; try it, and if it doesn't (if it returns nil or an unuseful bundle object), substitute [NSBundle bundleForClass:[self class]].
Either way, once you have the bundle, you can ask it for the path or URL for a resource. You generally should go for URLs unless you have a very specific reason to need a path (like passing it to a command-line tool using NSTask). Send the bundle a URLForResource:withExtension: message to get the resource's URL.
Then, for the purpose of reading a string from it, use [NSString stringWithContentsOfURL:encoding:error:], passing the URL you got from the bundle.
One thing to note is(It waste me serval minutes to figure it out):
Don't forget to add the test file to "Copy Bundle Resources" section of Build Phases
Without it, you couldn't load the file successfully.
Get main bundle
Use main bundle to get local mock data
Load data from JSON
let mainBundle = Bundle(identifier: "com.mainBundle.Identifier")
if let path = mainBundle?.path(forResource: "mockData", ofType: "json") {
do {
let testData = try Data(contentsOf: URL(fileURLWithPath: path))
networkTestSession.data = testData
} catch {
debugPrint("local json test data missing")
}
}
I know this specific question is asking for xml files, but if you're trying to do the same with json files, try this:
let url = Bundle.main.url(forResource: "data", withExtension: ".json")
guard let dataURL = url, let data = try? Data(contentsOf: dataURL) else {
fatalError("Couldn't read data.json file") }

Resources