This is database design with to-many relations.This is my method
-(void)fetchChannelData:(id)responseObject{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
NSArray *channels = [responseObject objectForKey:#"channels"] ;
for (NSDictionary *channelStack in channels) {
NSLog(#"Tenant id %#",[[ NSUserDefaults standardUserDefaults ] valueForKey:#"test"]);
ChannelsData *channels = [ChannelsData MR_importFromObject:channelStack inContext:localContext];
[channels setCategory:[[channelStack valueForKey:#"category"] firstObject]];
[channels setTenantID:[[ NSUserDefaults standardUserDefaults ] valueForKey:#"test"]];
NSArray *channelLogo = [channelStack objectForKey:#"channelImages"] ;
for (NSDictionary *channelLogoStack in channelLogo) {
ChannelImages *images = [ChannelImages MR_importFromObject:channelLogoStack inContext:localContext];
[channels addImagesObject:images];
[images setChannelID:channels.channelID];
}
}
} completion:^(BOOL contextDidSave, NSError *error) {
if (contextDidSave) {
[self fetchListing];
}
}];
}
Where I am importing my data. I have dropdown where I selected different API points for specific data. All fetching different data.Now issue is that when I launch my app again service is again called and instead data is updated it insert it again.I already Used relatedByAttribute on each entity letting their "ID" be the primary key.]1]1
Related
I have successfully save the data in sqlite db but I am unable to save this data in array.
- (void) createEventTable {
[self createAndDropTablesWithQuery:[NSString stringWithFormat:#"create table if not exists BEACONDATA (rowID integer primary key AUTOINCREMENT, beaconid text)"]];
}
-(void)insertData:(NSArray *)beacon_data
{
[self createEventTable];
[instance.database open];
BOOL isInserted;
#autoreleasepool
{
isInserted=[instance.database executeUpdate:#"insert into BEACONDATA (beaconid) values (?)",[NSString stringWithFormat:#"(%#)",beacon_data]];
}
[instance.database close];
if(isInserted)
NSLog(#"Inserted Successfully");
else
NSLog(#"Error occurred while inserting");
[self displayData];
}
-(void)displayData
{
[instance.database open];
FMResultSet *resultSet=[instance.database executeQuery:#"SELECT * FROM BEACONDATA"];
NSMutableArray *array_lastname=[[NSMutableArray alloc]init];
if(resultSet)
{
while([resultSet next])
NSLog(#"Beacon data %#",[resultSet stringForColumn:#"beaconid"]);
}
}
OUTPUT:
Beacon data (01000444)
Beacon data (01000466)
Beacon data (01000001)
Beacon data (01000468)
Beacon data (01000004)
Beacon data (01000006)
Beacon data (01000003)
How to save this data in array I have tried a lot of way but not success please help if you have any idea. I am new in sqlite.
FMResultSet have instance method resultDictionary will give you NSDictionary for that records. So you can add object of that dictionary in your array.
NSMutableArray *array = [[NSMutableArray alloc]init];
if(resultSet) {
while([resultSet next]) {
NSDictionary *dict = [resultSet resultDictionary];
[array addObject:dict];
//Or if you want to add simply `beaconid` to array then it should be simply
[array addObject: [resultSet stringForColumn:#"beaconid"]];
}
I saved my managedObjects. But my NSManagedObjectID is still temporary after saving. Why?
dispatch_async(privateQueue, ^{
__block NSMutableArray *ids = [NSMutableArray array];
[[[LPAppDelegate instance] privateContext] performBlockAndWait:^{
if ([responseObject isKindOfClass:[NSDictionary class]]) {
id arr = ((NSDictionary*)responseObject)[#"results"];
for (int i=0; i < ((NSArray *)arr).count; i++) {
LPFilm *film = [LPFilm MR_createEntityInContext:privateContext];
[ids addObject:film.objectID];
}
}
NSError *error = nil;
[privateContext save:&error];
if (error) {
NSLog(#"___fetch service error: %#", [error localizedDescription]);
}
}];
for (NSManagedObjectID *objID in ids) {
if (objID.isTemporaryID) {
NSLog(#"__tempID %#", objID);
}
}
});
When you save changes, new NSManagedObject instances get a new object ID. Previously existing instances of NSManagedObjectID are not converted in-place, they're replaced with new instances. But you have to ask the managed object for its new ID.
In your case, you're saving up an array of object IDs before saving. Those objects will never change, even if you save changes. But, if you go back to your managed objects and ask them for their object IDs again, you'll get different results, which will not be temporary.
I'm using MagicalRecord to change my custom CoreData functions with something better.
I have two entities: Offer (videogame offers) and System (consoles, PC, etx). An Offer can have one or many Systems, and I get all the data from a Parse query, where I save all my data.
I only have 8 Systems so when my app starts, I fetch all of them and save with Magical Record. Then I call my method to fetch some offers and transform the PFObjects from Parse into entities.
This is how
+ (void)createOrUpdateOffersWithArray:(NSArray *)objects completion:(void (^)())completion
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
for (PFObject *object in objects) {
Offer *offer = [Offer MR_findFirstByAttribute:#"offerId" withValue:object.objectId inContext:localContext];
if (offer == nil) {
offer = [Offer MR_createEntityInContext:localContext];
}
// Here I set all the offer values (title, date, etc)...
NSSet *offerSavedSystems = [offer mutableSetValueForKey:#"systems"];
if (offerSavedSystems == nil) {
offerSavedSystems = [[NSMutableSet alloc] init];
}
/* This is a set of all the systems related with the offer */
NSArray *offerSystems = [object objectForKey:#"sistemas"];
NSMutableSet *updatedSystems = [[NSMutableSet alloc] init];
/* Here I query one of my System entities for each in the "offerSystems" array */
for (NSString *systemKey in offerSystems) {
System *system = [System MR_findFirstByAttribute:#"key" withValue:systemKey inContext:localContext];
[updatedSystems addObject:system];
}
offer.systems = updatedSystems;
}
} completion:^(BOOL contextDidSave, NSError *error) {
/* UI update*/
}];
}
The weird this happens inside the last for loop. Despite I'm sure that all the 8 systems are inside my CoreData model, this line
System *system = [System MR_findFirstByAttribute:#"key" withValue:systemKey inContext:localContext];
returns nil
But the most weird thing is that using NSLOG just before the for loop like this
NSLog(#"SYSTEMS %d", [System MR_countOfEntities]);
NSLog(#"SYSTEMS WITH LOCAL CONTEXT %d", [System MR_countOfEntitiesWithContext:localContext]);
I got this
SYSTEMS 8
SYSTEMS WITH LOCAL CONTEXT 0
My Systems are previously saved with this method
+ (void)initializeSystems
{
NSArray *systemsKeys = [[self systems] allKeys];
for (NSString *systemKey in systemsKeys) {
System *system = [System MR_findFirstByAttribute:#"key" withValue:systemKey];
if (system == nil) {
system = [System MR_createEntity];
}
[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) {
system.key = systemKey;
system.name = [self literalForSystem:systemKey];
system.subscribed = [NSNumber numberWithBool:NO];
}];
}
}
What I'm doing wrong?
Thanks for your time
There are several possible problems with this line of code
System *system = [System MR_findFirstByAttribute:#"key" withValue:systemKey inContext:localContext];
systemKey value does not exist inside all of your entities.
system.key value not setted (or nil)
So, check firstly - fetch all system entities and log 'key' value. Insure that your key really exist.
Secondly,it's better to refactor your code for background saving
+ (void)initializeSystemsWithCompletion:(void (^)())completion
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
NSArray *systemsKeys = [[self systems] allKeys];
for (NSString *systemKey in systemsKeys) {
System *system = [System MR_findFirstByAttribute:#"key" withValue:systemKey inContext:localContext];
if (system == nil) {
system = [System MR_createEntityInContext:localContext];
}
system.key = systemKey;
system.name = [self literalForSystem:systemKey];
system.subscribed = [NSNumber numberWithBool:NO];
}
} completion:^(BOOL success, NSError *error) {
}];
}
I am trying to remove an item from my UICollectionView. The item is also saved using CoreData. Removing the item seems to work until I reload the view or restart the app. Both of which call getCards below. When that happens I the item is back and even in the Core Data database it seems the object has not been removed.
Code:
-(void)removeCard:(int)position{
UserModel *selectedUser = [self getSelectedUserFromDB];
CardModel *cardToRemove;
for(CardModel *cardmodel in selectedUser.cards){
if(cardmodel.position.intValue == position){
cardToRemove = cardmodel;
break;
}
}
int positionOldCard = cardToRemove.position.intValue;
[selectedUser removeCardsObject:cardToRemove];
NSMutableArray *cards = [selectedUser.cards.array mutableCopy];
NSLog(#"CARDSCOUNT: %d", cards.count);
//This changes the position of the cards to accomodate the removing of cards above
for(CardModel *cardmodel in cards){
if(cardmodel.position.intValue > positionOldCard){
[cardmodel setPosition:[NSNumber numberWithInt:(cardmodel.position.intValue - 1)]];
[selectedUser replaceObjectInCardsAtIndex:cardmodel.position.intValue withObject:cardmodel];
}
}
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Removing the card failed %#", error);
}
NSLog(#"Saved cards: %d", [self getCards].count);
}
-(NSMutableArray *)getCards{
UserModel *selectedUser = [self getSelectedUserFromDB];
NSMutableArray *cards = [[NSMutableArray alloc] init];
for(CardModel *cardModel in selectedUser.cards){
[cards addObject:[self modelToCard:cardModel]];
}
NSLog(#"Loaded cards: %d", cards.count);
return cards;
}
Output:
2015-03-17 11:13:56.259 BeNext[3593:349310] Loaded cards: 4
2015-03-17 11:13:56.260 BeNext[3593:349310] Saved cards: 4
2015-03-17 11:13:56.271 BeNext[3593:349310] Loaded cards: 4
//RELOADING VIEW
2015-03-17 11:14:02.351 BeNext[3593:349310] Loaded cards: 5
If you want to delete the card you need to call [context deleteObject:card model] before you call save - you are manipulating the relationship but this is not the same as actually deleting the card.
This is a SyncEngine from an RW tutorial. I need help understanding how only UPDATED records from the web are fetched and processed into Core Data.
- (void)processJSONDataRecordsIntoCoreData {
NSManagedObjectContext *managedObjectContext = [[SDCoreDataController sharedInstance] backgroundManagedObjectContext];
// Iterate over all registered classes --- CHECK!
for (NSString *className in self.registeredClassesToSync) {
if (![self initialSyncComplete]) {
NSDictionary *JSONDictionary = [self JSONDictionaryForClassWithName:className];
NSArray *records = [JSONDictionary objectForKey:#"results"];
for (NSDictionary *record in records) {
[self newManagedObjectWithClassName:className forRecord:record];
}
} else {
NSArray *downloadedRecords = [self JSONDataRecordsForClass:className sortedByKey:#"objectId"];
if ([downloadedRecords lastObject]) {
NSArray *storedRecords = [self managedObjectsForClass:className sortedByKey:#"objectId" usingArrayOfIds:[downloadedRecords valueForKey:#"objectId"] inArrayOfIds:YES];
int currentIndex = 0;
//if dl count is < current index, there is an updated object dl from the web
for (NSDictionary *record in downloadedRecords) {
NSManagedObject *storedManagedObject = nil;
//Quick check to see if they indeed match, if they do, update the stored object with remote service objects
if ([storedRecords count] > currentIndex) {
storedManagedObject = [storedRecords objectAtIndex:currentIndex];
}
//Othwerwise its a new object and you need to create a new NSManagedObject to represent it in CDdb
if ([[storedManagedObject valueForKey:#"objectId"] isEqualToString:[record valueForKey:#"objectId"]]) {
[self updateManagedObject:[storedRecords objectAtIndex:currentIndex] withRecord:record];
} else {
[self newManagedObjectWithClassName:className forRecord:record];
}
currentIndex++;
}
}
}
// After all NSMO are created in your context, save it!
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
if (![managedObjectContext save:&error]) {
NSLog(#"Unable to save context for class %#", className);
}
}];
// Cleanup time
[self deleteJSONDataRecordsForClassWithName:className];
[self executeSyncCompletedOperations];
}
[self downloadDataForRegisteredObjects:NO];
}
From what I understand, on the first or initial sync, it fetches JSONDictionaryForClassWithName which reads the downloaded data from disk and creates a newManagedObjectWithClassName.
My confusion is in the update else block. downloadedRecords is populated from JSONDataRecordsForClass which simply calls JSONDictionaryForClassWithName. Then it checks to see if there is at least 1 object in that array. If there is it does this:
NSArray *storedRecords = [self managedObjectsForClass:className sortedByKey:#"objectId" usingArrayOfIds:[downloadedRecords valueForKey:#"objectId"] inArrayOfIds:YES];
This fetches all managedObjectsForClass:sortedByKey which is below:
- (NSArray *)managedObjectsForClass:(NSString *)className sortedByKey:(NSString *)key usingArrayOfIds:(NSArray *)idArray inArrayOfIds:(BOOL)inIds {
__block NSArray *results = nil;
NSManagedObjectContext *managedObjectContext = [[SDCoreDataController sharedInstance] backgroundManagedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:className];
NSPredicate *predicate;
if (inIds) {
predicate = [NSPredicate predicateWithFormat:#"objectId IN %#", idArray];
} else {
predicate = [NSPredicate predicateWithFormat:#"NOT (objectId IN %#)", idArray];
}
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:#"objectId" ascending:YES]]];
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
}];
return results;
}
The next bit which compares the [storedRecords count] > currentIndex is confusing. Can someone please explain this? I think my confusion lies in what the managedObjectsForClass method does with the usingArraysOfIds & inArrayOfIds.
I would expect that at some point it gets the the updatedAt field from the downloaded records and compares it to the updatedAt field of the CoreData fetched records.
This function is processing the stored JSON. The actual remote fetching and updateAt checking happens in downloadDataForRegisteredObjects and mostRecentUpdatedAtDateForEntityWithName.
[storedRecords count] > currentIndex is a bit crazy. Although in defense of the original programmer, writing any decent syncengine will quickly make you go googoo. Basically he needs to work out which records are existing and which ones are new and update the local data store accordingly, that's all.
I had another look and this code is actually horribly broken. It will only works if either you have the same records both locally and remotely. Or if the new objects have an objectId that sort-wise comes after the last object the local store has. Which is not the case with Parse objectId's.
If you are testing with just one device this works because new objects will be inserted locally before being pushed to the server. Therefor you will always have the same amount of records. If additional records get inserted any other way, this code will do weird things.