Compare two plist files or two NSDictionary in iOS - ios

I have downloaded a plist file from server, that contains key value pair. Once app resumes/restarts then again I have to download file and check if file has changed.
below is the code to download... I am storing the key values in NSDictionary.
task1 = [session dataTaskWithURL:[NSURL URLWithString:[S3_SERVER_URL stringByAppendingString:propFile]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
propfilePath = [documentsDirectory stringByAppendingString:propFile];
NSLog(#"DestPath : %#", propfilePath);
[receivedData appendData:data];
NSLog(#"Succeeded! Received %lu bytes of data",(unsigned long)[data length]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[data writeToFile:propfilePath atomically:YES];
if(error == nil){
plistDictionary = [[NSDictionary dictionaryWithContentsOfFile:propfilePath] retain];
[task2 resume];
} else {
}
}];
How can i compare the contents of the two plist files or NS dictionary?
Which function is best suited to do the above As I have to do this on app create/resume/restart?
It should be compatible to both ios7 and ios8 SDK.

If you want all changed key follow this:- This will return array of all changed keys.
Create a category for NSDictionary
NSDictionary+newDict.h
#import <Foundation/Foundation.h>
#interface NSDictionary (newDict)
- (NSArray*)changedKeysIn:(NSDictionary*)d;
#end
NSDictionary+newDict.m
#import "NSDictionary+newDict.h"
#implementation NSDictionary (newDict)
- (NSArray*)changedKeysIn:(NSDictionary*)d {
NSMutableArray *changedKs = [NSMutableArray array];
for(id k in self) {
if(![[self objectForKey:k] isEqual:[d objectForKey:k]])
[changedKs addObject:k];
}
return changedKs;
}
#end
Calling:-
#import "NSDictionary+newDict.h"
and:-
NSArray *keys = [dict1 changedKeysIn:dict2];
NSLog(#"%#", keys);

You could use the isEqualToDictionary: method from the NSDictionary class.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/index.html#//apple_ref/occ/instm/NSDictionary/isEqualToDictionary:

Related

NSDirectoryEnumerator iterated element can't compare suffix

I am using NSDirectoryEnmerator to find all file with a suffix of png and jpg with following code:
NSDirectoryEnumerator *directoryEnumerator = [[NSFileManager defaultManager] enumeratorAtURL:containerURL includingPropertiesForKeys:[NSArray array] options:0 errorHandler:^BOOL(NSURL *url, NSError *error) {
// handle error
return NO;
}];
NSString *fileOrDirectory = nil;
while ((fileOrDirectory = [directoryEnumerator nextObject])) {
if([fileOrDirectory hasSuffix:#".jpg"] || [fileOrDirectory hasSuffix:#".png"]){
NSLog(#" find a image file %#", fileOrDirectory );
}
}
But there is an error said that NSURL don't have a method hasSuffix
What happened and how to make this work? what does the type of the iterated elements exactly? the above code was frequently suggested by posts and was presumed to be a NSString but it can't work
The enumeratorAtURL method works with NSURL objects rather than strings (which the exception reason clearly reveals), you can simply compare the pathExtension:
if ([fileOrDirectory.pathExtension isEqualToString:#"jpg"] ||
[fileOrDirectory.pathExtension isEqualToString:#"png"]) { ...

Dictionary initialized in AppDelegate has nil value in UIViewController

I am using open weather API to get live weather data and displaying it in a UIViewController. However I make the http request in AppDelegate. So I made the API request in AppDelegate in a method called weatherForcast(), converted the JSON response to a NSDictionary object, and printed the object to the console just to make sure everything worked fine, and it did.
NSString *urllink = [NSString stringWithFormat:#"http://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&appid=%#&units=metric", lat, lng, WEATHERAPIKEY];
NSURL *jsonURL = [NSURL URLWithString:[self urlEncodeValue:urllink]];
NSString *jsonDataString = [[NSString alloc]initWithContentsOfURL:jsonURL];
NSData *jsonData = [jsonDataString dataUsingEncoding:NSUTF16StringEncoding];
NSLog(#"This is jsonURL:%#", jsonURL);
NSError *err = nil;
if(jsonData == nil)
{
NSLog(#"Error laoding jsonData");
}
else
{
self.weatherInfo = [NSJSONSerialization JSONObjectWithData: jsonData options: NSJSONReadingMutableContainers error: &err];
NSLog(#"This is weatherInfo dictionary:%#", self.weatherInfo);
}
The dictionary is perfect.
Then in the UIViewController in viewDidLoad I call the method weatherForecast() and then call a method UpdateTemperature() which sets all the texts of the labels to data in the dictionary. Here is the code in the method UpdateTemperature:
NSLog(#"This is the weatherInfo dictionary: %#", appDel.weatherInfo);
if([appDel.weatherInfo count] > 0 && appDel.isNetworkAvailable)
{
NSLog(#"Went into weatherInfo.count > 0");
lblCondition.text = [NSString stringWithFormat:#"condition:%#", [[[appDel.weatherInfo valueForKey:#"weather"] objectAtIndex:0] valueForKey:#"description"]];
lblHumidity.text = [NSString stringWithFormat:#"humidity:%#", [[appDel.weatherInfo valueForKey:#"main"] valueForKey:#"humidity"]];
lblTemperature.text = [NSString stringWithFormat:#"%# Celsius", [[appDel.weatherInfo valueForKey:#"main"] valueForKey:#"temp"]];
imgWeather.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#", WEATHERCONDITIONIMGURL, [appDel.weatherInfo valueForKey:#"icon"]]]]];
lblDegree.hidden = FALSE;
[getTemp stopAnimating];
}
else
{
lblDegree.hidden = TRUE;
}
All the labels will only be set if the dictionary has at least one object within it, which it should. But it turned not not too. So I printed the dictionary, and got nil.
In AppDelegate when I printed the dictionary it was fine, but than in viewDidLoad when I printed the same dictionary it turned out to be nil. What is happening?
It's likely that when viewDidLoad gets called, weatherInfo has not been initialized yet. If it requires an http call the data may have not returned yet and therefore when you access it in viewDidLoad there is no object to access. You might want to try reconfiguring where you make your http request and create weatherInfo.
when you create object of appdelegate then all variable of appdelegate is reinitialized so it return nil. Just put your code into a fuction and simply return a dictionary
plz try this,
-(NSDictionary *) getWeatherInfo
{
NSString *urllink = [NSString stringWithFormat:#"http://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&appid=%#&units=metric", 10.0, 10.0, #"api"];
NSURL *jsonURL = [NSURL URLWithString:[self urlEncodeValue:urllink]];
NSData *jsonData = [NSData dataWithContentsOfURL:jsonURL];
NSLog(#"This is jsonURL:%#", jsonURL);
NSError *err = nil;
NSDictionary *weather_info=[NSDictionary dictionary];
if(jsonData == nil)
{
NSLog(#"Error laoding jsonData");
}
else
{
weather_info = [NSJSONSerialization JSONObjectWithData: jsonData options: NSJSONReadingMutableContainers error: &err];
NSLog(#"This is weatherInfo dictionary:%#", weather_info);
}
return weather_info;
}

NSMutableDictionary inside JSONModel - EXC_BAD_ACCESS KERN_INVALID_ADDRESS

Crashlytics reported this crash in one of my apps and I am not able to reproduce it at all, no matter what I do.
This happens to about 5% of the users, so it's a pretty big deal.
I'm posting screenshots with the crash report and also the methods that are mentioned in the crash report.
Any idea how to solve this?
This is where the app crashed:
#pragma mark - custom transformations
-(BOOL)__customSetValue:(id<NSObject>)value forProperty:(JSONModelClassProperty*)property
{
if (!property.customSetters)
property.customSetters = [NSMutableDictionary new];
NSString *className = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:[value class]]);
if (!property.customSetters[className]) {
//check for a custom property setter method
NSString* ucfirstName = [property.name stringByReplacingCharactersInRange:NSMakeRange(0,1)
withString:[[property.name substringToIndex:1] uppercaseString]];
NSString* selectorName = [NSString stringWithFormat:#"set%#With%#:", ucfirstName, className];
SEL customPropertySetter = NSSelectorFromString(selectorName);
//check if there's a custom selector like this
if (![self respondsToSelector: customPropertySetter]) {
property.customSetters[className] = [NSNull null]; // this is line 855
return NO;
}
//cache the custom setter selector
property.customSetters[className] = selectorName;
}
if (property.customSetters[className] != [NSNull null]) {
//call the custom setter
//https://github.com/steipete
SEL selector = NSSelectorFromString(property.customSetters[className]);
((void (*) (id, SEL, id))objc_msgSend)(self, selector, value);
return YES;
}
return NO;
}
This is the originating method:
-(void)reloadUserInfoWithCompletion:(void (^) (LoginObject *response))handler andFailure:(void (^)(NSError *err))failureHandler {
NSString *lat;
NSString *lon;
lat = [NSString stringWithFormat:#"%.6f",[[LocationManager sharedInstance] getPosition].coordinate.latitude];
lon = [NSString stringWithFormat:#"%.6f",[[LocationManager sharedInstance] getPosition].coordinate.longitude];
NSMutableDictionary *params = [NSMutableDictionary new];
[params setObject:lat forKey:#"latitude"];
[params setObject:lon forKey:#"longitude"];
[[LoginHandler sharedInstance] getLoginToken:^(NSString *response) {
NSDictionary *headers;
if (response) {
headers = #{#"Login-Token":response};
}
GETRequest *req = [GETRequest new];
[req setCompletionHandler:^(NSString *response) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"response: %#",response);
NSError *err = nil;
self.loginObject.userDetails = [[User alloc] initWithString:response error:&err]; // <- this is the line reported in the crash
[self storeLoginObject];
NSLog(#"%#",self.loginObject.userDetails);
// [Utils updateFiltersFullAccessIfAll];
dispatch_async(dispatch_get_main_queue(), ^{
if (handler) {
handler(self.loginObject);
}
});
});
}];
[req setFailedHandler:^(NSError *err) {
if (failureHandler) {
failureHandler(err);
}
}];
NSLog(#"%#",params);
[req requestWithLinkString:USER_DETAILS parameters:nil andHeaders:headers];
}];
}
So setObject:forKey: can cause problems in two ways. 1. If object is nil or 2. the key is nil. Both could cause the crash you are seeing. Given that you are setting the object to [NSNull null] it is probably safe to assume that it is the key giving you problems (on line 855).
Walking back from there that would reveal that className is nil. If you look, your code does not protect against this. You make an assumption here that NSStringFromClass (a couple lines before) is giving you back a valid string, which assumes that the value originally passed into the method is non-nil. If it is nil it would make it past all of your checks, including !property.customSetters[className], since this would be !nil allowing it to enter the if.
If I am reading your code right (a bit hard since I cannot test any of my assumptions) NSLog(#"response: %#",response); would print out a nil response.
Try seeing how your code handles these unexpected nils and let me know in the comments how things go.
If you don't use model custom setters you can replace JSONModel __customSetValue:forProperty: with swizzling or Aspects library
#import "JSONModel+Aspects.h"
#import "JSONModel.h"
#import "Aspects.h"
#implementation JSONModel (Aspects)
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[JSONModel aspect_hookSelector:#selector(__customSetValue:forProperty:) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> aspectInfo) {
return NO;
} error:NULL];
});
}
#end

Response is null

My response is shown null. But when you enter the url in your browser it shows 1. But in my code it returns 0.
The url is http://boomagift.ramansingla.com/forgotpassword.php?email=nihal#gmail.com. I would really appreciate it if someone could help me out. I am new to iOS .
+(NSDictionary *)forgotpassword:(NSString *)email
{
NSDictionary *dict=[[NSDictionary alloc]init];
NSString *urlStr=[NSString stringWithFormat:#"http://boomagift.ramansingla.com/forgotpassword.php?email=%#",email];
NSLog(#"%#",urlStr);
NSMutableURLRequest *request=[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlStr]];
dict = [self sendRequest:request];
NSLog(#"%#",dict);
return dict;
}
+(NSDictionary *)sendRequest:(NSMutableURLRequest *)request
{
NSHTTPURLResponse *response;
NSError *error;
NSData *responseData;
responseData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(responseData&&[responseData length])
{
NSDictionary *dictionary=[NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
return dictionary;
}
else
{
UIAlertView *noInternetAlert=[[UIAlertView alloc] initWithTitle:#"Boom A GIft" message:#"Server Error" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[noInternetAlert show];
noInternetAlert=nil;
return nil;
}
}
The response data is no valid JSON, because its top item is a number, not a Collection (list or object).
An object that may be converted to JSON must have the following
properties:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray,
NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
You can configure the serializer to accept non-collection objects at top level with the options paramater.
enum {
NSJSONReadingMutableContainers = (1UL << 0),
NSJSONReadingMutableLeaves = (1UL << 1),
NSJSONReadingAllowFragments = (1UL << 2)
};
typedef NSUInteger NSJSONReadingOptions;
NSJSONReadingAllowFragments
Specifies that the parser should allow
top-level objects that are not an instance of NSArray or NSDictionary.
Available in OS X v10.7 and later.
BTW: returning nil (not NULL) with an error-out parameter is not joking, but a hint, what the problem is. ;-)

Objective C memory management - "pointer being freed was not allocated" errors

I'm trying to learn objective-c (I'm very new to that) and I have issues with memory management...
I'm developing an iPad app that uses TouchXML.
I've created my class that extends CXMLDocument and does some initialisation by reading some contents and saving into properties.
Here is my code (SimpleManifest.h):
#interface SimpleManifest : CXMLDocument {
CXMLNode *_defaultOrganization;
NSString *_title;
NSDictionary *dictionary;
}
#property (readonly) CXMLNode *defaultOrganization;
#property (readonly) NSString* title;
- (id) initWithPath:(NSString *)path options:(NSUInteger)options error:(NSError **)error;
#end
(SimpleManifest.m):
#import "SimpleManifest.h"
#import "CXMLNode_XPathExtensions.h"
#implementation SimpleManifest
- (id) initWithPath:(NSString *)path options:(NSUInteger)options error:(NSError **)error
{
/*
NSURL *theURL = [[[NSURL alloc] initFileURLWithPath:path] autorelease];
self = [self initWithContentsOfURL:theURL options:options error:error];
*/
NSData *data = [NSData dataWithContentsOfFile:path];
NSString *s = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
self = [self initWithXMLString:s options:options error:error];
if (self==nil) return nil;
// load main props
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
#"http://www.imsglobal.org/xsd/imscp_v1p1", #"imscp",
#"http://ltsc.ieee.org/xsd/LOM", #"lom", nil];
// defualt organization
#try {
CXMLNode *orgsElem = [[[self childAtIndex:0] nodesForXPath:#"//imscp:organizations" namespaceMappings:dictionary error:nil] objectAtIndex:0];
NSString *xpath = [NSString stringWithFormat:#"//imscp:organization[#identifier='%#']", [[orgsElem attributeForName:#"default"] stringValue]];
_defaultOrganization = [[[self childAtIndex:0] nodesForXPath:xpath namespaceMappings:dictionary error:nil] objectAtIndex:0];
/*
NSArray *nodes = [[self childAtIndex:0] nodesForXPath:#"//imscp:organizations" namespaceMappings:dictionary error:nil];
NSString *xpath = [NSString stringWithFormat:#"//imscp:organization[#identifier='%#']", [[[nodes objectAtIndex:0] attributeForName:#"default"] stringValue]];
_defaultOrganization = [[[self childAtIndex:0] nodesForXPath:xpath namespaceMappings:dictionary error:nil] objectAtIndex:0];
*/
CXMLNode *titleElem = [[[self childAtIndex:0]
nodesForXPath:#"//lom:general/lom:title/lom:string"
namespaceMappings:dictionary
error:nil] objectAtIndex:0];
_title = [[titleElem stringValue] copy];
} #catch (NSException * e){
self = nil;
return nil;
}
return self;
}
#end
Later on in another class I do:
- (BOOL) isValidSCORMLesson:(NSString*) path {
NSString *manifPath = [path stringByAppendingPathComponent:#"imsmanifest.xml"];
if (![[NSFileManager defaultManager] fileExistsAtPath: manifPath isDirectory: NO])
return NO;
SimpleManifest *manifest = [[[SimpleManifest alloc] initWithPath:manifPath options:0 error:nil] autorelease];
NSLog(#"%#", manifest.defaultOrganization);
NSLog(#"%#", manifest.title);
return (manifest!=nil);
}
It gives me tons of "pointer being freed was not allocated" errors...
The thing changes if I comment out the NSLog calls above or just log the manifest.title property.
Project is not using ARC, so I'm sure I'm doing something wrong with memory management.
Can someone please help me understand where I'm doing wrong? Thanks!
There isn't anything obviously wrong with that code that would cause malloc errors. Best guess is that there is a bug in the CXMLDocument class/library or some mistake in the way you are using it.
Note that a "pointer being freed was not allocated" means that someone called free() (or dealloc, effectively) on a pointer to a piece of memory that was not allocated in the first place. It usually gives you a breakpoint you can set that will then give you a backtrace of exactly where it happened.
Some comments:
(1) Do not #try/#catch in that fashion. Just don't catch at all. The pattern you are using will hide any errors. Exceptions are not meant to be recoverable in iOS/Cocoa.
(2) You can create an NSString instance directly from a file; no need to load via NSData first.
(3) You should use ARC.

Resources