I'm fetching data from a weather API. I'm not sure how to access the description?
"weather": <__NSSingleObjectArrayI 0x608000012910>(
{
description = "overcast clouds";
icon = 04n;
id = 804;
main = Clouds;
}
)
I tried:
print(weatherDict["weather"]!.description!)
It just gave me this:
(
{
description = "overcast clouds";
icon = 04n;
id = 804;
main = Clouds;
}
)
How do I properly access the description?
weather contains an array of dictionaries.
description is a key in the first item of the array.
The code unwraps weather safely and checks if the array is not empty:
if let weatherArray = weatherDict["weather"] as? [[String:Any]],
let weather = weatherArray.first {
print(weather["description"]) // the value is an optional.
}
Related
I have built a survey using laanlabs/SwiftSurvey and I am able to get the results from the surveyComplete method as a dictionary.
The results return in a complex structure and I need to get the values for each response by the key tag of the questions array -> question object. This object contains an array of choices and within each choice object there is a key of selected. If the selected key's value is true (1) I need to get the text key's value that is in the same object. Some of these choices will have multiple selected keys with a value of true (1), if this is the case I'd like to concatenate the text key values with a comma in between the values.
The intention is then to insert the keys in to a SQLite database.
I am new to decoding dictionaries and traversing them in the correct way, I can access the dictionary print(dictionary) and also get into the the correct NSArray - print(dictionary["questions"] but from there I am stumped, could someone show me how please.
The results are below unfortunately its a large block apologies.
[
"version": 001,
"metadata":
{
"app_version" = "1.1";
build = 22;
debug = true;
},
"questions": <__NSArrayI 0x600000614d20>(
{
question = {
allowsMultipleSelection = 0;
choices = (
{
allowsCustomTextEntry = 0;
selected = 1;
text = Physician;
uuid = "224E1B76-D220-4068-AA22-6861E5F836CB";
},
{
allowsCustomTextEntry = 0;
selected = 0;
text = Dietitian;
uuid = "2DB2B6FB-E344-4BBF-A551-2FABE0DFF6AA";
},
{
allowsCustomTextEntry = 0;
selected = 0;
text = "Genetic Counsellor";
uuid = "A9BE7093-B95C-4BF4-B629-12FDA3154ABE";
},
{
allowsCustomTextEntry = 0;
selected = 0;
text = "Nurse/Nurse Practitioner/Physician Assistant";
uuid = "8E75A41B-0D8C-4ADA-A31C-2BC408F8269D";
},
{
allowsCustomTextEntry = 0;
selected = 0;
text = "Pharmacist / Pharmaceutical Industry";
uuid = "C943430D-EA48-4BCB-8ADF-011A223BDF36";
},
{
allowsCustomTextEntry = 0;
selected = 0;
text = "Academic/Researcher";
uuid = "E28377A4-37FC-4351-A857-88383A3D5A3B";
},
{
allowsCustomTextEntry = 0;
selected = 0;
text = "Patient/Patient Advocacy Group";
uuid = "E5836187-6C08-4272-A88E-40578F4FCF44";
},
{
allowsCustomTextEntry = 1;
selected = 0;
text = "Other (please specify)";
uuid = "EFF22342-48A9-4B8E-81A0-BB44D0E86EBC";
}
);
required = 1;
tag = "hcp-specialty";
title = "Please select the option that best describes your specialty:";
uuid = "7F77E248-8429-463E-9291-241B94BEE4F8";
};
type = 0;
},
{
question = {
autoAdvanceOnChoice = 1;
choices = (
{
allowsCustomTextEntry = 0;
selected = 1;
text = Yes;
uuid = "3C7A330D-F16B-4F3E-8ABC-6767A1A6332A";
},
{
allowsCustomTextEntry = 0;
selected = 0;
text = No;
uuid = "0E4F5360-FCCD-4860-9971-86E23BB8F6C1";
}
);
required = 1;
tag = "newborn-screening";
title = "Is newborn screening for classical homocystinuria available in your region/country?";
uuid = "F7C1A9D5-43AB-420D-80CF-F6644B95C73E";
};
type = 1;
},
{
question = {
feedback = "This is a free text response";
required = 1;
tag = "biggest-unmet-need";
title = "What do you believe is the biggest unmet need for patients living with classical HCU?";
uuid = "133E2EDC-8FF4-48D1-8BFA-3A20E5DA0052";
};
type = 4;
}
)
]
Based on their result example, you are getting JSON. Converting JSON to Dictionary is the easiest, of course, but also is the dirtiest. You are basically getting "whatever", so when it's time to use the data, you have to do a lot of - as you said - "decoding", validation, etc.
Instead of that, create a few Decodable structures that match your response. In this case you just need 3 structures:
struct Survey: Codable {
let questions: [Question]
}
struct Question: Codable {
let allowsMultipleSelection: Int?
let choices: [Choice]?
let required: Int
let tag: String
let title: String
let uuid: String
let feedback: String?
}
struct Choice: Codable {
let allowsCustomTextEntry: Int
let selected: Int
let text: String
let uuid: String
}
(I didn't verify every fields, you can adjust as needed. And you can omit any properties you don't need / don't care about.)
And then you decode it like this:
// Assume jsonData is your original JSON, which you currently decode as dictionary. So instead you do this:
let decoder = JSONDecoder()
let product = try decoder.decode(Survey.self, from: jsonData)
This approach allows you to
Most importantly, having a well-defined data makes working with database easier. You can even implement database encoder, based on your codable structures, which means you don't need to manually walk through columns of the database. Also when. you read from database, you get the same structures, no need to have 2 sets of rules / validations for database and dictionary you decoded.
This also allows you to be confident about data you decoded: it has proper names and types. You don't need to validate it (it was validated for you on decoding). You are in control which properties are required, which defaults to set, and so on. In more complicated cases, you may need to add manual decoding to your structures, but even then this manual decoding is inside the structure itself, easy to change / work with / test. All this instead of giant messy dictionary with "some stuff" in it.
My realm model class look like
class RoomRealmModel : Object {
dynamic var id: String = ""
var details = List<RoomDetailRealmModel>()
func saveItem() {
do {
let realm = try Realm()
realm.beginWrite()
realm.add(self, update: true)
try realm.commitWrite()
} catch{}
}
}
class RoomDetailRealmModel : Object{
dynamic var detailId: String = ""
dynamic var displayText: String = ""
}
I want to retrieve 'details' from the following.
details = RLMArray<RoomDetailRealmModel> <0x600000114f40> (
[0] RoomDetailRealmModel {
text = hello;
Counters = 9;
ParentID = ;
detailId = 33;
displayText = hello ;
}
);
I always get empty like in my console
(lldb) po (destinationData?[index]?.details)!
List<RoomDetailRealmModel> <0x600000853620> (
)
I am updating ‘details’ list via realm update command. I always get realm array.But I want to retrieve array type from realm array.Please help me, how to solve this issue
If you want to obtain [myObject] instead of List you can do something like this:
var array: [myObject] = [myObject]()
for object in myObjectList {
array.append(object)
}
Where myObjectList is List.
You can simply create a regular Swift Array from a Realm List by calling the initializer of Array accepting a Sequence, since List conforms to the sequence protocol.
So you can simply do
let room = RoomRealmModel()
let roomDetailsArray = Array(room.details)
i want to update data after editing. for that i need the previous data.
but in my case after editing, the variable that hold the previous value are replaced by the present value.
how can i hold the previous value??
if itemToEdit == nil{
item = TestDataModel()
else{
item = itemToEdit
selectedItemToEdit = itemToEdit
// print(selectedItemToEdit?.title)
print(item.title)
// print(itemToEdit?.title)
}
if let title = titleField.text {
item.title = title
print(item.title)
}
// print(selectedItemToEdit.title!)
print(itemToEdit?.title!)
if let price = pricefield.text {
item.price = (price as NSString).doubleValue
print(item.price)
}
if let details = detailsField.text {
item.details = details
print(item.details)
}
itemToEdit, is that which i want to edit/ update. that's why i stored it in selectedItemToEdit. in item, i have stored the data after editing.
i need both the previous value and the present value that i entered in textfield.
how can i get that one.please any one help....
What you're doing when you try to save previous information is simply creating a new variable that points to the same object. When the object changes, it doesn't matter what variable you use to examine it, you will see the changes in that one object.
Here's a playground sample that should give you an idea of the difference between having two objects vs. having two pointers to the same object.
class MyData {
var title = "Default"
init() {
}
init(source: MyData)
{
title = source.title
}
}
var d1 = MyData()
var d2 = d1
var d3 = MyData(source: d1)
d1.title = "changed"
print(d2.title)
print(d3.title)
yeah i solved the problem:
i just declared a variable of string type as bellow:
var selectedItemToEdit: string!
selectedItemToEdit = itemToEdit.title
i passed that string type variable and the edited variable to the save function.
Here is my JSON response for a particular API.
Case 1
ChallengeConfiguration = {
AnswerAttemptsAllowed = 0;
ApplicantChallengeId = 872934636;
ApplicantId = 30320480;
CorrectAnswersNeeded = 0;
MultiChoiceQuestion = (
{
FullQuestionText = "From the following list, select one of your current or previous employers.";
QuestionId = 35666244;
SequenceNumber = 1;
},
{
FullQuestionText = "What color is/was your 2010 Pontiac Grand Prix?";
QuestionId = 35666246;
SequenceNumber = 2;
}
)
}
The key "MultiChoiceQuestion" returns an array with two questions. So here is my code.
let QuestionArray:NSArray = dict1.objectForKey("ChallengeConfiguration")?.objectForKey("MultiChoiceQuestion") as! NSArray
Case 2
ChallengeConfiguration =
{
AnswerAttemptsAllowed = 0;
ApplicantChallengeId = 872934636;
ApplicantId = 30320480;
CorrectAnswersNeeded = 0;
MultiChoiceQuestion = {
FullQuestionText = "From the following list, select one of your
current or previous employers.";
QuestionId = 35666244;
SequenceNumber = 1;
}
}
For Case 2 my code does not work and app crashes because it returns a dictionary for that specific Key. So how could I write a generic code that would work for all objects?
It looks like the key can contain either an array of dictionary values or a dictionary, so you just need to try casting to see which one you have.
so I would likely do it like this:
if let arr = dict1.objectForKey("ChallengeConfiguration")?.objectForKey("MultiChoiceQuestion") as? Array {
// parse multiple items as an array
} else if let arr = dict1.objectForKey("ChallengeConfiguration")?.objectForKey("MultiChoiceQuestion") as? [String:AnyObject] {
// parse single item from dictionary
}
You should never really use ! to force unwrap something unless you are completely certain that the value exists and is of the type you are expecting.
Use conditional logic here to test the response and parse it safely so that your app doesn't crash, even in failure.
I have an application which retrieves a JSON file. Here you are a piece of my code:
Array definition
var photos: NSArray = []
How I populate the array:
ezJson().createRequest("http://myapiurl/load", type: "GET", params: nil, completion: {(returnedObject : AnyObject?, error : NSError?)in
if returnedObject{
self.photos = returnedObject as NSArray
self.tableView.reloadData()
}
})
println(self.photos)
{
created = {
date = "2014-06-13 18:35:46";
timezone = "Europe/Madrid";
"timezone_type" = 3;
};
description = description1;
id = 3;
name = 539b286277617;
},
{
created = {
date = "2014-06-13 18:38:38";
timezone = "Europe/Madrid";
"timezone_type" = 3;
};
description = description2;
id = 4;
name = 539b290ed8577;
}
println(self.photos[0])
{
created = {
date = "2014-06-13 18:35:46";
timezone = "Europe/Madrid";
"timezone_type" = 3;
};
description = description1;
id = 3;
name = 539b286277617;
}
The problem is, I don't know how to get a particular item. I've tried:
println(self.photos[0]) // it works
println(self.photos[0]["name"] // Xcode crash "Command /Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift failed with exit code 254"
println(self.photos[0].name) // returns nil
How can I access to the name parameter ?
It seems that you are casting a String to NSArray. This won't give you the effect that you want.
First of all, if you want to acces your elements by name, you want an NSDictionary.
Then it propably still won't work, as there is no implicit conversion between the types so you will have to either parse it yourself or use some JSON library.
Last : your JSON is incorrect.