Core Data iCloud fetch sometimes yields no data (iOS) - ios

I have a problem in my test app using iCloud Core Data. The specific problem is triggered when a partial entry in a table view is touched which triggers the full table entry to be displayed on a full screen. Kind of standard table view stuff.
The table view cell contains an unique record ID which is passed into the detail display view. Upon starting up the view, the view controller get uses the full record data and paints it up on the screen.
Here is the code used to do that:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *temp_text;
Item *fetchSelectedItem;
NSString *updateUniqueID;
NSArray *tempFetchedObjects;
NSError *error;
int stopper;
localFindItDataController = [[FindItDataController alloc] init];
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Item" inManagedObjectContext: localFindItDataController.managedObjectContext];
[fetch setEntity:entityDescription];
updateUniqueID = [localItem.itemUniqueID stringValue];
[fetch setPredicate:[NSPredicate predicateWithFormat:#"(ANY itemUniqueID = %#)",updateUniqueID]];
tempFetchedObjects = [localFindItDataController.managedObjectContext executeFetchRequest:fetch error:&error];
NSLog(#"did the fetch work %# %lu", updateUniqueID, (unsigned long)[tempFetchedObjects count]);
if ([tempFetchedObjects count] == 0) {
stopper = 1;}
fetchSelectedItem = [tempFetchedObjects objectAtIndex:0];
NSLog(#"only executes if fetch was ok");
localItem = fetchSelectedItem;
temp_text = localItem.itemName;
contentsWhatBox.text = temp_text;
temp_text = localItem.itemLocation;
contentsWhereBox.text = temp_text;
temp_text = localItem.itemDescription;
contentsOtherInfoBox.text = temp_text;
UIImage *tempImage = [UIImage imageWithData:localItem.itemPicture];
CGImageRef imageRef = [self CGImageRotatedByAngle:[tempImage CGImage] angle:270];
UIImage* img = [UIImage imageWithCGImage: imageRef];
contentsImageBox.image = img;
tapImage = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapImageGesture)];
[contentsImageBox setUserInteractionEnabled:YES];
[contentsImageBox addGestureRecognizer:tapImage];
}
Mostly this works just fine but occasionally the fetch comes up empty even though there definitely is data that matches the fetch predicate. Sometimes I can tap on a table view entry, see it come up, go back and forth a few times between table view entries, go back to the original entry and the fetch fails.
I'm at a bit of a loss and any help would be welcome. Thanks in advance.
Edit #1:
This is the object that is being fetched. Might matter so I added it in later:
#interface Item : NSManagedObject {
}
#property (nonatomic, retain) NSString * itemName;
#property (nonatomic, retain) NSString * itemLocation;
#property (nonatomic, retain) NSString * itemDescription;
#property (nonatomic, retain) NSData * itemPicture;
#property (nonatomic, retain) NSNumber * itemUniqueID;
#property (nonatomic, retain) NSNumber * itemNumber1;
#property (nonatomic, retain) NSNumber * itemNumber2;
#property (nonatomic, retain) NSString * itemAttribute1;
#property (nonatomic, retain) NSString * itemAttribute2;
#property (nonatomic, retain) NSString * itemAttribute3;
#property (nonatomic, retain) NSString * itemTimestamp;
#end
Edit #2:
I forgot to mention that the error object from the fetch is nil after the 'bad' fetch.
Edit #3:
Added a loop to check if I got a result and retry a certain number of times before quitting and that worked ok. I'm not sure why it's going on but I suspect the image in the iCloud data might be part of the problem even though the data at this point for sure is replicated to the iOS test device(s).

Added a loop to check if I got a result and retry a certain number of times before quitting and that worked ok. I'm not sure why it's going on but I suspect the image in the iCloud data might be part of the problem even though the data at this point for sure is replicated to the iOS test device(s).

Related

iOS/objective-c: Spooky error setting value of object

I have copied some code that works fine for saving an api to one entity in core data over to save a similar api to another entity. Although everything is largely the same, I cannot get rid of exception error.
Here is the code that throws the exception:
- (NSMutableArray *) convertFeedtoObject:(NSMutableArray*)feed {
NSMutableArray * _newitems = [[NSMutableArray alloc] init];
for (int i = 0; i < feed.count; i++) {
NSDictionary *feedElement = feed[i];
ItemOnServer *newItem = [[ItemOnServer alloc] init];
newItem.itemtitle = feedElement[#"itemtitle"]; //throws exception
newItem.item = feedElement[#"item"];//also throws an exception if above commented out
newItem.descript = #"some item"; //if above lines commented out, littoral throws exception too.
//same goes for any other values I try to set. The first one throws exception.
Here is the error:
2015-11-20 05:26:03.281 [20610:60b] -[ItemOnServer setItem:]: unrecognized selector sent to instance 0x175721b0
No matter what values I try to set, I get a similar error even though I know the values are there.
One item of the feed looks like this:
{
lastviewed = "2015-11-17 15:21:45";
itemtitle = "New";
id = 944;
item = "cotton shirt";
}
)
Thanks for any suggestions.
Edit:
Code from ItemOnServer.h
//.m file is largely empty
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class ItemsVC;
#class Vendors;
#interface ItemOnServer : NSObject
#property (nonatomic, retain) NSString * item;
#property (nonatomic, retain) NSNumber * id;//this is permanent id shared with server
#property (nonatomic, retain) NSNumber * localid; //this is temp id if first created locally
#property (nonatomic, retain) NSString * itemtitle;
#property (nonatomic, retain) NSDate * lastviewed;
//this is relationship with vendors
#property (nonatomic, retain) Vendors *vendor;
#end
Instead of using an NSDictionary declare feedElement as an NSMutableArray, so something like:
- (NSMutableArray *) convertFeedtoObject:(NSMutableArray*)feed {
NSMutableArray * _newitems = [[NSMutableArray alloc] init];
NSMutableArray *feedElement = [[NSMutableArray alloc] init];
for (int i = 0; i < feed.count; i++) {
feedElement = feed[i];
ItemOnServer *newItem = [[ItemOnServer alloc] init];
newItem.itemtitle = feedElement[#"itemtitle"];
...
It looks like your class ItemOnServer doesn't have implementations for the methods setItem:, setItemtitle and so on. I don't know how you managed to do this, but you should look at the interface and implementation of ItemOnServer.
Look at what the error message says:
-[ItemOnServer setItem:]: unrecognized selector
That means a setItem: message is sent to an object, that object is an ItemOnServer object, and it doesn't implement setItem: The first two of these three points are exactly what you expect to happen when you write newItem.item = ...
Do you have another class named ItemOnServer in your application, maybe linked through some library? You can easily check by renaming ItemOnServer with ItemOnServer2 and checking what happens.

EXC_BAC_ACCESS error when saving core data Boolean field

I am getting a EXC_BAC_ACCESS error when attempting to save a TRUE value into a managed object that contains a Boolean attribute.
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TrafficCameraInfo"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"code=%#",self.selectedTrafficCamera.code]];
NSError *error;
TrafficCameraInfo *cgTrafficCamera;
cgTrafficCamera = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] lastObject];
NSString *alertMessage;
if (cgTrafficCamera.favourite == NO){
cgTrafficCamera.name = #"TEST"; <-- works ok
cgTrafficCamera.favourite = 1; <-- causes an error
} else {
cgTrafficCamera.favourite = 0;
}
error = nil;
if (![self.managedObjectContext save:&error]) {
The managed object interface looks like this:
#interface TrafficCameraInfo : NSManagedObject
#property (nonatomic, retain) NSString *code;
#property (nonatomic, retain) NSString *postCode;
#property (nonatomic, retain) NSNumber *latitude;
#property (nonatomic, retain) NSNumber *longitude;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *url;
#property (nonatomic) Boolean favourite;
#end
Elsewhere in my app i am updating another Boolean field by passing a 1 to it and am not encountering a problem.
Any ideas what is causing the error?
I think in your core data attribute table you define "favorite" variable to BOOL,well that means it's an NSNumber Type,so you should set the data using NSNumber
A Boolean is a simple, scalar, non-pointer datatype. Core Data properties are always stored as objects. The Objective-C object wrapper for numeric datatypes is NSNumber. So if favourite is a regular stored property, you should declare it as:
#property (nonatomic, retain) NSNumber *favourite;
Assignment would be done like this:
cgTrafficCamera.favourite = [NSNumber numberWithBool:YES]; // Obj-C style is "YES/NO" for BOOL
Or this if you prefer:
cgTrafficCamera.favourite = [NSNumber numberWithBool:1];
If you don't need to store the Boolean, you can leave it as such and make it a transient property. You'll probably need to get rid of the "(nonatomic)" in that case.

NSData in CoreData is not saving properly

I am currently trying to store images I download from the web into an NSManagedObject class so that I don't have to redownload it every single time the application is opened. I currently have these two classes.
Plant.h
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) PlantPicture *picture;
PlantPicture.h
#property (nonatomic, retain) NSString * bucketName;
#property (nonatomic, retain) NSString * creationDate;
#property (nonatomic, retain) NSData * pictureData;
#property (nonatomic, retain) NSString * slug;
#property (nonatomic, retain) NSString * urlWithBucket;
Now I do the following:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
PlantCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
Plant *plant = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.plantLabel.text = plant.name;
if(plant.picture.pictureData == nil)
{
NSLog(#"Downloading");
NSMutableString *pictureUrl = [[NSMutableString alloc]initWithString:amazonS3BaseURL];
[pictureUrl appendString:plant.picture.urlWithBucket];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:pictureUrl]];
AFImageRequestOperation *imageOperation = [AFImageRequestOperation imageRequestOperationWithRequest:request success:^(UIImage *image) {
cell.plantImageView.image = image;
plant.picture.pictureData = [[NSData alloc]initWithData:UIImageJPEGRepresentation(image, 1.0)];
NSError *error = nil;
if (![_managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}];
[imageOperation start];
}
else
{
NSLog(#"Already");
cell.plantImageView.image = [UIImage imageWithData:plant.picture.pictureData];
}
NSLog(#"%#", plant.name);
return cell;
}
The plant information is present, as well as the picture object. However, the NSData itself is NEVER saved throughout the application opening and closing. I always have to REDOWNLOAD the image! Any ideas!? [Very new to CoreData... sorry if it is easy!]
thanks!
Edit & Update:
The image download is NOT nil and appears just fine after it was downloaded. It looks like the data returned by UIImageJPEGRepresentation is not nil also. My managedObjectContext is not nil as well. The request is done on the main-thread according to my debugger. Anything else I should check?!
Also, if I quit the view and come back, the images won't be downloaded again and the "Already" log will appear. However, if I close and reopen the app, they will have to be redownloaded, which is weird because the rest of the CoreData is still present.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
Update #2
It appears that all my ManagedObjectContext are the same. Could it have to do with the cache?
First thing to check, is plant or plant.picture nil after fetch? Second, you say that the request is made on the main thread and that your context is not nil. However, the response(inside the block) is it on the main thread? From the code you shared I would say plant.picture is nil, but other things are possible too.
You need to make sure that pictureData should have type transformable
And also in your class you need pictureData to be id not NSData
Like this
#property (nonatomic, retain) id pictureData;

how to fetch a particular value using NSFetchRequest

I need to fetch a particular managed entity where value is xxx.
my managed object is
Subscriptions : NSManagedObject{
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSNumber * id;
#property (nonatomic, retain) NSNumber * category;
#property (nonatomic, retain) NSNumber * frequency;
#property (nonatomic, retain) NSNumber * alertType;
}
I need to fetch only the one entities where name is "xxx".
(There will be only one entities where name is xxx.)
I also need to fetch all entities where category is 1.
How can I do this using NSFetchRequest. I know how to use NSFetchedResultsController to fetch all entity values for an entity, but I want to fetch only one where name is xxx.
Suppose you have a NSManagedObjectContext *context, then:
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; //Note don't use autorelease if you're using ARC
request.entity = [NSEntityDescription entityForName:#"Subscriptions" inManagedObjectContext:context];
NSString *someName = #"xxx";
request.predicate = [NSPredicate predicateWithFormat:#"name ==[c] %#", someName]; //[c] means case insensitive
NSArray *result = [context executeFetchRequest:request error:nil];
The result array will contain all returned results and will be of type Subscriptions. If the predicate matches only one item there will be only one result, if matches none, the array would be initialized, but its count property would be 0. You may take a look at NSPredicate Class Reference and how you use it with CoreData.

Get related / associated entities from NSSet - CoreData

I have 2 autogenerated entities :
#interface ContactEntity : Entity
#property (nonatomic, retain) NSString *caption;
#property (nonatomic, retain) NSString *image;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *text;
#property (nonatomic, retain) NSSet *pointLink;
#end
#interface PointEntity : Entity
#property (nonatomic, retain) NSNumber *latitude;
#property (nonatomic, retain) NSNumber *longitude;
#property (nonatomic, retain) NSSet *entityLink;
#property (nonatomic, retain) EntityEntity *entityTypeLink;
#end
They are linked between each other as ManyToMany, i.e. one contact has many points, one point has many contacts inside.
Then a i get first entity :
ContactModel *contact = [[[ContactModel alloc] init] autorelease];
// this is FetchRequest, returns array of all entities
self.items = [contact list:contact];
// i get only one, all is OK here, this entity has related PointEntity in DB
ContactEntity *contactEntity = [self.items objectAtIndex:self.selection];
And when i try to get related PointEntity using NSSet in selected ContactEntity i always get NULL or empty array. Neither of this works :
NSArray *points = [contactEntity.pointLink allObjects];
PointEntity *pointEntity = [contactEntity.pointLink anyObject];
NSInteger x1 = [points count]; // always 0
id x2 = pointEntity.latitude; // always 0
for (PointEntity *x in contactEntity.pointLink) // isn't enumerated because count = 0
{
id x3 = x.latitude;
}
Any thoughts are appreciated. Did i miss something, maybe i need to use NSPredicate to select entities from PointEntity that are related to ContactEntity?
Thanks.
P.S. My question is similar to this but that suggestion does not work for me, i cannot get loaded associated entities using NSSet of main entity :(
CoreData: many-to-many relationship
Answer is found ... i tried to use property of the autogenerated entities when created new records in CoreData, in the meantime the correct way is to use generated methods like - addPointLinkObject, addEntityLinkObject, etc
Example, i have 3 tables :
Contacts (one person may have many locations)
<< - >>
Points (one location can contain many people)
<< - >
EntityTypes (just a type of a location, in this case type is CONTACT)
One of the entities autogenerated by xCode :
#interface PointEntity : Entity
#property (nonatomic, retain) NSNumber *latitude;
#property (nonatomic, retain) NSNumber *longitude;
#property (nonatomic, retain) NSSet *entityLink; // reference to Contacts table (ManyToMany)
#property (nonatomic, retain) EntityEntity *entityTypeLink; // reference to EntityType table (OneToMany)
#end
#interface PointEntity (CoreDataGeneratedAccessors)
- (void)addEntityLinkObject:(ContactEntity *)value;
- (void)removeEntityLinkObject:(ContactEntity *)value;
- (void)addEntityLink:(NSSet *)values;
- (void)removeEntityLink:(NSSet *)values;
#end
I tried to do the following :
// create 3 new instances - one for each entity
ContactEntity *contactEntity = [model create:model];
PointEntity *pointEntity = [point create:point];
EntityModel *entity = [[[EntityModel alloc] init] autorelease];
entity.name = model.table;
EntityEntity *entityEntity = [[entity list:entity] objectAtIndex:0];
// then i tried to use entity's properties directly to bind entities
// it works, but it works only on DB level when we add new records, but somehow something was missed and thus such selection did not work later - [pointEntity allObjects]
//pointEntity.entityTypeLink = entityEntity; // WRONG !!!
//pointEntity.entityLink = contactEntity.pointLink;
//contactEntity.pointLink = pointEntity.entityLink;
// then i replaced 3 lines above with these ones
[pointEntity addEntityLinkObject:contactEntity]; // CORRECT !!!
[contactEntity addPointLinkObject:pointEntity];
[entityEntity addPointLinkObject:pointEntity];
[context save]; // save changes made with entities in current CoreData context
// now [pointEntity allObjects] and [pointEntity anyObject] work as expected
Useful links -
Coredata and Generated subclass for NSManagedObject with relations
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdAccessorMethods.html#//apple_ref/doc/uid/TP40002154

Resources