Is there a method (NSPredicate or other function) that returns ANY single existing NSManagedObject. Could also be the count of an entity type. No further requirements other than mere existence on self.
NSPredicate *fetch = [NSPredicate predicateWithFormat: "#ANY"]
....
[fetch setEntity:entityDescription];
[fetch setPredicate: predicate];
[fetch setFetchLimit:1];
Note that this is the 'root' object, thus no instance at this boot stage.
I am not sure if I understand your question correctly, but to get a one (arbitrary) object
of your entity just don't add a predicate to the fetch request and set the fetch limit to
one:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"YourEntity"];
[request setFetchLimit:1];
NSError *error;
NSArray *result = [context executeFetchRequest:request error:&error];
if (result == nil) {
// Error executing fetch request
} else if ([result count] == 0) {
// Found none
} else {
// Found one
NSManagedObject *obj = result[0];
}
I'm coming across this a few years later & thought it might be helpful to see this in Swift. In my testing, this returns the first entry in the data store, not necessarily a random entry from the whole store or even a different one each time.
Assuming that by "ANY" you're OK simply returning the first entry, here's how to do it in Swift.
for Swift 4.2:
let fetchRequest : NSFetchRequest<yourEntityName> = yourEntityName.fetchRequest()
fetchRequest.fetchLimit = 1
do {
let fetchResults = try managedContext.fetch(fetchRequest)
if fetchResults.count == 0 {
// found none
} else {
// found one
var obj: NSManagedObject = fetchResults[0]
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
Related
I am having the following problem: I use RestKit to get objects form a REST Api. The object mapping works, which I can see from the RK Debugger Output. However, when I perform a fetch request afterwards, the result is empty. I am talking about NSManagedObjects. I have the following setup.
1: Restkit and Coredata stack initialization:
NSError *error;
NSURL *baseURL = [NSURL URLWithString:#"https://mfihost.us/gocoffee/api/V1/"];
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
[objectManager.HTTPClient setDefaultHeader:#"Token" value:[NSString stringWithFormat:#"%#",[FBSDKAccessToken currentAccessToken].tokenString]];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
//[RKObjectManager setSharedManager:objectManager];
[FetchRequests registerFetchRequests:objectManager];
[Descriptors registerDescriptors:objectManager];
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add inmemory store with error: %#", error);
[managedObjectStore createManagedObjectContexts];
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
2: call to get objects from server and performing a fetch request afterwards:
[[RKObjectManager sharedManager]
postObject:nil path:#"/gocoffee/api/V1/login/IOS"
parameters:nil
success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Objects have been saved in core data.");
NSManagedObjectContext *managedObjCtx = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
// Shout* sh=[managedObjCtx insertNewObjectForEntityForName:#"Shout"];
// sh.inviterUserId=#"22223";
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Shout" inManagedObjectContext:managedObjCtx];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *result = [managedObjCtx executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Unable to execute fetch request.");
NSLog(#"%#, %#", error, error.localizedDescription);
} else {
NSLog(#"%#", result);
}
completionBlock();
}
failure: ^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Load failed with error: %#", error);
}];
The fetch result is empty, although the server returns objects and these objects are properly mapped by using RKEntityMappings and the corresponding response descriptors. Confusingly, if I uncomment the two lines //Shout * .... (i.e. manually insert a managed oject into the context), then this object is fetched by the fetch request. Consequently, the fetch request should be working fine.
I am searching for ages now what the problem might be. Could it be that I am calling on the wrong context or something ? By the way: core-data multi-threading debugging is enabled and does not show any error, i.e. no "AllThatIsLeftToUsIsHonor" error.
The corresponding route from the above example is:
[objectManager.router.routeSet
addRoute:[RKRoute routeWithName:#"loginAndOrSignup"
pathPattern:#"login/IOS"
method:RKRequestMethodPOST]
];
The descriptors look like (example):
[objectManager addResponseDescriptor:
[RKResponseDescriptor responseDescriptorWithMapping:[shoutMapping inverseMapping]
method:RKRequestMethodPOST
pathPattern: #"login/IOS"
keyPath:#"response.incomingshoutapplications"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
]
];
Shout mapping is as follows:
RKEntityMapping *shoutMapping = [RKEntityMapping mappingForEntityForName:#"Shout" inManagedObjectStore:managedObjectStore];
shoutMapping.identificationAttributes = #[ #"id" ];
[shoutMapping addAttributeMappingsFromDictionary:#{
#"id" : #"id",
#"inviterUserId" : #"inviterUserId",
#"eventType" : #"eventType",
#"eventTime" : #"eventTime",
#"locationLat" : #"locationLat",
#"locationLng" : #"locationLng",
#"radius" : #"radius",
#"targetGender" : #"targetGender",
#"state" : #"state",
#"buddyOnly" : #"buddyOnly",
#"timeCreated" : #"timeCreated"
}
];
The "manager" ist the one from above, the managedObjectStore is manager.managedObjectStore
All the mappings and descriptors are setup in another method that is calles by [Descriptors registerDescriptors:objectManager]; (see first block of code)
The problem is probably that you're using inverseMapping. That is used mainly for request mapping, because it's a mapping that creates instances of NSMutableDictionary, which isn't what you want.
So, what I expect is happening is that the response is mapped successfully, but to plain objects, not managed objects, and then you're throwing the result away.
Just change to
... responseDescriptorWithMapping:shoutMapping ...
I am retrieving some data from an API resource and I want to store the result inside my City entity using Magical Record and when the process finish, reload a tableView in my ViewController with the results.
All is fine but when I start the app for the first time,dowload process is started and the data is saved in core data.
but the table view in my ViewControllers is empty.
If I launch the app after the first time
the tableView refresh correctly.
I don't know if the problem is in threads... Can anybody help me?
ViewController :
Here I start the request. When block is called, I store cities array and reload tableView
- (void)getCitiesFromDataStore {
[[APIManager sharedManager] getCitiesWithCompletion:^(NSArray *cities) {
_dataSourceArray = cities;
[self.citiesTableView reloadData];
} failure:^(NSError *error) {
NSLog(#"%#",error.localizedDescription);
}];
}
APIMAnager
- (void)getCitiesWithCompletion:(void (^)(NSArray *))succesBlock
failure:(void (^)(NSError *))errorBlock
{
NSArray *cachedCities = [City findAllCities];
if ([cachedCities count] == 0) {
[self GET:#"cities" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *results = responseObject[#"cities"];
[City MR_importFromArray:results];
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
NSArray *cities = [City findAllCities];
succesBlock(cities);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
errorBlock(error);
}];
return;
}
// Si ya hay ciudades almacenadas en CoreData, devuelvo el
// succesblock con las ciudades de CoreData
succesBlock(cachedCities);
}
I have a Category also to manage actions with the City entity
City+DBOperations
+ (NSArray *)findAllCities
{
NSArray *cities = [City MR_findAll];
return cities;
}
I know you said you resolved it, but for others who might be coming here another thing you could try is wrapping the import in a saveWithBlock:completion: and do your find in the completion block.
Also make sure you know which context each method is using. It is often helpful to be explicit about that.
Therefore you could change it to (this is untested, but should give you the concept):
[self GET:#"cities" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *results = responseObject[#"cities"];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
[City MR_importFromArray:results inContext:localContext];
} completion:^(BOOL contextDidSave, NSError *error) {
NSArray *cities = [User MR_findAllInContext:[NSManagedObjectContext MR_defaultContext]];
succesBlock(cities);
}];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
errorBlock(error);
}];
how does my fetchedResultsController method look like, if I want to fetch all my attributes for an entity from core data? I only know and understand how to fetch data for a tableView and I think that is where all my confusion is coming from.
Here is my Core-Data setup:
I'm trying to fill an array with all the Attributes my Setting entity has and the show those values via NSLog output in my debug console.
Here is what I changed so far:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newEntry = [NSEntityDescription insertNewObjectForEntityForName:#"Setting" inManagedObjectContext:context];
//NSManagedObject *newSetting = [NSEntityDescription insertNewObjectForEntityForName:#"Setting" inManagedObjectContext:context];
[newEntry setValue: #"StudiSoft" forKey:#"settingName"];
if (_overrideSysTimeSwitch.on) {
[newEntry setValue: #YES forKey:#"settingSysTimeOverride"];
//editSetting.settingSysTimeOverride = #YES;
NSLog(#"IF A");
} else {
//[newEntry setValue: #NO forKey:#"settingSysTimeOverride"];
//editSetting.settingSysTimeOverride = #NO;
NSLog(#"IF B");
}
if (_timeFormatSwitch.on) {
//[newEntry setValue: #YES forKey:#"settingTimeFormat"];
//editSetting.settingTimeFormat = #YES;
NSLog(#"IF C");
} else {
//[newEntry setValue: #NO forKey:#"settingTimeFormat"];
//editSetting.settingTimeFormat = #NO;
NSLog(#"IF D");
}
[self.settingsArray addObject:#"StudiSoft"];
NSError *error;
[context save:&error];
I'm using this code-snipped that and I'm able to modify the core data content.
However, every time I run this code, it of course adds a new object.
I've been looking for a way to update existing Attributes in my Entity, or modify them, but I could NOT find them.
Anyhow this is a good step into the right direction.
I created a completely new project, with just one view, once I have it working on the main view I'm going to experiment with segues....
But for now, how would I update or change existing attributes?
Thanks guys!!
This is my editSave Method to store some data in core data:
- (IBAction)editSave:(UIBarButtonItem *)sender
{
if ([_editSaveButton.title isEqualToString:#"Edit"])
{
[self setTitle:#"Edit Settings"];
//self.title = #"Edit Settings";
_overrideSysTimeSwitch.userInteractionEnabled = YES;
_timeFormatSwitch.userInteractionEnabled = YES;
_editSaveButton.title = #"Save";
} else if ([_editSaveButton.title isEqualToString:#"Save"])
{
[self setTitle:#"Settings"];
//self.title = #"Settings";
_overrideSysTimeSwitch.userInteractionEnabled = NO;
_timeFormatSwitch.userInteractionEnabled = NO;
_editSaveButton.title = #"Edit";
// #############################################################
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
//NSManagedObject *newEntry = [NSEntityDescription insertNewObjectForEntityForName:#"Setting" inManagedObjectContext:context];
//[newEntry setValue: #"StudiSoft" forKey:#"settingName"];
/*NSString *firstName = [anEmployee firstName];
Employee *manager = anEmployee.manager;
Setting *newSetting = [NSString #"Test"];
[newSetting setValue:#"Stig" forKey:#"settingName"];
[aDepartment setValue:[NSNumber numberWithInteger:100000] forKeyPath:#"manager.salary"];*/
//editSetting.settingName = #"Test";
if (_overrideSysTimeSwitch.on) {
//[newEntry setValue: #YES forKey:#"settingSysTimeOverride"];
editSetting.settingSysTimeOverride = #YES;
NSLog(#"IF A");
} else {
//[newEntry setValue: #NO forKey:#"settingSysTimeOverride"];
editSetting.settingSysTimeOverride = #NO;
NSLog(#"IF B");
}
if (_timeFormatSwitch.on) {
//[newEntry setValue: #YES forKey:#"settingTimeFormat"];
editSetting.settingTimeFormat = #YES;
NSLog(#"IF C");
} else {
//[newEntry setValue: #NO forKey:#"settingTimeFormat"];
editSetting.settingTimeFormat = #NO;
NSLog(#"IF D");
}
//[self.settingsArray addObject:#"StudiSoft"];
NSError *error = nil;
//if ([self.managedObjectContext hasChanges]) {
//NSLog(#"SAVE & DISMISS conetx has changed");
if (![context save:&error]) { // save failed
NSLog(#"Save failed: %#", [error localizedDescription]);
} else { // save succeeded
NSLog(#"Save Succeeded");
}
//}
//[self.tableView reloadData];
// #############################################################
}
}
Debug Output:
2014-06-10 19:09:29.881 SettingsCoreData[508:60b] Entry #5: <Setting: 0x8f983e0> (entity: Setting; id: 0x8f97030 <x-coredata://FA78AB86-3225-4B1E-97DD-3F31F5323A18/Setting/p6> ; data: {
settingName = StudiSoft;
settingSysTimeOverride = 0;
settingTimeFormat = 0;
})
2014-06-10 19:09:29.883 SettingsCoreData[508:60b] Entry #6: <Setting: 0x8f98430> (entity: Setting; id: 0x8f97040 <x-coredata://FA78AB86-3225-4B1E-97DD-3F31F5323A18/Setting/p7> ; data: {
settingName = StudiSoft;
settingSysTimeOverride = 1;
settingTimeFormat = 1;
})
Now I should be able to use something like this in my viewDidLoad, right?
if (editSetting.settingSysTimeOverride.boolValue == 0) {
_overrideSysTimeSwitch.on = NO;
} else {
_overrideSysTimeSwitch.on = YES;
}
But it doesn't work as I thought it will :-(
Next you need to call -performFetch: on the NSFetchedResultsController. Make sure you check the response and handle the error if the response is NO.
From there your NSFetchedResultsController is populated and ready to be used. You can then grab individual elements via -objectAtIndex: or you can grab them all with -fetchedObjects.
I would suggest just reviewing the documentation on the methods that are available as it has pretty strong and clear documentation.
Update
If you are not receiving any data then break it down. Take the NSFetchRequest that you created and call -executeFetchRequest:error: against your NSManagedObjectContext and see if you get any data back.
If you do then there is something wrong with your handling of the NSFetchedResultsController.
If you don't then there is something wrong with your NSFetchRequest or you don't have any data in your store.
Update
Sounds like you need to read a book on how Core Data works.
A NSFetchRequest is a query against Core Data so that objects can be returned from the store. You can pass a NSFetchRequest to a NSFetchedResultsController so that the NSFetchedResultsController can monitor the store for changes and let your view controller know when those changes occur.
A NSFetchRequest can also be executed directly against the NSManagedObjectContext and you can retrieve the results directly. You do that by calling -executeFetchRequest:error: against your NSManagedObjectContext and getting a NSArray back. You can then check that NSArray to see if you get any results.
If you do not understand that paragraph then you need to take a step back and read the tutorials on Core Data and/or read a book on Core Data. I can recommend an excellent book on the subject ;-)
Please direct me to the right way.
I implemented this code to fetch my objects:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSPredicate *predicate = nil;
if (self.selectedCategory)
{
predicate = [NSPredicate predicateWithFormat:#"ANY category_ids.category_id == %#", self.selectedCategory];
}
_fetchedResultsController = [EyeArtist fetchAllGroupedBy:nil withPredicate:predicate sortedBy:#"artist_id" ascending:NO delegate:self];
return _fetchedResultsController;
}
So when app run the at first time fetch works without predicate, so at second time I need new fetch with predicate.
I tap on the button and set string self.selectedCategory, but I don't know how to refetch data from - (NSFetchedResultsController *)fetchedResultsController;
So I suppose it has to be like execute new request for fetchedResultsController instance.
After changing the search criteria, you have to set the instance variable self.fetchedResultsController to nil,
so that the next call to the "lazy getter" function creates a new FRC with the
changed predicate. Something like this:
self.fetchedResultsController = nil;
[self.fetchedResultsController performFetch:&error];
[self.tableView reloadData];
This is the pattern I use for where a fetch controller needs a property:
- (void)setSelectedCategory:(id)selectedCategory{
if(selectedCategory == _selectedCategory){
return _selectedCategory
}
_selectedCategory = selectedCategory;
self.fetchedResultsController = nil;
if(self.isViewLoaded){
[self.tableView reloadData]; // but better to put this in an update views method that you can also call from viewDidLoad.
}
}
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
id selectedCategory = self.selectedCategory;
// Only need this if a category is required.
if(!selectedCategory){
return nil;
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY category_ids.category_id == %#", selectedCategory];
_fetchedResultsController = [EyeArtist fetchAllGroupedBy:nil withPredicate:predicate sortedBy:#"artist_id" ascending:NO delegate:self];
return _fetchedResultsController;
}
I am not sure if sandbox is taking too long to update or if my code is funky.
I am simply grabbing the local players last entered score and adding another score to it and trying to post the result.
Here is my code:
- (void) reportScore: (int64_t) score forCategory: (NSString*) category
{
GKScore *scoreReporter = [[[GKScore alloc]initWithCategory:category] autorelease];
scoreReporter.value = score;
[scoreReporter reportScoreWithCompletionHandler:^(NSError *error) {
if (error != nil)
{
// handle the reporting error
NSLog(#"Error reporting score");
}
}];
}
-(void)postScore:(int64_t)score forCategory:(NSString *)category {
GKLeaderboard *query = [[GKLeaderboard alloc]init];
query.category = category;
if (query != nil)
{
[query loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil){
// Handle the error.
NSLog(#"Error loading scores");
}
if (scores != nil){
// Process the score.
int64_t newScore = query.localPlayerScore.value + score;
[self reportScore:newScore forCategory:category];
}
}];
}
[query release];
}
Thanks for any help.
EDIT: Sandbox leaderboard has the first score, but will not update the subsequent scores.
Having the same issue at my end. It will provide the score correctly for the first time in a session. After that, it keep sending back the same score even if we update the score in that session.
You need to check property of GKleaderBoard class.For Your Info. see below code.
GKLeaderboardViewController *leaderController = [[GKLeaderboardViewController alloc] init];
if (leaderboardController != NULL)
{
leaderController.category = self.currentLeaderBoard;
leaderController.timeScope = GKLeaderboardTimeScopeWeek;
leaderController.leaderboardDelegate = self;
[self presentModalViewController: leaderController animated: YES];
}
AND
you can also check apple docs for both GKLeaderBoard and GKAchievementViewController class below.
for GKLeaderBoard
http://developer.apple.com/library/ios/#documentation/GameKit/Reference/GKLeaderboard_Ref/Reference/Reference.html
for GKAchievementViewController
http://developer.apple.com/library/ios/#documentation/GameKit/Reference/GKAchievementViewController_Ref/Reference/Reference.html