Crashing app due to null string added to dictionary - ios

func addSongToQueue(){
let post = [
"data": [
"QueueId": MyViewState.selectedQueId,
"track":[
[
"title":selectedTrack.title,
"stream_url": selectedTrack.stream_url,
"userName" : selectedTrack.userName,
"artWorkURL": selectedTrack.artWorkURL,
"userAvatar": selectedTrack.userAvatar,
"trackID" : selectedTrack.trackID,
"duration" : selectedTrack.duration
]]]
]
Tracks Getting Code :
-(instancetype) initWithDictionary: (NSDictionary*) SCTrackDict {
self = [self init];
if (self) {
self.title = SCTrackDict[#"title"];
self.stream_url = SCTrackDict[#"stream_url"];
self.userDict = SCTrackDict[#"user"];
self.userName = self.userDict[#"username"];
self.artWorkURL = SCTrackDict[#"artwork_url"];
self.trackID = SCTrackDict[#"id"];
self.userAvatar = self.userDict[#"avatar_url"];
self.duration = SCTrackDict[#"duration"];
}
return self;
}
Parsing Data into Tracks :
+(NSMutableArray *) parseJSONData: (NSData *) JSONData {
NSError* error;
NSMutableArray* SCTrackArray = [NSMutableArray new];
NSArray *JSONArray= [NSJSONSerialization JSONObjectWithData:JSONData options:0 error: &error];
if ([JSONArray isKindOfClass:[NSArray class]]) {
for (NSDictionary* trackDict in JSONArray) {
SCTrack* trackObject = [[SCTrack alloc]initWithDictionary:trackDict];
//Need to print out trackDict to see JSON dictionary
[SCTrackArray addObject:trackObject];
}
}
return SCTrackArray;
}
How can I add "" (empty string) if NSNull is there and also crashing on trackid as it is long value.
Is it possible to modify this code without checking individual key before adding in dict?

Clarify to yourself: What action do you want to take if a key that you expect is not present, or is [NSNull null], or is an empty string, or is a dictionary when you expected a string and so on and so on.
Once you've explained that to yourself, you write the code accordingly. It seems at the moment your code follows the rule "If it's not there, I'll crash".

before adding an object just check like this that is this null or not
if(yourObject isEqual:(id)[NSNUll null])
{
// Then do nothing
}

You can use something like this:
"stream_url": selectedTrack.stream_url != nul ? selectedTrack.stream_url : #"";
Hope it help.
You can read about Ternary operation

Related

How to get a value from dictionary which is inside an array?

I want to store a message - "hi this is John KL", in some string, how to parse following example.
[
{
"message": "hi this is John KL"
}
]
Swift:
guard let anArray = input as? [[String:String]],
let message = anArray.first["message"] else {
print("unable to fetch data"
}
Objective-C:
- (void) readJSON {
NSError *result;
NSURL *url = [[NSBundle mainBundle] URLForResource: #"sample"
withExtension: #"JSON"];
NSData *data = [NSData dataWithContentsOfURL: url
options: 0
error: &result];
if (result != nil) {
NSLog(#"Error reading file: %#", result);
return;
}
NSArray<NSDictionary<NSString*, NSString*> *> *array = [NSJSONSerialization JSONObjectWithData: data options: 0 error: &result];
if (result != nil) {
NSLog(#"Error converting JSON: %#", result);
return;
}
else {
NSLog(#"\nJSON data = \n%#", array);
if (array.count < 1) {
NSLog(#"Not enough elements in array");
return;
}
NSString *message = array[0][#"message"];
if (message == nil) {
NSLog(#"Unable to fetch message");
} else {
NSLog(#"Message = \"%#\"", message);
}
}
}
The above Objective-C code does not test to make sure the object read from the JSON file is the correct type. It will crash if it is not an array containing a dictionary with a string key and string value. For a production app you'll want to add code to type-check the data.
Swift
Assign your json array to a variable type of [String : String] or [String : Any] dictionary array. [String : Any] is most commonly used dictionary but according to your data it suites with [String : String]
if let array = [
{
“message” : “hi this is John KL”
}
] as [Any]
Now, get your json/dictionary from array using index value and then get string from json using json key.
if let dictionary = array.first as? [String : Any] {
if let stringMessage = dictionary["message"] as? String {
print("stringMessage - \(stringMessage)")
}
}
Objective-C
NSArray * array = [
{
“message” : “hi this is John KL”
}
];
NSDictionary * dictionary = (NSDictionary *)[array objectAtIndex: 0];
NSString * stringMessage = (NSString *)[dictionary valueForKey: "message"];
NSLog(#"stringMessage - %#",stringMessage);
You can try this answer.
NSArray *result = [json objectForKey:#"result"];
for(NSString *currenObject in result){
NSLog(#"%#",currenObject);
NSString *currentValue = [currenObject valueForKey:#"message"];
}
NSLog(#"%#",currentValue);

Parsing JSON with AFNetworking

I'm trying to parse a JSON page in Objective-C by creating a subclass of NSDictionary and adding getSomeProperty methods. I have been able to do this with JSON pages that precede every [ or { with keys but am having trouble parsing the following sort of page
[ {"id":12345,"name":"name1","account_id":10002000015631,
"start_at":"2015-09-02T20:24:13","enrollments":
[{"type":"student","role":"enrollment","role_id":821,
"user_id":10000001736511,"enrollment_state":"active"}],"hide_final_grades":false,
"workflow_state":"available","restrict_enrollments_to_course_dates":false},
{"id":100000055661076,"name":"name2","account_id":100000230095635,
"start_at":"2015-08-28T21:22:41Z","grading_standard_id":null,"is_public":null,
"course_code":"name2","default_view":"wiki","enrollment_term_id":10003000007529,"end_at":null,
"public_syllabus":false,"storage_quota_mb":500,"is_public_to_auth_users":false,
"apply_assignment_group_weights":false,"calendar":{"ics":"https://someurl.ics"},
"enrollments":[{"type":"student","role":"StudentEnrollment","role_id":821,
"user_id":10000001736511,"enrollment_state":"active"}],"hide_final_grades":false,"
workflow_state":"available","restrict_enrollments_to_course_dates":false}
]
For example, for this webpage http://www.raywenderlich.com/demos/weather_sample/weather.php?format=json
I am able to create methods
- (NSDictionary *)currentCondition
{
NSDictionary *dict = self[#"data"];
NSArray *ar = dict[#"current_condition"];
return ar[0];
}
and
-(NSString*) cloudcover
{
return self[#"cloudcover"];
}
to retrieve the string #"16".
How can I use a similar method to get the #"name1" or the id #"12345" from my first example JSON code?
You have an array of dictionaries. To have an array of outputs you can use the following method:
- (NSMutableArray *)getValueString: (NSString*)string fromArray: (NSArray *)inputArray {
NSString *outputString;
NSMutableArray *outputArray = [NSMutableArray new];
for (id dict in inputArray) {
if ([[dict class] isSubclassOfClass:[NSDictionary class]]) {
outputString = [dict valueForKey: string];
}
else {
outputString = #"Name not found";
}
if (!(outputString.length > 0)) {
outputString = #"Name not found";
}
[outputArray addObject: outputString];
}
return outputArray;
}
And use it to get name with:
NSArray *resultArray = [self getValueString: #"name" fromArray: inputArray];
NSString *firstName = resultArray[0];
And to get id with:
NSArray *resultArray = [self getValueString: #"id" fromArray: inputArray];
NSString *firstId = resultArray[0];
The [ ] at the beginning and end of your JSON string indicate that it is an array, so when you parse the JSON, you will get an NSArray*, not an NSDictionary*. The first element of the array is an object, so that will be an NSDictionary*. Access id like this:
NSNumber* id = self[0][#"id"];
It looks like your currentCondition is a category on NSDictionary. If that's true and you want the above code to work, you need to make it on a category of NSArray. If it's not true, I don't understand what self is without more info.

Convert Swift method using extend and append functions to Objective-C

I have this Swift method that I want to convert to Objective-C. The method is using the extend method of Array and as far as I understand it just adds an object to the array while calling itself again.
Swift:
///the method to serialized all the objects
func serializeObject(object: AnyObject,key: String?) -> Array<HTTPPair> {
var collect = Array<HTTPPair>()
if let array = object as? Array<AnyObject> {
for nestedValue : AnyObject in array {
collect.extend(self.serializeObject(nestedValue,key: "\(key!)[]"))
}
} else if let dict = object as? Dictionary<String,AnyObject> {
for (nestedKey, nestedObject: AnyObject) in dict {
var newKey = key != nil ? "\(key!)[\(nestedKey)]" : nestedKey
collect.extend(self.serializeObject(nestedObject,key: newKey))
}
} else {
collect.append(HTTPPair(value: object, key: key))
}
return collect
}
What I've done so far in Objective-C.
- (NSArray*) serializeObject:(id)obj key:(NSString*)key
{
NSMutableArray* collect = [NSMutableArray array];
if ([obj isKindOfClass:[NSArray class]])
{
NSArray* objArray = obj;
if (obj)
{
for (id nestedObj in objArray)
{
[collect addObject:[self serializeObject:nestedObj key:[NSString stringWithFormat:#"%#[]", key]]];
}
}
}
else if ([obj isKindOfClass:[NSDictionary class]])
{
NSDictionary* dict = obj;
if (dict)
{
for (NSString* nestedKey in dict)
{
NSString* newKey = key != nil ? [NSString stringWithFormat:#"%#[%#]", key, nestedKey] : nestedKey;
id nestedObject = [dict objectForKey:newKey];
if (nestedObject)
{
[collect addObject:[self serializeObject:nestedObject key:newKey]];
}
}
}
}
else
{
[collect addObject:[[WEEHTTPPair alloc] initWithValue:obj andKey:key]];
}
return collect;
}
The goal is to get an NSArray of WEEHTTPPair objects for every key/value pair in the dictionary but I lose the meaning of extend and append to apply in my Objective-C code. For me it looks like both are adding the object to the array which is created new anyway but it's more that I lack in knowledge so far.
[EDIT]
The method is used accordingly.
Swift.
///convert the parameter dict to its HTTP string representation
func stringFromParameters(parameters: Dictionary<String,AnyObject>) -> String {
return join("&", map(serializeObject(parameters, key: nil), {(pair) in
return pair.stringValue()
}))
}
I converted to Objective-C borrowed BlockKits map extension.
Objective-C.
- (NSString*) stringFromParameters:(NSDictionary*)parameters
{
WEENSArrayBlocksKit* blockKit = [WEENSArrayBlocksKit new];
NSArray* serializedParams = [self serializeObject:parameters key:nil];
NSArray* arrayParams = [blockKit bk_map:serializedParams withBlock:^id(id obj)
{
// obj is an array without the desired results
WEEHTTPPair* httpPair = obj;
NSString* stringValue = nil;
if (httpPair)
{
stringValue = [httpPair stringValue];
}
return stringValue;
}];
NSString* joinedString = [arrayParams componentsJoinedByString:#"&"];
return joinedString;
}
When you do this:
[collect addObject:[self serializeObject:nestedObject key:newKey]];
You are adding an NSArray to your collect object. Instead, you want to add the objects contained within the response to collect:
[collect addObjectsFromArray:[self serializeObject:nestedObject key:newKey]];

Any ideas what I am doing wrong when trying to store json from webservice into NSDictionary

I am using webservice to pull out some information in json format. I am able to view them in my app. However when I try to store them in dictionary i keep getting error saying value "courseId" not compliant. This is what I have
NSMutableArray *results = [NSMutableArray array];
for (id courseDictionary in response) {
ListOfCourses *course = [[ListOfCourses alloc] initWithDictionary:courseDictionary];
[results addObject:course];
}
and in my ListOfCourses.m I have
- (id)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
_courseId = [NSNumber numberWithInt:[[dictionary valueForKey:#"courseId"]
_room1 = [dictionary objectForKey:#"room1"];
_subjectCode = [dictionary objectForKey:#"subjectCode"];
}
return self;
}
json file
{
"Template":{
"Room1":"Day 1 room name/number; string; required; 10 characters",
"SubjectId":"Subject identifier; number; required; Subject identifier must exist",
{
"Links":[
{
"Rel":"self",
"Href":"/api/courses/2"
},
{
"Rel":"collection",
"Href":"/api/courses/"
}
],
"Item":{
"Link":{
"Rel":"self",
"Href":"/api/courses/2"
},
"Id":1,
"SubjectCode":"D9920",
"Room1":"S2153"
}
UPDATED: I am returning a collection of objects, when i do this
_courses = (NSArray *)[response objectForKey:#"Collection"];
I am able to view the objects. I am trying to put them each individually into dictionary now
When you are taking entities on and off a stream, you should use the NSCoding protocol. There are many ways to do that with JSON, e.g. JSONCoding.
These types of problems would qualify as refactor smells: you should not have to worry about how something was bundled up.
Change your line of code from:
_courseId = [NSNumber numberWithInt:[[dictionary valueForKey:#"courseId"]
to:
_courseId = [NSNumber numberWithInt[[dictionary valueForKey:#"Id"] intValue]];
If this won't help please post the json file.
//EDITED
You use [dictionary valueForKey:#"courseId"] but the key is Id, please change courseId to Id.
//EXTENDED
You have dictionary embedded in dictionary try this:
NSDictionary *dic = dictionary[#"Item"];
_courseId = [NSNumber numberWithInt:[dic[#"Id"] intValue]];
_room1 = dict[#"Room1"];
_subjectCode = dict[#"SubjectCode"];
If it doesn't work you have to post beginning of your json file.

Get a value from nsdictionary

I want to give a key value from my NSDictionary and get the value associated to it.
I have this:
NSArray *plistContent = [NSArray arrayWithContentsOfURL:file];
NSLog(#"array::%#", plistContent);
dict = [plistContent objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"code"];
with plistContent :
(
{
code = world;
key = hello;
},
{
code = 456;
key = 123;
},
{
code = 1;
key = yes;
}
)
So how do I get "hello" by giving the dictionary "world"?
If I understand your question correctly, you want to locate the dictionary where "code" = "world" in order to get the value for "key".
If you want to keep the data structure as it is, then you will have to perform a sequential search, and one way to do that is simply:
NSString *keyValue = nil;
NSString *searchCode = #"world";
for (NSDictionary *dict in plistContents) {
if ([[dict objectForKey:#"code"] isEqualToString:searchCode]) {
keyValue = [dict objectForKey:#"key"]); // found it!
break;
}
}
However if you do alot of this searching then you are better off re-organizing the data structure so that it's a dictionary of dictionaries, keyed on the "code" value, converting it like this:
NSMutableDictionary *dictOfDicts = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in plistContents) {
[dictOfDicts setObject:dict
forKey:[dict objectForKey:#"code"]];
}
(note that code will break if one of the dictionaries doesn't contain the "code" entry).
And then look-up is as simple as:
NSDictionary *dict = [dictOfDicts objectForKey:#"world"]);
This will be "dead quick".
- (NSString*) findValueByCode:(NSString*) code inArray:(NSArray*) arr
{
for(NSDictonary* dict in arr)
{
if([[dict valueForKey:#"code"] isEqualToString:code])
{
return [dict valueForKey:#"key"]
}
}
return nil;
}

Resources