Swift 2(.1) migration error - migration

I updated my Swift 1.2 Project to Swift 2.1 (installing Xcode 7.1). Now I get 2 errors.
1st error:
Downcast from 'NSURL?' to 'NSURL' only unwraps optionals; did you mean to use '!'?
in this line of code:
let documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as! NSURL
2nd error:
Cannot convert value of type 'Set<NSObject>' to expected argument type 'Set<UIUserNotificationCategory>?'
In this line of code:
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: NSSet(array: [todoCategory]) as! Set<NSObject> as Set<NSObject>))
Do you know how to fix it?

The first error is fixed when you remove the casting. However who probably want to check if documentDirectory is not nil so you would put it in a if let:
if let documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last {
// do something with the documents directory
}
The second error can be fixed like this:
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: Set<UIUserNotificationCategory>.init(arrayLiteral: todoCategory)))

Related

Swift 3 NSDictionary to Dictionary conversion causes NSInvalidArgumentException

I have just converted my project from Swift 2.2 to 3.0, and I'm getting a new exception thrown in my tests. I have some Objective C code in one of my tests which reads in some JSON from a file:
+ (NSDictionary *)getJSONDictionaryFromFile:(NSString *)filename {
/* some code which checks the parameter and gets a string of JSON from a file.
* I've checked in the debugger, and jsonString is properly populated. */
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
return jsonDict;
}
I'm calling this from some Swift code:
let expectedResponseJSON = BZTestCase.getJSONDictionary(fromFile: responseFileName)
This works just fine most of the time, but I have one JSON file which causes the error:
failed: caught "NSInvalidArgumentException", "-[__NSSingleObjectArrayI enumerateKeysAndObjectsUsingBlock:]: unrecognized selector sent to instance 0x608000201fa0"
The strange thing about this is that the error is generated after the getJSONDictionaryFromFile method returns and before expectedResponseJSON in the Swift code is populated. To me, this seems to say that it's the conversion from an NSDictionary to Dictionary which is the problem. The offending JSON file is this one:
[
{
"status": "403",
"title": "Authentication Failed",
"userData": {},
"ipRangeError": {
"libraryName": "Name goes here",
"libraryId": 657,
"requestIp": "127.0.0.1"
}
}
]
If I remove the outermost enclosing [], this error goes away. I can't be the only person using an array as the top level entity of a JSON file in Swift 3, am I doing something wrong? What can I do to get around this error?
As is referenced in the comments, the problem is that getJSONDictionaryFromFile returns an NSDictionary * and my JSON input is an array. The only mystery is why this used to work in Swift 2.2! I ended up changing expectedResponseJSON to be an Any?, and rewrote my Objective C code in Swift:
class func getStringFrom(file fileName: String, fileExtension: String) -> String {
let filepath = Bundle(for: BZTestCase.self).path(forResource: fileName, ofType: fileExtension)
return try! NSString(contentsOfFile: filepath!, usedEncoding: nil) as String
}
class func getJSONFrom(file fileName: String) -> Any? {
let json = try! JSONSerialization.jsonObject(with: (getStringFrom(file: fileName, fileExtension: ".json").data(using: .utf8))!, options:.allowFragments)
return json
}
As a note to anyone who might cut and paste this code, I used try! and filepath! instead of try? and if let... because this code is used exclusively in tests, so I want it to crash as soon as possible if my inputs are not what I expect them to be.

what is the proper syntax for the following expression in swift 3?

as you can guess, this is an issue regarding swift's whole API renovation. I've read the documentation extensively but can't seem to lay my finger on a proper workaround.
I am receiving the error
Value of type 'Error' has no member 'userInfo'
on the following line:
else if let secondMessage = error?.userInfo["error"] as? String
of this block:
let query = PFQuery(className: "Images")
query.whereKey("Subject", equalTo: self.subjectName)
query.findObjectsInBackground { (objects, error) -> Void in
if error == nil {
// do something
}else if let secondMessage = error?.userInfo["error"] as? String {
// do something
}
Any suggestions? feel's like I'm missing something painstakingly obvious... dev block i guess :(

call can throw error message Swift 2

I just converted my app to Swift 2, and of course, am getting one error message:
"Call can throw, but it is not marked with 'try' and the eror is not handled"
I did search here for how to fix this, but the answers are even more confusing to me than the error itself. LOL.
My application worked perfectly until I converted it into Swift 2. Wonderful...
var myPlayer = AVAudioPlayer()
var yourSound1 = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("RemSound_01", ofType: "wav")!)
func initYourSound() {
myPlayer = AVAudioPlayer(contentsOfURL: yourSound1, fileTypeHint: nil)
myPlayer.prepareToPlay()
myPlayer.volume = 1.0 // < for setting initial volume, still not perfected.
}
The error throws in the following line:
myPlayer = AVAudioPlayer(contentsOfURL: yourSound1, fileTypeHint: nil)
How am I supposed to fix this? I understand that Swift is trying to "universalize" error handling, but doing so by breaking even the simplest of code seems silly to me.
Thank you for your help.
Here is the pattern you are looking for.
<throwing function> // compiler error in Swift 2
do { try <throwing function> } catch { }
In catch you usually get an error that you can handle further.
This works in Swift 2.0, put it in your viewDidLoad method ...
do {
try AudioPlayer = AVAudioPlayer(contentsOfURL: ButtonAudioURL, fileTypeHint: nil)
} catch {
print("errorin do-try-catch")
}
where ButtonAudioURL is
var ButtonAudioURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Besides", ofType: "mp3")!)

error handling in Swift 2

i have to fix the follow code for swift2.
if !UIImagePNGRepresentation(img).writeToFile(imagePath, options: nil, error: &error) {
if let actualError = error {
NSLog("Image not saved. \(actualError)")
}
}
To compile it i have this error on if row: Cannot invoke writeToFile with an argument list of type (String, options: _, error: inout NSError?)
How I can fix it.
Try it with
UIImagePNGRepresentation(img)?.writeToFile(imagePath, atomically: true)
instead. Check the Apple Docs.
Edit:
To answer your question more precisely use the error handling in Swift 2.
do {
try UIImagePNGRepresentation(img)?.writeToFile(imagePath, options: .DataWritingAtomic)
} catch let error as NSError {
print("Image not saved. \(error.description)")
}

How to handle a NSDictionary as a return type in iOS swift?

I'm trying to convert and existing, working objective c application over to swift and I'm getting a little tripped up with "closures". Here's the old working objective c block that makes returns a value from a web service:
- (IBAction)didTapSayHiButton {
[self.meteor callMethodName:#"sayHelloTo" parameters:#[self.username.text] responseCallback:^(NSDictionary *response, NSError *error) {
NSString *message = response[#"result"];
[[[UIAlertView alloc] initWithTitle:#"Meteor Todos" message:message delegate:nil cancelButtonTitle:#"Great" otherButtonTitles:nil] show];
}];
}
So here I'm getting either getting back a dictionary or response. And it works.
And here's how I'm trying to go about this with swift (the method is slightly different):
#IBAction func sayHi(sender : AnyObject) {
var params = [
"name": "Scotty"
]
meteor.callMethodName("sayHi", parameters: params, responseCallback: {
(response: Dictionary<String, String>, error: NSError) in
println("the name recieved back is: \(response)")
})
}
The error I'm getting in xCode: "NSDictionary is not a subtype of 'Dictionary'"
After looking through the swift book this is the best educated attempt that I can make. I've tried a few other things but each resulted in another type of error.
How do I make this work with swift?
Edit: I've also tried just using Dictionary and Dictionary<String, String>
I should also note that I'm using a bridging header to access objective c code (objectiveDDP). And that callMethodNamed is written in objective c as can be seen here: https://github.com/boundsj/ObjectiveDDP/blob/master/ObjectiveDDP/MeteorClient.h#L47
Update: by changing the method to:
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback:nil)
we were able to get it to work. But the second we try to add in the closure, it starts throwing the same original errors.
Try changing from using a Swift dictionary to explicitly using NSDictionary:
#IBAction func sayHi(sender : AnyObject) {
var params: NSDictionary = [
"name": "Scotty"
]
meteor.callMethodName("sayHi", parameters: params, responseCallback: {
(response: NSDictionary!, error: NSError) in
println("the name recieved back is: \(response)")
})
}
The trick, in this particular case, is to completely omit the closure's parameter types, and let the compiler figure it out. After searching around for a while, I found this post which led me to the solution.
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback: {
response, error in
let me:String = response["result"] as String!
println("called back \(me)")
})
If your not worried about accessing the closures parameters, apparently you can also using an underscore to ignore them completely:
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback: {
_ in
// Do something here
})