fetching coredata in objective c - objective-c

I've been trying to learn Objective C and been following a tutorial teaching using CoreData.
I have made a viewcontroller with a cancel and save button, the tutorial provides the code, however after copying and pasting the code, I get errors.
Could it be that this tutorial i'm following is several years old, and in some way this code is outdated?
The error are from "NSManagedObject *newDevice...." to "if (![context save:&error])"
This is my code:
#import "DeviceDetailViewController.h"
#interface DeviceDetailViewController ()
#end
#implementation DeviceDetailViewController
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (IBAction)cancell:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new managed object
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Device" inManagedObjectContext:context];
[newDevice setValue:self.nameTextField.text forKey:#"name"];
[newDevice setValue:self.versionTextField.text forKey:#"version"];
[newDevice setValue:self.companyTextField.text forKey:#"company"];
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];
}
#end

It looks like you haven't imported the Core Data framework header file. You need a line at the top of the file reading
#import <CoreData/CoreData.h>

Related

How to use CoreData in Xcode 8?

I am trying use CoreData, but when I add it to my project I only get two new methods :
- (NSPersistentContainer *)persistentContainer
and
- (void)saveContext
Now I can't get old methods to work with CoreData, and I can't find any tutorials with these new methods and Objective-C. How can I save and get data from CoreData using persistentContainer in Xcode 8 with Objective-c?
You can Get context as -
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
or as in Objective-C
NSManagedObjectContext *context = ((AppDelegate*)[[UIApplication sharedApplication] delegate]).persistentContainer.viewContext;
And fetch data like -
var resultArray = try self.context.fetch(EntityName.fetchRequest())
or as in Objective-C
NSFetchRequest<EntityName *> *fetchRequest = [EntityName fetchRequest];
NSError *error ;
NSArray *resultArray= [context executeFetchRequest:fetchRequest error:&error];
And fetch data with sorting -
var resultArray = [EntityName]()
do {
let request : NSFetchRequest<EntityName> = EntityName.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "somekey", ascending: true)
let sortDescriptors = [sortDescriptor]
request.sortDescriptors = sortDescriptors
resultArray = try self.context.fetch(request)
} catch {
print("Error")
}
or as in Objective-C
NSFetchRequest<EntityName *> *fetchRequest = [EntityName fetchRequest];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"someKey" ascending:YES];
fetchRequest.sortDescriptors = #[sortDescriptor];
NSError *error ;
NSArray *resultArray= [context executeFetchRequest:fetchRequest error:&error];
And add data like -
let entityNameObj = EntityName(context: context)
entityNameObj.title = "title"
or as in Objective-C
NSManagedObject *entityNameObj = [NSEntityDescription insertNewObjectForEntityForName:#"EntityName" inManagedObjectContext:context];
[entityNameObj setValue:#"someValue" forKey:#"someKey"];
And save context like -
do {
try self.context.save()
} catch _ as NSError {
print("Error")
}
or as in Objective-C
[((AppDelegate*)[[UIApplication sharedApplication] delegate]) saveContext];
-(void)profileDatabase {
NSManagedObjectContext* context=[ADM.persistentContainer viewContext];
NSManagedObject *profile=[NSEntityDescription insertNewObjectForEntityForName:#"Profile" inManagedObjectContext:context];
[profile setValue:[self.serverResponseOfProfileDict objectForKey:#"firstName"] forKey:#"firstName"];
[profile setValue:[self.serverResponseOfProfileDict objectForKey:#"surName"] forKey:#"surName"];
[profile setValue:[self.serverResponseOfProfileDict objectForKey:#"batchID"] forKey:#"batchID"];
[profile setValue:[self.serverResponseOfProfileDict objectForKey:#"profileImagePath"] forKey:#"profileImagePath"];
[profile setValue:[self.serverResponseOfProfileDict objectForKey:#"registeredEmail"] forKey:#"registeredEmail"];
[profile setValue:[self.serverResponseOfProfileDict objectForKey:#"role"] forKey:#"role"];
[profile setValue:[self.serverResponseOfProfileDict objectForKey:#"studentID"] forKey:#"studentID"];
NSLog(#"userObj:%#",profile);
NSError* error;
[context save:&error];
NSFetchRequest *fetchRequest=[[NSFetchRequest alloc]initWithEntityName:#"Profile"];
fetchRequest.returnsObjectsAsFaults=NO;
NSArray* results=[context executeFetchRequest:fetchRequest error:&error];
NSLog(#"Result:%#",results);
NSManagedObject *result=[results objectAtIndex:0];
[ADM.databaseResponseOfProfileDict setObject:[result valueForKey:#"firstName"] forKey:#"firstName"];
[ADM.databaseResponseOfProfileDict setObject:[result valueForKey:#"surName"] forKey:#"surName"];
[ADM.databaseResponseOfProfileDict setObject:[result valueForKey:#"batchID"] forKey:#"batchID"];
[ADM.databaseResponseOfProfileDict setObject:[result valueForKey:#"profileImagePath"] forKey:#"profileImagePath"];
[ADM.databaseResponseOfProfileDict setObject:[result valueForKey:#"registeredEmail"] forKey:#"registeredEmail"];
[ADM.databaseResponseOfProfileDict setObject:[result valueForKey:#"role"] forKey:#"role"];
[ADM.databaseResponseOfProfileDict setObject:[result valueForKey:#"studentID"] forKey:#"studentID"];
NSLog(#"dic:%#",ADM.databaseResponseOfProfileDict);}
I have found a solution using Objective C. It runs, but I'm not sure that it is the correct solution.
- (void)dbManager {
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context 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();
}
NSManagedObject *customAnimal = [NSEntityDescription insertNewObjectForEntityForName:#"Animals" inManagedObjectContext:context];
[customAnimal setValue:#"Lion" forKey:#"type"];
[customAnimal setValue:#"Rabit" forKey:#"name"];
[customAnimal setValue:#"Blue" forKey:#"color"];
[customAnimal setValue:#12 forKey:#"age"];
NSLog(#"Get data from DB");
NSMutableArray* animalsArray;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Animals"];
animalsArray = [[context executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSLog(#"array is %#", animalsArray); // array is (
"<Animals: 0x6000000aee80> (entity: Animals; id: 0x60000022e120 <x-coredata:///Animals/tAAC7332D-6BEF-441C-9041-0ECB57469FA62> ; data: {\n age = 12;\n color = Blue;\n name = Rabit;\n type = Lion;\n})"
}
For all the beginners out there, this will give you the basic idea.
welcome.m
==========
#import "welcomepage.h"
#import "Register.h"
#import "login.h"
#import "AppDelegate.h"
#import "Student+CoreDataProperties.h"
#import "loginbtn.h"
#import "King+CoreDataProperties.h"
#interface welcomepage ()
{
AppDelegate *a;
NSManagedObjectContext *context;
}
#end
#implementation welcomepage
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (IBAction)login:(id)sender
{
loginbtn *lb=[[loginbtn alloc]init];
[self.navigationController pushViewController:lb animated:YES];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)register:(id)sender
{
a=(AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context1=((AppDelegate *)[UIApplication sharedApplication].delegate).persistentContainer.viewContext;
Student *ss=[NSEntityDescription insertNewObjectForEntityForName:#"Student" inManagedObjectContext:context1];
ss.name=[NSString stringWithFormat:#"%#",_txtfld1.text];
ss.age=[NSString stringWithFormat:#"%#",_txtfld2.text];
ss.place=[NSString stringWithFormat:#"%#",_txtfld3.text];
[a saveContext];
if (_txtfld1.text.length && _txtfld2.text.length && _txtfld3.text.length != 0)
{
UIAlertView *al=[[UIAlertView alloc]initWithTitle:#"THANK YOU" message:#"DATA SUCESSFULLY SAVER. YOU CAN LOGIN NOW WITH YOUR PASSWORD AND NAME" delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[al show];
_txtfld1.text=#"";
_txtfld2.text=#"";
_txtfld3.text=#"";
Register *rg=[[Register alloc]init];
[self.navigationController pushViewController:rg animated:YES];
}
else
{
UIAlertView *al2=[[UIAlertView alloc]initWithTitle:#"WARRNING" message:#"PLEASE FILL THE DATA COMPLETELY" delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[al2 show];
}
}
===================================================
Register.m
==========
#import "Register.h"
#import "AppDelegate.h"
#import "Student+CoreDataProperties.h"
#import "welcomepage.h"
#interface Register ()<UITableViewDelegate,UITableViewDataSource>
{
AppDelegate *a;
NSManagedObjectContext *context01;
NSArray *array01;
}
#end
#implementation Register
- (void)viewDidLoad
{
[super viewDidLoad];
a=((AppDelegate *)[UIApplication sharedApplication].delegate);
context01=((AppDelegate *)[UIApplication sharedApplication].delegate).persistentContainer.viewContext;
// specifying nsrequest and nsentity
NSFetchRequest *req=[[NSFetchRequest alloc]init];
NSEntityDescription *entity01=[NSEntityDescription entityForName:#"Student" inManagedObjectContext:context01];
[req setEntity:entity01];
// putting datas from reto array
NSError *err=nil;
array01=[context01 executeFetchRequest:req error:&err];
// Do any additional setup after loading the view from its nib.
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return array01.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ci=#"hai";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ci];
if (cell==nil) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ci];
}
if (indexPath.row==0) {
cell.textLabel.text=[[array01 objectAtIndex:indexPath.section]valueForKey:#"name"];
}
if (indexPath.row==1) {
cell.textLabel.text=[[array01 objectAtIndex:indexPath.section]valueForKey:#"age"];
}
if (indexPath.row==2) {
cell.textLabel.text=[[array01 objectAtIndex:indexPath.section]valueForKey:#"place"];
}
return cell;
}
==================================================
Loginbtn.m
============
#import "loginbtn.h"
#import "AppDelegate.h"
#import "Student+CoreDataProperties.h"
#import "login.h"
#interface loginbtn ()
{
AppDelegate *a;
NSArray *arraylb;
}
#end
#implementation loginbtn
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)lgbtn:(id)sender
{
a=(AppDelegate *)[UIApplication sharedApplication].delegate;
//xcode 8.2.1 specification code
NSManagedObjectContext *context=((AppDelegate *)[UIApplication sharedApplication].delegate).persistentContainer.viewContext;
//creating feathch request and entity
NSFetchRequest *req=[[NSFetchRequest alloc]init];
NSEntityDescription *entity=[NSEntityDescription entityForName:#"Student" inManagedObjectContext:context];
[req setEntity:entity];
// setting predicate
NSPredicate *pre=[NSPredicate predicateWithFormat:#"age like %# and place like %#",_txt1.text,_txt2.text];
[req setPredicate:pre];
//creating error and array to store
NSError *err=nil;
arraylb=[[NSArray alloc]init];
arraylb=[context executeFetchRequest:req error:&err];
login *lg=[[login alloc]init];
if (arraylb.count!=0)
{
lg.array001=arraylb;
}
//
// if (arraylb.count!=0)
//// {
////
//// lv *lvi=[[lv alloc]init];
////
//// //passing value
//// lvi.array001=arraylb;
////
////
//// //lv.str1=self.tf1.text;
//// //lv.str2=self.tf2.text;
//// [self.navigationController pushViewController:lvi animated:YES];
////
////
//// }
//// else
//// {
//
// self.lb.text=#"Invalid username & password";
//
// }
//
[self.navigationController pushViewController:lg animated:YES];
}
=====================================================
Lohin.m
=========
#import "login.h"
#import "Student+CoreDataProperties.h"
#import "AppDelegate.h"
#import "loginbtn.h"
#interface login ()
{
AppDelegate *a;
NSArray *array01;
}
#end
#implementation login
- (void)viewDidLoad
{
[super viewDidLoad];
_txt11.text=[[_array001 objectAtIndex:0]valueForKey:#"name"];
_txt22.text=[[_array001 objectAtIndex:0]valueForKey:#"age"];
_txt33.text=[[_array001 objectAtIndex:0]valueForKey:#"place"];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
// Dispose of any resources that can be recreated.
}

Unable to "pop" view controller using "Show" segue in XCode 6

I have an app that allows you to scan a QR code for meta data. The root view controller has two text fields, and allows you to fill one of those fields with the scanner. The button to access the scanner uses the "Show" segue to push the Scanning view onto the navigation stack.
My intention is that once a valid scan has been completed, the view controller will pass data back to the parent controller, and then be removed.
Being that the view has been pushed, I should be able to implement popViewControllerAnimated, but this does not work. I have also tried iterating through the view controllers in the navigation stack, matching the class I'm trying to pop to and using popToViewController, but am still stuck with the view that I'm trying to pop off the stack.
My viewcontroller.m
#interface ScanQRViewController ()
#end
#implementation ScanQRViewController
#synthesize scanPreview, scanPreviewLayer, scanSession, addyString, delegate;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
scanSession = nil;
[self startScanning];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark QR code scanning
-(void)startScanning {
addyString = nil;
NSError *error;
// create capture device and input
AVCaptureDevice *capDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:capDevice error:&error];
// error checking
if(!input) {
NSLog(#"%#", [error localizedDescription]);
}
// init the capture session
scanSession = [[AVCaptureSession alloc] init];
[scanSession addInput:input];
AVCaptureMetadataOutput *metaOutput = [[AVCaptureMetadataOutput alloc] init];
[scanSession addOutput:metaOutput];
// assign to dispatch queue
dispatch_queue_t dispatchQueue;
dispatchQueue = dispatch_queue_create("qrQueue", NULL);
[metaOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
[metaOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];
// create camera view for user
scanPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:scanSession];
[scanPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[scanPreviewLayer setFrame:scanPreview.layer.bounds];
[scanPreview.layer addSublayer:scanPreviewLayer];
// start running sesssion
[scanSession startRunning];
}
- (void)stopScanning {
[scanSession stopRunning];
scanSession = nil;
[scanPreviewLayer removeFromSuperlayer];
}
#pragma mark AV Delegate Methods
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
// check for objects
if (metadataObjects != nil && [metadataObjects count] > 0) {
//get the last object
AVMetadataMachineReadableCodeObject *metaObj = [metadataObjects objectAtIndex:0];
if([[metaObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
// remove url string if exists
if ([[[metaObj stringValue] substringToIndex:9] isEqualToString:#"zetacoin:"]) {
addyString = [[metaObj stringValue] substringFromIndex:9];
} else {
addyString = [metaObj stringValue];
}
}
[self stopScanning];
[self dismissView];
}
}
#pragma mark - Navigation
- (void)dismissView {
[delegate ScanQRCodeDidFinish:self];
[self.navigationController popViewControllerAnimated:YES];
}
#end
So I figured out the issue to this problem. Essentially when passing the data back to the parent controller to the delegate, I wasn't on the main thread. Therefore it would eventually timeout and return to the view, but very slowly. My two views:
QRScanner.m < The scanning view
#interface ScanQRViewController ()
#end
#implementation ScanQRViewController
#synthesize scanPreview, scanPreviewLayer, scanSession, addyString, delegate;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
scanSession = nil;
[self startScanning];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark QR code scanning
-(void)startScanning {
addyString = nil;
NSError *error;
// create capture device and input
AVCaptureDevice *capDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:capDevice error:&error];
// error checking
if(!input) {
NSLog(#"%#", [error localizedDescription]);
}
// init the capture session
scanSession = [[AVCaptureSession alloc] init];
[scanSession addInput:input];
AVCaptureMetadataOutput *metaOutput = [[AVCaptureMetadataOutput alloc] init];
[scanSession addOutput:metaOutput];
// assign to dispatch queue
dispatch_queue_t dispatchQueue;
dispatchQueue = dispatch_queue_create("qrQueue", NULL);
[metaOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
[metaOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];
// create camera view for user
scanPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:scanSession];
[scanPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[scanPreviewLayer setFrame:scanPreview.layer.bounds];
[scanPreview.layer addSublayer:scanPreviewLayer];
// start running sesssion
[scanSession startRunning];
}
- (void)stopScanning {
[scanSession stopRunning];
scanSession = nil;
[scanPreviewLayer removeFromSuperlayer];
}
#pragma mark AV Delegate Methods
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
// check for objects
if (metadataObjects != nil && [metadataObjects count] > 0) {
//get the last object
AVMetadataMachineReadableCodeObject *metaObj = [metadataObjects objectAtIndex:0];
if([[metaObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
// remove url string if exists
if ([[[metaObj stringValue] substringToIndex:9] isEqualToString:#"zetacoin:"]) {
addyString = [[metaObj stringValue] substringFromIndex:9];
} else {
addyString = [metaObj stringValue];
}
}
[self stopScanning];
[self dismissView];
}
}
#pragma mark - Navigation
- (void)dismissView {
NSLog(#"%#", self.navigationController);
[delegate ScanQRCodeDidFinish:self];
}
#end
AddAddress.m < The view I was trying to return to
#import "AddAddressViewController.h"
#import "ScanQRViewController.h"
#interface AddAddressViewController ()
#end
#implementation AddAddressViewController
#synthesize nameField, addressField, addressText;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationController.navigationItem.backBarButtonItem.title = #"Back";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated {
// check to see if there's an address (QR) add to text field
if (addressText != nil) {
addressField.text = addressText;
NSLog(#"Address: %#", addressText); // debugging
}
}
#pragma mark delegate methods
- (void)ScanQRCodeDidFinish:(ScanQRViewController *)sqrvc {
if (![NSThread isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
addressField.text = sqrvc.addyString;
[self.navigationController popViewControllerAnimated:YES];
});
} else {
addressField.text = sqrvc.addyString;
[self.navigationController popViewControllerAnimated:YES];
}
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
ScanQRViewController *sqvc = [segue destinationViewController];
sqvc.delegate = self;
}
#end
By adding the dispatch_sync(dispatch_get_main_queue(), ^{ it executed on the main thread and returned back the view as expected.

Reloading NSTableView data from a AFNetworking callback

Edit: as it turned out I was going completely the wrong way with this. I discovered that when I tried to wire a button to call a method on the controller, it still crashed, even when that controller action did absolutely nothing. This led me to wonder, and I realized in my main window controller, I was creating another controller and then moving on with no references to that new controller. So essentially my crash was due to the view controller being deallocated, not anything to do with reloading the table or fetching data. I've solved this by adding a strong property for the child view controller in the main window controller, though I'm not sure this is 100% the best strategy.
Driving me slightly batty here... I'm trying to do an AFNetworking call and reload an NSTable when I get the results. This, however, crashes with rather unhelpful errors. I am guessing that this is due to a threading issue, but various methods I've tried to avoid this (such as using __block) haven't helped. I'm at a loss as to how to get this to work.
Here is the hopefully trimmed down relevant code.
// Controller.h
#property (strong, nonatomic) NSMutableArray *messages;
// Controller.m
#synthesize messages = _messages;
- (id)initWithNibName:(NSString *)nibNameOrNil room: (Room *) theRoom bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_messages = [[NSMutableArray alloc] init];
}
}
- (void)loadView {
[super loadView];
[self updateData];
}
- (void) updateData {
...
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *responseObjs = responseObject[#"messages"];
for (NSDictionary *message in responseObjs) {
Message *m = [[Message alloc] init];
m.property = message[#"property];
...
[_messages addObject: m];
}
[messagesTableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView {
return [_messages count];
}
- (NSView *) tableView: (NSTableView *) tableView viewForTableColumn: (NSTableColumn *) tableColumn row:(NSInteger) row {
NSTableCellView *cellView = [tableView makeViewWithIdentifier:#"MainCell" owner: self];
[cellView.textField setStringValue: #"foo"];
return cellView;
}
The error I'm getting is: Thread 1: EXC_BAD_ACCESS (code=EXC_i386_GPFLT)
Any clues? If I skip the reloadData call then it runs without crashing, though obviously shows no data.

Grand Central Dispatch: App crashes on instance member assignment

To start with it all works without using the the GCD but I want this happening in a separate thread so trying GCD. I've got a login screen where on pressing the login button i've got the following action:
- (void)login
{
dispatch_queue_t buckyballLoginFetcherQ = dispatch_queue_create("Login Queue", NULL);
dispatch_async(buckyballLoginFetcherQ, ^
{
NSDictionary *resultDictionary = [MyService login:self.name.text password:self.password.text];
self.userDetails = [resultDictionary valueForKey:USER_DETAILS_ATTRIBUTE];
[self performSegueWithIdentifier:#"Login" sender:self];
});
}
In MyService method being called above:
+ (NSDictionary *)executeRequest:(NSDictionary *)requestDictionary
{
// Prepare the URL request and do the following
NSData *results = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&urlRequestError];
// Process results
...
}
NOW the bit that crashes:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Login"])
{
MyDestinationTableViewController *myDestinationTableViewController = nil;
UITabBarController *tbc = (UITabBarController *)[segue destinationViewController];
for (UIViewController *vc in [tbc viewControllers])
{
if ([vc isKindOfClass:[UINavigationController class]])
{ // in our case all view controlers are navigation controllers :-)
UINavigationController *nc = (UINavigationController *)vc;
if ([[[nc viewControllers] lastObject] isKindOfClass:[BuckyballsTableViewController class]])
{
myDestinationTableViewController = [[nc viewControllers] lastObject];
/**************CRASH LINE************/
buckyballsTableViewController.userDetails = self.userDetails;
}
}
}
}
Again without GCD it works, but it holds up screen so i'd want to do it asynchronously. Is it the instance member causing a problem? OR do i need to use it differently or do more with it? Thank you...
Only the main thread may manipulate the UI, so use this code fragment to call those bits on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"Login" sender:self];
});

NSFetchedResultsController doesn't fetch results even though items are created in the database

I'm new to iOS, so apologies if this is brain dead simple... I've been iteratively working through some small proof of concept apps before starting to implement my full app so that it wouldn't be as overwhelming. I had my table view working fine when I created it following the "Your Second iOS App" tutorial on the Apple website. Now I've tried creating it in a tab bar app though, and I'm seeing problems with the NSFetchedResultsController, and I'm not sure if it's related to something that I'm doing wrong in the Storyboard, or something else.
I have a Tab Bar Controller that connects to a Table View Controller (CatalogViewController.h/m) that is embedded in a Navigation Controller. The Table View Controller is configured to have static cells. In the first static cell I have a push segue to another Table View Controller (FoodCatalogViewController.h/m) which is configured to use dynamic prototypes - this is the view in which I expect to see the objects from my database (from the Food entity - currently just shows name and calories). This view has an "Add" button to create new entries in the database - the add button has a modal segue to another static table view (AddFoodViewController.h/m) that is embedded in it's own navigation controller. I know that the "Add" button is working and that it's view is correctly connecting to the database (i.e. I'm passing/setting the NSManagedObjectContext correctly), because if I open the app's sqlite database file using "SQLite Database Browser", I see the items that I've added in the simulator. I just don't understand why they're not getting displayed in my table view via the NSFetchedResultsController. I stepped through the code using breakpoints and confirmed that the performFetch code is being called in my FoodCatalogViewController's fetchedResultsController function. I added a debug NSLog line in the numberOfRowsInSection code, and it seems to be nil, so I never get into cellForRowAtIndexPath or configureCell. So it looks like the NSFetchedResultsController is the culprit - I just don't know why it's not fetching the results correctly, and what I can do to debug this further. Can anyone help me with this?
In order to pass the Core Data info through the hierarchy, I have the following code snippets:
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// Setup the Catalogs Tab
UINavigationController *navigationController = [[tabBarController viewControllers] objectAtIndex:0];
CatalogViewController *catalogViewController = [[navigationController viewControllers] objectAtIndex:0];
catalogViewController.managedObjectContext = self.managedObjectContext;
return YES;
}
CatalogViewController.m (the first table view controller in the sequence - I pass the NSManagedObjectContext through to it):
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"BrowseFoodCatalog"]) {
[[segue destinationViewController] setManagedObjectContext:self.managedObjectContext];
}
}
FoodCatalogViewController.h (the second table view controller in the sequence - I use the NSManagedObjectContext to setup the NSFetchedResultsController):
#interface FoodCatalogViewController : UITableViewController <NSFetchedResultsControllerDelegate>
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (void) addFoodWithName:(NSString *)name calories:(NSNumber *)calories;
#end
FoodCatalogViewController.m (the second table view controller in the sequence - I use the NSManagedObjectContext to setup the NSFetchedResultsController):
#interface FoodCatalogViewController () <AddFoodViewControllerDelegate>
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation FoodCatalogViewController
#synthesize fetchedResultsController = __fetchedResultsController;
#synthesize managedObjectContext = __managedObjectContext;
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Food" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"FoodCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[managedObject valueForKey:#"name"] description];
NSNumber *calorieNum = [managedObject valueForKey:#"calories"];
cell.detailTextLabel.text = [[calorieNum stringValue] description];
}
Additional Info
Not sure if this is relevant, but in order to get CoreData to automatically be included in my project, I started with the Single View template, but modified it's TemplateInfo.plist to add the following line under the similar line for storyboarding:
<string>com.apple.dt.unit.coreDataCocoaTouchApplication</string>
I'd found this online somewhere in someone's forum or something. Could this have messed up the CoreData somehow?
Additional Code
As requested, here's the code that I use to add new elements to the database:
AddFoodViewController.h:
#import <UIKit/UIKit.h>
#protocol AddFoodViewControllerDelegate;
#interface AddFoodViewController : UITableViewController <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *foodNameInput;
#property (weak, nonatomic) IBOutlet UITextField *caloriesInput;
#property (weak, nonatomic) id <AddFoodViewControllerDelegate> delegate;
- (IBAction)save:(id)sender;
- (IBAction)cancel:(id)sender;
#end
#protocol AddFoodViewControllerDelegate <NSObject>
- (void)addFoodViewControllerDidCancel:(AddFoodViewController *)controller;
- (void)addFoodViewControllerDidSave:(AddFoodViewController *)controller name:(NSString *)name calories:(NSNumber *)calories;
#end
AddFoodViewController.m:
#import "AddFoodViewController.h"
#implementation AddFoodViewController
#synthesize foodNameInput;
#synthesize caloriesInput;
#synthesize delegate = _delegate;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[self setFoodNameInput:nil];
[self setCaloriesInput:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if ((textField == self.foodNameInput) || (textField == self.caloriesInput )) {
[textField resignFirstResponder];
}
return YES;
}
- (IBAction)save:(id)sender {
int caloriesInt = [self.caloriesInput.text intValue];
NSNumber *caloriesNum = [NSNumber numberWithInt:caloriesInt];
[[self delegate] addFoodViewControllerDidSave:self name:self.foodNameInput.text calories:caloriesNum];
}
- (IBAction)cancel:(id)sender {
[[self delegate] addFoodViewControllerDidCancel:self];
}
#end
FoodCatalogViewController.m (the AddFoodViewControllerDelegate protocol code to add to the database):
- (void)addFoodViewControllerDidCancel:(AddFoodViewController *)controller {
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)addFoodViewControllerDidSave:(AddFoodViewController *)controller name:(NSString *)name calories:(NSNumber *)calories {
if ([name length]) {
[self addFoodWithName:name calories:calories];
[[self tableView] reloadData];
}
[self dismissModalViewControllerAnimated:YES];
}
- (void) addFoodWithName:(NSString *)name calories:(NSNumber *)calories {
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSLog(#"entity name is %#", [entity name]);
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:name forKey:#"name"];
[newManagedObject setValue:calories forKey:#"calories"];
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
NSString* uuid = [NSString stringWithString:(__bridge NSString *)uuidStringRef];
[newManagedObject setValue:uuid forKey:#"uuid"];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
More Debug Info
Strange - it looks like the fetchedResultsController isn't working correctly in the FoodCatalogViewController even though the managedObjectContext seems to be working... Here's the modified fetchedResultsController function from FoodCatalogViewController.m with some debug NSLog statements and replacing self.fetchedResultsController with __fetchedResultsController (because I wondered if that was causing the problem).
Here's the output from the fetchedResultsController function NSLog calls:
2012-01-29 10:22:21.118 UltraTrack[19294:fb03] Result: (
"<Food: 0x6e651b0> (entity: Food; id: 0x6e64630 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p1> ; data: <fault>)",
"<Food: 0x6e653e0> (entity: Food; id: 0x6e61870 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p3> ; data: <fault>)",
"<Food: 0x6e65450> (entity: Food; id: 0x6e64420 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p4> ; data: <fault>)",
"<Food: 0x6e654c0> (entity: Food; id: 0x6e64430 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p5> ; data: <fault>)",
"<Food: 0x6e65530> (entity: Food; id: 0x6e64e80 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p2> ; data: <fault>)",
"<Food: 0x6e655b0> (entity: Food; id: 0x6e64e90 <x-coredata://8A10B827-9F10-4760-934C-0061A982B73C/Food/p6> ; data: <fault>)"
)
2012-01-29 10:22:21.907 UltraTrack[19294:fb03] Number or objects: 6
And here's the modified fetchedResultsController function:
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Food" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
__fetchedResultsController = aFetchedResultsController;
NSArray *result = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
NSLog(#"Result: %#", result);
NSError *error = nil;
if (![__fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"Number or objects: %d", [__fetchedResultsController.fetchedObjects count]);
return __fetchedResultsController;
}
Someone suggested that the sections were the problem, so I hard-coded numberOfSectionsInTableView to return 1, and then the first object from the fetchResults seems to be handled correctly, but I get the following exception:
2012-01-29 10:29:27.296 UltraTrack[19370:fb03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no object at index 1 in section at index 0'
If I hardcode numberOfRowsInSection to also return 1, then the first object from my database is correctly displayed in the table view. What could be the problem with regard to the sections info in the fetchedResultsController? Could I have setup something incorrectly in the storyboard for the table view with regard to sections?
Here's the 2 table view functions where I've tried the hard-coding:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
//NSLog(#"Number of sections in table view is %d", [[self.fetchedResultsController sections] count]);
//return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Number or objects: %d", [self.fetchedResultsController.fetchedObjects count]);
// If I return 1, the object is displayed correctly, if I return count, I get the exception
//return 1;
return [self.fetchedResultsController.fetchedObjects count];
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSLog(#"Number of rows being returned is %d", [sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
From your description it seems that the code with the sections is the culprit. But you are not actually using any sections. So try this to simplify:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.fetchedResultsController.fetchedObjects count];
}
If this still returns zero rows, check your fetchedObjects.count in the fetchedResultsController getter method.