Getting Flickr RecentActivity images - ios

I want to retrieve Recent Activity images from flickr application, please any one suggest me how to retrieve that. thanks in advance.

This is not the best way of doing this but I will provide you a way to retrieve recent pictures on flickr. Standford iTunesU had a few lectures that related to this. I am getting almost all of my information from them. Here is a link to the course material:
http://www.stanford.edu/class/cs193p/cgi-bin/drupal/
For the basic, non multithreaded version look under lecture 10 and download Shutterbug Universal. You will also need a flickr API key which you can currently get here:
http://www.flickr.com/services/api/misc.api_keys.html
I will attempt to outline for you though how to go about completing your request, especially since either of those links may not be around for long.
You will want to create a class FlickrFetcher or something, then have a public class method
+ (NSArray *)latestGeoreferencedPhotos;
Implementation
+ (NSArray *)latestGeoreferencedPhotos
{
NSString *request = [NSString stringWithFormat:#"http://api.flickr.com/services/rest/?method=flickr.photos.search&per_page=500&license=1,2,4,7&has_geo=1&extras=original_format,tags,description,geo,date_upload,owner_name,place_url"];
return [[self executeFlickrFetch:request] valueForKeyPath:#"photos.photo"];
}
Where executeFlickrFetch is implemented:
+ (NSDictionary *)executeFlickrFetch:(NSString *)query
{
query = [NSString stringWithFormat:#"%#&format=json&nojsoncallback=1&api_key=%#", query, FlickrAPIKey];
query = [query stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *jsonData = [[NSString stringWithContentsOfURL:[NSURL URLWithString:query] encoding:NSUTF8StringEncoding error:nil] dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *results = jsonData ? [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves error:&error] : nil;
if (error) NSLog(#"[%# %#] JSON error: %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), error.localizedDescription);
return results;
}
You will need to get your API key and either define it (#define FlickrAPIKey #"myAPIkey") or just directly insert it here.
In the Standford course they call latestGeoreferencedPhotos from a TVC subclass and set an array of photos in viewDidLoad:
// photos is a defined property
self.photos = [FlickrFetcher latestGeoreferencedPhotos];
Then in the setter for photos they reload a tableView that presents the images:
- (void)setPhotos:(NSArray *)photos
{
_photos = photos;
[self.tableView reloadData];
}
photos is now an array of dictionaries where so you can access specific image data by doing things like:
return [self.photos[row][#"title"] description]; // description because could be NSNull
I did find a github resource that may be valuable:
https://github.com/lukhnos/objectiveflickr.
I did not look into it much but it is probably worth checking into!
Your question did not provide much detail so I hope that this answer is sufficient for your needs.

Related

Access data stored in AsyncStorage from ios native code (objective c)

I need to access data stored in AsyncStorage from iOS native Objective C code.
This is needed to get the data in sync instead of sending App event to JS and then send it back to native code.
I've just been faced with the same problem.
My solution was to move the code native side.
On iOS:
#import <React/RCTAsyncLocalStorage.h>
#import <React/RCTBridgeModule.h>
RCTResponseSenderBlock completion = ^(NSArray *response) {
NSString *theme = response[1][0][0][1];
// Setup RCTRootView with initialProperties
};
RCTAsyncLocalStorage *storage = [[RCTAsyncLocalStorage alloc] init];
dispatch_async(storage.methodQueue, ^{
[storage performSelector:#selector(multiGet:callback:) withObject:#[#"theme"] withObject:completion];
});
You could additionally use dispatch_semaphore_wait to perform this synchronously
Update:
I needed the variable in the global state not just in the component props so the above doesn't go far enough.
I've had to work this into the React Native source at the point that the Javascript source is loaded.
NSString *javascriptPrepend = [NSString stringWithFormat:#"var ThemeMode = '%#';", self.theme];
NSMutableData *prependData = [[javascriptPrepend dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
[prependData appendData:sourceCode];
sourceCode = prependData;
I'll see if they're open to a PR to allow this kind of functionality and post back here if I get it through.
Enhancing #luke’s solution – fixing an issue with the location of the data in the response; converting the JSON data to NSDictionary and ensuring the safety of the code – here is the complete method:
+(void)jsonFromLocalRNStrogeForKey:(NSString *)key completion:(void (^)(NSDictionary * _Nullable, NSError * _Nullable))completion
{
RCTResponseSenderBlock rnCompletion = ^(NSArray *response) {
NSString *jsonAsString;
if (response.count > 1) {
NSArray *response1 = response[1];
if (response1.count > 0) {
NSArray *response2 = response1[0];
if (response2.count > 1) {
jsonAsString = response2[1];
}
}
}
NSData *jsonAsData = [jsonAsString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:jsonAsData options:NSJSONReadingMutableContainers error:&error];
completion(json, error);
};
RCTAsyncLocalStorage *storage = [RCTAsyncLocalStorage new];
dispatch_async(storage.methodQueue, ^{
[storage performSelector:#selector(multiGet:callback:) withObject:#[key] withObject:rnCompletion];
});
}
My current solution. Which technically isn't a direct answer to the question but does offer a work-around for sharing data between Javascript and native code, is to use NSUserDefaults.
Using this package, react-native-default-preferences to easily persist the data into NSUserdefaults and then on the native side easily retrieve them as normal.
For my purposes I am persisting a token retrieved in Javascript, to be used later by extensions of the app. This means I also persist to a UserDefaults suite using the same key as my app.
React Native (Javascript):
DefaultPreference.setName('group.myApp');
DefaultPreference.set('TOKEN', token);
Objective-C:
NSString *TOKEN = [[[NSUserDefaults alloc] initWithSuiteName:#"group.myApp"] stringForKey:#"TOKEN"];
I think you can use the code from the implementation AsyncStorage for this purpose. You can see it here. It basically loads the files that store the data using a NSFileManager and then parses them accordingly. In your case, have a look for instance at the multiGet method and then trace all the required functions present in the file. With that you should be able to re-implement them (or adapt) to fit your needs.

Extract data from Json into Label Text IOS

So I am using the pokedex API as a learning curve for IOS and web services,
Here is my didrecivedata when the connection completes
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//If the resposne recieved is good the call this function
// NSLog(#"data is %#", data);
//NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//NSLog(#"string is %#", myString);
//Put data into a string
NSError *e = nil;
pokeDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&e];
NSLog(#"dictionary is %#", pokeDictionary);
}
This outputs Json to the console, I can log it into the console like this
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded!");
NSLog(#"The Pokemon's name is %#", pokeDictionary[#"name"]);
NSLog(#"The Pokemon's attack is %#", pokeDictionary[#"attack"]);
NSLog(#"The Pokemon's speed is %#", pokeDictionary[#"speed"]);
}
However tried to extract Json into text fields like this
{
self.pokemonAttack.text = (#"The Pokemon's speed is %#", pokeDictionary[#"name"]);
self.pokemonAttack.text = (#"The Pokemon's speed is %#", pokeDictionary[#"attack"]);
self.pokemonSpeed.text = (#"The Pokemon's speed is %#", pokeDictionary[#"speed"]);
}
Error is "expression result unused", I guess my main issue is I am not comfortable with objective-c and just hacking around in IOS. For this I apologise and understand the comments of saying do objective-c courses
If you can point me in the right direction I can continue my trial by fire, I guess I should also be moving to swift soon
First of all you should know that the delegate method connection:didReceiveData: may be called multiple times as the connection loads the data incrementally. It may be called once if your returned data is very short, but it will most likely break at some point as you'll end up trying to parse incomplete data.
Regarding the warning you're getting - you just can't format strings like that. You're trying to use string formatting like you're calling NSLog, but the NSLog method does the formatting for you. You need to do something like this:
self.pokemonAttack.text = [NSString stringWithFormat:#"The Pokemon's speed is %#", pokeDictionary[#"speed"]];

Json string limit

I am working on an iPhone app which runs remote queries on my db and displays them in a table view. I have a PHP script which fetches the data and encodes it in Json to be downloaded in Xcode. Right now, my app works for smaller queries but but not for larger ones.
When I was researching my issue, I read a few posts about Json issues with the length limit of the content of a string. I am not sure I 100% grasp the concept though... Right now, for small outputs, it displays everything properly, but for large ones it displays an empty table. When I print out all the fields of any one of my element (ex Name, Address, City) those values are always null (for the large queries).
Can anyone explain how the length of my string affects my fields and how would I go about fixing it? I saw some solutions online (such as breaking up the string) but since I do not yet fully understand the problem I didn't want to rush into things. Any explanation would be great.
I have attached the code for convenience (from the tutorial http://codewithchris.com/iphone-app-connect-to-mysql-database/):
#interface HomeModel()
{
NSMutableData *_downloadedData;
}
#end
#implementation HomeModel
- (void)downloadItems
{
// Download the json file
NSURL *jsonFileUrl = [NSURL URLWithString:#"http://localhost/www/service.php"];
// Create the request
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:jsonFileUrl];
// Create the NSURLConnection
[NSURLConnection connectionWithRequest:urlRequest delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// Initialize the data object
_downloadedData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the newly downloaded data
[_downloadedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Create an array to store the locations
NSMutableArray *_locations = [[NSMutableArray alloc] init];
// Parse the JSON that came in
NSError *error;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:_downloadedData options:NSJSONReadingAllowFragments error:&error];
NSLog(#"JSON IS %#.", jsonArray);
// This log prints it out fine
for (int i = 0; i < jsonArray.count; i++){
NSDictionary *jsonElement = jsonArray[i];
Location *newLocation = [[Location alloc] init];
newLocation.name = jsonElement[#"name"];
NSLog(#"PRINTING project_id %#", jsonElement[#"name"]);
//Here the log will print out NULL
...
}
}
}
I was able to fix my problem. It wasn't related to the json, which was properly formatted all along. My issue had to do with how I was storing the fields and printing them out, sorry. Thank you for the help.

Retrieving data from web service and populating SQLite3 database efficiently

I am currently developing an app that that will be used by association members that are going to a large annual conference.
The app will pull data from a database that is created by the app and populate it via a web service. The web service is split into 8 pages (this will likely go up). Each page represents a table in the database. The app will have several table views that will be populated by data in one or more of the tables in the database.
What I need is a the best method for going through the list of tables, connecting to their respective web service pages and then populating the respective database tables. This updating needs to take place in the background so the UI doesn't become unresponsive and/or show a downloading/updating/waiting kind of status.
So far I have a static array of the table names and have a loop that goes through the array and appends a URL string with the names, for example:
-(void)startUpdate
{
NSArray* tableNames = #[#"speaker", #"exhibitor", #"workshop", #"workshopspeakers", #"schedule", #"location", #"feedback", #"note", #"usage", #"user"];
NSUInteger loopCount = tableNames.count;
for (int i = 0; i < loopCount; ++i){
NSString *tableName = [tableNames objectAtIndex:i];
[self fetchObjectsWithTableName:[tableName mutableCopy] completion:^(NSArray* objects, NSError*error){
if (error) {
} else {
}
}];
}
}
fetchObjectsWithTableName method then has the connections and retrieves the data:
-(void)fetchData:(NSString *)tableName
withCompletion:(completion_t)completionHandler
{
NSString *currentURL = [NSString stringWithFormat:#"https://testapi.someURL.com/api/congress/%#", tableName];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:currentURL]];
[request addValue:#"application/json" forHTTPHeaderField:(#"Accept")];
[NSURLConnection sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSError* err = error;
NSArray* objects; // final result array as a representation of JSON Array
if (response) {
NSHTTPURLResponse *newResp = (NSHTTPURLResponse*)response;
if (newResp.statusCode == 200) {
NSLog(#"FetchData - Status code = %li", (long)newResp.statusCode);
if ([data length] >0 && error == nil)
{
NSError* localError;
objects = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (objects) {
if (completionHandler) {
completionHandler(objects, nil);
}
//NSLog(#"Objects in current table - %# = %#", tableName, objects);
[self.tables addObject:objects];
// NSLog(#"Tables now = %#", self.tables);
NSLog(#"FetchData - Objects in current table - %# = %lu", tableName, (unsigned long)objects.count);
return;
} else {
err = localError;
}
} else {
NSLog(#"FetchData - objects is empty");
return;
// err = ...
}
}
NSLog(#"FetchData - Response code not 200#");
}
if (objects == nil) {
NSLog(#"FetchData - Nothing found in table: %#", tableName);
//assert(err);
if (completionHandler) {
completionHandler(nil, err);
}
}
}];
}
This currently goes through the array of table names, makes a connection based on each one and pulls back JSON data and stores it in a temporary array 'objects'. I think what I need now is that in each iteration of this 'objects' array is copied to the relevant table in the database, i.e. 'speaker' table name makes a connection: https://testapi.someURL.com/api/congress/speaker and the JSON is entered into the database under the table 'speaker'. How and where do I do that? Will I need to add a completion handler to startUpdate? If so, how? I don't understand completion handlers despite looking at several examples. Thanks.
No, do it in the NSURLConnection completion block after you have updated your temporary storage.
But, change your approach overall.
If you're only willing to change a bit, start using NSOperationQueue to limit the number of connections that you're trying to make at the same time. Preferably also use Core Data.
If you're willing to make a bigger change, definitely move to Core Data and look at using a framework like RestKit to do all of the download, mapping and storage for you.
(note, in both cases you need to set the max concurrent operation limit to prevent the app from flooding the network with requests - a limit of 5 should be good).

Any way to pre populate core data?

I've been creating a list app and backing it with core data.
I would like to have a default list of say 10 airport's items, so that the user doesn't have to start from scratch.
Is there any way to do this?
Any help is appreciated.
Thanks in advance.
Here's the best way (and doesn't require SQL knowledge):
Create a quick Core Data iPhone app (Or even Mac app) using the same object model as your List app. Write a few lines of code to save the default managed objects you want to the store. Then, run that app in the simulator. Now, go to ~/Library/Application Support/iPhone Simulator/User/Applications. Find your application among the GUIDs, then just copy the sqlite store out into your List app's project folder.
Then, load that store like they do in the CoreDataBooks example.
Yes there is in fact the CoreDataBooks example does this, you can download the code here: sample code
What you do is create the internal store (database) using the normal procedure to initialize your store just like you would with any other store, then you simply run your code and let it execute the code as described in the CoreDataBooks example (code snippet below). Once the store has been initialized you will want to create a NSManagedObjectContext and initialize it with the created persistent store, insert all the entities you need, and save the context.
Once the context has been successfully saved, you can stop your application, then go to finder and go to folder: ~/Library/Developer type in the search .sqlite and look under /Developer, sorting by date will give you the most recent .sqlite database which should match the time that the code was executed, you can then take this store and add it as a resource of your project. This file then can be read by a persistent store coordinator.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator) {
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"CoreDataBooks.sqlite"];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"CoreDataBooks" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSError *error;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
return persistentStoreCoordinator;
}
Hope that helps.
-Oscar
With this method you don't need to make a separate app or have any SQL knowledge. You only need to be able to make a JSON file for your initial data.
I use a JSON file that I parse into objects, then insert them in Core Data. I do this when the app initializes. I also make one entity in my core data that indicates if this initial data is already inserted, after I insert the initial data I set this entity so the next time the script runs it sees that the initial data has already been initialized.
To read json file into objects:
NSString *initialDataFile = [[NSBundle mainBundle] pathForResource:#"InitialData" ofType:#"json"];
NSError *readJsonError = nil;
NSArray *initialData = [NSJSONSerialization
JSONObjectWithData:[NSData dataWithContentsOfFile:initialDataFile]
options:kNilOptions
error:&readJsonError];
if(!initialData) {
NSLog(#"Could not read JSON file: %#", readJsonError);
abort();
}
Then you can make entity objects for it like this:
[initialData enumerateObjectsUsingBlock:^(id objData, NSUInteger idx, BOOL *stop) {
MyEntityObject *obj = [NSEntityDescription
insertNewObjectForEntityForName:#"MyEntity"
inManagedObjectContext:dataController.managedObjectContext];
obj.name = [objData objectForKey:#"name"];
obj.description = [objData objectForKey:#"description"];
// then insert 'obj' into Core Data
}];
If you want a more detailed description on how to do this, check out this tutorial:
http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated
For 10 items, you can just do this within applicationDidFinishLaunching: in your app delegate.
Define a method, say insertPredefinedObjects, that creates and populates the instances of the entity in charge of managing your airport items, and save your context. You may either read the attributes from a file or simply hardwire them in your code. Then, call this method inside applicationDidFinishLaunching:.
Bear in mind, when following the CoreDataBooks example code, that it probably breaks the iOS Data Storage Guidelines:
https://developer.apple.com/icloud/documentation/data-storage/
I've had an app rejected for copying the (read-only) pre-populated database to the documents directory - as it then gets backed up to iCloud - and Apple only want that to happen to user-generated files.
The guidelines above offer some solutions, but they mostly boil down to:
store the DB in the caches directory, and gracefully handle situations where the OS purges the caches - you will have to rebuild the DB, which probably rules it out for most of us.
set a 'do not cache attribute' on the DB file, which is a little arcane, as it needs to be done differently for different OS versions.
I don't think it is too tricky, but be aware that you have a bit extra to do to make that example code work alongside iCloud...
This answer is only for people who are
including a pre-populated database in your app
making an app for multiple platforms (iOS, Android, etc.)
I had made a prepopulated SQLite database for an Android app. Then when I was making an iOS version of the app I thought it would be best to use Core Data. So I spent quite a long time learning Core Data and then rewriting the code to prepopulate the database. Learning how to do every single step in both platforms required lots of research and trial and error. There was a lot less overlap than I would have hoped.
In the end I just decided to use the same SQLite database from my Android project. Then I used the FMDB wrapper to directly access the database in iOS. The benefits:
Only need to make the prepopulated database once.
Doesn't require a paradigm shift. The syntax between Android and FMDB, while different, is still fairly similar.
Have a lot more control over how Queries are performed.
Allows full text search.
Although I don't regret learning Core Data, if I were to do it over I could have saved a lot of time by just sticking to SQLite.
If you are starting in iOS and then planning to move to Android, I would still use a SQLite wrapper like FMDB or some other software to prepopulate the database. Although you can technically extract the SQLite database that you prepopulate with Core Data, the schema (table and column names, etc.) will be strangely named.
By the way, if you don't need to modify your prepopulated database, then don't copy it to the documents directory after the app is installed. Just access it directly from the bundle.
// get url reference to databaseName.sqlite in the bundle
let databaseURL: NSURL = NSBundle.mainBundle().URLForResource("databaseName", withExtension: "sqlite")!
// convert the url to a path so that FMDB can use it
let database = FMDatabase(path: databaseURL.path)
This makes it so that you don't have two copies.
Update
I now use SQLite.swift rather than FMDB, because it integrates better with Swift projects.
This worked for me. This is a modification of this answer by Andrea Toso and inspired by this blog. The only issue with the answer is that there is a chance of data loss when moving sqlite files with FileManager. I saved around 500 rows of data by using replacePersistentStore instead of FileManager.default.copyItem
Step 1
Populate your Core Data in another app and get files' path using this code:
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
print(documentsDirectory)
Step2
Drag your 3 files with .sqlite extension into your xCode project. (Be sure to select Add to targets option).
Step3
Create function to check app's first run in AppDelegate.swift
func isFirstLaunch() -> Bool {
let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag"
let isFirstLaunch = !UserDefaults.standard.bool(forKey: hasBeenLaunchedBeforeFlag)
if (isFirstLaunch) {
UserDefaults.standard.set(true, forKey: hasBeenLaunchedBeforeFlag)
UserDefaults.standard.synchronize()
}
return isFirstLaunch
}
Step4
Copy this function in AppDelegate.swift to get url where sqlite database should be moved:
func getDocumentsDirectory()-> URL {
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
Step 5
Replace declaration of persistentContainer with this one:
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "ProjectName")
let storeUrl = self.getDocumentsDirectory().appendingPathComponent("FileName.sqlite")
if isFirstLaunch() {
let seededDataUrl = Bundle.main.url(forResource: "FileName", withExtension: "sqlite")
try! container.persistentStoreCoordinator.replacePersistentStore(at: storeUrl, destinationOptions: nil, withPersistentStoreFrom: seededDataUrl!, sourceOptions: nil, ofType: NSSQLiteStoreType)
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
So I have developed a generic method that loads from a dictionary (possibly from JSON) and populates the database.
It should be used ONLY with trusted data (from a safe channel), it can't handle circular references and schema migrations can be problematic... But for simple use cases like mine it should be fine
Here it goes
- (void)populateDBWithDict:(NSDictionary*)dict
withContext:(NSManagedObjectContext*)context
{
for (NSString* entitieName in dict) {
for (NSDictionary* objDict in dict[entitieName]) {
NSManagedObject* obj = [NSEntityDescription insertNewObjectForEntityForName:entitieName inManagedObjectContext:context];
for (NSString* fieldName in objDict) {
NSString* attName, *relatedClass, *relatedClassKey;
if ([fieldName rangeOfString:#">"].location == NSNotFound) {
//Normal attribute
attName = fieldName; relatedClass=nil; relatedClassKey=nil;
} else {
NSArray* strComponents = [fieldName componentsSeparatedByString:#">"];
attName = (NSString*)strComponents[0];
relatedClass = (NSString*)strComponents[1];
relatedClassKey = (NSString*)strComponents[2];
}
SEL selector = NSSelectorFromString([NSString stringWithFormat:#"set%#:", attName ]);
NSMethodSignature* signature = [obj methodSignatureForSelector:selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:obj];
[invocation setSelector:selector];
//Lets set the argument
if (relatedClass) {
//It is a relationship
//Fetch the object
NSFetchRequest* query = [NSFetchRequest fetchRequestWithEntityName:relatedClass];
query.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:relatedClassKey ascending:YES]];
query.predicate = [NSPredicate predicateWithFormat:#"%K = %#", relatedClassKey, objDict[fieldName]];
NSError* error = nil;
NSArray* matches = [context executeFetchRequest:query error:&error];
if ([matches count] == 1) {
NSManagedObject* relatedObject = [matches lastObject];
[invocation setArgument:&relatedObject atIndex:2];
} else {
NSLog(#"Error! %# = %# (count: %d)", relatedClassKey,objDict[fieldName],[matches count]);
}
} else if ([objDict[fieldName] isKindOfClass:[NSString class]]) {
//It is NSString
NSString* argument = objDict[fieldName];
[invocation setArgument:&argument atIndex:2];
} else if ([objDict[fieldName] isKindOfClass:[NSNumber class]]) {
//It is NSNumber, get the type
NSNumber* argument = objDict[fieldName];
[invocation setArgument:&argument atIndex:2];
}
[invocation invoke];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"%#",[error description]);
}
}
}
}
And loads from json...
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"initialDB" ofType:#"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSError* error;
NSDictionary *initialDBDict = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers error:&error];
[ self populateDBWithDict:initialDBDict withContext: [self managedObjectContext]];
JSON examples
{
"EntitieA": [ {"Att1": 1 }, {"Att1": 2} ],
"EntitieB": [ {"Easy":"AS ABC", "Aref>EntitieA>Att1": 1} ]
}
and
{
"Country": [{"Code": 55, "Name": "Brasil","Acronym": "BR"}],
"Region": [{"Country>Country>code": 55, "Code": 11, "Name": "Sao Paulo"},
{"Country>Country>code": 55, "Code": 31, "Name": "Belo Horizonte"}]
}
How about check if any objects exist and if not, create one with some data?
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Settings"];
_managedObjectSettings = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
if ([_managedObjectSettings count] == 0) {
// first time, create some defaults
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Settings" inManagedObjectContext:managedObjectContext];
[newDevice setValue:[NSNumber numberWithBool: YES ] forKey:#"speed"];
[newDevice setValue:[NSNumber numberWithBool: YES ] forKey:#"sound"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey:#"aspect"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey: #"useH264"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey: #"useThumbnail"];
NSError *error = nil;
// Save the object to persistent store
if (![managedObjectContext save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
Another method for storing defaults is found by way of NSUserDefaults. (surprise!)
And its easy.
Suggested by some, put that into the applicationDidFinishLaunching
In the given case of 10 defaults, Airport0 thru 9
Setting
NSUserDefaults *nud = [NSUserDefaults standardUserDefaults];
[nud setString:#"MACADDRESSORWHY" forKey:#"Airport0"];
...
[nud setString:#"MACADDRESSORWHY" forKey:#"Airport9"];
[nud synchronize];
or
[[NSUserDefaults standardUserDefaults] setString:#"MACADDRESSORWHY" forKey:#"Airport9"]];
...
[[NSUserDefaults standardUserDefaults] synchronize];
And then, getting the defaults.
NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:#"Airport0"];
As most answers are quite old, I recommend the following tutorial. It explains how it can be done.
https://www.youtube.com/watch?v=xcV8Ow9nWFo

Resources