Swift 2 - fileURLWithPath returns NIL - swift2

I am trying to load a video using MPMoviePlayerController. I have added the file in my project and also added to the Build Phase "Copy Bundle Resource".
When I call the function to open the video I can see that the path its printed out however, the app crashes on 'fileURLWithPath`.
What am I doing wrong? Are .mp4 flies playable?
func firstView(){
if let filePath = NSBundle.mainBundle().pathForResource("firstVideo", ofType: "mp4") {
print(filePath)
//PRINTS: /var/mobile/Containers/Bundle/Application/B93BB049-DA87-4D89-AEBE-A5C92C01726E/MyApp.app/firstVideo.mp4
firstMoviePlayer!.contentURL = NSURL.fileURLWithPath(filePath) // fatal error
}
firstMoviePlayer!.repeatMode = .None
firstMoviePlayer!.controlStyle = MPMovieControlStyle.Embedded
firstMoviePlayer!.view.frame = FirstTimeVideoView.frame
FirstTimeVideoView.addSubview(firstMoviePlayer!.view)
firstMoviePlayer!.play()
}

You just need to change:
if let filePath = NSBundle.mainBundle().pathForResource("firstVideo", ofType: "mp4") {
print(filePath)
//PRINTS: /var/mobile/Containers/Bundle/Application/B93BB049-DA87-4D89-AEBE-A5C92C01726E/MyApp.app/firstVideo.mp4
firstMoviePlayer!.contentURL = NSURL.fileURLWithPath(filePath) // fatal error
}
TO:
if let filePath = NSBundle.mainBundle().URLForResource("firstVideo", ofType: "mp4") {
print(filePath)
//PRINTS: /var/mobile/Containers/Bundle/Application/B93BB049-DA87-4D89-AEBE-A5C92C01726E/MyApp.app/firstVideo.mp4
firstMoviePlayer!.contentURL = NSURL.fileURLWithPath(filePath) // fatal error
}

Related

Download a file to a temp location in Swift 4 (Mac OS) like with curl

I'm writing a CLI tool in Swift 4 for MacOS.
If I "cheat" and use a shell function, then the curl command, I get exactly the result I'm after:
func shell(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
shell("/usr/bin/curl", "-OL", "http://mywebserver/myfile.whatever")
How do I do the same thing natively in Swift 4?
I tried using the just library: https://github.com/dduan/Just
but I can't figure out how to simply get it to download a file from a URL to a specific location on the file system.
I've also found various solutions for Swift 3 and 5, and iOS which obviously don't work for me.
Simple example: Due to the asynchronous behavior of URLSession you have to start the runloop after resuming the data task and stop it in the completion handler.
let runLoop = CFRunLoopGetCurrent()
let url = URL(string: "http://mywebserver/myfile.whatever")!
let dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
let returnValue : Int32
if let error = error {
fputs("\(error.localizedDescription)\n", stderr)
returnValue = EXIT_FAILURE
} else {
let result = String(data:data!, encoding:.utf8)!
fputs("\(result)\n", stdout)
returnValue = EXIT_SUCCESS
}
CFRunLoopStop(runLoop)
exit(returnValue)
}
dataTask.resume()
CFRunLoopRun()
The code returns the data in stdout. You can write the data directly to disk, replace
let result = String(data:data!, encoding:.utf8)!
fputs("\(result)\n", stdout)
with
let fileURL = URL(fileURLWithPath: "/Users/me/path/to/file.ext")
do {
try data!.write(to: fileURL)
} catch {
fputs("\(error.localizedDescription)\n", stderr)
}
You can download using downloadTask
Create session configuration
Create URLSession with this configuration
Apply download task
When the download is done successfully, copy the data to the destination
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("downloadedFile.jpg")
//Create URL to the source file you want to download
let fileURL = URL(string: "https://en.wikipedia.org/wiki/Saint_Martin#/media/File:Saint_martin_map.PNG")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription);
}
}
task.resume()
}

Pass CSV file to Swift 2 File for UI Automation Testing

Hi i am absolute beginner to the swift and Xcode tool. I am trying to Test application through UI Testing bundle introduced from Xcode 7+. I want to migrate my hardcoded data from scripts to cvs file. please help.
currently i have created a class csvScanner but it is showing Argument labels(contentsOfFile:,encoding :,error) error in code below
import Foundation
class CSVScanner {
class func debug(string:String){
print("CSVScanner: \(string)")
}
class func runFunctionOnRowsFromFile(theColumnNames:Array<String>, withFileName theFileName:String, withFunction theFunction:(Dictionary<String, String>)->()) {
if let strBundle = NSBundle.mainBundle().pathForResource(theFileName, ofType: "csv") {
var encodingError:NSError? = nil
if let fileObject = NSString(contentsOfFile: strBundle, encoding: NSUTF8StringEncoding, error: &encodingError){
var fileObjectCleaned = fileObject.stringByReplacingOccurrencesOfString("\r", withString: "\n")
fileObjectCleaned = fileObjectCleaned.stringByReplacingOccurrencesOfString("\n\n", withString: "\n")
let objectArray = fileObjectCleaned.componentsSeparatedByString("\n")
for anObjectRow in objectArray {
let objectColumns = anObjectRow.componentsSeparatedByString(",")
var aDictionaryEntry = Dictionary<String, String>()
var columnIndex = 0
for anObjectColumn in objectColumns {
aDictionaryEntry[theColumnNames[columnIndex]] = anObjectColumn.stringByReplacingOccurrencesOfString("\"", withString: "", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
columnIndex++
}
if aDictionaryEntry.count>1{
theFunction(aDictionaryEntry)
}else{
CSVScanner.debug("No data extracted from row: \(anObjectRow) -> \(objectColumns)")
}
}
}else{
CSVScanner.debug("Unable to load csv file from path: \(strBundle)")
if let errorString = encodingError?.description {
CSVScanner.debug("Received encoding error: \(errorString)")
}
}
}else{
CSVScanner.debug("Unable to get path to csv file: \(theFileName).csv")
}
}
}
Thanks in advance
If you are using Xcode 7+, you are also using Swift 2. Instead of passing an error pointer to the method, you need to use try.
The method signature has changed to convenience init(contentsOfFile path: String,
encoding enc: UInt) throws.
(Note: no error:.)

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.

Setting the Desktop background on OSX using Swift 2

After an introduction to Javascript, I'm trying to tackle OSX/iOS programming by creating some simple tools to scratch my own itches.
However, right from the jump I hit a roadblock.
I found two examples that should work.
https://github.com/hinderberg/ios-swift-kurs/blob/master/swift-intro/wallpaper.swift
https://www.snip2code.com/Snippet/196825/Swift-shell-script-to-randomize-wallpape
Here's the second:
#!/usr/bin/env xcrun swift
import Foundation
import AppKit
let imagesDir = "/Users/david/Dropbox/Graphics/Wallpaper-HD/"
var err: NSError?
let fs = NSFileManager.defaultManager()
let filenames = fs.contentsOfDirectoryAtPath(imagesDir, error: &err) as [String]?
if let error = err {
NSLog(error.localizedDescription)
} else {
let imagenames = filenames!.filter { $0.hasSuffix(".jpg") || $0.hasSuffix("png") }
let ir = Int(arc4random_uniform(UInt32(imagenames.count)))
let imgurl = NSURL.fileURLWithPath(imagesDir + imagenames[ir])
let workspace = NSWorkspace.sharedWorkspace()
let screen = NSScreen.mainScreen()
let ok : Bool = workspace.setDesktopImageURL( imgurl!, forScreen: screen!, options: nil, error: nil )
if ok {
println( "New wallpaper: " + imagenames[ir] )
} else {
println("Oops!")
}
}
This didn't work in XCode 7 beta 3.
Hoping to reduce to the essentials, I arrived at:
#!/usr/bin/env xcrun swift
import Foundation
import AppKit
let imagesDir = "/Users/josh/Downloads/"
let singleImage = "/Users/josh/Downloads/xlarge.png"
let imgurl = NSURL.fileURLWithPath(singleImage)
let workspace = NSWorkspace.sharedWorkspace()
let screen = NSScreen.mainScreen()
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen!, options: nil, error: nil )
if ok {
print( "New wallpaper set!" )
} else {
print("Oops!")
}
And saved as the file wallpaper.swift.
On execution, the error is:
./wallpaper.swift:17:49: error: extra argument 'error' in call
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen!, options: nil, error: nil )
And now I'm completely stuck...
I've tried referring to NSWorkspace and NSScreen documentation as well as running through playground, but it's beyond my current skills.
Removing the extra argument it complains about (error: nil) simply gives a different error:
./wallpaper.swift:13:31: error: cannot invoke 'setDesktopImageURL' with an argument list of type '(NSURL, forScreen: NSScreen?, options: nil)'
let ok : Bool = workspace.setDesktopImageURL( imgurl, forScreen: screen, options: nil )
Where is the code failing, and how can I understand how to make it work properly?
In your example you're passing nil as options to the method.
I guess it worked before but now in the comments you showed the current method signature:
(url: NSURL, forScreen screen: NSScreen, options: [String : AnyObject]) throws
We see that options should be a non-Optional Dictionary.
It means you can't use nil anymore for the options parameter: if you don't have options, just pass an empty Dictionary.
Also, now in Swift 2, this method doesn't return a Bool anymore, it throws.
Meaning you have to use it with do try catch:
do {
let imgurl = NSURL.fileURLWithPath(singleImage)
let workspace = NSWorkspace.sharedWorkspace()
if let screen = NSScreen.mainScreen() {
try workspace.setDesktopImageURL(imgurl, forScreen: screen, options: [:])
}
} catch {
print(error)
}
Updated example for Swift 3:
do {
let imgurl = NSURL.fileURL(withPath: singleImage)
let workspace = NSWorkspace.shared()
if let screen = NSScreen.main() {
try workspace.setDesktopImageURL(imgurl, for: screen, options: [:])
}
} catch {
print(error)
}
For those of us using Xamarin Mac, this can be achieved like so:
string filepath = "/Users/you/Desktop/sweet-wallpaper.jpg";
var workspace = NSWorkspace.SharedWorkspace;
var screen = NSScreen.MainScreen;
NSUrl url = NSUrl.FromFilename(filepath);
NSDictionary options = new NSDictionary();
NSError errorContainer = new NSError();
workspace.SetDesktopImageUrl(url, NSScreen.MainScreen, options, errorContainer);
Updated version for Swift 5.x:
do {
let imageURL = URL(fileURLWithPath: "/path/to/image")
if let screen = NSScreen.main {
try NSWorkspace.shared.setDesktopImageURL(imageURL, for: screen, options: [:])
}
} catch {
print(error)
}

Early return/golden path in Swift

I'm used to write code with early return/golden path in Objective-C. I tried this approach in Swift, and noticed that early return comes at the expense of using the forced unwrapping operator (!) when optionals are involved.
Take a method that calculates the size of a directory. First, the golden path version:
private func calculateSize_GoldenPath(directory:String) -> UInt64 {
let fileManager = NSFileManager.defaultManager()
var error : NSError?
var contents = fileManager.contentsOfDirectoryAtPath(directory, error: &error) as [String]?
if contents == nil {
NSLog("Failed to list directory with error \(error)")
return 0
}
var size : UInt64 = 0
for pathComponent in contents! {
let path = directory.stringByAppendingPathComponent(pathComponent)
let attributes : NSDictionary? = fileManager.attributesOfItemAtPath(path, error: &error)
if (attributes == nil) {
NSLog("Failed to read file size of \(path) with error \(error)")
continue
}
size += attributes!.fileSize()
}
return size;
}
Notice how I'm using the ! operator both for the contents and attributes variables.
I'm assuming that overusing the ! operator kind of defeats the purpose of optionals and the type safety they bring. This is how I feel the above method should be coded in Swift to avoid forced unwrapping:
private func calculateSize_IfLet(directory:String) -> UInt64 {
let fileManager = NSFileManager.defaultManager()
var error : NSError?
if let contents = fileManager.contentsOfDirectoryAtPath(directory, error: &error) as? [String] {
var size : UInt64 = 0
for pathComponent in contents {
let path = directory.stringByAppendingPathComponent(pathComponent)
if let attributes : NSDictionary = fileManager.attributesOfItemAtPath(path, error: &error) {
size += attributes.fileSize()
} else {
NSLog("Failed to read file size of \(path) with error \(error)")
}
}
return size
} else {
NSLog("Failed to list directory with error \(error)")
return 0
}
}
However, by using if let I can't do early return anymore. If some methods don't use early return and some do, then I end up with a project with mixed coding style.
My question is, is there a way to code in golden path style without resorting to forced unwrapping when optionals are involved?
Personally I would use extraction of methods, in this case extract the pathComponent section into a separate method thus avoiding the multiple indent and awkward code that mashes conceptually separate code together.
private func calculateSize_IfLet(directoryPath:String) -> UInt64 {
var size : UInt64 = 0
let fileManager = NSFileManager.defaultManager()
var error : NSError?
if let contents = fileManager.contentsOfDirectoryAtPath(directoryPath, error: &error) as? [String] {
size = self.calculateSizeOfDirectory(directoryPath, contents:contents)
} else {
NSLog("Failed to list directory with error \(error)")
}
return size
}
private func calculateSizeOfDirectory(directoryPath:String, contents:[String]) -> UInt64 {
var size : UInt64 = 0
for pathComponent in contents {
var error : NSError?
let fileManager = NSFileManager.defaultManager()
let path = directoryPath.stringByAppendingPathComponent(pathComponent)
if let attributes : NSDictionary = fileManager.attributesOfItemAtPath(path, error: &error) {
size += attributes.fileSize()
} else {
NSLog("Failed to read file size of \(path) with error \(error)")
}
}
return size
}

Resources