void PrintIntrospectionInfo()
{
// NSLog(#"Comes here1");
NSArray *myArray;
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
NSURL *urlObj = [NSURL URLWithString:#"http://www.yahoo.com"];
NSString *aString = #"string";
NSMutableString *mString = [NSMutableString stringWithFormat: #"Hello, %#", aString];
NSDictionary *stanford = [NSDictionary dictionaryWithObjectsAndKeys:#"http://www.stanford.edu", #"Stanford University", #"http://www.apple.com",#"Apple", #"http://cs193p.stanford.edu", #"CS193P",#"http://itunes.stanford.edu",#"Stanford on iTunesU",#"http://stanfordshop.com",#"Stanford Mall", nil];
myArray = [NSArray arrayWithObjects:aDate, aValue, aString,stanford,urlObj,mString, nil];
for(id someObject in myArray)
{
NSLog(#"Comes here");
if([someObject isKindofClass:[NSString string]])
{
}
}
}
Do
for(id someObject in myArray)
{
NSLog(#"Comes here");
if([someObject isKindOfClass:[NSString class]])
{
}
}
instead. Note that Of of isKindOfClass is in the upper case. I recommend you to use NSObject* instead of id. Then the compiler warns you that the method isKindofClass is not available. Usually, you don't need to use id unless you use something which is not an NSObject :
for(NSObject* someObject in myArray)
Related
I am using the code below to retrieve data from Url via PHP file, the tableview controller will start the Activity Indicator. What I am trying to do is to stop the indicator As soon as there are no data has been loaded to the tableview.
Her is the code;
NSString *urlString = [NSString stringWithFormat:#"http:/MyWebSite/ChoseMyLike.php?userName=%#", myString];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!data) {
NSLog(#"connection error: %#", error);
return;
}
NSError *parseError;
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parseError];
if (!json) {
NSLog(#"JSON Parsing error: %#", parseError);
NSLog(#"data = %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
return;
}
NSMutableArray *results = [[NSMutableArray alloc]init];
for (int i = 0; i < json.count; i++) {
NSString *cQasidaName = json[i][#"qasidaName"];
NSString *cQasidaShaerName = json[i][#"qasidaShaerName"];
NSString *cQasidaBody = json[i][#"qasidaBody"];
NSString *cQasidaDate = json[i][#"myDate"];
NSString *cQasidaTime = json[i][#"myTime"];
NSString *cQasidaRate = json[i][#"myRate"];
NSString *cQasidaId = json[i][#"qasidaId"];
NSString *cQasidaUserName = json[i][#"userName"];
NSString *cTheUserId = json[i][#"theUserId"];
NSString *cTheUserNameArabic = json[i][#"userNameArabic"];
[results addObject:[[ListOfObjects alloc] initWithQasidaName: (NSString *) cQasidaName andQasidaShaerName: (NSString *) cQasidaShaerName andQasidaBody: (NSString *) cQasidaBody andQasidaDate: (NSString *) cQasidaDate andQasidaTime: (NSString *) cQasidaTime andQasidaRate: (NSString *)cQasidaRate andQasidaId:cQasidaId andQasidaUserName:cQasidaUserName andTheUserId:cTheUserId andTheUserNameArabic:cTheUserNameArabic]];
}
dispatch_async(dispatch_get_main_queue(), ^{
self.listArray = results;
[self.tableView reloadData];
[spinner stopAnimating];
});
}];
[task resume];
}
At the moment the indicator is not stopping. What do I need to do?
Thanks
Simply call [spinner stopAnimating]; for all return paths:
if (!data) {
NSLog(#"connection error: %#", error);
[spinner stopAnimating];
return;
}
and
if (!json) {
NSLog(#"JSON Parsing error: %#", parseError);
NSLog(#"data = %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
[spinner stopAnimating];
return;
}
Note: it's much easier to have a single return statement and then you can always call [spinner stopAnimating]; at the end of the method... consider restructuring your code.
I am using this line of code; my array contains name, email and phone no. This code only sorts namewise, but I want email and phone no with name. How can I do this that my array separeNamesByLetters contains email and phone with name?
NSMutableSet *firstCharacters = [NSMutableSet setWithCapacity:0];
for( NSString*string in [ tableDataArray valueForKey:#"name"] ){
[firstCharacters addObject:[[string substringToIndex:1] uppercaseString]];
}
NSArray *allLetters = [[firstCharacters allObjects] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
int indexLetter = 0;
separeNamesByLetters = [NSMutableArray new];
for (NSString *letter in allLetters) {
NSMutableDictionary*userBegeinsWith = [NSMutableDictionary new];
[userBegeinsWith setObject:letter forKey:#"letter" ];
NSMutableArray *groupNameByLetters = [NSMutableArray new];
NSString *compareLetter1 = [NSString stringWithFormat:#"%#", allLetters[indexLetter]];
for (NSString*friendName in[ tableDataArray valueForKey:#"name"]) {
NSString *compareLetter2 = [[friendName substringToIndex:1] uppercaseString];
if ( [compareLetter1 isEqualToString:compareLetter2] ) {
[groupNameByLetters addObject:friendName];
}
}
indexLetter++;
[userBegeinsWith setObject:groupNameByLetters forKey:#"list"];
[separeNamesByLetters addObject: userBegeinsWith];
}
NSLog(#"%#", separeNamesByLetters);
}
Have you considered using NSSortDescriptors? See the following example:
NSMutableArray *array = [NSMutableArray new];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"Doe, John", #"name", #"3361231234", #"phone", #"johndoe#email.com", #"email", nil];
[array addObject:dict];
dict = [NSDictionary dictionaryWithObjectsAndKeys:#"Doe, Jane", #"name", #"9191234532", #"phone", #"janedoe#email.com", #"email", nil];
[array addObject:dict];
NSSortDescriptor *nameDescriptor =
[[NSSortDescriptor alloc]
initWithKey:#"name"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *phoneDescriptor =
[[NSSortDescriptor alloc]
initWithKey:#"phone"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *emailDescriptor =
[[NSSortDescriptor alloc]
initWithKey:#"email"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray * descriptors = [NSArray arrayWithObjects:nameDescriptor, phoneDescriptor, emailDescriptor, nil];
NSArray * sortedArray = [array sortedArrayUsingDescriptors:descriptors];
Let me know if this helps.
When I do this, I create a new NSArray of NSDictionaries. The NSDictionary has two properties: sectionTitle and records. The sectionTitle holds the first-letter of the field you are searching on. The records contain the actual objects that begin with that sectionTitle.
Add this sectionTitle method to your model class. I usually do this in an Extension to the actual model class.
- (NSString *)sectionTitle:(NSString*)sortByProperty {
NSString *propertyValue;
if ([sortByProperty isEqualToString:#"name"]) {
if (self.name == nil) return #"";
propertyValue = self.name;
} else if ([sortByProperty isEqualToString:#"email"]) {
if (self.email == nil) return #"";
propertyValue = self.phone;
} else if ([sortByProperty isEqualToString:#"phone"]) {
if (self.phone == nil) return #"";
propertyValue = phone
}
NSString *tmp;
if ([propertyValue length] > 0) {
tmp = [propertyValue substringToIndex:1];
} else {
return #"";
}
return tmp;
}
This should be quicker than iterating through your array each time you refresh your tableView.
Next, iterate through these sectionTitles, filter your array, and add the filtered results to the records attribute of the dictionary:
- (NSMutableArray*)sortedArrayForUITableView:(NSMutableArray *)tableDataArray sortBy:(NSString*)sortByProperty {
NSArray *tmp = [tableDataArray valueForKeyPath:#"#distinctUnionOfObjects.sectionTitle"];
NSArray *allLetters = [NSMutableArray arrayWithArray:[tmp sortedArrayUsingDescriptors:[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:#"self" ascending:YES]]]];
NSMutableArray *tree = [NSMutableArray new];
for (NSString *sectionTitle in allLetters) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.sectionTitle == %#", sectionTitle];
NSMutableArray *tmpArray = [NSMutableArray arrayWithArray:[tableDataArray filteredArrayUsingPredicate:predicate]];
NSArray *sortedArray = [tmpArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:sortByProperty ascending:YES]]];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:sectionTitle, #"sectionTitle", sortedArray, #"records", nil];
[tree addObject:dict];
}
return tree;
}
You will need to implement several UITableViewDataSource and UITableViewDelegate methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.tree count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *dict = [NSDictionary dictionaryWithDictionary:[self.tree objectAtIndex:section]];
NSArray *tmp = [NSArray arrayWithArray:[dict objectForKey:#"records"]];
return [tmp count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSDictionary *dict = [NSDictionary dictionaryWithDictionary:[self.tree objectAtIndex:section]];
return [dict objectForKey:#"sectionTitle"];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.allLetters;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return index;
}
Lastly, you will build your cell in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. Hope that helps.
Im trying to carry over a block variable and put it in my convenience initializer but the variable returns null, can someone show me the right way of doing it, Thanks, also please check if I have done my NSComparison Result correctly, here's the code,
- (void) retrieveData
{
NSURL *url = [NSURL URLWithString:jsonFile];
NSData *data = [NSData dataWithContentsOfURL:url];
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_salesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _jsonArray.count; i++) {
NSString *sID = [[_jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString *sName = [[_jsonArray objectAtIndex:i] objectForKey:#"name"];
NSString *sAddress = [[_jsonArray objectAtIndex:i] objectForKey:#"address"];
NSString *sPostcode = [[_jsonArray objectAtIndex:i] objectForKey:#"postcode"];
__block NSString *distance;
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder geocodeAddressString:sPostcode completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && placemarks.count > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocation *myLocation = self.manager.location;
CLLocationDistance miles = [location distanceFromLocation:myLocation];
//this is the variable i want in my convenience init.
distance = [NSString stringWithFormat:#"%.1f m", (miles/1609.344)];
}
}];
[_salesArray addObject:[[sales alloc] initWithSales:sID andName:sName andAddress:sAddress andPostcode:distance]];
}
[_salesArray sortUsingComparator:
^NSComparisonResult(id obj1, id obj2){
sales *p1 = (sales *)obj1;
sales *p2 = (sales *)obj2;
if (p1.postcode > p2.postcode) {
return (NSComparisonResult)NSOrderedDescending;
}
if (p1.postcode < p2.postcode) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}
];
[self.tableView reloadData];
}
Thanks in advance to anyone that can help, this happens to be the last part of my app until completion :)
-[CLGeocoder geocodeAddressString:completionHandler:] runs the completion handler asynchronously and it won't run the block before the rest of that function finishes. distance doesn't "get null"; rather, it hasn't been set yet.
your block is run AFTER the function completes ... doesn't matter if the 'line of code' is after or before the block.
the solution is to have to block call a 'continuation' function:
e.g. instead of - (void) retrieveData
have - (void) beginRetrieveData, which calls the block and then have a - (void) finishRetrieveData, which is called by the block and doesn't run before the block ;)
here is how your code could look:
- (void) beginRetrieveData
{
NSURL *url = [NSURL URLWithString:jsonFile];
NSData *data = [NSData dataWithContentsOfURL:url];
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_salesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _jsonArray.count; i++) {
NSString *sID = [[_jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString *sName = [[_jsonArray objectAtIndex:i] objectForKey:#"name"];
NSString *sAddress = [[_jsonArray objectAtIndex:i] objectForKey:#"address"];
NSString *sPostcode = [[_jsonArray objectAtIndex:i] objectForKey:#"postcode"];
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder geocodeAddressString:sPostcode completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && placemarks.count > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocation *myLocation = self.manager.location;
CLLocationDistance miles = [location distanceFromLocation:myLocation];
//this is the variable i want in my convenience init.
NSString *distance = [NSString stringWithFormat:#"%.1f m", (miles/1609.344)];
[self finishRetrieveData:distance]; //continue
}
}];
}
}
- (void) finishRetrieveData:(NSString*)distance
{
[_salesArray addObject:[[sales alloc] initWithSales:sID andName:sName andAddress:sAddress andPostcode:distance]];
}
I want to post a json object to a request. But JSONWriter doesn't seem to convert my object, don't know what I'm doing wrong ...
here's my code for the conversion..
customer = [[Customer alloc] init];
Address *tempAddr = [[Address alloc] init];
tempAddr.Line1=strAddr1 ;
tempAddr.Zip =strPost;
tempAddr.City =strStreet;
[customer setInvoiceAddress:tempAddr];
customer.FirstName=strFirstName;
customer.LastName=strLastName;
customer.CellPhone=strCellPhone;
customer.Phone=strPhone;
customer.Email=strEmail;
SBJsonWriter *writer = [SBJsonWriter alloc];
NSString *jsonConvertedObj = [writer stringWithObject:customer];
NSLog(#"the json converted object ... %#", jsonConvertedObj);
Please help. I've no idea what's wrong with the code above.
To figure out why your Json parsing fails, You shoud use this approach:
NSError *error = nil;
NSString * jsonTest = [[[SBJsonWriter alloc] init] stringWithObject:jsonTestDictionary error:&error];
if ( ! jsonTest ) {
NSLog(#"Error: %#", error);
}else{
NSLog(#"%#", jsonTest);
}
Here is a simple code demonstrating how to use it:
#import <Foundation/Foundation.h>
#import "SBJsonWriter.h"
#import "SBJsonParser.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSDictionary* aNestedObject = [NSDictionary dictionaryWithObjectsAndKeys:
#"nestedStringValue", #"aStringInNestedObject",
[NSNumber numberWithInt:1], #"aNumberInNestedObject",
nil];
NSArray * aJSonArray = [[NSArray alloc] initWithObjects: #"arrayItem1", #"arrayItem2", #"arrayItem3", nil];
NSDictionary * jsonTestDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
#"stringValue", #"aString",
[NSNumber numberWithInt:1], #"aNumber",
[NSNumber numberWithFloat:1.2345f], #"aFloat",
[[NSDate date] description], #"aDate",
aNestedObject, #"nestedObject",
aJSonArray, #"aJSonArray",
nil];
// create JSON output from dictionary
NSError *error = nil;
NSString * jsonTest = [[[SBJsonWriter alloc] init] stringWithObject:jsonTestDictionary error:&error];
if ( ! jsonTest ) {
NSLog(#"Error: %#", error);
}else{
NSLog(#"%#", jsonTest);
}
}
return 0;
}
outputs
{
"aDate":"2012-09-12 07:39:00 +0000",
"aFloat":1.2345000505447388,
"nestedObject":{
"aStringInNestedObject":"nestedStringValue",
"aNumberInNestedObject":1
},
"aJSonList":["arrayItem1","arrayItem2","arrayItem3"],
"aString":"stringValue",
"aNumber":1
}
Note:
Using 'error' allowed me to figure out that if you write [NSDate
date] instead of [[NSDate date] description] you will get a "JSON
serialisation not supported for
__NSTaggedDate" error.
notice the float rounding error... 1.2345 became 1.2345000505447388
AFAIK SBJSONWriter only supports conversion of dictionaries and arrays to JSON strings. It won't work for arbitrary objects.
Edit: Here is the corresponding implementation, which returns nil if the supplied object is neither a dictionary nor an array.
- (NSString*)stringWithObject:(id)value {
if ([value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSArray class]]) {
return [self stringWithFragment:value];
}
[self clearErrorTrace];
[self addErrorWithCode:EFRAGMENT description:#"Not valid type for JSON"];
return nil;
}
Given an NSArray of NSStrings, is there a quick way to join them together into a single NSString (with a Separator)?
NSArray * stuff = /* ... */;
NSString * combinedStuff = [stuff componentsJoinedByString:#"separator"];
This is the inverse of -[NSString componentsSeparatedByString:].
-componentsJoinedByString: on NSArray should do the trick.
There's also this variant, if your original array contains Key-Value objects from which you only want to pick one property (that can be serialized as a string ):
#implementation NSArray (itertools)
-(NSMutableString *)stringByJoiningOnProperty:(NSString *)property separator:(NSString *)separator
{
NSMutableString *res = [#"" mutableCopy];
BOOL firstTime = YES;
for (NSObject *obj in self)
{
if (!firstTime) {
[res appendString:separator];
}
else{
firstTime = NO;
}
id val = [obj valueForKey:property];
if ([val isKindOfClass:[NSString class]])
{
[res appendString:val];
}
else
{
[res appendString:[val stringValue]];
}
}
return res;
}
#end