Storing data from ObjectiveFlickr in a Singleton on iPhone - iphone

The goal is to have a singleton data controller class called FetchData.h/.m that pulls data using ObjectiveFlickr ( https://github.com/lukhnos/objectiveflickr ).
FetchData.m grabs the data with this:
OFFlickrAPIContext *context = [[OFFlickrAPIContext alloc] initWithAPIKey:YOUR_KEY sharedSecret:YOUR_SHARED_SECRET];
OFFlickrAPIRequest *request = [[OFFlickrAPIRequest alloc] initWithAPIContext:context];
// set the delegate, here we assume it's the controller that's creating the request object
[request setDelegate:self];
[request callAPIMethodWithGET:#"flickr.photos.getRecent" arguments:[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"per_page", nil]]
and then implement the following delegate:
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary;
Currently I have this code to save the NSDictionary as a property list to a file as an alternative to a singleton:
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse: (NSDictionary *)inResponseDictionary{
if([inResponseDictionary writeToFile:#"inResponseDictionary.xml" atomically:TRUE])
{
NSLog(#"%#", [[NSFileManager defaultManager] fileExistsAtPath:#"inResponseDictionary.xml"]);
}
}
When I read this file back, I get Null. The file is read back as such:
NSDictionary *inResponseDictionary = [NSDictionary dictionaryWithContentsOfFile:#"inResponseDictionary.xml"];
NSDictionary *photoDict = [[inResponseDictionary valueForKeyPath:#"photos.photo"] objectAtIndex:0];
NSLog(#"%#", [photoDict count]);
Is there a better way to store this data from ObjectiveFlickr so that it can be accessed by other classes and view controllers? Or is there a better way to implement this in the View Controller.

What is in the returned NSDictionary? Are you sure they are all valid plist objects? The photo data might need to be modified (say, base 64 encoded into an array) before your write will work.
The docs for NSDictionary writeToFile: say
This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
As for the singleton aspect - will you be making more than one of these calls at a time? Is there a need to persist the data? If no & no, just keep the dictionary in memory. If you have multiple calls happening at once, you'll need another layer of abstraction (some indexing) to put each call's results in it's own unique location. And that's not happening with your current implementation.

Related

memory management in class method in iOS

I have problems with memory in my class methods.
I have created a class method which will fetch records from core data and return a NSArray.
These are the problem i face:
sometimes the data is returned properly, it works fine.
sometimes it returns a CFArray
a.how to deal with with type of array??
b.what does this type of array mean??
sometimes the array becomes a invalid object when returned to the class which called the method
But in all the ways NSArray inside the method has data. Why does it react in different ways every time? Is there any way to manage this problem?
Code snippet used:
+(NSArray *)retrieveEvents
{
NSArray *arrData;
NSError *error;
NSFetchRequest *fetch = [APPDEL.managedObjectModel fetchRequestTemplateForName:#"fetchEvents"];
arrData = [NSArray arrayWithArray:[APPDEL.managedObjectContext executeFetchRequest:fetch error:&error]];
return arrData;
}

having an NSDictionary that is always stored in an app

I would like to have a "permanent" NSDictionary in my app, in which from the beginning of the app launch, I can get access to the elements and even after I kill the app and start it again.
This NSDictionary needs to be stored tightly with the app. One way would be to just to create the NSDictionary from a Web Service data every time the app launches, then create a singleton class that would represent this NSDictionary, but I don't think this is good.
The NSDictionary will approximately hold 10-20 objects, where the key would be a NSString or NSDate and the value would be a NSArray. The NSArray would have a maximum of approximately 50 entries in it (on average probably there will only be 5-25 entries).
I am planning to use this NSDictionary as a part of a calculation that I am doing inside the delegate locationManager:didUpdateToLocation:fromLocation: each time a user moves every 50-100 meters.
Suggestions are appreciated on the best way I could do this.
Just having the NSDictionary in a singleton will not keep it between app launches, you'll need to save it to disk, and then read it from the disk when the app starts.
If you have no custom objects (subclasses you've created) in the NSDictionary, or in the NSArrays or non at all, you can use this method to save the NSDictionary is:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
and to open the dictionary from disk:
- (id)initWithContentsOfFile:(NSString *)path
However if you do have custom objects they will need to conform to the NSCoding protocol. And you'll have to use 2 different methods to save and open it:
NSCoding is a protocol, so in your header you need to add it on the end of the interface line:
#interface myClassName : NSObject <NSCoding> {
(where the only thing you should add is <NSCoding>)
Then in your implementation of your subclass, you need to add the following methods:
- (id)initWithCoder:(NSCoder *)decoder
and:
- (void)encodeWithCoder:(NSCoder *)encoder
The initWithCoder: method gets called when you want to unarchive(/open) your NSDictionary (which somewhere contains this class)
encodeWithCoder: is what gets called when your NSDictionary is archived(/saved).
You don't call either of these yourself. You need to add code in them:
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super initWithCoder:decoder])) {
aProperty = [[decoder decodeObjectForKey:#"aProperty"] retain];
anotherProperty = [[decoder decodeObjectForKey:#"anotherProperty"] retain];
aFloat = [decoder decodeFloatForKey:#"aFloat"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[super encodeWithCoder:encoder];
[encoder encodeObject:aProperty forKey:#"aProperty"];
[encoder encodeObject:anotherProperty forKey:#"anotherProperty"];
[encoder encodeFloat:aFloat forKey:#"aFloat"];
}
You need to have similar lines for each value you want to store (generally all the properties, [and instance variables] your class has). Note the how the float line is different to the others.
The keys can be any string you want, as long each property has it's own unique key and that they match between the two methods. I personally use the name of the property as it's just easier to understand.
when you actually want to save your NSDictionary you use:
[NSKeyedArchiver archiveRootObject:myDictionary toFile:pathToMyDictionary];
and to open the dictionary:
NSDictionary *myDictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:pathToMyDictionary];
(depending on your code you may need to retain myDictionary)
To get the path to your dictionary (for both saving and opening) do:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pathToMyDictionary = [documentsDirectory stringByAppendingPathComponent:#"myDictionary.dat"];
Hope that helps, if you have any more questions about this answer, just comment :)
It sounds like you want NSUserDefaults. That stores app preferences but can be used other bits of data as well. It is essentially a dedicated, singleton dictionary that is universally accessible and is automatically saved.
You can easily generate an NSDictionary from a plist file using [NSDictionary dictionaryWithContentsOfFile:foo]
If appropriate, the plist could be included in your app bundle, or you could grab the data from a web service on first launch and write it out to a plist that the dictionary is loaded from in the future.

convert Core Data NSManagedObject in to JSON on iPhone?

I was to post some of my COre Data objects back to a web service and would like to send them as JSON. I am receiving objects from the server a JSON using this library:
http://code.google.com/p/json-framework/
But I cannot figure out how to change my objects back to JSON?
To create json from you r objects, you have to build an NSDictionary from your object, and then convert to string with the SBJsonWriter class.
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObject:(NSArray *)YourArrayOfElements forKey:#"objects"];
SBJsonWriter *jsonWriter = [SBJsonWriter new];
//Just for error tracing
jsonWriter.humanReadable = YES;
NSString *json = [jsonWriter stringWithObject:jsonDictionary];
if (!json){
NSLog(#"-JSONRepresentation failed. Error trace is: %#", [jsonWriter errorTrace]);
}
[jsonWriter release];
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
And then you can set as your post request's body.
If you would like a more full-featured solution that what is offered by a standalone parsing library, you may want to take a look at RestKit: http://restkit.org/
The framework wraps the operations of fetching, parsing, and mapping JSON payloads into objects. It also allows you to update remote representations by POST/PUT'ing the objects back with a request. By default, outbound requests are form-encoded but the library ships with a class for using JSON as the wire format for posting back to the server.
At a high level, here's what your fetch & post operations would feel like in RestKit:
- (void)loadObjects {
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[#"/path/to/stuff.json" delegate:self];
}
- (void)objectLoader:(RKObjectLoader*)loader didLoadObjects:(NSArray*)objects {
NSLog(#"These are my JSON decoded, mapped objects: %#", objects);
// Mutate and PUT the changes back to the server
MyObject* anObject = [objects objectAtIndex:0];
anObject.name = #"This is the new name!";
[[RKObjectManager sharedManager] putObject:anObject delegate:self];
}
The framework takes care of the JSON parsing/encoding on a background thread and let's you declare how attributes in the JSON map to properties on your object. Mapping to Core Data backed classes is fully supported.

How do I archive an NSArray of NSDictionary (with NSCoding)?

Suppose I am holding data in an array like this
wordList = [[NSMutableArray alloc] init];
while ([rs next]) //Some database return loop
{
wordDict = [[NSMutableDictionary alloc] init];
[wordDict setObject:[NSNumber numberWithInt:[rs intForColumn:#"id"]] forKey:#"id"];
[wordDict setObject:[rs stringForColumn:#"word"] forKey:#"word"];
[wordList addObject: wordDict];
[wordDict release];
wordDict = nil;
}
But I want to store this result (i.e. wordList) in SQLite for later use - I guess using NSCoding. How would I do that?
(Feel free to point out any errors in how stuff is being alloc'ed if there are problems there).
If you don’t insist on serialization using NSCoding, there’s a writeToFile:atomically: method both on NSArray and NSDictionary. This will serialize your object into a property list (*.plist). The only catch is that all the objects in the “tree” to be serialized must be NSString, NSData, NSArray, or NSDictionary (see the documentation). I’m not sure how NSNumber fits in, but with a bit of luck it will be serialized and deserialized too. The inverse method that will turn the file back into a dictionary or an array is called initWithContentsOfFile:.
As for your code, I would just use the [NSMutableDictionary dictionary] convenience method that gets you an autoreleased dictionary. It’s shorter than the usual alloc & init and you save one line for the explicit release.

Why am I having trouble with a deep copy in Objective C?

I'm assuming my understanding of how to perform a deep copy isn't just there yet. The same with some sub-optimal memory handling that I'm performing down below. This code below probably depicts a shallow copy, and I believe that's where my problem might be. I have some cookie-cutter code for an example that looks like the following:
NSArray *user = [[xmlParser createArrayWithDictionaries:dataAsXML
withXPath:kUserXPath] retain];
if([user count] > 0) {
self.name = [[user valueForKey:#"name"] copy];
}
// Crash happens if I leave the next line un-commented.
// But then we have a memory leak.
[user release];
[xmlParser release];
Unfortunately when I comment out [user release], the code works, but we have an obvious memory leak. The method createArrayWithDictionaries:withXPath: was refactored last night when the SO community helped me understand better memory management. Here's what it looks like:
- (NSArray *)createArrayWithDictionaries:(NSString *)xmlDocument
withXPath:(NSString *)XPathStr {
NSError *theError = nil;
NSMutableArray *dictionaries = [NSMutableArray array];
CXMLDocument *theXMLDocument = [CXMLDocument alloc];
theXMLDocument = [theXMLDocument initWithXMLString:xmlDocument
options:0
error:&theError];
NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError];
for (CXMLElement *xmlElement in nodes) {
NSArray *attributes = [xmlElement attributes];
NSMutableDictionary *attributeDictionary;
attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue]
forKey:[attribute name]];
}
[dictionaries addObject:attributeDictionary];
}
[theXMLDocument release];
return dictionaries;
}
I'm guessing there's a couple of issues that might be going on here:
Auto release on my dictionaries array is happening, thus my app crashing.
I'm not performing a deep copy, only a shallow copy. Thus when the user array is released, self.name is done for.
With NSZombieEnabled, I see the following:
*** -[CFString respondsToSelector:]:
message sent to deallocated instance 0x1ae9a0
Also, the final call where the backtrace shows this is crashing contains the following code in a separate module from the other two methods:
User *u = self.user;
NSString *uri = [NSString stringWithFormat:#"%#/user/%#/%#",
[self groupName], u.userId, kLocationsUri];
Between all the auto releasing/copies/retain happening between the client code and createArrayWithDictionaries:withXPath, I'm a bit confused as to the real problem here. Thanks again for helping me understand.
OK, you don't need to retain the return value from createArrayWithDictionaries: since you're not keeping it around. The return value is autoreleased. I'd strongly recommend reading up on how autoreleasing works. You only retain things that you intend to keep around in your object.
Also, user is an NSArray. If you call [user valueForKey:#"name"], you'll get another NSArray of values representing the values of the name key for each of the objects in users. Furthermore, how is the name property on your object defined? If you declared it as copy or retain (I believe retain is the default if you don't specify it yourself), you don't need to copy or retain the value. Indeed, the accessor should always be responsible for doing the memory management, not the caller. If you wrote your own accessor (i.e. you didn't use the #synthesize keyword), you need to make sure you do the memory management there.
I'm guessing what you meant to write was something more like this:
NSArray *user = [xmlParser createArrayWithDictionaries:dataAsXML withXPath:kUserXPath];
if ([user count] > 0)
self.name = [[user objectAtIndex:0] objectForKey:#"name"];
[xmlParser release];
I think your troubles are stemming from a misunderstanding of how memory management works in Objective-C.
Hope this helps.
Auto release on my dictionaries array is happening, thus my app crashing.
If the caller intends to keep the array around somewhere, it needs to retain it. Otherwise, it will crash when it tries to access the (now-deceased) object.
If the caller is going to store it in a property, it must use the self.dictionaries = […] syntax, not dictionaries = […]. The former is a property access, which calls the setter method; the latter is a direct instance variable assignment.
Coming back to your actual question, that of a deep copy: You need to get the sub-elements of every element and put them in each element's dictionary.
Basically, you need a recursive method (or a queue, but that's harder—file under premature optimization until you've proven you need it) that takes an element and returns a dictionary, and then you need to call this method on each of your element's child elements, and collect the results into an array and put that into the dictionary you're creating.
I would recommend making this recursive method an instance method of the element. Something like:
- (NSDictionary *) dictionaryRepresentation {
NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue] forKey:[attribute name]];
}
NSArray *childElements = [self childElements];
return [NSDictionary dictionaryWithObjectsAndKeys:
attributeDictionary, #"attributes",
[childElements valueForKey:#"dictionaryRepresentation"], #"childElements",
nil];
}
Then you replace the loop in createArrayWithDictionaries:withXPath: with a similar valueForKey: message. I'll leave you to fill it in.
valueForKey: is Key-Value Coding's principal method. In both places, we're making use of NSArray's handy implementation of it.
(If the use of valueForKey: still doesn't make sense to you, you should read the KVC Programming Guide. KVC is vitally important in modern Cocoa, so you do need to read this sooner or later.)