Why as? AVAudioSessionRouteChangeReason returns nil? - swift

I'm subscribed to the AVAudioSessionRouteChangeNotification.
Why the following returns nil?
notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? AVAudioSessionRouteChangeReason
All the other options work, e.g. return Optional(2):
notification.userInfo?[AVAudioSessionRouteChangeReasonKey]
notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt
notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? NSNumber
I find the first option the most clear/readable.
Edit 2021-04-15:
In the current Xcode version 12.4 (Swift 5.3.2) it does not return nil. The following works now:
notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? AVAudioSession.RouteChangeReason

It's because userInfo is an NSDictionary, and you can't add Swift enum constants to NSDictionary. You can add the raw values, though.
That is, it's not legal to declare userInfo as something like
let userInfo : NSDictionary = [ AVAudioSessionRouteChangeReasonKey : AVAudioSessionRouteChangeReason.NewDeviceAvailable ]
...because you get an error saying cannot convert value of type '[String : AVAudioSessionRouteChangeReason]' to specified type 'NSDictionary'. You can declare it like this:
let userInfo : NSDictionary = [ AVAudioSessionRouteChangeReasonKey : AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue ]
This is what you have (or the equivalent of what you have, since the framework is almost certainly using C or Objective-C instead of Swift).
Since AVAudioSessionRouteChangeReason is declared as
public enum AVAudioSessionRouteChangeReason : Uint {
...
The raw values are unsigned integers. You can use as? to downcast them to integer types or NSNumber, but they're not the same as the actual enum type.

Related

What is the return type of JSONSerialization.jsonObject(with:options:) in Swift?

I made the json parser in swift language.
But, many of people are using like below.
let jsonParsed = JSONSerialization.jsonObject(with: data, options: [])
guard let jsonDict = jsonParsed as? Dictionary<String, AnyObject> else { return }
...
Then, I wonder the type of jsonParsed. The JSONSerialization.jsonObject(with:options:) function reference describes that the result type is just Any.
I know the type is Dictionary because JSON. The KEY is String type but, VALUE is AnyObject? How about Any?
I know the difference between AnyObject and Any. Any also includes value type, function type.
Number is also value type in swift: Int, Float, Double...
Is that impossible return type is value type?
Json can be array also. This is valid josn
[
"1",
"2",
"3"
]
You can use this site to verify [https://jsoneditoronline.org/][1].
When you give fragmentsallowed option in the function json result can be Int,Float or any other primitive data type.
So it is not possible to return type in compile time. At run time it is possible. But return type is decided at run time.

Iterating dictionary swift 3

I have below code in my project.
for (key, value) in photoDic {
if let url = URL.init(string: value as! String){
let photo : PhotoRecord = PhotoRecord.init(name:key as! String, url:url)
self.photoRecords.append(photo)
}
}
My question is how can I make key and value in for loop optional, or check if either of them are nil?
I am not able to check if they are nil, getting warning saying any cannot be nil because it is nonoptional.
I was thinking of using something like
for(key:String?, value:String?){}
But it is not working.
The key in a dictionary can't be an optional. (The key must conform to the Hashable protocol, and optionals don't.) So you CAN'T make the keys in your dictionary optional
If you want the values of your dictionary to be Optionals then you need to declare them as Optionals.
So, for example, change
let photoDic: [String: String] = ["key1": "http://www.someDomain.com/image.jpg"]
to
let photoDic: [String: String?] = ["key1": "http://www.someDomain.com/image.jpg"]
(Note that the type of photoDic is changed to [String: String?].)
As mentioned already all keys in a dictionary are non-optional by definition.
Further in NSDictionary all values are non-optional by definition, too.
Be happy about that because
There is no need to check for nil.
The code will never crash.
A Swift dictionary can theoretically contain optional values but practically you are discouraged from using it. For compatibility reasons to NSDictionary a nil value indicates key is missing.

type 'Any' has no subscript members

let employerName = snapshot.value! ["employerName"] as! String
let employerImage = snapshot.value! ["employerImage"] as! String
let uid = snapshot.value! ["uid"] as! String
I reviewed previous posts but cannot seem to find a way to solve this problem. All three lines of code give the "type 'Any' has no subscript members" error. Fairly new to this so any help is appreciated.
snapshot.value has a type of Any. A subscript is a special kind of function that uses the syntax of enclosing a value in braces. This subscript function is implemented by Dictionary.
So what is happening here is that YOU as the developer know that snapshot.value is a Dictionary, but the compiler doesn't. It won't let you call the subscript function because you are trying to call it on a value of type Any and Any does not implement subscript. In order to do this, you have to tell the compiler that your snapshot.value is actually a Dictionary. Further more Dictionary lets you use the subscript function with values of whatever type the Dictionary's keys are. So you need to tell it you have a Dictionary with keys as String(AKA [String: Any]). Going even further than that, in your case, you seem to know that all of the values in your Dictionary are String as well, so instead of casting each value after you subscript it to String using as! String, if you just tell it that your Dictionary has keys and values that are both String types (AKA [String: String]), then you will be able to subscript to access the values and the compiler will know the values are String also!
guard let snapshotDict = snapshot.value as? [String: String] else {
// Do something to handle the error
// if your snapshot.value isn't the type you thought it was going to be.
}
let employerName = snapshotDict["employerName"]
let employerImage = snapshotDict["employerImage"]
let uid = snapshotDict["fid"]
And there you have it!
Since you want to treat snapshot.value as an unwrapped dictionary, try casting to one and, if it succeeds, use that dictionary.
Consider something like:
func findElements(candidate: Any) {
if let dict: [String : String] = candidate as? Dictionary {
print(dict["employerName"])
print(dict["employerImage"])
print(dict["uid"])
}
}
// Fake call
let snapshotValue = ["employerName" : "name", "employerImage" : "image", "uid" : "345"]
findElements(snapshotValue)

Convert or cast object to string

how can i convert any object type to a string?
let single_result = results[i]
var result = ""
result = single_result.valueForKey("Level")
now i get the error: could not assign a value of type any object to a value of type string.
and if i cast it:
result = single_result.valueForKey("Level") as! String
i get the error:
Could not cast value of type '__NSCFNumber' (0x103215cf0) to 'NSString' (0x1036a68e0).
How can i solve this issue?
You can't cast any random value to a string. A force cast (as!) will fail if the object can't be cast to a string.
If you know it will always contain an NSNumber then you need to add code that converts the NSNumber to a string. This code should work:
if let result_number = single_result.valueForKey("Level") as? NSNumber
{
let result_string = "\(result_number)"
}
If the object returned for the "Level" key can be different object types then you'll need to write more flexible code to deal with those other possible types.
Swift arrays and dictionaries are normally typed, which makes this kind of thing cleaner.
I'd say that #AirSpeedVelocity's answer (European or African?) is the best. Use the built-in toString function. It sounds like it works on ANY Swift type.
EDIT:
In Swift 3, the answer appears to have changed. Now, you want to use the String initializer
init(describing:)
Or, to use the code from the question:
result = single_result.valueForKey("Level")
let resultString = String(describing: result)
Note that usually you don't want valueForKey. That is a KVO method that will only work on NSObjects. Assuming single_result is a Dictionary, you probably want this syntax instead:
result = single_result["Level"]
This is the documentation for the String initializer provided here.
let s = String(describing: <AnyObject>)
Nothing else is needed. This works for a diverse range of objects.
The toString function accepts any type and will always produce a string.
If it’s a Swift type that implements the Printable protocol, or has overridden NSObject’s description property, you’ll get whatever the .description property returns. In the case of NSNumber, you’ll get a string representation of the number.
If it hasn’t, you’ll get a fairly unhelpful string of the class name plus the memory address. But most standard classes, including NSNumber, will produce something sensible.
import Foundation
class X: NSObject {
override var description: String {
return "Blah"
}
}
let x: AnyObject = X()
toString(x) // return "Blah"
"\(x)" // does the same thing but IMO is less clear
struct S: Printable {
var description: String {
return "asdf"
}
}
// doesn't matter if it's an Any or AnyObject
let s: Any = S()
toString(s) // reuturns "asdf"
let n = NSNumber(double: 123.45)
toString(n) // returns "123.45"
n.stringValue // also works, but is specific to NSNumber
(p.s. always use toString rather than testing for Printable. For one thing, String doesn’t conform to Printable...)
toString() doesn't seem to exist in Swift 3 anymore.
Looks like there's a failable initializer that will return the passed in value's description.
init?(_ description: String)
Docs here https://developer.apple.com/reference/swift/string/1540435-init

Dictionary Values in Swift

I am trying to access values I've set in a Swift dictionary. The dictionary has a String for the keys and values. My intent is to use the dictionary to set a string variable as I work through a series of URLs. In Objective-C I understand how this works. I have a string property called currentFeed that I pass the value of the dictionary to.
self.currentFeed = [NSString stringWithString: [self.URLDictionary objectForKey: #"FLO Cycling"]];
In Swift I am having a difficult time with this. I tried the following code and receive an error message.
self.currentFeed = self.URLDictionary["FLO Cycling"]
Error Message: "Cannot subscript a value of type '[String:String]' with an index of type 'String'.
For reference the dictionary was created in Swift in the following way. The constants were created with lets.
let kFLOCyclingURL = "http://flocycling.blogspot.com/feeds/posts/default?alt=rss"
let kTriathleteURL = "http://triathlon.competitor.com/feed"
let kVeloNewsURL = "http://velonews.competitor.com/feed"
let kCyclingNewsURL = "http://feeds.feedburner.com/cyclingnews/news?format=xml"
let kRoadBikeActionURL = "http://www.roadbikeaction.com/news/rss.aspx"
let kIronmanURL = "http://feeds.ironman.com/ironman/topstories"
The items were added to the dictionary with keys.
let URLTempDictionary = ["FLO Cycling" : kFLOCyclingURL, "Triathlete" : kTriathleteURL, "Velo News" : kVeloNewsURL, "Cycling News" : kCyclingNewsURL, "Road Bike Action" : kRoadBikeActionURL, "Ironman" : kIronmanURL]
Thanks for any help.
Take care,
Jon
This compiles fine for me. The only thing I noticed was that your dictionary is named URLTempDictionary and you're accessing URLDictionary. :)
Option a (safest, most robust)
if let dict = self.URLDictionary
{
self.currentFeed = dict["FLO Cycling"]
}
Option b (use with caution, possible runtime error)
self.currentFeed = dict!["FLO Cycling"]