Initialising CoreData in AppleScript-objc - objective-c

I'm trying to write and read from CoreData on OSX, combinig cocoa classes with applescript-objc.
I wrote methods to handle my CoreData as in tutorial (youtube, Cocoa Tutorial: Core Data Introduction in iOS and Mac OS Programming Part 2) <- those are cocoa classes in my project
I have a class called CoreDataHelper that defines methods to manipulate Core Data. Here are some of them:
+(NSManagedObjectContext *) managedObjectContext{
NSError*error;
[[NSFileManager defaultManager] createDirectoryAtPath:[CoreDataHelper directoryForDatabaseFilename] withIntermediateDirectories:YES attributes:nil error:&error];
if(error){
NSLog(#"%#", [error localizedDescription]);
return nil;
}
NSString* path = [NSString stringWithFormat:#"%#/%#",[CoreDataHelper directoryForDatabaseFilename],[CoreDataHelper databaseFilename]];
NSURL *url = [NSURL fileURLWithPath:path];
NSManagedObjectModel* managedModel = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator* storeCordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedModel];
if(![storeCordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]){
NSLog(#"%#",[error localizedDescription]);
return nil;
}
NSManagedObjectContext* managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
return managedObjectContext;
}
+(id)insertManagedObjectOfClass:(Class) aClass inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext{
NSManagedObject* managedObject = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(aClass) inManagedObjectContext:managedObjectContext];
return managedObject;
}
I'm using .xcdatamodeld for my Model.
I have 2 entities in it with classes for each one of them:
Tab
Service
Tab.h looks like this
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Service;
NS_ASSUME_NONNULL_BEGIN
#interface Tab : NSManagedObject
// Insert code here to declare functionality of your managed object subclass
#end
NS_ASSUME_NONNULL_END
#import "Tab+CoreDataProperties.h"
and Tab.m looks like this:
#import "Tab.h"
#import "Service.h"
#implementation Tab
// Insert code here to add functionality to your managed object subclass
#end
Tab+CoreDataProperties.h:
#import "Tab.h"
NS_ASSUME_NONNULL_BEGIN
#interface Tab (CoreDataProperties)
#property (nullable, nonatomic, retain) NSString *nZakladka;
#property (nullable, nonatomic, retain) NSString *uZakladka;
#property (nullable, nonatomic, retain) Service *podstronaSerwisu;
#end
NS_ASSUME_NONNULL_END
Tab+CoreDataProperties.m:
#import "Tab+CoreDataProperties.h"
#implementation Tab (CoreDataProperties)
#dynamic nZakladka;
#dynamic uZakladka;
#dynamic podstronaSerwisu;
#end
Now, my AppDelegate.applescript looks like this
...
property CoreDataHelper: class "CoreDataHelper"
property cService: class "Service"
property cTab: class "Tab"
script AppDelegate
on applicationWillFinishLaunching_(aNotification)
set theContext to CoreDataHelper's managedObjectContext()
set ccTab to cTab's alloc()'s init()
//the one below cause an error "CoreData: error: Failed to call designated initializer on NSManagedObject class 'Tab'"
set theTab to CoreDataHelper's insertManagedObjectOfClass_inManagedObjectContext_(ccTab, theContext)
end applicationWillFinishLaunching_
...
end Script
When I run whole thing I got an error message posted in a commented line in AppleScriptObjC code. The method that is causing an error is NSManagedObject* managedObject = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(aClass) inManagedObjectContext:managedObjectContext];
What is the problem?

Related

How to handle NSData as a #property

I'm trying to get a #property to work with NSData so that it becomes available to all methods in the class.
I've set up a little test project to help me debug the problem.
In my main.m is
FxPlug *data = [[FxPlug alloc] init];
[FxPlug makeCube];
NSLog (#"%#",[FxPlug.cubeData]);
and in the Class FxPlug.h is
#import <Foundation/Foundation.h>
#interface FxPlug : NSObject
#property NSData *cubeData;
+(void) makeCube;
#end
and FxPlug.m is
#import "FxPlug.h"
#implementation FxPlug
+ (void) makeCube; {
NSString *filePath = #"/Development/testNSDataProperty/test_cube_data.dat";
NSData *cubeData = [[NSData alloc] initWithContentsOfFile:filePath];
}
#end
So this is just a test to see if I can set the #property NSData *cubeData; in class FxPlug to the contents of a file and to see if it is passed back into the main.m class to NSLog out to the terminal.
I'd be really really appreciative of some help.
Basically, I'm trying to pass an NSData object between classes.
Many thanks!
Because my main project is an FxPlug, which is basically just one big class with a load of methods that are named specifically for the host app. So I need to add a new class method to that existing class to handle the NSData object. Or at least that's how I think it works!
If you want to use a property then you have to use and create a class instance and avoid using class methods, so:
FxPlug.h:
#import <Foundation/Foundation.h>
#interface FxPlug : NSObject
#property NSData *cubeData;
-(void) makeCube;
#end
FxPlug.m:
#import "FxPlug.h"
#implementation FxPlug
- (void) makeCube {
NSString *filePath = #"/Development/testNSDataProperty/test_cube_data.dat";
self.cubeData = [[NSData alloc] initWithContentsOfFile:filePath];
}
#end
main.m:
FxPlug *data = [[FxPlug alloc] init];
[data makeCube];
NSLog (#"%#", data.cubeData);
It can be done with class methods, and class-level variables, but not with #propertys. One halfway house is to use the singleton pattern where you only allow one instance of the class to exist at any one time.
EDIT: If you want to do it as a static class (not recommended), the code is below:
FxPlug.h:
#import <Foundation/Foundation.h>
#interface FxPlug : NSObject
+ (NSData *)cubeData;
+ (void)setCubeData:(NSData *)cubeData;
+ (void) makeCube;
#end
FxPlug.m:
#import "FxPlug.h"
static NSData *_cubeData = nil;
#implementation FxPlug
+ (NSData *)cubeData
{
return _cubeData;
}
+ (void)setCubeData:(NSData *)cubeData
{
_cubeData = cubeData;
}
+ (void) makeCube {
NSString *filePath = #"/Development/testNSDataProperty/test_cube_data.dat";
_cubeData = [[NSData alloc] initWithContentsOfFile:filePath];
}
#end
main.m:
[FxPlug makeCube];
NSLog (#"%#", [FxPlug cubeData]);

this method crashes if I use a property and works fine if I declare the variable within the method--why?

I'm working on a programmable calculator, and for the life of me I can't understand what I'm doing wrong.
Here are the relevant parts of the code. (The code is unfinished, so I know there's extra stuff floating around.)
CalculatorViewController.m
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController ()
#property (nonatomic) BOOL userIsEnteringNumber;
#property (nonatomic) BOOL numberIsNegative;
#property (nonatomic,strong) CalculatorBrain *brain;
#property (nonatomic) NSArray *arrayOfDictionaries;
#property (nonatomic) NSDictionary *dictionary;
#end
#implementation CalculatorViewController
#synthesize display = _display;
#synthesize history = _history;
#synthesize userIsEnteringNumber = _userIsEnteringNumber;
#synthesize numberIsNegative;
#synthesize brain = _brain;
#synthesize arrayOfDictionaries;
#synthesize dictionary;
-(CalculatorBrain *)brain
{
if (!_brain) _brain = [[CalculatorBrain alloc] init];
return _brain;
}
/*snip code for some other methods*/
- (IBAction)variablePressed:(UIButton *)sender
{
NSString *var = sender.currentTitle;
NSDictionary *dict = [self.dictionary initWithObjectsAndKeys:[NSNumber numberWithDouble:3],#"x",[NSNumber numberWithDouble:4.1],#"y",[NSNumber numberWithDouble:-6],#"z",[NSNumber numberWithDouble:8.7263],#"foo",nil];
[self.brain convertVariable:var usingDictionary:dict];
self.display.text = [NSString stringWithFormat:#"%#",var];
self.history.text = [self.history.text stringByAppendingString:sender.currentTitle];
[self.brain pushOperand:[dict objectForKey:var] withDictionary:dict];
}
#end
And here's CalculatorBrain.m.
#import "CalculatorBrain.h"
#interface CalculatorBrain ()
#property (nonatomic, strong) NSMutableArray *operandStack;
#end
#implementation CalculatorBrain
#synthesize operandStack = _operandStack;
-(void)pushOperand:(id)operand withDictionary:(NSDictionary *)dictionary
{
NSNumber *operandAsObject;
if (![operand isKindOfClass:[NSString class]])
{
operandAsObject = operand;
}
else
{
operandAsObject = [dictionary objectForKey:operand];
}
[self.operandStack addObject:operandAsObject];
}
-(double)popOperand
{
NSNumber *operandAsObject = [self.operandStack lastObject];
if (operandAsObject) [self.operandStack removeLastObject];
return [operandAsObject doubleValue];
}
-(double)convertVariable:(NSString *)variable usingDictionary:dictionary
{
double convertedNumber = [[dictionary objectForKey:variable] doubleValue];
return convertedNumber;
}
#end
The thing I'm having trouble understanding is in the CalculatorViewController.m method - (IBAction)variablePressed:(UIButton *)sender. This line crashes the program:
NSDictionary *dict = [self.dictionary initWithObjectsAndKeys:[list of objects and keys]];
But if I make it
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:[list of objects and keys]];
then everything works fine. But if I try to do
NSDictionary *dict = [[self.dictionary alloc] initWithObjectsAndKeys:[list of objects and keys]];
which seems to me the right thing to do, then XCode won't let me, so I'm obviously not understanding something.
Any thoughts?
+alloc allocates memory for an object. -init... methods initialize the object.
[self.dictionary initWithObjectsAndKeys:... calls -dictionary which is either going to return a dictionary set in that property or nil and then attempts to call init... on it. If the dictionary exists then you are attempting to initialize an object more than once which is not valid. If the property has not been set then the getter will return nil and sending an init... message to nil will do nothing. Either way this is not what you want to do.
[[self.dictionary alloc] init... is also invalid, as the compiler warns you. Now you try to obtain an object from -dictionary and then call the class method +alloc on it.
There seems to be some fundamental confusion here about how objects are created and what property accessors do. I'm not sure how to address that besides suggesting looking at object creation and dot syntax.

How to add CoreData to exisiting Tab Based IOS project

I have tried a lot of different answers, but I just can't seem to get this to work. I am trying to add Core Data to an existing Tab Based Project that I have. I added the core data framework through the targets, I set up the DataModel and entities correctly, but I can't seem to access it. I have gotten many different errors, but the most recent is:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Cannot create an NSPersistentStoreCoordinator with a nil model'
I set up a new utility based project using the preset Core Data and copied the code as directly. I simply changed the File URL name to what current project is and it doesn't work. Here is my code:
appDelegate.h
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
BOOL isNotFirstTime;
NSMutableArray *teamMembers;
NSMutableArray *projects;
NSMutableArray *tasks;
}
#property(readwrite, retain) NSMutableArray *teamMembers;
#property(readwrite, retain) NSMutableArray *projects;
#property(nonatomic, retain) NSMutableArray *tasks;
#property(nonatomic)BOOL isNotFirstTime;
#property (strong, nonatomic) UIWindow *window;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
- (NSURL *)applicationDocumentsDirectory;
- (void)saveContext;
#end
AppDelegate.m
For some reason when I create the File URL it remains null.....I have no idea why..
#import "AppDelegate.h"
#import "Task.h"
#import "Project.h"
#import "TeamMember.h"
#import "newTeamMemberWindow.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize tasks;
#synthesize teamMembers;
#synthesize projects;
#synthesize isNotFirstTime;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize managedObjectModel = __managedObjectModel;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
NSLog(#"No Context on App Load");
}
newTeamMemberWindow *newTeamMemberWindowObject = [[newTeamMemberWindow alloc]init];
newTeamMemberWindowObject.managedObjectContext = context;
return YES;
}
//Removed all normal methods to consolidate code on stack overflow
#pragma mark - Core Data stack
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil)
{
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created from the application's model.
*/
- (NSManagedObjectModel *)managedObjectModel
{
if (__managedObjectModel != nil)
{
return __managedObjectModel;
}
This part the modelURL remains Null.....
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"TimeLines" withExtension:#"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSLog(#"Created managedObjectModel with Url: %#", modelURL);
return __managedObjectModel;
}
/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"TimeLines.sqlite"];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&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.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
#pragma mark - Application's Documents directory
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#end
If I understood you have a separate project utility for coredata, if yes take a look at this question of mine. It made me crazy time ago !!!
How to include a bundle in main project xcode 4.1

Objective C Inheritence

I am trying to subclass some Core Data classes. I've got the following classes:
Core Data class:
#interface CDExplanatoryMaterial : NSManagedObject
#property (nonatomic, retain) NSString * document;
#property (nonatomic, retain) NSString * id;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * pageNumber;
#property (nonatomic, retain) NSNumber * realPageNumber;
#property (nonatomic, retain) NSString * url;
#end
Business logic class's protocol:
#protocol BLDataClass <NSObject>
- (NSArray*)favouriteInGroups;
#property (nonatomic, readonly, retain) NSString* type;
#property (nonatomic, readonly, retain) NSArray* inFavouriteGroups;
- (void)addAddToFavouriteGroup: (NSString*) groupName;
- (void)removeFromFavouriteGroup: (NSString*) groupName;
- (void)addToHistory;
#end
Interface for BLExplanatoryMaterial:
#interface BLExplanatoryMaterial : CDExplanatoryMaterial <BLDataClass>
I get the data like this:
+ (NSMutableArray*) explanatoryMaterials {
NSMutableArray* results = [[NSMutableArray alloc] init];
for(CDExplanatoryMaterial *item in [Helper fetchDataObjectsOfType:#"CDExplanatoryMaterial"])
{
[results addObject: (BLExplanatoryMaterial*)item];
}
return results;
}
The helper class looks like this:
#implementation Helper
+ (NSArray*) fetchDataObjectsOfType:(NSString *)type {
DataManager* manager = [DataManager sharedInstance];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:type inManagedObjectContext:manager.mainObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [manager.mainObjectContext executeFetchRequest:fetchRequest error:&error];
return fetchedObjects;
}
#end
This issue that I have is that the fetchedObjects array in fetchDataObjectsOfType and the results array in explanatoryMaterials only contain NSManagedObject objects. I'd expect fetchedObjects to contain CDExplanatoryMaterial objects and results to contain BLExplanatoryMaterial. I need the end result to be BLExplanatoryMaterial objects or I can't use any of my instance methods, what is the best way to do this?
Thanks,
Joe
EDIT:
Just to clarify it is the following code that fails because expMat is an NSManagedObject and doesn't support the addToFavouriteGroup method.
NSMutableArray* expMats = [Data explanatoryMaterials];
BLExplanatoryMaterial* expMat = (BLExplanatoryMaterial*) [expMats objectAtIndex:0];
[((BLExplanatoryMaterial*)expMat) addToFavouriteGroup:#"Test Group"]
One thing that I forgot to mention is that all of the code in my original post is in a static library. The code posted in this edit is in a IOS App project. I'm not sure if this makes a difference. All of the classes in the static library are marked as public.
You need to specify BLExplanatoryMaterial as the class for the entity in your managed object model. This will tell Core Data to instantiate objects of that class instead of NSManagedObject.
Thanks for all of the help. It turns out that because the core data classes were in a seperate static library they were not being built in to the main bundle. To get around this I subclassed them within my main application and then pointed the core data file at those subclasses.

Ignore "Failed to call designated initializer on NSManagedObject class

A core database keeps track of user info. The goal is to login using the core data, simple enough.
Simulating the app and logging in works perfectly.
Xcode does not show any errors or warnings.
Console output shows:
Failed to call designated initializer on NSManagedObject class 'Login'
Can I ignore this output ??
Login.h and Login.m are created by Xcode itself from the data model.
Login.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class User;
#interface Login : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * password;
#property (nonatomic, retain) User *user;
+ (User *)loginWithEmail:(NSString *)email withPassword:(NSString *)password inManagedObjectContext:(NSManagedObjectContext *)context;
#end
Login.m
#import "Login.h"
#import "User.h"
#interface Login ()
- (User *)isValidEmail:(NSString *)email inManagedObjectContext:(NSManagedObjectContext *)context;
#end
#implementation Login
#dynamic password;
#dynamic user;
- (User *)isValidEmail:(NSString *)email inManagedObjectContext:(NSManagedObjectContext *)context
{
User *user = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:#"email = %#", email];
NSError *error = nil;
user = [[context executeFetchRequest:request error:&error] lastObject];
[request release];
return user;
}
+ (User *)loginWithEmail:(NSString *)email withPassword:(NSString *)password inManagedObjectContext:(NSManagedObjectContext *)context
{
Login *loginHelper = [[Login alloc] init];
User *user = nil;
if ((user = [loginHelper isValidEmail:email inManagedObjectContext:context])) {
if ([user.login.password isEqualToString:password]) {
// correct login
} else {
// invalid password
user = nil;
}
} else {
// user does not exist
user = nil;
}
[loginHelper release];
return user;
}
#end
My understanding is that you typically don't alloc/init NSManagedObjects or subclasses of NSManagedObjects explicitly- CoreData handles instantiating and deallocating managed objects as necessary - but you are attempting to alloc/init your own subclass in your loginWithEmail method. So that's probably why you're getting the error.
In the broader sense, this implementation seems to blur the lines between what should be a cut-and-dry data model (your NSManagedObject subclass), and the application logic of "logging in" - so I'd recommend reconsidering your architecture just a bit to more firmly reflect model-view-controller principles! Happy coding.