nsscreencast / comments

Public comments on NSScreencast.com
0 stars 0 forks source link

Importing into Core Data - NSScreencast #59

Open subdigital opened 3 years ago

subdigital commented 3 years ago

Written on 01/24/2015 10:58:37

URL: https://nsscreencast.com/episodes/12-importing-into-core-data

subdigital commented 3 years ago

originally written by Alex Hedley on 04/21/2012 16:11:22

Great tut, was looking at some code on Github and came across this tut on Mogen, thought it might be useful [http://raptureinvenice.com/...]

subdigital commented 3 years ago

originally written by Donwlarson on 05/01/2012 16:23:50

Two requests:

1) How to save and retrieve a .png image from CoreData.
2) How to programmatically delete the old database when the refresh button is pressed as part of the refresh process. This would be required on the actual iOS device, right?

Thank you. :-)

subdigital commented 3 years ago

originally written by Ben Scheirman on 05/01/2012 17:09:13

1) You can add binary attributes to a model.  Here's a stack overflow question which demonstrates this in action:  http://stackoverflow.com/qu...

I would probably lean on saving the image to disk and putting the path to the image in your core data model.  I'm not sure if binary data is retrieved by default or not, but it would be cumbersome to have to exclude this attribute from general fetches.

2) It's actually really easy to delete the old database (if you wanted to) by just removing the sqlite file and re-initializing it.  This isn't really needed on a device, because we are checking the server id before inserting or updating.  But if you didn't have this type of unique key (or if you needed to make sure to delete records that aren't there), then you might have to do this.

subdigital commented 3 years ago

originally written by Martin Juhasz on 05/08/2012 20:11:08

a little request: it would be nice if you add the complete lines of code you write during a show in the show-notes. f.e. i'm missing the shell script for mogenerator here.

subdigital commented 3 years ago

originally written by Marcioluizdesign on 05/10/2012 19:10:00

Hello Ben!! Great Work Mate! I'm building a wine cart for iPad and this just fitted well!!To populate my database I've create an update button and two functions as written bellow:- (IBAction)botaoAtualizacao:(id)sender {        [self updateDataMainMenu:aWLBaseURL];}-(void) updateDataMainMenu:(NSString *)aWLBaseURL{        if (!_hud) {        _hud = [[MBProgressHUD alloc] initWithView:self.view];    }        [_hud setMode:MBProgressHUDModeIndeterminate];    [_hud setLabelText:@"CarregandoMenuPrincipal..."];    [self.view addSubview:_hud];        NSString *stringURL = [aWLBaseURL stringByAppendingString:@"json/titulos/1"];    NSURL *url = [NSURL URLWithString:stringURL];    NSLog(@"%@",url);    NSURLRequest *req = [NSURLRequest requestWithURL:url];    NSLog(@"%@",req);       AFJSONRequestOperation *op;    op = [AFJSONRequestOperation JSONRequestOperationWithRequest:req                                                         success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSONmainMenu) {                                                             [self parseMainMenu:JSONmainMenu];                                                         } failure:^(NSURLRequest *request, NSHTTPURLResponse *response,                                                                     NSError *error, id JSONmainMenu) {                                                             [self displayError];                                                         }];        [_hud show:YES];    [op start];    }- (void)parseMainMenu:(id)menus {    [_hud setMode:MBProgressHUDModeDeterminate];    [_hud setProgress:0];    [_hud setLabelText:@"LoadingData..."];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{        NSInteger totalRecords = [menus count];        NSInteger currentRecord = 0;                NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];        [context setPersistentStoreCoordinator:[[AWLDataModel sharedDataModel] persistentStoreCoordinator]];                for (NSDictionary *dictionary in menus) {            NSInteger codigoCliente = [[dictionary objectForKey:@"CodigoCliente"] intValue];            MenuPrincipal *menuPrincipal = [MenuPrincipal menuPrincipalWithCodigoCliente:codigoCliente usingManagedObjectContext:context];            if (menuPrincipal == nil) {                menuPrincipal = [MenuPrincipal insertInManagedObjectContext:context];            }                        [menuPrincipal updateAttributes:dictionary];                        currentRecord++;                        dispatch_async(dispatch_get_main_queue(), ^{                float percent = ((float)currentRecord)/totalRecords;                [_hud setProgress:percent];            });        }                NSError *error = nil;        if ([context save:&error]) {            dispatch_async(dispatch_get_main_queue(), ^{                [_hud setLabelText:@"Done!"];                [_hud hide:YES afterDelay:2.0];            });        } else {            NSLog(@"ERROR: %@ %@", [error localizedDescription], [error userInfo]);            exit(1);        }    });}The JSON fetched is in the following link: http://www.cntecnologia.com... I'm getting the following error from the objectForKey method:2012-05-10 15:18:10.660 Adega's WineList[213:4c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString objectForKey:]: unrecognized selector sent to instance 0x2e7e00'*** First throw call stack:(0x3597888f 0x3359c259 0x3597ba9b 0x3597a915 0x358d5650 0x11f19 0x33d05c59 0x33d117bb 0x34b91dfb 0x34b91cd0)terminate called throwing an exception2012-05-10 15:18:10.661 Adega's WineList[213:5307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString objectForKey:]: unrecognized selector sent to instance 0x2da3a0'*** First throw call stack:(0x3597888f 0x3359c259 0x3597ba9b 0x3597a915 0x358d5650 0x11599 0x33d05c59 0x33d117bb 0x34b91dfb 0x34b91cd0)terminate called throwing an exceptionCould you help me with that?

subdigital commented 3 years ago

originally written by Ben Scheirman on 05/10/2012 19:16:47

Since the code formatting didn't come through, it's hard to read (maybe post it to gist.github.com?).

The error message is telling me that you're calling objectForKey on what you think is a dictionary, but it's actually a string.

subdigital commented 3 years ago

originally written by Marcioluizdesign on 05/10/2012 20:25:24

Well,Here's the code pasted in pastebin: <iframe src="http://pastebin.com/embed_i..." style="border:none;width:100%"></iframe>The JSON fetched is in the following link: http://www.cntecnologia.com.br...

subdigital commented 3 years ago

originally written by Marcioluizdesign on 05/10/2012 20:26:47

ok, doesn't accpet iframes... here's the link: http://pastebin.com/xCxEsYXw

subdigital commented 3 years ago

originally written by Ben Scheirman on 05/10/2012 20:26:58

The comment system won't allow HTML.  Just paste a link and it will become clickable.

subdigital commented 3 years ago

originally written by Ben Scheirman on 05/10/2012 20:30:52

Can you post the JSON link again?  Looks like the other one got truncated.

subdigital commented 3 years ago

originally written by Ben Scheirman on 05/10/2012 20:46:44

Sorry about that!  I've added the script in the show notes.  I also added some more to Episode 11's notes in the same vein.  Thanks for catching it.

subdigital commented 3 years ago

originally written by Marcioluizdesign on 05/10/2012 20:50:32

http://www.cntecnologia.com...

subdigital commented 3 years ago

originally written by Ben Scheirman on 05/10/2012 20:57:36

The JSON returned in that is an object with a single attribute.

Summarized, it looks like:

{
    "foo":"[1, 2, 3]"
}

While the value may look like JSON, it's actually being returned as a string.

I'd advise to fix this on the server side.  You can handle it on the client side of course, but that's not fixing the problem at the source, and you'll likely have to apply this fix elsewhere if you have more than one client.

If you _must_ parse this without touching the server, then you can do this:

id fooValue = [dictionary objectForKey:@"foo"];
id values = [fooValue JSONValue];

This assumes you're using SBJSON.  If you're using JSONKit, you'll have to look up the syntax, but it is similar.

subdigital commented 3 years ago

originally written by Marcioluizdesign on 05/10/2012 21:07:34

Thanks for that! Well I've talked to my server develper, and he changed the json now it looks better: http://www.cntecnologia.com..., but the way it is isn't working either..

subdigital commented 3 years ago

originally written by Ben Scheirman on 05/10/2012 21:26:28

Well the root element you're treating like an array, but it's an NSDictionary. 

Try logging out the value you get for the "menus" variable.  It should help shed some light on what the structure looks like after it is parsed.

subdigital commented 3 years ago

originally written by Marcioluizdesign on 05/11/2012 17:48:59

Another question, how to fetch the data and show it on a UIViewController instead of a UITableViewController?

subdigital commented 3 years ago

originally written by Eric Baker on 05/25/2012 23:17:35

When you create the 'Run Mogenerator' build phase script, you can drag the build phase to be above 'Compile Sources'. That way the entities can be regenerated before the first compile, saving you from have to compile a second time to get the updates.

subdigital commented 3 years ago

originally written by subdigital on 05/25/2012 23:20:14

Good call, thanks for pointing that out :)

subdigital commented 3 years ago

originally written by Hernandoz on 10/25/2012 14:45:06

Nice tutorial, would be nice if you can MapKit tap on a cell and show the  brewery location on the map.

subdigital commented 3 years ago

originally written by jamra on 02/15/2013 04:16:21

One thing that you passed over here was how to handle the one to many relationship of a Beer to a Brewery.  So far, you download the name of the Brewery, but not the Beer itself.  It seems odd that we would create the to-many relationship and not use it.  Am I missing something?  

subdigital commented 3 years ago

originally written by subdigital on 02/15/2013 18:40:50

What we did was map the array to all of the breweries for a beer, but our property simply grabbed the first one's name and returned it. We created the to-many relationship to map to the response format. We might be able instruct RestKit to flatten this for us, but I didn't see an easy way to do this.
Does this answer your question?

subdigital commented 3 years ago

originally written by jamra on 02/15/2013 19:36:52

So we created the relationship in order to parse the JSON response.  I think I understand.  When fiddling with the parseBeers method, it explains why I am able to get an (NSSet *) of Beers for each Brewery using the mutableSetValueForKey method.  

I was trying to implement a detail page where you can see each beer for a given brewery, but that is probably not within the scope of this video. 

Would you recommend looping through each beer to insert them or is there a way to insert the entire set?
Thanks. 

subdigital commented 3 years ago

originally written by subdigital on 02/16/2013 14:38:04

Sorry for the confusion.  I responded to your comment from my email, and I assumed you were talking about a more recent video (51-53) where I worked with a similar beers API using RestKit.  These videos cover what you are talking about.  Basically we don't download the beers until you tap on a brewery and then those beers are downloaded.

If your API was returning all of the data in one go, then you could loop over the beers as well and import those, however this would increase the import time by a large number.  In that case I might suggest moving the context save operation to happen in batches of 500 records or something, to avoid a gigantic, time-consuming save at the very end.  If you did this, then you could actually start filling up the UI with records as they are created by observing the contextDidSave notification and merging those changes into the main queue's managed object context.

Hope this helps.

subdigital commented 3 years ago

originally written by Kris Utter on 04/04/2013 17:20:47

I don't know if anyone had this problem, but I had to put 'PATH=${PATH}:/usr/local/bin' at the top of the shell script other wise I got a failing error. Great tutorial by the way.

subdigital commented 3 years ago

originally written by Matt on 04/09/2013 15:43:02

Performing a fetch for each record you get from your JSON feed is very expensive. Do you have a more efficient way of doing that? You could load all existing core data records into a dictionary keyed by your server ID and then just do a lookup. This would use more memory, but it would mean you only needed a single fetch.

subdigital commented 3 years ago

originally written by subdigital on 04/09/2013 15:49:20

Yeah that's probably a better idea.

subdigital commented 3 years ago

originally written by Roy Li on 04/25/2013 22:24:26

What if you have multiple users for one app that uses Core Data? Since all the paths these guys are gonna use are the same does that mean we need create multiple stores to distinguish one user from others?

subdigital commented 3 years ago

originally written by subdigital on 04/25/2013 22:26:33

You can solve this a few ways. One is to create a persistent store for that given user. Another way would be to delete the store when the user logs out. Lastly, you could control it by maintaining a user model in your store, and everything would be related to the user that was logged in.

subdigital commented 3 years ago

originally written by Jin on 05/20/2013 21:51:00

Nice Tutorial!!

However, mogenerator script is not working for me.

It keep gives me "/bin/sh failed with exit code 127 mogenerator"

Can you help me with this?

Thank you

subdigital commented 3 years ago

originally written by David Cespedes on 05/24/2013 21:07:00

IF you don't insert the following line

self.serverId = [NSNumber numberWithInt:[[attributes objectForKeyOrNil:@"serverId"] intValue]];

in updateAttributes: method of Brewery.m, it won't recognize the already created breweries and will create them many times.

subdigital commented 3 years ago

originally written by subdigital on 05/24/2013 22:53:11

You're absolutely right. The sample code has this fix, but I forgot to include this in the video.

subdigital commented 3 years ago

originally written by icarod on 06/12/2013 11:10:01

$ sudo ln -s /usr/local/bin/mogenerator /usr/bin/

subdigital commented 3 years ago

originally written by John Grange on 07/12/2013 21:02:28

Sorry maybe I'm wrong but in your screencast you mention that the original name of the model is the new version and the numbered is the old, and that's Xcode auto switches. In my experience and research that is backwards :

1) the numbered version is the new version.
2) you must select the new version in properties or the system will continue to use the existing version.

subdigital commented 3 years ago

originally written by Matt on 09/28/2013 00:35:49

I'm getting the same error as Jin. It says 'Command /bin/sh failed with exit code 127.' Can you help please? Thanks.

subdigital commented 3 years ago

originally written by subdigital on 09/30/2013 03:11:12

That typically fails if the script can't find the mogenerator binary. Make sure the path to your installation of mogenerator is correct.

subdigital commented 3 years ago

originally written by Kennedy on 04/30/2014 12:30:10

Hi. How can I ensure that I do not get duplicated data. Every time I click the refresh button, my core data is populated with duplicate info. :(

subdigital commented 3 years ago

originally written by subdigital on 04/30/2014 14:57:05

There are a few ways do this. The easiest and most inefficient is to try to fetch the entity before inserting. If one exists you just update the properties. In this case the records never change so we only insert if the object doesn’t exist. For this we can make the process more efficient by grabbing the ids of the records that exist locally up front, that way you can do a simple in memory lookup to see if you need to insert or not.

subdigital commented 3 years ago

originally written by Kennedy on 04/30/2014 15:27:54

That's exactly what I would like to do but am still newbie to iOS dev.. Will be nice if you could organize a screencast on that.. Will really appreciate.. Am sure other subscribers will appreciate too. :)

subdigital commented 3 years ago

originally written by subdigital on 04/30/2014 15:29:39

As luck would have it, I have an episode in the works that will sort of address this. It’s a couple weeks out though.

subdigital commented 3 years ago

originally written by Kennedy on 05/01/2014 08:11:23

Thank you.. I am still newbie to ios but I have an app idea and thanks to NSScreencast lessons, am almost half way. :) My app relies on syncing the remote server with core data. Once I achieve that, then I will be the happiest ios newbie ever hehe.

subdigital commented 3 years ago

originally written by Surge Pedroza on 05/19/2014 21:19:18

what is the serverId? i did not see serverid in the json data that you displayed in the beginning. (new to iOS)

subdigital commented 3 years ago

originally written by onmyway133 on 05/24/2014 14:23:48

Sorry, but

1. I think you misunderstand setPropertiesToFetch, see http://stackoverflow.com/qu...
2. In your breweryWithServerId: you have to go to ask the database for every object, is that efficient ?

subdigital commented 3 years ago

originally written by subdigital on 05/24/2014 16:01:03

Thanks, I didn't realize setPropertiesToFetch worked that way!

You're right, this solution isn't the most efficient. Loading all of the entities in memory up front is also probably not a good idea, as you might have thousands of them. A good balance would probably to divide up the work in batches of maybe 50 so you don't have so many SELECTs required.

subdigital commented 3 years ago

originally written by subdigital on 05/24/2014 16:05:00

serverId is the "id" key in the JSON response. It will not be advisable to create a property named "id" because that is also a type.

subdigital commented 3 years ago

originally written by Marios Mourelatos on 07/03/2014 14:09:34

Hey Great Video.
Where is the file breweries.json I am trying to run your sample code and I would like to test it with data.

subdigital commented 3 years ago

originally written by subdigital on 07/03/2014 14:22:54

This is dynamically returned by a Rails server.

--
Ben Scheirman

subdigital commented 3 years ago

originally written by Marios Mourelatos on 07/03/2014 14:24:03

Ok I know that but how can I call that service in order to test the application?

subdigital commented 3 years ago

originally written by subdigital on 07/03/2014 14:39:54

You can use this one I put on heroku: http://beerapi.herokuapp.co...

--
Ben Scheirman

subdigital commented 3 years ago

originally written by Marios Mourelatos on 07/03/2014 14:42:27

Great! Thanks

subdigital commented 3 years ago

originally written by Ashish Porecha on 09/06/2014 10:28:04

This is insane!