save records from WCF service in CoreData - objective-c

I am new to iOS development and CoreData too. I am calling a .Net WCF service for displaying data in a UITableViewcontroller in my app.I am saving this data in CoreData. When I add a new record on the server,I want it to get displayed in the UITableViewController as well as saved in CoreData.But this doesnt happen.I have to do a "Reset Contents and Settings" on the Simulator and then run the application again. When I do this,the app displays the latest records from the service.It also saves the new record in CoreData.I am using SUDZC for interacting with the wcf service.The code for calling the service,displaying data in UITableViewController and saving it to CoreData looks like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
[my_table setDataSource:self];
[my_table setDelegate:self];
EDViPadDocSyncService *service = [[EDViPadDocSyncService alloc]init];
[service getAllCategories:self action:#selector(handleGetAllCategories:)];
}
-(void)handleGetAllCategories:(id)value
{
if([value isKindOfClass:[NSError class]])
{
NSLog(#"This is an error %#",value);
return;
}
if([value isKindOfClass:[SoapFault class]])
{
NSLog(#"this is a soap fault %#",value);
return;
}
NSMutableArray *result = (NSMutableArray*)value;
self.myData = [[NSMutableArray array] init];//array for storing 'category name'
self.catId = [[NSMutableArray array]init];//array for storing 'category ID'
self.myData=[self getCategories];
/*store data in Core Data - START*/
NSMutableArray *coreDataCategoryarray = [[NSMutableArray alloc]init];
NSManagedObjectContext *context = [self managedObjectContext];
Categories *newCategory;//this is the CoreData 'Category' object
for(int j=0;j<[result count];j++)
{
EDVCategory *edvCat = [[EDVCategory alloc]init];//this is the SUDZC 'Category' object
edvCat = [result objectAtIndex:j];
if ([self.catId count]>0) {
for (int i=0; i<[self.catId count]; i++) {
if ([edvCat categoryId] == [[self.catId objectAtIndex:i] integerValue]) {
checkFlag=TRUE;
}
}
}
if (checkFlag == FALSE) {
newCategory = [NSEntityDescription insertNewObjectForEntityForName:#"Categories" inManagedObjectContext:context];
[newCategory setCategoryId:[NSNumber numberWithInt:[edvCat categoryId]]];
[newCategory setCategoryName:edvCat.categoryName];
[newCategory setDocCount:[NSNumber numberWithInt:[edvCat docCount]]];
[newCategory setCategoryType:[NSNumber numberWithShort:[edvCat categoryType]]];
[newCategory setSubCategoryId:[NSNumber numberWithInt:[edvCat subCategoryId]]];
[coreDataCategoryarray addObject:newCategory];
}
}
/*store data in Core Data - END*/
NSError *error = nil;
if (![context save:&error])
{
[coreDataCategoryarray release];
}
else
{
//return [coreDataCategoryarray autorelease];
[coreDataCategoryarray autorelease];
}
self.myData=[self getCategories];
[my_table reloadData];
}
-(NSMutableArray *)getCategories
{
NSFetchRequest *request = [[[NSFetchRequest alloc] init]autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Categories" inManagedObjectContext:__managedObjectContext];
NSSortDescriptor *sortByName = [[[NSSortDescriptor alloc] initWithKey:#"categoryId" ascending:YES] autorelease];
[request setSortDescriptors:[NSArray arrayWithObject:sortByName]];
[request setEntity:entity];
entity = nil;
NSError *error = nil;
NSMutableArray *fetchResults = [[__managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
[request setReturnsObjectsAsFaults:NO];
NSManagedObject *aTabrss;
NSMutableArray *arForGetCategory=[[NSMutableArray alloc]init];
for (aTabrss in fetchResults){
[arForGetCategory addObject:[aTabrss valueForKey:#"categoryName"]];
[self.catId addObject:[aTabrss valueForKey:#"categoryId"]];
}
return (arForGetCategory);
}
What changes should I make in my code so that it reflects the latest data from the service and saves it to CoreData(sqlite) at the same time?

It seems like the class that you really need is NSUserDefaults:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsuserdefaults_Class/Reference/Reference.html

Related

NRGridView won't show data from my core data database

I am building a app for a local football club. I want to show all players names and pictures in a grid. Therefore I am using the NRGridview. But it won't load up with my data. I have an NSArray with all players information. Here you see the method which generates this array.
- (NSArray *)getTeam
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Team"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *mutableFetchResults = [self.genkDatabase.managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"first error log %#", [error localizedDescription]);
if (mutableFetchResults == nil) {
NSLog(#"second error log %#", [error localizedDescription]);
}else if ([mutableFetchResults count] == 0){
NSLog(#"geen resultaten voor team");
}else{
NSLog(#"team names: %#",[mutableFetchResults valueForKey:#"name"]);
return mutableFetchResults;
}
return mutableFetchResults;
}
And this is what I do in the tableview.
- (NRGridViewCell*)gridView:(NRGridView *)gridView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyCellIdentifier = #"MyCellIdentifier";
NRGridViewCell* cell = [gridView dequeueReusableCellWithIdentifier:MyCellIdentifier];
if(cell == nil){
cell = [[NRGridViewCell alloc] initWithReuseIdentifier:MyCellIdentifier];
[[cell textLabel] setFont:[UIFont boldSystemFontOfSize:11.]];
[[cell detailedTextLabel] setFont:[UIFont systemFontOfSize:11.]];
}
NSLog(#"players array %#",players);
for (int i = 0; i <= [players count]; i++) {
// NSData *imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[[players objectAtIndex:i]valueForKey:#"image"]]];
// UIImage *image = [[UIImage alloc]initWithData:imgData];
//cell.imageView.image = image;
cell.textLabel.text = [[players objectAtIndex:i]valueForKey:#"name"];
cell.detailedTextLabel.text = [[players objectAtIndex:i]valueForKey:#"position"];
return cell;
}
return cell;
}
The NSLog gives always (null). My question is now, where should I put the code "NSArray *players = [self getTeam] . so that my tableview will fill up with data?
EDIT
It did give me back the right amount of sections, and numberOfRowsInsection. For numbersOfRowsIn section I created 4 methods. 1 method whichs gets all off the goalkeepers, 1 for the defenders, 1 for the wingers, and 1 for the attackers. Then In my tableview method I did the following.
- (NSInteger)gridView:(NRGridView *)gridView numberOfItemsInSection:(NSInteger)section
{
if(section == 0){
return [[self getDoelmannen]count];
}else if (section == 1){
return [[self getVerdedigers]count];
}else if (section == 2){
return [[self getMiddenvelders]count];
}else{
return [[self getAanvallers]count];
}
return [[self getAanvallers]count];
}
This works. But still have the problem for my cell self.
EDIT2
Okay I think my problem is with filling my players Array up. I do the following in my viewDidLoad
-(void)viewDidLoad{
_players = [self getTeam];
NSLog(#"players array: %#",_players);
}
Which gives the following log.
2012-10-17 12:11:22.099 RacingGenk[63122:c07] nil
2012-10-17 12:11:22.099 RacingGenk[63122:c07] players array: (null)
Here is my code for getTeam
- (NSArray *)getTeam
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Team"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *mutableFetchResults = [self.genkDatabase.managedObjectContext executeFetchRequest:request error:&error];
if (mutableFetchResults == nil) {
NSLog(#"nil");
}else if ([mutableFetchResults count] == 0){
NSLog(#"geen resultaten voor team");
}else{
NSLog(#"team names: %#",[mutableFetchResults valueForKey:#"name"]);
return mutableFetchResults;
}
return mutableFetchResults;
}
It looks like players isn't getting initialized. You can put your [self getTeam] call in the viewDidLoad method and make players a property.
If NRGridView is anything like UITableView, there are probably other methods that you need to overload.
For example, UITableView has tableView:numberOrRowsInSection:. Failure to return > 0 value from this method results in nothing being shown. Or numberOfSectionsInTableView:, which returns the number of sections and so on.
Check the documentation for the control you're using.
Update:
Since your executeFetchRequest:error: is failing, you should check if there's an error message instead of just printing out (nil):
NSLog(#"%#", [error localizedDescription]);

assign value from NSDictionary to NSManagedObject

My app requires to get data from a .Net WCF service when the device is connected to WiFi.If there's a new row added on the server,it should add it to its CoreData database. I am using a NSDictionary for comparing the local objects with the remote objects. The code is:
-(void)handleGetAllCategories:(id)value
{
if([value isKindOfClass:[NSError class]])
{
NSLog(#"This is an error %#",value);
return;
}
if([value isKindOfClass:[SoapFault class]])
{
NSLog(#"this is a soap fault %#",value);
return;
}
NSMutableArray *result = (NSMutableArray*)value;
NSMutableArray *remoteObj = [[NSMutableArray alloc]init];
for (int i = 0; i < [result count]; i++)
{
EDVCategory *catObj = [[EDVCategory alloc]init];
catObj = [result objectAtIndex:i];
[remoteObj addObject:catObj];
}
NSArray *remoteIDs = [remoteObj valueForKey:#"categoryId"];
NSFetchRequest *request = [[[NSFetchRequest alloc] init]autorelease];
request.predicate = [NSPredicate predicateWithFormat:#"categoryId IN %#", remoteIDs];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Categories" inManagedObjectContext:__managedObjectContext];
[request setEntity:entity];
NSMutableArray *results = [[NSMutableArray alloc]initWithArray:[__managedObjectContext executeFetchRequest:request error:nil]];
NSArray *existingIDs = [results valueForKey:#"categoryId"];
NSDictionary *existingObjects = [NSDictionary dictionaryWithObjects:results forKeys:existingIDs];
for (NSDictionary *remoteObjectDic in remoteObj)
{
Categories *existingObject = [existingObjects objectForKey:[remoteObjectDic valueForKey:#"categoryId"]];
if (existingObject)
{
NSLog(#"object exists");
}
else
{
NSLog(#"create new local object");
// Categories *newCategory;
// newCategory = [NSEntityDescription insertNewObjectForEntityForName:#"Categories" inManagedObjectContext:__managedObjectContext];
// [newCategory setCategoryId:[NSNumber numberWithInt:[[remoteObjectDic objectForKey:#"categoryId"]intValue]]];
// [newCategory setCategoryName:[remoteObjectDic objectForKey:#"categoryName"]];
// [newCategory setDocCount:[NSNumber numberWithInt:[[remoteObjectDic objectForKey:#"docCount"]intValue]]];
// [newCategory setCategoryType:[NSNumber numberWithInt:[[remoteObjectDic objectForKey:#"categoryType"]intValue]]];
// [newCategory setSubCategoryId:[NSNumber numberWithInt:[[remoteObjectDic objectForKey:#"subCategoryId"]intValue]]];
// [__managedObjectContext insertObject:newCategory];
}
}
[my_table reloadData];
}
The problem is,I am not able to extract values from the remote object and assign it to the NSManagedObject.I have commented the code which (according to me) should save the values in new object to the managed object. Can someone please help me achieve this?
Thanks
Here is an example of a save I did in a recent project. I have somethings in wrappers so fetching a managed object and saving look a little weird on my end. Really the only major difference I see is the act of saving. Are you saving the new NSManagedObject elsewhere in the code?
dict = (NSDictionary*)data;
#try {
if (dict) {
CaretakerInfo* info = [GenericDataService makeObjectWithEntityName:NSStringFromClass([CaretakerInfo class])];
[info setName:[dict valueForKey:#"name"]];
[info setImageURL:[dict valueForKey:#"photo"]];
[info setCaretakerID:[dict valueForKey:#"id"]];
[GenericDataService save];
}
else {
theError = [Error createErrorMessage:#"No Data" Code:-42];
}
}
#catch (NSException *exception) {
//return an error if an exception
theError = [Error createErrorMessage:#"Exception Thrown While Parsing" Code:-42];
}
If not it should looks something like this...
NSError *error = nil;
[context save:&error];
If you have anymore information about what's happening when you extract or assigning data that would be helpful (error/warning/log messages).

how to import videos using coredata?

I am developing one app named safekeep.I have to import videos using coredata by converting into data.
if ([[info objectForKey:UIImagePickerControllerMediaType] isEqualToString:#"public.movie"])
{
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSLog(#"Q: video path: %#",[videoURL description]);
self.videoData=[NSData dataWithContentsOfURL:videoURL];
if(self.videoData)
{
NSLog(#"data is present");
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *imagetblObj= [NSEntityDescription insertNewObjectForEntityForName:#"VideoData" inManagedObjectContext:context];
[imagetblObj setValue:self.videoData forKey:#"videodata"];
[imagetblObj setValue:str forKey:#"date"];
NSError *err;
if (![context save:&err])
{
NSLog(#"Couldn't save history item into coredata");
}
NSLog(#"data saved");
[self videodatafromdb];
}
}
-(void)videodatafromdb
{
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"VideoData" inManagedObjectContext:context];
[request setEntity:entity];
NSError *error;
NSArray *recordsData=[context executeFetchRequest:request error:&error];
self.videoarray=[[recordsData reverseObjectEnumerator]allObjects];
NSLog(#"Array count is %d",[self.videoarray count]);
if ([self.videoarray count]>0)
{
[self createScrollViewvideo];
}
}
-(void)createScrollViewvideo
{
NSLog(#"in create scrollviewvideo");
//add views to scrolview
int x=5;
int y=17;
for(int i=0;i<[videoarray count];i++)
{
UIImage *imag=[[UIImage alloc] init];
UIView *videoview=[[UIView alloc] initWithFrame:CGRectMake(x, y, 100, 100)];
videoview.tag=i;
UIButton *userButton=[[UIButton alloc]initWithFrame:CGRectMake(1, 1, 100,100)];
[userButton addTarget:self action:#selector(userVideoClicked:) forControlEvents:UIControlEventTouchUpInside];
userButton.tag=i;
imag=[UIImage imageWithData:videoData];
[userButton setBackgroundImage:imag forState:UIControlStateNormal];
[videoview addSubview:userButton];
[self.scrollview addSubview:videoview];
[userButton release];
[videoview release];
x+=104;
if ((i+1)%3==0)
{
y+=110;
x=5;
}
}
NSLog(#"10000in create scrollview");
if (y+110>self.scrollview.frame.size.height)
{
self.scrollview.contentSize=CGSizeMake(320, y+110);
}
else
{
self.scrollview.contentSize=CGSizeMake(320, self.scrollview.frame.size.height+60);
}
}
}
-(IBAction)userVideoClicked:(id)sender
{
NSLog(#"video clicked");
UIButton *button=(UIButton*)sender;
VideoData *videoobj=(VideoData *)[self.videoarray objectAtIndex:[button tag]];
NSLog(#"video data is %#",videoobj);
VidoesVIewController *videoviewcontroller=[[VidoesVIewController alloc] initWithNibName:#"VidoesVIewController" bundle:nil];
videoviewcontroller.videodata=[videoobj valueForKey:#"videodata"];
[self.navigationController pushViewController:videoviewcontroller animated:YES];
[videoviewcontroller release];
}
Videos are not showing in sroll view.But When click on video file it is navigating. When image is set to button, image is showing in view.Thanks in advance.
See my answer in:
Can I access the files used for external binary storage in Core Data?
I came up with a 'creative' solution (whether it best practice is anyones guess), but it will allow you to use Core Data to manage your files while still being able to access the REAL files by their path.
By using the 'Allows External Storage' option on a Binary field, you can allow core data to manage references to your files for you, abstracting out a fair amount of the busy work.

store result from wcf service in core data

I am using a WCF service in my app.When the app is run for the first time on the iPad,I want it to call a WCF service and display the result in a UITableView.Alongwith displaying the data in UITableView,i want to store the data in Core Data so when the user is "offline"(not connected to wifi)the data will be displayed from the Core Data.The AppDelegate.m looks like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![defaults objectForKey:#"firstRun"])
{
self.firstRun = TRUE;
[defaults setObject:[NSDate date] forKey:#"firstRun"];
}
else
{
self.firstRun = FALSE;//flag does exist so this ISNT the first run
}
[[NSUserDefaults standardUserDefaults] synchronize];
}
The code in UITableView looks like this:
- (void)viewDidLoad
{
[super viewDidLoad];
[my_table setDataSource:self];
[my_table setDelegate:self];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.firstRun){
NSLog(#"IS FIRST RUN");
EDViPadDocSyncService *service = [[EDViPadDocSyncService alloc]init];
[service getAllCategories:self action:#selector(handleGetAllCategories:)];
}
else
{
NSLog(#"NOT FIRST RUN");
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Categories" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *errormsg;
self.allCats = [managedObjectContext executeFetchRequest:fetchRequest error:&errormsg];
NSLog(#"allCATS=%#",self.allCats);
self.title = #"Categories";
}
}
-(void)handleGetAllCategories:(id)value
{
if([value isKindOfClass:[NSError class]])
{
NSLog(#"This is an error %#",value);
return;
}
if([value isKindOfClass:[SoapFault class]])
{
NSLog(#"this is a soap fault %#",value);
return;
}
NSMutableArray *result = (NSMutableArray*)value;
NSMutableArray *categoryList = [[NSMutableArray alloc] init];
NSMutableArray *docCount = [[NSMutableArray alloc]init];
NSMutableArray *catIdList = [[NSMutableArray alloc]init];
self.myData = [[NSMutableArray array] init];
self.myDocCount = [[NSMutableArray array]init];
self.catId = [[NSMutableArray array]init];
for (int i = 0; i < [result count]; i++)
{
EDVCategory *catObj = [[EDVCategory alloc]init];
catObj = [result objectAtIndex:i];
[categoryList addObject:[catObj categoryName]];
[docCount addObject:[NSNumber numberWithInt:[catObj docCount]]];
[catIdList addObject:[NSNumber numberWithInt:[catObj categoryId]]];
}
self.myData = categoryList;
self.myDocCount = docCount;
self.catId = catIdList;
[my_table reloadData];
/*store data in Core Data - START*/
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newCategory;
for(int j=0;j<[result count];j++)
{
newCategory = [NSEntityDescription insertNewObjectForEntityForName:#"Categories" inManagedObjectContext:context];
/*HOW TO STORE DATA FOR THE "CATEGORIES" OBJECT IN CORE DATA*/
}
/*store data in Core Data - END*/
}
I am not able to figure out how to store the data received from the wcf service to the core data object directly.I know how to store it from a text box on the screen to a core data object.eg.:-
coreDataAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newContact;
newCat = [NSEntityDescription insertNewObjectForEntityForName:#"Categories" inManagedObjectContext:context];
[newCat setValue:name.text forKey:#"name"];
name.text = #"";
[context save:&error];
But this doesn't help in my case.Any help is appreciated.
You are mixing networking and UI code. It is a recipe for unmaintainable code.
Your UI should be looking at Core Data and only Core Data to display its data.
Separately, and asynchronously you should be requesting data from WCF and pushing it into Core Data.
Your UI does not need to care about first run vs. subsequent run. It just looks at Core Data via a NSFetchedResultsController.
Your network code is the only part that cares about new vs. update.
Update 1
how can I achieve this? When the app is running and connected to WiFi,it has to get the latest data from the WCF service.
NSURLConnection can do async requests built-in. I generally recommend writing your networking code as NSOperation subclasses and then put them into a queue.
It appears that WCF can return XML and takes standard HTTP requests. Therefore you can write NSOperation subclasses that build your request, send it to the server and wait for a reply. When the reply comes you parse the XML and insert it into Core Data. When you save the Core Data NSManagedObjectContext your NSFetchedResultsController instances will automatically fire and allow you to update your UI.
I have several code samples that perform these feats although they are written for JSON responses as opposed to XML responses. It would not be difficult to take those examples and alter them to your needs.
You can start with this stackoverflow question and its response.
To store the data into the attributes of your NSManagedObject, simply set the values using KVC:
EDVCategory *catObject = [result objectAtIndex:j];
[newCategory setValue:[catObject categoryName] forKey#"categoryName"];
[newCategory setValue:[catObject docCount] forKey#"docCount"];
[newCategory setValue:[catObject categoryID] forKey#"categoryID"];
// after the loop
[context save:&nil];

NSManagedObject fail to save it's attributes, but able to save when adding related objects

I'm developing an iOS app using Core Data. And I have a Log entity with one-to-many relationships with Audio, Photo entities, and one-to-one relationship with Status entity. The log also has text, longitude, latitude properties. I can create the log, change its properties, add status entity, these changes would display right, until I quit the App. All the changes would disappear, and I was looking at the sqlite database, all these changes were never persisted in the database. In the database, the status object will just be created, but not linked to the log object.
But if I add an audio or photo object into the log.audioSet or log.photoSet, the changes I made to log, including the changes to text or status, will suddenly be saved into the database.
So it seems the changes are only maintained in the NSManagedObjectContext, until a related one_to_many entity is added and the [[LTLogStore sharedStore] saveChanges] will suddenly start to work.
I am using a singleton to manage the NSManagedObjectContext. Any ideas?
I would post some code if it's relevant. Thanks.
UPDATE: I'm not sure these code is enough. But basically everything works, and displays, it just doesn't save to the database. I'm using the mogenerator to set the text and latitude, but since everything is in the context. I am not sure this is the code you might need.
CODE:
#interface LTLogStore : NSObject{
}
+ (LTLogStore *)sharedStore;
- (void)removeItem:(Log *)p;
- (Log *)createItem;
- (BOOL)saveChanges;
#property(nonatomic, strong) NSFetchedResultsController *resultsController;
#property(nonatomic, strong) NSManagedObjectModel *model;
#property(nonatomic, strong) NSManagedObjectContext *context;
#end
#implementation LTLogStore
#synthesize resultsController;
#synthesize context, model;
+ (LTLogStore *)sharedStore
{
static LTLogStore *sharedStore = nil;
if(!sharedStore){
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedStore];
}
- (id)init
{
self = [super init];
if(self) {
model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc =
[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// Where does the SQLite file go?
NSString *path = [self itemArchivePath];
NSURL *storeURL = [NSURL fileURLWithPath:path];
NSError *error = nil;
if (![psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:nil
error:&error]) {
[NSException raise:#"Open failed"
format:#"Reason: %#", [error localizedDescription]];
}
// Create the managed object context
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:psc];
// The managed object context can manage undo, but we don't need it
[context setUndoManager:nil];
}
return self;
}
- (NSFetchedResultsController *)resultsController {
if (resultsController !=nil) {
return resultsController;
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *e = [[model entitiesByName] objectForKey:#"Log"];
[request setEntity:e];
NSSortDescriptor *sd = [NSSortDescriptor
sortDescriptorWithKey:#"created_at"
ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sd]];
[request setReturnsObjectsAsFaults:NO];
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:nil cacheName:#"Root"];
NSError *error;
BOOL success = [fetchedResultsController performFetch:&error];
if (!success) {
//handle the error
}
return fetchedResultsController;
}
- (NSString *)itemArchivePath
{
NSArray *documentDirectories =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
// Get one and only document directory from that list
NSString *documentDirectory = [documentDirectories objectAtIndex:0];
NSString *storePath = [documentDirectory stringByAppendingPathComponent:#"store.data"];
return storePath;
}
- (BOOL)saveChanges
{
NSError *err = nil;
BOOL successful = [context save:&err];
NSLog(#"Saving changes to the database");
if (!successful) {
NSLog(#"Error saving: %#", [err localizedDescription]);
}
return successful;
}
- (void)removeItem:(Log *)l
{
[context deleteObject:l];
[self saveChanges];
}
- (Log *)createItem
{
Log *p = [NSEntityDescription insertNewObjectForEntityForName:#"Log"
inManagedObjectContext:context];
[self saveChanges];
return p;
}
#end
#interface Log : _Log {
}
//these two are some custom convenience methods for location attributes, but it does the work of setting the longitude and latitude value in the log object, but calling the [[LTLogStore sharedStore] saveChanges] still won't save it into the database.
-(CLLocation*)location;
-(void)setLocation:(CLLocation*)location;
//this all works
-(Audio*)newAudio;
-(Audio*)newAudioWithPath:(NSString*)audioPath;
//after calling this method, even the log.text changes will be saved to the database.
-(void)addAudioWithPath:(NSString*)audioPath;
-(void)removeAudio:(Audio*)audio;
#end
#import "Log.h"
#import "Audio.h"
#import "LTLogStore.h"
#implementation Log
-(CLLocation*)location{
if (!self.longitude || !self.latitude) {
return nil;
}
CLLocation *l = [[CLLocation alloc] initWithLatitude:[self.latitude doubleValue] longitude:[self.longitude doubleValue]];
return l;
}
-(void)setLocation:(CLLocation*)location{
if (location==nil) {
self.latitude = nil;
self.longitude = nil;
}
self.latitude = [NSNumber numberWithDouble: location.coordinate.latitude];
self.longitude = [NSNumber numberWithDouble:location.coordinate.longitude];
[[LTLogStore sharedStore] saveChanges];
}
-(Audio*)newAudio{
Audio *a = [Audio new];
a.log = self;
return a;
}
-(Audio*)newAudioWithPath:(NSString*)audioPath{
Audio *new = [self newAudio];
[new setKey:audioPath];
return new;
}
-(void)addAudioWithPath:(NSString*)audioPath{
Audio *new = [self newAudio];
[new setKey:audioPath];
[[LTLogStore sharedStore] saveChanges];
}
-(void)removeAudio:(Audio*)audio{
[self.audiosSet removeObject:audio];
[[[LTLogStore sharedStore] context] deleteObject:audio];
[[LTLogStore sharedStore] saveChanges];
}
#end
UPDATE:
Problem solved, see answer.
UPDATE QUESTION: Why is my overriding causing the problem? Can someone explain the cause behind the magic of Core Data or maybe KVO behind scene?
Problem solved, I overrode the willChangeValueForKey method in the Log class, which caused the problem, I thought the code is irrelevant. But it IS:
- (void)willChangeValueForKey:(NSString *)key{
//I added the following line to fix my problem
[super willChangeValueForKey:key];
//this is the original line, I want to have this
//because I want to have a isBlank property
//so I can see if the user modified the log
_isBlank = false;
//I tried to also add the following line to be safe.
//turns out this line is not needed, and it will make the problem occur again
//[super didChangeValueForKey:key];
}