I wrote this code in the previous version of Swift and Xcode and now when I updated xcode to 7.0.1 and updated swift to another version I got a lot of errors. This is something I can't solve and it would be so nice if you could help me. The fat text is the error. The part from >>> to <<< is the part that I need help with so the > and < is not in the real code.
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
>>> var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)! <<<
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as! GameScene
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}
Here, I made your code nicer and safe. This code (in theory) cannot crash whatever happens:
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
guard let
path = NSBundle.mainBundle().pathForResource(file, ofType: "sks"),
sceneData = try? NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe) else {
return nil
}
let archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
guard let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as? GameScene else {
return nil
}
archiver.finishDecoding()
return scene
}
}
If you don't know how the new error handling and guard works, you should check online.
Also if you really want to be fancy, you can use the new defer:
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
guard let
path = NSBundle.mainBundle().pathForResource(file, ofType: "sks"),
sceneData = try? NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe) else {
return nil
}
let archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
defer { archiver.finishDecoding() }
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
return archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as? GameScene
}
}
In swift 2.0 you have to manage errors thrown:
do {
var sceneData = try NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
} catch let error as NSError {
// manage error case
}
There is no longer an NSData.init(contentsOfFile:options:error:) constructor. Most Cocoa calls that returned an error this way are now throwing. The new way to do what you've done here is:
var sceneData = try! NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
Rather than getting back an optional and then force-unwrapping it, this uses a force-try to do the same thing. In both cases, this will crash if the read fails. Generally a normal try inside of a do/catch is preferred (just like you generally should not force-unwrap optionals).
You need to download the new version of the Swift language book. Enums that consist of bits that should be combined are now represented as sets, not as integer values. Everything is nicely explained in Apple's documentation.
Reading release notes is also very, very useful. At this point in time, Apple doesn't have the slightest scruples to modify the language in a way that breaks existing code. You need to keep up with it yourself.
Related
I'm trying to run animated "DAE" model in SceneKit:
let url = Bundle.main.url(forResource: "art.scnassets/Player(walking)", withExtension: "dae")
let sceneSource = SCNSceneSource(url: url!, options: [SCNSceneSource.LoadingOption.animationImportPolicy: SCNSceneSource.AnimationImportPolicy.playRepeatedly] )
let animationIds: NSArray = sceneSource?.identifiersOfEntries(withClass: CAAnimation)
for eachId in animationIds {
let animation: CAAnimation = (sceneSource?.entryWithIdentifier(eachId as! String, withClass: CAAnimation.self))!
animations.add(animation)
}
character?._walkAnimations = animations
But compiler It throws on the line:
let animationIds: NSArray = sceneSource?.identifiersOfEntries(withClass: CAAnimation)
and writes an error:
Cannot convert value of type '[String]?' to specified type 'NSArray'
Please help me to fix that problem.
Thanks in advance.
Why you are converting [String]? to NSArray and then convert each element to String again, no need of it simply use Swift native Array and wrapped the optional value with if let or guard let.
guard let animationIds = sceneSource?.identifiersOfEntries(withClass: CAAnimation) else {
return
}
for eachId in animationIds {
if let animation = sceneSource?.entryWithIdentifier(eachId, withClass: CAAnimation.self) {
animations.add(animation)
}
}
character?._walkAnimations = animations
I have worked my way through this tutorial here. One of the last things you do is have a button make sound when it is pressed. I wanted to then continue using that same logic to make a sound board app. However when I stripped the non-essential parts besides it making noise inside a new project I started getting a fatal error.
Here is my ViewController.swfit file:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var sample : AVAudioPlayer?
func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer? {
//1
let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
let url = NSURL.fileURLWithPath(path!)
//2
var audioPlayer:AVAudioPlayer?
// 3
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
return audioPlayer
}
override func viewDidLoad() {
super.viewDidLoad()
if let sample = self.setupAudioPlayerWithFile("Stomach", type:"aif") {
self.sample = sample
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonPressed() {
sample?.play()
}
}
Here is my project when the fatal error hits
I have also tried this solution but I got errors as well.
I am running on El Capitan 10.11.3 with Xcode 7.3
Have you included the an audio file named "Stomach.aif" in your project? If not, pathForResource will return nil and you'll crash when attempting to force unwrap that path. You can use a safer version of this function, although if it can't locate that file it still won't play the audio On the bright side it shouldn't crash.
func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer? {
var audioPlayer:AVAudioPlayer? = nil
if let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String) {
let url = NSURL.fileURLWithPath(path)
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
} catch {
print("Player not available")
}
}
return audioPlayer
}
I am new to code and I am not sure how to get rid of my optional values here. I read somewhere that this could be my problem. any help would be great!
I have been following this tutorial:https://www.youtube.com/watch?v=Qyy8pJd4IWU
#IBAction func dropPhoto(sender: AnyObject) {
presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [NSObject : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
let thumbnail = image.resizedImageWithContentMode(UIViewContentMode.ScaleAspectFit, bounds: CGSizeMake(400, 400), interpolationQuality: CGInterpolationQuality.High)
let imgData = UIImagePNGRepresentation(thumbnail)
let base64EncodedImage = imgData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
let uniqueReference = firebase?.childByAutoId()
uniqueReference!.setValue(base64EncodedImage)
let key = uniqueReference?.key
_ = mapView.userLocation.location
geofire!.setLocation(mapView.userLocation.location,forKey: key)
}
Whenever you see this error, look for "!"s
There are two lines here that contain a force-unwrap
geofire!.setLocation(mapView.userLocation.location,forKey: key)
and
uniqueReference!.setValue(base64EncodedImage)
you should be able to fix it by simply replacing the ! with a ? , e.g.
geofire?.setLocation(mapView.userLocation.location,forKey: key)
which will cause setLocation to be called only if geoFire is a real value, otherwise if you want to also handle the nil case, the swift way is:
if let geoFire = geoFire {
geoFire.setLocation(mapView.userLocation.location, forKey: key)
}
else{
*do something*
}
You could also add an assert or precondition checking firebase and geofire at the beginning of the function. Here is a third approach that will check those values and stop execution on your debug builds but otherwise simply return on release builds. That will make the later method calls into firebase and geofire safe.
You will still need to identify why one of your references is unexpectedly nil and handle that case. Maybe never call this image picker function in the first place or simply remove the assertFailure statements and let the function return silently without doing anything. Your choice.
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [NSObject : AnyObject]?) {
guard let firebase = firebase else {
assertionFailure("Missing Firebase reference")
return
}
guard let geofire = geofire else {
assertionFailure("Missing Geofire reference")
return
}
self.dismissViewControllerAnimated(true, completion: nil)
let thumbnail = image.resizedImageWithContentMode(UIViewContentMode.ScaleAspectFit, bounds: CGSizeMake(400, 400), interpolationQuality: CGInterpolationQuality.High)
let imgData = UIImagePNGRepresentation(thumbnail)
let base64EncodedImage = imgData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
uniqueReference = firebase.childByAutoId()
uniqueReference.setValue(base64EncodedImage)
geofire.setLocation(mapView.userLocation.location,forKey: uniqueReference.key)
}
I'm using the SODA Client for swift (Created by Socrata), I just updated to XCode 7 and swift 2 and found some troubles. The one I haven't been able to solve is the completion handler case when it finds an error, it's not accepting the line "syncCompletion(.Error (reqError))" that supposedly should get the error and return to main thread.
I've seen many errors with the same description here "Type of expression is ambiguous without more context", but not in completion handlers, I saw one using do - catch that is different. I'm don't know enough of swift to find out the way to change this.
Some answers suppose you should rewrite the code because some types could have change in swift 2, but I wouldn't know where to start rewriting.
Thanks in advance for your help.
var task = session.dataTaskWithRequest(request, completionHandler: { data, response, reqError in
// We sync the callback with the main thread to make UI programming easier
let syncCompletion = { res in NSOperationQueue.mainQueue().addOperationWithBlock { completionHandler (res) } }
// Give up if there was a net error
if reqError != nil {
syncCompletion(.Error (reqError))
return
}
// Try to parse the JSON
// println(NSString (data: data, encoding: NSUTF8StringEncoding))
var jsonError: NSError?
var jsonResult: AnyObject!
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
} catch var error as NSError {
jsonError = error
jsonResult = nil
} catch {
fatalError()
}
if let error = jsonError {
syncCompletion(.Error (error))
return
}
// Interpret the JSON
if let a = jsonResult as? [[String: AnyObject]] {
syncCompletion(.Dataset (a))
}
else if let d = jsonResult as? [String: AnyObject] {
if let e : AnyObject = d["error"] {
if let m : AnyObject = d["message"] {
syncCompletion(.Error (NSError(domain: "SODA", code: 0, userInfo: ["Error": m])))
return
}
}
syncCompletion(.Dataset ([d]))
}
else {
syncCompletion(.Error (NSError(domain: "SODA", code: 0, userInfo: nil)))
}
})
Solved, I replaced:
if reqError != nil
With
if let error = reqError
and:
syncCompletion(.Error (reqError))
with
syncCompletion(.Error (error))
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)
}