NSDictionary annidate in swift - ios

I have this json result.
I would take the field "alert".
I try this:
var alert: NSString = jsonResult["features"]["properties"]["alert"]
but this is the error: does not have a member named 'subscript'.
I can not how to access a field in a nested dictionary
{
features = (
{
geometry = {
coordinates = (
"-97.95359999999999",
"37.2382",
5
);
type = Point;
};
id = usb000si7g;
properties = {
alert = green;
cdi = "5.8";
code = b000si7g;
detail = "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/usb000si7g.geojson";
dmin = "0.017";
felt = 1258;
gap = 38;
ids = ",usb000si7g,";
mag = "4.3";
magType = mwr;
mmi = "4.94";
net = us;
nst = "<null>";
place = "8km SE of Harper, Kansas";
rms = "0.51";
sig = 864;
sources = ",us,";
status = reviewed;
time = 1412272884590;
title = "M 4.3 - 8km SE of Harper, Kansas";
tsunami = "<null>";
type = earthquake;
types = ",cap,dyfi,general-link,geoserve,losspager,moment-tensor,nearby-cities,origin,phase-data,shakemap,tectonic-summary,";
tz = "-300";
updated = 1412614943519;
url = "http://earthquake.usgs.gov/earthquakes/eventpage/usb000si7g";
};
type = Feature;
}
);
metadata = {
api = "1.0.13";
count = 1;
generated = 1412617232000;
status = 200;
title = "USGS Significant Earthquakes, Past Week";
url = "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/significant_week.geojson";
};
type = FeatureCollection;
}
I don't know what to do. swift is changed every beta.

As #Kirsteins said is his answer, you have to do a lot of unwrapping, and it's recommended to use a JSON library.
If you still want to stick with hand made extraction, then I suggest you to made it programmatically, such as adding an extension to NSDictionary as follows:
extension NSDictionary {
func objectForTreeKeys<T>(keys:[String]) -> T? {
var dict: NSDictionary? = self
var retValue: T?
for key in keys {
var value: AnyObject? = dict?.objectForKey(key)
if let unwrapped = value as? NSDictionary {
dict = unwrapped
} else if let unwrapped = value as? T {
retValue = unwrapped
break
} else {
retValue = nil
break
}
}
return retValue
}
}
You pass an array of keys to the function, and it traverses all nested dictionaries until:
a value of type T is encountered
a value having type different than NSDictionary and T is found
a nil value is found
In the first case, it returns the value of T type - in the other cases it returns nil.
You can use it as follows:
let ret: String? = jsonResult.objectForTreeKeys(["features", "properties", "alert"])
As you can see, it's a generic method, and the return type is inferred from the type of the variable the result is assigned to - so it's necessary to explicitly define its type, which must be optional (String? in this specific case).

Related

sending parsed JSON data to views

I'm stuck because I can't send JSON data from URLSession func to views. I read the 90% of previous commends and watch lots of videos but I didn't migrate to my project. Here is my code blocks that I need help ;
This ones my json struct
struct APIResponse: Decodable{
let stocks: [Stocks]
}
struct Stocks: Decodable{
let id: Int
let difference: Float
let bid:Float
let isDown: Bool
let isUp: Bool
let offer: Float
let price: Float
let symbol: String
let volume: Double
}
this one is mine JsonDecode code block;
if let data2 = data2 {
do {
// let json = try JSONSerialization.jsonObject(with: data2, options: [])
let apiResponse = try JSONDecoder().decode(APIResponse.self, from: data2)
print(apiResponse.stocks[2].volume)
DispatchQueue.main.async {
completed()
}
}catch{
print(error)
}
}
}.resume()
when I watch videos about it they were [APIResponse].self but when I try that way my code is failed, in my way json parse is working (I can call like 'apiResponse.stocks[2].id') but I can't send this apiResponse data to views.
example of my JSON file
{
status = {
error = {
code = 0;
message = "";
};
isSuccess = 1;
};
stocks = (
{
bid = "31.5";
difference = "-0.2";
id = 1190;
isDown = 1;
isUp = 0;
offer = "31.6";
price = "31.81";
symbol = "P4jfFAYOTiLEih2Ic+NAkg==";
volume = "801457.5";
},
{
bid = "4.25";
difference = "-0.04";
id = 471;
isDown = 1;
isUp = 0;
offer = "4.26";
price = "4.31";
symbol = "zomIgqEl79jIE+TJ7xV4yQ==";
volume = "349264.21";
},
{
bid = "2.86";
difference = "-0.01";
id = 472;
isDown = 1;
isUp = 0;
offer = "2.87";
price = "2.87";
symbol = "2DlR317+autGo3fiKwNhFA==";
volume = "19279.4";
},
{
bid = 55;
difference = 1;
id = 473;
isDown = 0;
isUp = 1;
offer = "55.25";
price = "56.74";
symbol = "fvo0GQ+pqUmHXwm062Gatw==";
volume = "2647954.25";
}, {
bid = "1.22";
difference = "-0.04";
id = 465;
isDown = 1;
isUp = 0;
offer = "1.23";
price = "1.26";
symbol = "wR/24WChHVRFWZSUW1UdwQ==";
volume = "2206441.67";
}
);
}
First if you want to send your response back to the point from where initiated the API call you need to write completion handler and send your response model with the handler; you can take reference from Closures for Swift.
Also apart from that I noticed few errors in your decodable structure, for example you are expecting 'difference' as float type but the example JSON you have posted contains 'difference' as String and it applies for all your float and double values.
Also it will be a good practice If we will declare all the variable optional in decodable structure as if anytime any parameter won't come in response there won't be any problem in parsing it.

Traverse nsdictionary in swift

I am new to swift.
I have my dictionary as
monthData =
{
"2018-08-10" = {
accuracy = 71;
attempted = 7;
correct = 5;
reward = Bronze;
};
"2018-08-12" = {
accuracy = 13;
attempted = 15;
correct = 2;
reward = "";
};
"2018-08-13" = {
accuracy = 33;
attempted = 15;
correct = 5;
reward = "";
};
"2018-08-14" = {
accuracy = 100;
attempted = 15;
correct = 15;
reward = Gold;
};
"2018-08-16" = {
accuracy = 73;
attempted = 15;
correct = 11;
reward = Silver;
};
"2018-08-21" = {
accuracy = 26;
attempted = 15;
correct = 4;
reward = "";
};
"2018-08-23" = {
accuracy = 46;
attempted = 15;
correct = 7;
reward = "";
};
}
I want to get all the dates for which reward is Gold
Can anyone please help me do that?
What I have tried 'till now is:
for (key,value) in monthData{
let temp = monthData.value(forKey: key as! String) as! NSDictionary
for (key1,value1) in temp{
if((value1 as! String) == "Gold"){
print("keyFINAL \(key)")
}
}
but it outputs the error Could not cast value of type '__NSCFNumber' to 'NSString'
The error occurs because when you are iterating the dictionary you force cast the Int values to String which is not possible
The (highly) recommended Swift way is to use the filter function. This is much more efficient than a loop.
In the closure $0.1 represents the value of the current dictionary ($0.0 would be the key). The result is an array of the date strings.
let data : [String:Any] = ["monthData" : ["2018-08-10": ["accuracy" : 71, "attempted" ... ]]]
if let monthData = data["monthData"] as? [String:[String:Any]] {
let goldData = monthData.filter { $0.1["reward"] as? String == "Gold" }
let allDates = Array(goldData.keys)
print(allDates)
}
The code safely unwraps all optionals.
However if there is only one Gold entry the first function is still more efficient than filter
if let monthData = data["monthData"] as? [String:[String : Any]] {
if let goldData = monthData.first( where: {$0.1["reward"] as? String == "Gold" }) {
let goldDate = goldData.key
print(goldDate)
}
}
In Swift avoid the ObjC runtime (value(forKey:)) and Foundation collection types (NSDictionary) as much as possible.
From the first for in loop, you are getting the NSDictionary in temp variable
"2018-08-16" = {
accuracy = 73;
attempted = 15;
correct = 11;
reward = Silver;
};
So, you should directly check .value(forKey:) on temp and get the value for reward.
You should try it like this
for (key,value) in monthData {
let temp = monthData.value(forKey: key as! String) as! NSDictionary
if(((temp.value(forKey: "reward")) as! String) == "Gold"){
print("keyFINAL \(key)")
}
}
Try and share results
EDIT
Please checkout the answer from vadian for in-depth explanation and pure swift approach to achieve the same.
Thanks

How Parse JSON from API [duplicate]

This question already has answers here:
type 'Any' has no subscript members
(2 answers)
Closed 5 years ago.
i know to much ask like this. i already searching but not match with my problems.
oke i will try explain with my code
i have data API Like this
["profile": {
accountId = 58e470a0c50472851060d083;
androidDeviceId = "[\"3453247ddcf3f809\"]";
androidVersion = 21;
appId = (
"c46b4c10-62ce-11e6-bdd4-59e4df4b8410",
"fac915f0-fe2b-11e6-9dfb-55339bd7be35"
);
appVersion = "v5.1.0";
avatar = "https://account.8villages.com/uploads/images/5/1491366164_bnx1t0rudi.jpg";
birthDate = "12/03/1994";
"channel-group" = android;
communityId = 553e09b251906884443eff85;
coordinates = {
coordinates = (
"106.9602383333333",
"-6.249333333333334"
);
type = Point;
};
crop = "";
crops = "<null>";
customerId = 5369bd85cae84d0e03246a7c;
dateSubmitted = {
iso = "2017-04-05T04:20:48.483Z";
timestamp = 1491366048;
};
fullName = "Megi Fernanda";
gender = "Laki-laki";
homeAddress = Payakumbuah;
location = "Kota Payakumbuh";
moderation = {
at = {
iso = "2017-04-05T04:20:48.483Z";
timestamp = 1491366048;
};
by = auto;
status = moderated;
};
skill = "Budidaya pertanian";
state = "Sumatera Barat";
storeType = "";
subdistrict = "Payakumbuh Barat";
totalConversations = {
articles = 0;
forums = 0;
questions = 2;
responses = 0;
storeItems = 1;
};
type = users;
university = "Politeknik Negeri Pertanian Payakumbuh";
}, "accessToken": {
key = "lH5aYvnp2JAZ6zoKQK4mpfsxCI0.";
secret = "yfZfTZbsaVIhKCbksGHQnPcPg9mKtoRAKyvjg_cgMeo.";
}]
i already can got fullName, Addres, Skill State etc
if let profile = json["profile"] as? NSDictionary {
let name = profile["fullName"]
let alamat = profile["Skill"]
}
but i don't know how to get atribut in totalConversation like question, storeItems, points
skill = "Budidaya pertanian";
state = "Sumatera Barat";
storeType = "";
subdistrict = "Payakumbuh Barat";
totalConversations = {
articles = 0;
forums = 0;
questions = 2;
responses = 0;
storeItems = 1;
};
i tried like
let profile = json["profile"]["totalConversation"] as? NSDictionary
error sign : Type 'any?' has no subscript members
You got that error because json["profile"] is Any type and it doesn't have any subscript. So you need to cast json["profile"] to a dictionary, [String: Any] is dictionary type in Swift.
if let profile = json["profile"] as? [String: Any] {
if let totalConversations = profile["totalConversations"] as? [String: Any] {
let questions = totalConversations["questions"] as? Int
}
}

How to compare values of NSDictionary with String

I have two orgunit_id's, test["orgunit_id"] and API.loginManagerInfo.orgUnit, which I would like to compare. The problem is that the variables have different types. test["orgunit_id"] is value of a NSDictionary and the other one is a String.
I've tried several ways to cast it into Integers, but without success.
Code:
if(!orgUnits.isEmpty){
print(orgUnits) //See at console-output
for test: NSDictionary in orgUnits {
println(test["orgunit_id"]) //See at console-output
println(API.loginManagerInfo.orgUnit) //See at console-output
if(Int(test["orgunit_id"]? as NSNumber) == API.loginManagerInfo.orgUnit?.toInt()){ // This condition fails
...
}
}
}
Output:
[{
name = Alle;
"orgunit_id" = "-1";
shortdescription = Alle;
}, {
name = "IT-Test";
"orgunit_id" = 1;
shortdescription = "";
}]
Optional(-1)
Optional("-1")
Edit:
Here's the definition of API.loginManagerInfo.orgUnit: var orgUnit:String?
Use if let to safely unwrap your values and typecast the result.
If test["orgunit_id"] is an Optional Int and if API.loginManagerInfo.orgUnit is an Optional String:
if let testID = test["orgunit_id"] as? Int, let apiIDString = API.loginManagerInfo.orgUnit, let apiID = Int(apiIDString) {
if testID == apiID {
// ...
}
}
You may have to adapt this example given what is in your dictionary, but you get the point: safely unwrap the optional value and either typecast it (with if let ... = ... as? ...) or transform it (with Int(...)) before comparing.

How can I access the values in this NSDictionary in Swift?

I am trying to access the 'address' object (String) in a dictionary:
Chain.sharedInstance().getAddress("19b7ZG3KVXSmAJDX2WXzXhWejs5WS412EZ"){ dictionary, error in
NSLog("%#", dictionary)
let value = dictionary["address"] as? String //returns nil
}
this is the data I receive:
results = (
{
address = 19b7ZG3KVXSmAJDX2WXzXhWejs5WS412EZ;
confirmed = {
balance = 0;
received = 20000000;
sent = 20000000;
};
total = {
balance = 0;
received = 20000000;
sent = 20000000;
};
}
); }
How do I access the values in this dictionary when I keep getting nil?
To clarify, the data you are posting is not JSON, but rather looks like JSONP. You aren't showing any code deserializing the the object, so I assume Chain.sharedInstance().getAddress is handling that aspect. If this is an instance of the Bitcoin API, you might look at their documentation. If it is their API, the documentation says the return is
A dictionary with a single "results" key, whose value is a array
containing a single Address Object as a dictionary.
If that is the case if would be
if let resultsArray = dictionary["results"] as NSArray {
if let dict = results[0] as NSDictionary {
//dict["address"] should have your address
}
}
Try:
if let dict = dictionary["results"] as NSDictionary
{
let value = dict["address"] as NSString
}
or:
if let dict = dictionary["results"] as NSArray
{
if let di = dict[0] as NSDictionary
{
let value = di["address"] as NSString
}
}

Resources