Pause function while JSON call is running - ios

I would like my save button to run the JSON call and then finish it's function, but it finishes the function while running the JSON call, so the variables become initialized as nil.
My DeviceDetailViewController.m
//
// DeviceDetailViewController.m
// Steam Backpack Viewer
//
// Created by Vishwa Iyer on 5/22/14.
// Copyright (c) 2014 MoAppsCo. All rights reserved.
//
#import "DeviceDetailViewController.h"
#import "MasterViewController.h"
#import "ProfileManager.h"
#import "ProfileCommunicator.h"
#import "SteamProfile.h"
#import "DeviceViewController.h"
#interface DeviceDetailViewController () <ProfileManagerDelegate> {
ProfileManager *_manager;
NSArray *profile;
SteamProfile *s;
}
extern NSString *ID;
#end
#implementation DeviceDetailViewController
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (IBAction)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new managed object
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"BackpackViewer" inManagedObjectContext:context];
[newDevice setValue:self.steamIDTextField.text forKey:#"steamID"];
ID = [NSString stringWithFormat:#"%#", [newDevice valueForKey:#"steamID"]];
[self startFetchingGroups]; // I would like this JSON call to finish before calling the rest of the function below
[newDevice setValue:s.personaname forKey:#"steamName"];
[newDevice setValue:s.avatar forKey:#"imageURL"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_manager = [[ProfileManager alloc] init];
_manager.communicator = [[ProfileCommunicator alloc] init];
_manager.communicator.delegate = _manager;
_manager.delegate = self;
// Do any additional setup after loading the view.
}
- (void)startFetchingGroups
{
[_manager fetchGroups];
}
- (void)didReceieveProfileInfo:(NSArray *)groups
{
//the JSON call finishes here, when the groups are receives from the call. I would then like the rest of the save button method above to run after this runs, so that the s variable (which corresponds to a SteamProfile object) becomes initialized correctly.
profile = groups;
s = [profile objectAtIndex:0];
NSLog(s.personaname);
}
- (void)fetchingGroupsFailedWithError:(NSError *)error
{
NSLog(#"Error %#; %#", error, [error localizedDescription]);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

I think you looking for something like this. Note this syntax could be wrong it is untested. I will leave it to you to read the documentation on function call backs.
#interface MyClass: NSObject
{
void (^_completionHandler)(int someParameter);
}
- (void)startFetchingGroups:(void(^)(int))handler;
#end
#implementation MyClass
- (void)startFetchingGroups:(void(^)(void))handler
{
[_manager fetchGroups];
if (handler) {
handler();
}
}
#end
[var startFetchingGroups:^{
[newDevice setValue:s.personaname forKey:#"steamName"];
[newDevice setValue:s.avatar forKey:#"imageURL"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}];
The behavior of when a callback gets called depends on what the _manager fetchGroups actually does. You could also use delegation as some of the people in the comments suggested, and is definatly a clean solution as well.

Sorry for ugly formatting. This code does exactly what you want..
#import "DeviceDetailViewController.h"
#import "MasterViewController.h"
#import "ProfileManager.h"
#import "ProfileCommunicator.h"
#import "SteamProfile.h"
#import "DeviceViewController.h"
typedef void(^EmptyBlock_t)();
#interface DeviceDetailViewController () <ProfileManagerDelegate> {
ProfileManager *_manager;
NSArray *profile;
SteamProfile *s;
// 1
// here you define a block, an anonymous function pointer that will be called right after you callback is called..
EmptyBlock_t _blockAfterJSONFetched;
}
extern NSString *ID;
#end
#implementation DeviceDetailViewController
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (IBAction)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new managed object
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"BackpackViewer" inManagedObjectContext:context];
[newDevice setValue:self.steamIDTextField.text forKey:#"steamID"];
ID = [NSString stringWithFormat:#"%#", [newDevice valueForKey:#"steamID"]];
[self startFetchingGroups]; // I would like this JSON call to finish before calling the rest of the function below
// 2
// here you assign a value to your block. Notice that all objects inside block are called "retain" automatically. Also they a called "release" when you release the block itself..
_blockAfterJSONFetched=^{
[newDevice setValue:s.personaname forKey:#"steamName"];
[newDevice setValue:s.avatar forKey:#"imageURL"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
};
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_manager = [[ProfileManager alloc] init];
_manager.communicator = [[ProfileCommunicator alloc] init];
_manager.communicator.delegate = _manager;
_manager.delegate = self;
// Do any additional setup after loading the view.
}
- (void)startFetchingGroups
{
[_manager fetchGroups];
}
- (void)didReceieveProfileInfo:(NSArray *)groups
{
//the JSON call finishes here, when the groups are receives from the call. I would then like the rest of the save button method above to run after this runs, so that the s variable (which corresponds to a SteamProfile object) becomes initialized correctly.
profile = groups;
s = [profile objectAtIndex:0];
NSLog(s.personaname);
// 3
// finally after your callback is fired you check if block pointer is not null and if it is you call it as a casual function. Assigning nil in the end is optional..
if(_blockAfterJSONFetched){
_blockAfterJSONFetched();
_blockAfterJSONFetched=nil;
}
}
- (void)fetchingGroupsFailedWithError:(NSError *)error
{
NSLog(#"Error %#; %#", error, [error localizedDescription]);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

Related

Background fetch and refresh completed after viewDidLoad in iOS 10

I'm trying to implement background fetch as well as refresh in iOS 10.
I'm using XML parsing to parse the data and then storing it in a file in the document's directory. For parsing XML I'm using a custom class (XMLParser) that confirms the NSXMLParserDelegate protocol.
The background fetch works fine. But I'm having problems in displaying the refreshed data, both when I click on the refresh button as well as in viewDidLoad.
I'm calling the refreshData method in viewDidLoad.
Here's how far I've gotten.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//--Set background fetch--//
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
}
...
#pragma mark Background data fetch methods
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
NSDate *fetchStart = [NSDate date];
ArtsViewController *artsViewController = (ArtsViewController *)self.window.rootViewController;
[artsViewController fetchNewDataWithCompletionHandler:^(UIBackgroundFetchResult result) {
completionHandler(result);
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
NSLog(#"Background Fetch Duration: %f seconds", timeElapsed);
}];
}
ArtsViewController.h
#interface ArtsViewController : UIViewController <UIPageViewControllerDataSource>
#property BOOL newsAvailable;
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; // No problems here
#end
ArtsViewcontroller.m
#interface ArtsViewController ()
#property (nonatomic, strong) NSArray *arrNewsData;
-(void)refreshData;
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray;
#end
...
#implementation ArtsViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self refreshData];
//--Load the file that saves news--//
[self loadNews];
if (_newsAvailable == YES)
{
[self setupPageViewController];
}
else
{
[self showNoNewsMessage];
}
}
...
#pragma mark Data Fetch methods
-(void)refreshData{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
[self performNewFetchedDataActionsWithDataArray:dataArray];
}
else{
NSLog(#"%#", [error localizedDescription]);
}
}];
}
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray{
// 1. Initialize the arrNewsData array with the parsed data array.
if (self.arrNewsData != nil) {
self.arrNewsData = nil;
}
self.arrNewsData = [[NSArray alloc] initWithArray:dataArray];
// 2. Write the file and reload the view.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * docDirectory = [paths objectAtIndex:0];
NSString * newsFilePath = [NSString stringWithFormat:#"%#",[docDirectory stringByAppendingPathComponent:#"arts2"]]; // NewsFile
if (![self.arrNewsData writeToFile:newsFilePath atomically:YES]) {
_newsAvailable = NO;
NSLog(#"Couldn't save data.");
}
else
{
_newsAvailable = YES;
NSLog(#"Saved data.");
[self viewWillAppear:YES];
}
}
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
NSDictionary *latestDataDict = [dataArray objectAtIndex:0];
NSString *latestTitle = [latestDataDict objectForKey:#"title"];
NSDictionary *existingDataDict = [self.arrNewsData objectAtIndex:0];
NSString *existingTitle = [existingDataDict objectForKey:#"title"];
if ([latestTitle isEqualToString:existingTitle]) {
completionHandler(UIBackgroundFetchResultNoData);
NSLog(#"No new data found.");
}
else{
[self performNewFetchedDataActionsWithDataArray:dataArray];
completionHandler(UIBackgroundFetchResultNewData);
NSLog(#"New data was fetched.");
}
}
else{
completionHandler(UIBackgroundFetchResultFailed);
NSLog(#"Failed to fetch new data.");
}
}];
}
...
#pragma mark IBActions
- (IBAction)reloadNews:(UIBarButtonItem *)sender
{
[self viewDidLoad];
}
I've debugged the application and found that after viewDidLoad
completes execution, the data file is written but the view isn't
updated. I've also tried calling the refreshData method in the main
thread, but there's no change.
after viewDidLoad is complete the showNoNewNews method is called.
I'm suspecting that my logic isn't wrong but implementation is. Threads at play here..
Any help would be appreciated.
Update:
Hope this helps those with similar problems...
I moved the logic of viewDidLoad to a different method, called the method for the first time in viewDidLoad and again in refreshData, after
[self performNewFetchedDataActionsWithDataArray:dataArray];

What is the best way to fetch data in core data between two regular view controllers?

Sorry if the title is bad. What I'm trying to understand is: in the core data examples I have found online they explain how to use core data with nsfetchresultcontroller. But from what I understand nsfetchresultcontroller is made for table views. But I have only 2 regular view controllers.
It's really basic, I have 1 view controller for a create page to create a task, and another one which is the homepage with a label to show that single task that was created:
HomePageViewController.m:
#import "HomePageViewController.h"
#import "CreatTargetViewController.h"
#interface HomePageViewController ()
#property (weak, nonatomic) IBOutlet UILabel *currentTarget;
#end
#implementation HomePageViewController
- (IBAction)unwindToHomePage:(UIStoryboardSegue *)segue
{
CreatTargetViewController *source = (CreatTargetViewController *)[segue sourceViewController];
self.currentTarget.text = source.target.content;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
CreatTargetViewController.h:
#import <UIKit/UIKit.h>
#import "Targets.h"
#interface CreatTargetViewController : UIViewController
#property (nonatomic, strong) Targets *target;
#end
CreatTargetViewController.m:
#import "CreatTargetViewController.h"
#import "Targets.h"
#import "CoreDataStack.h"
#interface CreatTargetViewController ()
#property (weak, nonatomic) IBOutlet UIBarButtonItem *saveButton;
#property (weak, nonatomic) IBOutlet UITextView *myTextView;
#end
#implementation CreatTargetViewController
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.saveButton) return;
if (self.target != nil) {
self.myTextView.text = self.target.content;
}
}
- (void)insertNewTargetEntry
{
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
Targets *entry = [NSEntityDescription insertNewObjectForEntityForName:#"Targets" inManagedObjectContext:coreDataStack.managedObjectContext];
entry.content = self.myTextView.text;
[coreDataStack saveContext];
}
- (void)updateTargetEntry
{
self.target.content = self.myTextView.text;
CoreDataStack *stack = [CoreDataStack defaultStack];
[stack saveContext];
}
- (IBAction)saveWasPressed:(id)sender
{
if (self.target != nil) {
[self updateTargetEntry];
} else {
[self insertNewTargetEntry];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[self.myTextView becomeFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
and another class for the core data stack:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface CoreDataStack : NSObject
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
+ (instancetype)defaultStack;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
#end
CoreDataStack.m:
#import "CoreDataStack.h"
#implementation CoreDataStack
#pragma mark - Core Data stack
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
+ (instancetype)defaultStack
{
static CoreDataStack *defaultStack;
static dispatch_once_t onceToken;
dispatch_once (&onceToken, ^{
defaultStack = [[self alloc] init];
});
return defaultStack;
}
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "digitalCrown.LaserApp" in the application's documents directory.
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"LaserApp" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"LaserApp.sqlite"];
NSError *error = nil;
NSString *failureReason = #"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = #"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
How do I communicate the data here?

Error and Crash when using the Core Data to create a UITableView

I try to use Core Data to make a UITableView, but I come across a crash when I run it:
014-07-29 10:13:12.443 TableAndCoreData[797:60b] -[AppDelegate managedObjectContext]: unrecognized selector sent to instance 0x8f319a0
2014-07-29 10:13:12.446 TableAndCoreData[797:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[AppDelegate managedObjectContext]: unrecognized selector sent to instance 0x8f319a0'
I generally check it and I guess it might be the problem of the creating the managedObjectContext. Does any one have some idea to help me to fix this problem?
#interface DetailViewController ()
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *ageTextField;
#end
#implementation DetailViewController
// Set NSManagedObjectContext
- (NSManagedObjectContext *) managedOjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication]delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedOjectContext];
}
return context;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)cancelButton:(UIButton *)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)saveButton:(UIButton *)sender
{
[self save];
}
- (void)save
{
// Get ManagedObjectContext
NSManagedObjectContext *context = [self managedOjectContext];
// Create a ManagedObject
NSManagedObject *aPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
// Set value for the attributes of the entity
[aPerson setValue:self.nameTextField.text forKey:#"name"];
[aPerson setValue:self.ageTextField.text forKey:#"age"];
// Check the error
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't save due to %#%#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Besides, I am trying another way to create UITableView using Core Data:
- (void) save
{
// Create UIManagedDocument
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentDirectory = [[fileManager URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask]firstObject];
NSString *documentName = #"Model";
NSURL *url = [documentDirectory URLByAppendingPathComponent:documentName];
UIManagedDocument *document = [[UIManagedDocument alloc]initWithFileURL:url];
if ([fileManager fileExistsAtPath:[url path]]) {
[document openWithCompletionHandler:^(BOOL success) {
if (success) {
if (document.documentState == UIDocumentStateNormal) {
// Get a ManagedObjectContext
NSManagedObjectContext *context = document.managedObjectContext;
// Set managed object (entity)
NSManagedObject *aPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
// Set value for the attribute (which are "name" and "age") of the entity
[aPerson setValue:self.nameTextField.text forKey:#"name"];
[aPerson setValue:self.ageTextField.text forKey:#"age"];
// Check whether there is an error
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't save due to %#%#", error, [error localizedDescription]);
}
// Close the window
[self dismissViewControllerAnimated:YES completion:nil];
}
}
if (!success) {
NSLog(#"couldn't open document at %#", url);
}
}];
}
else {
[document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (success) {
if (document.documentState == UIDocumentStateNormal) {
// Get a ManagedObjectContext
NSManagedObjectContext *context = document.managedObjectContext;
// Set managed object (entity)
NSManagedObject *aPerson = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
// Set value for the attribute (which are "name" and "age") of the entity
[aPerson setValue:self.nameTextField.text forKey:#"name"];
[aPerson setValue:self.ageTextField.text forKey:#"age"];
// Check whether there is an error
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't save due to %#%#", error, [error localizedDescription]);
}
// Close the window
[self dismissViewControllerAnimated:YES completion:nil];
}
}
if (!success) {
NSLog(#"couldn't open document at %#", url);
}
}];
}
}
However, it just couldn't find the UIManagedDocument. I really wonder the reason why I should create a UIManagedDocument and the document name I should name it.
Your crash does not have anything to do with Core Data.
Change this:
[delegate performSelector:#selector(managedObjectContext)]
to:
[delegate respondsToSelector:#selector(managedObjectContext)]
The crash was happening because you were sending the message managedObjectContext to the application delegate object, which does not respond to that message. It was being sent because where you meant to check to see if it responds to that message with respondsToSelector:, you had performSelector instead. Your application delegate object still needs to implement managedObjectContext for your code to be functional, but the portions you have posted should no longer crash as you describe.
In general you want to avoid calling a method on the application delegate this way. It's preferred to pass a value like this from the application delegate into the root view controller at startup, and it's passed along to the next view controller and the next.
There is a slightly outdated, but still relevant section of the Core Data documentation that discusses this:
A view controller typically shouldn’t retrieve the context from a global object such as the application delegate—this makes the application architecture rigid. Neither should a view controller create a context for its own use (unless it’s a nested context).
...as well as the iOS 5 release notes:
Nested contexts make it more important than ever that you adopt the “pass the baton” approach of accessing a context (by passing a context from one view controller to the next) rather than retrieving it directly from the application delegate.

Can only save core data when text in message is altered? Xcode

So in Xcode, I have a notes app that utilizes core data. There are two fields, a title textfield and a message textview. One issue I'm having is that if the user makes a note and decides to go and edit it later, the user has to edit the message to have it be able to save again. I want it to save regardless if I change the message or title. Thanks
#import "DeviceDetailViewController.h"
#interface DeviceDetailViewController ()
#end
#implementation DeviceDetailViewController
#synthesize device;
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
#synthesize message;
#synthesize titles;
#synthesize button;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[button setEnabled:NO];
titles.delegate = self;
message.delegate = (id)self;
// Do any additional setup after loading the view.
if (self.device) {
[self.titles setText:[self.device valueForKey:#"name"]];
[self.message setText:[self.device valueForKey:#"version"]];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
if (self.device) {
// Update existing device
[self.device setValue:self.titles.text forKey:#"name"];
[self.device setValue:self.message.text forKey:#"version"];
} else {
// Create a new device
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Device" inManagedObjectContext:context];
[newDevice setValue:self.titles.text forKey:#"name"];
[newDevice setValue:self.message.text forKey:#"version"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (titles.text.length >= 35 && range.length == 0)
return NO;
return YES;
}
//Code beyond this point is disabling keyboard when you click outside of textfield
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[message resignFirstResponder];
[titles resignFirstResponder];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[titles resignFirstResponder];
return NO;
}
//Enabling/disabling 'Save' button based on texfield data
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([titles.text length] != 0 && [message.text length] != 0) {
[button setEnabled:YES];
}
else
{
[button setEnabled:NO];
}
return YES;
}
#end

Core-Data : Data is not been saved up

while following the ebook of http://timroadley.com/ i am inserting. but when i check in sqlite their is no data present in it.also i have used -com.apple.CoreData.SQLDebug to debug but no query is being shown.Source Code
Solution:- Data will only show in sqlite when i will terminate the app or the app will go in background after pressing home button.
AppDelegate.h
#import <UIKit/UIKit.h>
#import "CoreDataHelper.h"
#import <CoreData/CoreData.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic, strong, readonly) CoreDataHelper *coreDataHelper;
#end
AppDelegate.m
- (void)demo {
NSArray *newItemNames = [NSArray arrayWithObjects:
#"Apples", #"Milk", #"Bread", #"Cheese", #"Sausages", #"Butter", #"Orange Juice", #"Cereal", #"Coffee", #"Eggs", #"Tomatoes", #"Fish", nil];
for (NSString *newItemName in newItemNames) {
Item *newItem =
[NSEntityDescription insertNewObjectForEntityForName:#"Item" inManagedObjectContext:_coreDataHelper.context];
newItem.name = newItemName;
NSLog(#"Inserted New Managed Object for '%#'", newItem.name);
}
}
- (CoreDataHelper*)cdh {
if (!_coreDataHelper) {
_coreDataHelper = [CoreDataHelper new];
[_coreDataHelper setupCoreData];
}
return _coreDataHelper;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[self cdh] saveContext];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self cdh];
[self demo];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
[[self cdh] saveContext];
}
CoreDataHelper.h
#property(nonatomic,readonly) NSManagedObjectContext *context;
#property(nonatomic,readonly) NSManagedObjectModel *model;
#property(nonatomic,readonly) NSPersistentStore *store;
#property(nonatomic,readonly) NSPersistentStoreCoordinator *coordinator;
-(void)setupCoreData;
-(void)saveContext;
CoreDataHelper.m
#pragma mark - FILES
NSString *storeFilename = #"Grocery-Dude.sqlite";
#pragma mark - PATHS
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES) lastObject];
}
- (NSURL *)applicationStoresDirectory {
NSURL *storesDirectory =
[[NSURL fileURLWithPath:[self applicationDocumentsDirectory]]
URLByAppendingPathComponent:#"Stores"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:[storesDirectory path]]) {
NSError *error = nil;
if ([fileManager createDirectoryAtURL:storesDirectory
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
}
else {
NSLog(#"FAILED to create Stores directory: %#", error);}
}
return storesDirectory;
}
- (NSURL *)storeURL {
return [[self applicationStoresDirectory]
URLByAppendingPathComponent:storeFilename];
}
#pragma mark - SETUP
- (id)init {
self = [super init];
if (!self) {return nil;}
_model = [NSManagedObjectModel mergedModelFromBundles:nil];
_coordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:_model];
_context = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSMainQueueConcurrencyType];
[_context setPersistentStoreCoordinator:_coordinator];
return self;
}
- (void)loadStore {
if (_store) {return;} // Don’t load store if it's already loaded
NSDictionary *options =
#{NSSQLitePragmasOption: #{#"journal_mode": #"DELETE"}};
NSError *error = nil;
_store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[self storeURL]
options:options error:&error];
if (!_store) {NSLog(#"Failed to add store. Error: %#", error);abort();}
else {NSLog(#"Successfully added store: %#", _store);}
}
- (void)setupCoreData {
[self loadStore];
}
#pragma mark - SAVING
- (void)saveContext {
if ([_context hasChanges]) {
NSError *error = nil;
if ([_context save:&error]) {
NSLog(#"_context SAVED changes to persistent store");
} else {
NSLog(#"Failed to save _context: %#", error);
}
} else {
NSLog(#"SKIPPED _context save, there are no changes!");
}
}
Try saving item in the loop itself like
NSArray *newItemNames = [NSArray arrayWithObjects:
#"Apples", #"Milk", #"Bread", #"Cheese", #"Sausages", #"Butter", #"Orange Juice", #"Cereal", #"Coffee", #"Eggs", #"Tomatoes", #"Fish", nil];
for (NSString *newItemName in newItemNames) {
Item *newItem =
[NSEntityDescription insertNewObjectForEntityForName:#"Item" inManagedObjectContext:_coreDataHelper.context];
newItem.name = newItemName;
NSLog(#"Inserted New Managed Object for '%#'", newItem.name);
[[self cdh] saveContext];
}

Resources