Extract Value from Locksmith with SwiftyJson - ios

I'm trying the value of an access token in Swift. I'm loading the token using Locksmith like so:
let (dictionary, error) = Locksmith.loadDataForUserAccount("userAccount")
This returns the following in the console:
Optional({
"access_token" = 123123123123123;
})
I'm using SwiftyJSON to extract the value of the access token
var access_token = dictionary["access_token"].stringValue
I'm getting the following error:
'NSDictionary?' does not have a member named 'subscript'
Any idea why?

LockSmith returns (NSDictionary?, NSError?):
public class func loadDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> (NSDictionary?, NSError?)
So, in your case dictionary itself is Optional:
Try:
var access_token = dictionary?["access_token"] as? String
Here, access_token is String?, it can be nil. If you want to store it to AnyObject variable, you must unwrap it. For example, using "Optional Binding":
if let token = access_token {
// Here, `token` is `String`, while `access_token` is `String?`
self.access_token = token
}
Or more directly:
if let access_token = dictionary?["access_token"] as? String {
// Here, access_token is `String`, not `String?`
self.access_token = token
}
else {
// error handling...
}
BTW, If you want to use SwiftyJSON, you should create JSON object from dictionary.
if let dict = dictionary {
var json = JSON(dict)
var access_token = json["access_token"].stringValue
}

As described at your 'NSDictionary?' Dictionary is an optional. You have to unwrap your optional before extracting stringValue from it. You can safely unwrap it using if let:
Edit
taking a hint from Rintaro as mentioned by him LockSmith returns an optional Dictionary so you need to first unwrap your dictionary and them you can unwrap access_token key value
if let dictionary = dictionary {
if let access_token = dictionary["access_token"] as? Int { // or as? String
println(access_token)
} else {
println(false)
}
} else {
println(false)
}
or if it returns a [String:String] Dictionary there is no need to cast the value to String
if let dictionary = dictionary {
if let access_token = dictionary["access_token"] {
println(access_token)
} else {
println(false)
}
} else {
println(false)
}

Related

How to access dictionary value by key inside array of dictionary on Swift 3?

I've been away for a while from swift development. And today I got bug report from my client on my old project. The thing is I used to access value from my dictionary with ease just like dictionary[key] then I already got the value, but today I got below error by doing that:
Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members
and here is my code:
var rollStatusArray = NSMutableArray()
for statusDict in rollStatusArray{
if statusId == "\(statusDict["mark_status_id"]!!)"{
return "\(statusDict["mark_status_name"]!!)"
}
}
How can I access dictionary value on swift 3?
In Swift's use native type Array/[] instead of NS(Mutable)Array.
if let dicArray = rollStatusArray.objectEnumerator().allObjects as? [[String:Any]] {
for statusDict in dicArray {
//Here compiler know type of statusDict is [String:Any]
if let status_id = statusDict["mark_status_id"] as? String, status_id == statusId {
return "\(statusDict["mark_status_name"]!)"
}
}
}
Note I'm force wrapping the mark_status_name value you can use Nil coalescing operator to return empty string to remove force wrapping.
You need to tell compiler the type of your statusDict this way:
for statusDict in rollStatusArray {
if let obj = statusDict as? [String: AnyObject] {
let statusIdFromDict = obj["mark_status_id"] as? String ?? ""
if statusId == statusIdFromDict {
return statusIdFromDict
}
}
}
Using Swift 3, you can access the dictionary value the following way:
var rollStatusArray = NSMutableArray()
for statusDict in rollStatusArray{
if statusId == "\((statusDict as! NSDictionary)["mark_status_id"]!)"{
return "\((statusDict as! NSDictionary)["mark_status_name"]!)"
}
}

converting AnyObject to String in swift not working

I make POST request to the server from my app, and I get jsonString as the response. I have made function to convert string to dictionary which looks like this:
func convertStringToDictionary(text: String) -> [String:AnyObject]? {
if let data = text.dataUsingEncoding(NSUTF8StringEncoding) {
do {
return try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String:AnyObject]
} catch let error as NSError {
print(error)
}
}
return nil
}
after getting the response from the server I convert the string to dictionary by function and then I want to check if user is logged in:
let result = convertStringToDictionary(jsonString as String)
if (result!["loggedIn"] == "1")
{
print("You are logged in!")
}
And then I get the error "Cannot convert value of type AnyObject? to expected argument String". I suppose I have to convert variable of type AnyObject to String if i want to compare it to string. I have tried every option which I have found on the Google but I havent got it to work.
Because result is a dictionary of type [String : AnyObject], the values you extract from it will be typed as AnyObject?, which has no valid operator overload for == with a String. You need to cast the extracted value to a String before you can compare it to "1".
What you probably want is something like:
if let result = convertStringToDictionary(jsonString as String),
loggedIn = result["loggedIn"] as? String
where loggedIn == "1" {
print("You are logged in")
}
Note that if your JSON has the "loggedIn" field as an Integer, like:
{
"loggedIn" : 1
}
You would want to cast loggedIn as Integer, not String.
You have to tell the compiler that the expected type is a String
if let loggedIn = result?["loggedIn"] as? String where loggedIn == "1" { ...

Swift--Reading in JSON file

Using Xcode 6.4 and programming in swift.
I am typing a program that should read in JSON from a URL. A sample of the JSON can be found at this URL (https://itunes.apple.com/us/rss/topmovies/limit=2/json) and Ive been using this site to parse the JSON in order to read it better (http://json.parser.online.fr/).
Now I need to work through the levels of the JSON in order to get to
the actual movie names and image URL's but I am lost at what kind of variable entryDictionary should be. I was thinking it should be an array of dictionaries, and this compiles, but the output of entryDictionary in the console is sloppy looking, starting with Optionl( and not entry{ as it should. And when I go to loop through entryDictionary, I get an error saying entry does not have a subscript of type AnyObject.
So I am asking how I retrieve the im:name fields and im:image from the JSON.
func downloadDataFromURLString(urlString: String) {
//Downloaded data and is now stored in data. Took code out because
//irrelevant to my problem at this point. Data variable has correct
//JSON, now I am trying to parse it.
} else { //download suceeded, time to parse
var error: NSError? = nil
var names = [String]()
if let rootDictionary = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? [String: AnyObject] {
let feedDictionary = rootDictionary["feed"] as! [String: AnyObject]
let entryDictionary: AnyObject? = feedDictionary["entry"]
println(entryDictionary) //For debugging
//for entry in entryDictionary as! NSArray {
// let name = entryDictionary["name"]
// let image = entryDictionary["image"]
// let movie = Movie(name: name!, image: image!)
// weakSelf!.movies.append(movie)
//}
here is a blueprint of the JSON
"feed":{
"author":{},
"entry":[
{
"im:name":{
"label":"Deadpool"
},
"im:image":[],
"summary":{},
"im:price":{},
"im:contentType":{},
"rights":{},
"title":{},
"link":[],
"id":{},
"im:artist":{},
"category":{},
"im:releaseDate":{}
AnyObject is indeed not subscriptable (you're trying to subscript a variable whose type is AnyObject? with ["feed"]). You should also avoid casting to Cocoa container types like NSArray and NSDictionary whenever you can. Here's an example of how you might get the labels out of the entries array's names array:
import Foundation
func labels(feedDictionary:[String:AnyObject]) -> [String] {
guard let entries = feedDictionary["entry"] as? [String:AnyObject] else {
return []
}
return entries.flatMap { (key:String, value:AnyObject) -> String? in
guard key == "im:name" else {
return nil
}
guard let name = value as? [String:String] else {
return nil
}
return name["label"]
}
}
I'd however advise against using NSJSONSerialization on its own in Swift for anything but the simplest case, as you end up casting and wrapping optionals until the cows come home.
There are good 3rd party libraries such as Freddy and SwiftyJSON which apply Swift language features to accomplish a very convenient JSON (de)serialization experience.
For instance with Freddy you could express your problem in the following style:
let json = try JSON(data: data)
json.decode("feed", type:Feed.self)
struct Feed: JSONDecodable {
let entries:[Entry]
init(json: JSON) throws {
self.entries = try json.arrayOf("entry", type:Entry.self)
}
}
struct Entry:JSONDecodable {
let name:IMName
init(json: JSON) throws {
self.name = try json.decode("im:name", type:IMName.self)
}
}
struct IMName:JSONDecodable {
let label:String
init(json: JSON) throws {
self.label = try json.string("label")
}
}

How to convert between Key and String in Swift?

I'm making an extension on Dictionary, just a convenience method to traverse a deep json structure to find a given dictionary that might be present. In the general extension of a Dictionary, i'm not able to subscript because i give a String instead of a Key
extension Dictionary {
func openingHoursDictionary() -> Dictionary<String,AnyObject>? {
if let openingHours = self["openingHours"] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
return nil
}
}
Error: String is not convertible to DictionaryIndex<Key, Value>
on self["openingHours"]
How can i make a Key from the String "openingHours" or check the dictionary for ths string?
You can check at runtime if the string is a valid key for the dictionary:
extension Dictionary {
func openingHoursDictionary() -> [String : AnyObject]? {
if let key = "openingHours" as? Key {
if let openingHours = self[key] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
}
return nil
}
}
But this will "silently" return nil if called for other dictionaries
like [Int, AnyObject].
If you want the compiler to check if it is safe to subscript the
dictionary with a string then you have to use a (generic) function:
func openingHoursDictionary<T>(dict : [String : T]) -> [String : AnyObject]? {
if let openingHours = dict["openingHours"] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
return nil
}
It is (currently) not possible to write Dictionary (or Array)
extension methods that apply only to a restricted type of the
generic parameters.
Just cast the String as Key using "openingHours" as Key
if let pickupPoints = self["openingHours" as Key] as? Array<AnyObject> {
}
Downside is that if doing this i will get a crash if i have a Dictionary<Int,AnyObject> and use the method there.
0x10e8c037d: leaq 0x362aa(%rip), %rax ; "Swift dynamic cast failure"

Convert JSON into Objects in Swift

I have a json response which is fetched from server and it should be parsed as dictionary and convert it to NSObjects in Swift. How can i do this.
func convertJSONToObjects(object:Dictionary<String,AnyObject>?) -> CommonResponse{
var response:CommonResponse;
if(object != nil){
response = CommonResponse()
var responseObject = object //It is dictionary
var responseHeader = object["responseHeader"] //error
response.status = responseHeader["status"] as Int //error
//response.status is Int
}
return response
}
var parsedJson = NSJSONSerialization.JSONObjectWithData(object as NSData, options: NSJSONReadingOptions.MutableContainers, error: &parseError) as NSDictionary
calling function
convertJSONToObjects(parsedJson)
object is optional. You need some kind of unwrapping optional chaining, or optional bidding.
if let object = object {
var responseHeader = object["responseHeader"]
}
or
var responseHeader = object!["responseHeader"]
or
if let responseHeader = object?["responseHeader"] {
// ...
}
and so on.
You could set a break point before:
var responseHeader = object["responseHeader"] //error
to make sure that object does contains a key named "responseHeader".
Otherwise you should let variable responseHeader to be an Optional value.
I wrote a class that parses JSON text and maps it to Swift structs/classes.
let p = CutePossumParser(json: json)
let model = Possum(
name: p.parse("name", miss: ""),
home: Address(
planet: p["home"].parse("planet", miss: "")
)
)
if !p.success { /* mapping failed */ }
https://github.com/exchangegroup/cute-possum-parser

Resources