I am new to xcode and I seem to be stuck at trying to create a tableview with sections.
My code below creates the header however I cannot seem to display the rows that belong to the section. I'm sure it has to do with the multidimensional array. I say this because "dobSection" value is "results" when I suppose it should display the dates under "DOB".
I have spent hours trying many different ways to get this to work..... eh! No luck.
The Json is somewhat like this:
{"results":
[{"name":"joe","surname":"blow","DOB":"1990-01-01"},
{"name":"jane","surname":"doe","DOB":"1990-01-01"},
{"name":"john","surname":"doe","DOB":"1995-06-06"}]
}
and the code
NSMutableArray *jsonResults;
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
jsonResults = [json objectForKey:#"results"];
self.dob = json;
self.keys = [jsonResults valueForKey:#"DOB"];
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [keys count];
//return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
// NSString *key = [keys objectAtIndex:section];
NSString *key = [keys objectAtIndex:section];
NSArray *dobSection = [dob objectForKey:key];
return [dobSection count];
}
in addition by replacing this:
NSArray *dobSection = [dob objectForKey:key];
with
NSArray *dobSection=[[games objectForKey:#"results"]valueForKey:#"dob"];
I get all rows.
I cannot get them to group by DOB
My code above may be flawed however I would love some input on how to get this right.
Thank you
Related
A lot of modern programming languages have JSON libraries that support encoding and decoding json to/from "plain old objects" - i.e. instances of classes that primarily just have data properties (properties can either be types that can be trivially de/encoded or other plain old objects). Examples include Google's GSON, golang's encoding/json and others.
Is there something similar to Objective-C?
I know that it is possible to enumerate properties for Objective-C classes, and it seems reasonable that someone would have used that capability to create a JSON "bean mapper", but Google searching yielded no results for me, except this blog post on Apple's Swift website showing how to manually deserialize JSON to "model objects" and why they think that doing this automatically (DRYing the code) is a bad idea (*).
*) The reasoning is basically, that not needing to write a lot of boilerplate (their sample implementation is 36 LoC to parse 3 properties) is not a significant improvement and building a couple of optional callbacks to allow data validation is hard. I obviously disagree with all of this.
Here is my solution, which is not based on a library - as I couldn't find any - but instead using the Foundation and Objective-C runtime methods - as discussed in the comments above:
#import <objc/runtime.h>
NSArray<NSString*>* classPropertyList(id instance) {
NSMutableArray* propList = [NSMutableArray array];
unsigned int numProps = 0;
objc_property_t* props = class_copyPropertyList(object_getClass(instance), &numProps);
for (int i = 0; i < numProps; i++)
[propList addObject:[NSString stringWithUTF8String:property_getName(props[i])]];
free(props);
return propList;
}
NSString* typeOfProperty(Class clazz, NSString* propertyName) {
objc_property_t prop = class_getProperty(clazz, [propertyName UTF8String]);
NSArray<NSString*>* propAttrs = [[NSString stringWithUTF8String:property_getAttributes(prop)] componentsSeparatedByString:#","];
if ([(propAttrs[0]) hasPrefix:#"T#\""])
return [propAttrs[0] componentsSeparatedByString:#"\""][1];
return nil;
}
#implementation JSONMarshallable
- (NSData*)toJSON {
return [self toJSON:self withNullValues:YES];
}
- (NSString*)toJSONString {
return [self toJSONString:self withNullValues:YES];
}
- (NSData*)toJSON:_ withNullValues:(bool)nullables {
NSError* error;
NSDictionary* dic = [self toDictionary:self withNullValues:nullables];
NSData* json = [NSJSONSerialization dataWithJSONObject:dic options:0 error:&error];
if (!json) {
NSLog(#"Error encoding DeviceConfigurationRequest: %#", error);
return nil;
}
return json;
}
- (NSString*) toJSONString:_ withNullValues:(bool)nullables {
NSData* json = [self toJSON:self withNullValues:nullables];
return [[NSString alloc] initWithBytes:[json bytes] length:[json length] encoding:NSUTF8StringEncoding];
}
- (NSDictionary*)toDictionary:_ withNullValues:(bool)nullables {
NSMutableDictionary* dic = [NSMutableDictionary new];
for (id propName in classPropertyList(self)) {
id val = [self valueForKey:propName];
if (!nullables && (val == nil || val == NSNull.null))
continue;
if ([val respondsToSelector:#selector(toDictionary:withNullValues:)])
val = [val toDictionary:val withNullValues:nullables];
[dic setObject:(val == nil ? NSNull.null : val) forKey:propName];
}
return dic;
}
- (instancetype)initWithJSONString:(NSString*)json {
return [self initWithJSON:[json dataUsingEncoding:NSUTF8StringEncoding]];
}
- (instancetype)initWithJSON:(NSData*)json {
NSError* error;
if (json == nil)
return nil;
NSDictionary* dataValues = [NSJSONSerialization JSONObjectWithData:json options:0 error:&error];
if (!dataValues) {
NSLog(#"Error parsing invalid JSON for %#: %#", NSStringFromClass(object_getClass(self)), error);
return nil;
}
return [self initWithDictionary:dataValues];
}
- (instancetype)initWithDictionary:(NSDictionary*)dataValues {
if (dataValues == nil)
return nil;
if (self = [super init])
for (id key in dataValues) {
id val = [dataValues objectForKey:key];
if (![self respondsToSelector:NSSelectorFromString(key)])
continue;
NSString* typeName = typeOfProperty([self class], key);
if ([val isKindOfClass:[NSNull class]]) { // translate NSNull values to something useful, if we can
if (typeName == nil)
continue; // don't try to set nil to non-pointer fields
val = nil;
} else if ([val isKindOfClass:[NSDictionary class]] && typeName != nil)
val = [[NSClassFromString(typeName) alloc] initWithDictionary:val];
[self setValue:val forKey:key];
}
return self;
}
#end
It is then easy to create custom model objects by inheriting from JSONMarshallable, like so:
model.h:
#import "JSONMarshallable.h"
#interface MyModel : JSONMarshallable
#property NSString* stringValue;
#property NSNumber* numericValue;
#property bool boolValue;
#end
model.m:
#implementation MyModel
#end
SomeThingElse.m:
// ...
NSData* someJson;
MyModel* obj = [[MyModel alloc] initWithJSON:someJson];
NSString* jsonObj = [obj toJSONString:nil withNullValues:NO];
Critics are welcome! (I'm not very good at Objective C and probably made a lot of faux pas ðŸ¤)
Issues:
I can handle nullable numbers with NSNumber* (though C primitives work fine for non-nullable numbers), but I don't know how to represent nullable booleans - i.e. a field that is optional and not encoded when using withNullValues:NO.
Sending fields for which there are no properties (for example, the server I work with sends values in both snake-case and underscrore-case to make it easy to parse) throws exception. (solved by using respondsToSelector: and setValue: instead of setValuesForKeysWithDictionary:).
Trying to set nil values to primitive-typed fields causes exceptions. (solved by checking for property type and NSNull).
Doesn't work at all for nesting objects - i.e. a custom model object with properties that are also custom model objects. (solved by checking for property types and recursing encoding/decoding).
Probably doesn't handle arrays well - I have yet to need those in my software, so I haven't implemented proper support (though I verified that encoding simple string arrays works well).
Quick disclaimer. I've programmed Java for years but this is the first Objective C I've ever written.
I've written some code which almost unfortunately works but frankly hurts my eyes with the number of lines of code and quality. Basically looking for the right way to convert the original string:
<TUHandle 0x280479f50 type=PhoneNumber, value=07700000000, normalizedValue=(null), isoCountryCode=(null)>
Into JSON (ignoring the TUHandle 0x280479f50) part which I don't need:
{"value": "07700000000",
"normalizedValue": "(null)",
"type": "PhoneNumber",
"isoCountryCode": "(null)"}
Line breaks and indents are NOT important, only that this is valid JSON
//Format of string
//<TUHandle 0x280479f50 type=PhoneNumber, value=07700000000, normalizedValue=(null), isoCountryCode=(null)>
NSString *original = [NSString stringWithFormat:#"%#", hand];
//Trim off unused stuff
NSRange startKeyValues = [original rangeOfString:#"type="];
NSRange endKeyValues = [original rangeOfString:#">"];
NSRange rangeOfString = NSMakeRange(startKeyValues.location, endKeyValues.location - startKeyValues.location);
NSString *keysValues = [original substringWithRange:rangeOfString];
//Create a dictionary to hold the key values
NSMutableDictionary *dict = [[NSMutableDictionary alloc]initWithCapacity:10];
//Split the keysValuesstring
NSArray *items = [keysValues componentsSeparatedByString:#","];
for (NSString* o in items)
{
//Create key value pairs
NSArray *item = [o componentsSeparatedByString:#"="];
NSString *key=[item objectAtIndex:0];
NSString *value=[item objectAtIndex:1];
[dict setObject:value forKey:key];
}
[dict setObject:currentUUID forKey:#"uid"];
//Convert to Json Object
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
Any tips which would make this look a hell of a lot less clunky?
Depending on what parts of the format you're comfortable with hardcoding, this may serve.
It assumes that only parts of the input string that look like "x=y" go into the json, and cuts off the last character from y's value, since that's either a "," or the trailing ">".
- (void) tuhandleToJSON {
NSString* input = #"<TUHandle 0x280479f50 type=PhoneNumber, value=07700000000, normalizedValue=(null), isoCountryCode=(null)>";
NSMutableDictionary<NSString*, NSString*>* dict = [NSMutableDictionary dictionary];
NSArray* tokens = [input componentsSeparatedByString:#" "];
for (NSString* token in tokens) {
NSArray<NSString*>* parts = [token componentsSeparatedByString:#"="];
if (parts.count != 2) {
continue;
}
NSString* key = parts[0];
NSString* value = parts[1];
NSUInteger index = value.length - 1;
dict[key] = [value substringToIndex:index];
}
NSError* error;
NSData* data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
NSString* json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", json);
}
Hi I am using NSURLSession to fetch data from a server and I am using the following code for that purpose.
[[[NSURLSession sharedSession] dataTaskWithURL:urlForDiseaseTypes completionHandler:^(NSData *data, NSURLResponse *response,NSError *error)
{
if(!error)
{
NSHTTPURLResponse *httpRespc = (NSHTTPURLResponse *)response;
if(httpRespc.statusCode == 200)
{
NSArray *result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
diseaseArray = [result valueForKeyPath:#"Value"];
diseaseIDarray = [result valueForKeyPath:#"ID"];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableViewMain reloadData];
});
}
}
}]resume];
And I am getting value inside both arrays(diseaseArray,diseaseIDarray). diseaseArray values are supposed to get displayed on table view. But when tableview opens no values are getting visible. When checked inside in Table View methods the array count is zero. How can I load my tableview with the values I get from NSURLSession block.
Currently I am calling this function in viewDidLoad.
I have a nsmutable array that returns a companies address. I currently having the array returning all the States. But I would like it to only return one of each State.
Current Example: Ca. Wa. Wa. Mi. Ca. And when I select a state I need to take the info to the next page. Here is my code so far.
- (NSArray *)readCompanies:(NSURL *)url {
//create a nsurlrequest with the given Url
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:
NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:30.0];
//get the data
NSURLResponse *response;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
//now create a nsdictionary from the json data
NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:data
options:0 error:nil];
//create a new array to hold the comanies
NSMutableArray *companies = [[NSMutableArray alloc] init];
//get an array of dictionaries with the key "company"
NSArray *array = [jsonDictionary objectForKey:#"companies"];
//iterate throught the array of dictionaries
for (NSDictionary *dict in array) {
//create a new company object with information in the dictionary
Company *company = [[Company alloc] initWithJSONDictionary:dict];
//add the Company object to the array
[companies addObject:company];
}
//return the array of Company objects
return companies;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark -table view controller methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section {
return [companies count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"CellIDState";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil){
//single line on table view
//cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellID];
// dual line on table view
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
}
Company *company = [companies objectAtIndex:indexPath.row];
//cell.textLabel.text = company.company_id;
cell.textLabel.text = company.state;
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#",company.companyName];
//adds cheveron to tableviewl
[cell setAccessoryType: UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
thanks for the help.
Looking at your code, you have an NSMutableArray of Company objects. Then you display a table with a button for each Company's state on it.
Company 1, CA -> [CA]
Company 2, WA -> [WA]
Company 3, CA -> [CA]
If I understand your question correctly, you desire to display only a single cell for each state that has a company in it. That means you need a way to represent more than one company for each state.
One possibility for this is to have a top-level structure as a dictionary, where the key is the state and the value is an array of Company's in that state. The above three Company's would end up in this structure as:
NSDictionary
key1 (CA)
Company 1
Company 3
key2 (WA)
Company 2
The code to do this is a bit more complex than the loop you made. The readCompanies for it would look like this (please excuse my rough pseudocode):
-(NSDictionary*) readCompanies(NSURL* url) {
Get URL data
Parse URL data into array of dictionaries (you have these steps already)
NSDictionary* companyByStates = [[NSDictionary alloc] init];
//iterate throught the array of dictionaries
for (NSDictionary *dict in array) {
//create a new company object with information in the dictionary
Company *company = [[Company alloc] initWithJSONDictionary:dict];
if companyByStates does not have entry for key(company->state) {
create new NSMutableArray
add this company to new array
add array to companyByStates under key (company->state)
}
else // already have entry for that state
{
get array for that state: companyByStates[company->state]
add this company to the array for that state
}
}
return companyByStates
}
Also, your tableView method would need to use a [NSDictionary keyEnumerator] to access the indexed entry since the states are no longer an array, but keys in the top-level dictionary.
Hope that helps.
In my json file I have a title, subtitle, and url.
I sort the title to set the items alphabetically, but the url isn't sorted with the title and I don't know why.
This is what i've done:
NSDictionary *allDataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
NSArray *arrayOfItems = [allDataDictionary objectForKey:#"items"];
for (NSDictionary *diction in arrayOfItems) {
NSString *titles = [diction objectForKey:#"title"];
NSString *station = [diction objectForKey:#"url"];
[jsonArray addObject:titles];
[jsonStations addObject:station];
// SORT JSON
NSArray *sortedArray;
sortedArray = [jsonArray sortedArrayUsingComparator:^NSComparisonResult(NSString *title1, NSString *title2)
{
if ([title1 compare:title2] > 0)
return NSOrderedDescending;
else
return NSOrderedAscending;
}];
[jsonArray setArray:sortedArray];
What happens is, if I press the first item in the tableView, I get get the url from a total diffrent title. What should I do to get the title to match the url and title in the tableView?
Any help appreciated
EDIT: here's the tableView:didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if(indexPath.row == _currentRadio) {
return;
}
if(_radio) {
[_radio shutdown];
[_radio release];
_radio = nil;
}
[_statusLabel setText:#""];
[_titleLabel setText:#""];
_currentRadio = indexPath.row;
NSString *radioUrl = [jsonStations objectAtIndex:indexPath.row];
if([radioUrl hasPrefix:#"mms"]) {
_radio = [[MMSRadio alloc] initWithURL:[NSURL URLWithString:radioUrl]];
} else {
_radio = [[HTTPRadio alloc] initWithURL:[NSURL URLWithString:radioUrl]];
}
if(_radio) {
[_radio setDelegate:self];
[_radio play];
}
[self.tableview reloadData];
}
The code was placed wrong, another setup for the code, fixed the sorting problem.