Copying plist to App Group Container - iOS 8 - plist

I am adding an App Group to my app for sharing a single plist between the app and the watch. I used to copy a plist from the bundle to Documents for when the app first started up. But with the watch I am now trying to convert it to save to the container but it always seems to be empty. The targets have app group enabled, and I am using the right name in my code. What could be going wrong?
Old Way
// COPY PLIST TO DOCUMENTS
NSFileManager *fileManger=[NSFileManager defaultManager];
NSError *error;
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath=[pathsArray objectAtIndex:0];
NSString *destinationPath= [doumentDirectoryPath stringByAppendingPathComponent:#"badger.com.vacations.plist"];
NSLog(#"plist path %#",destinationPath);
if (![fileManger fileExistsAtPath:destinationPath]){
NSString *sourcePath=[[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:#"badger.com.vacations.plist"];
[fileManger copyItemAtPath:sourcePath toPath:destinationPath error:&error];
}
New way - not working
// COPY PLIST TO CONTAINER
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.xxxxx.xxx.container"];
containerURL = [containerURL URLByAppendingPathComponent:#"name.com.data.plist"];
NSString *destinationPath= containerURL.path;
NSLog(#"destinationPath %#", containerURL);
NSString *sourcePath=[[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:#"name.com.data.plist"];
[fileManger copyItemAtPath:sourcePath toPath:containerURL.path error:&error];

You can´t share a plist as a file (or I simply don´t know about this feature), instead you just generate a new NSUserDefaults instance, which is shareable between targets.
Have a look here:
https://devforums.apple.com/message/977151#977151
or the Apple documentation
https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html
In the Apple Member Center, under 'Identifiers > App Groups' register a new App Group (e.g. group.de.myApp.sharedGroup)
In the Apple Member Center, under 'Identifiers > App IDs' refresh your App Ids for the targets that need sharing to use the App Groups feature
Regenerate all needed Provisioning Profiles and get them into Xcode
Back to Xcode: Under 'Capabilities' in each of your targets that need to share data, set 'App Groups' to on and add the previously registered App group.
Talk to the shareable NSUserDefaults container like this:
Store stuff:
NSUserDefaults *groupDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.de.myApp.sharedGroup"];
[groupDefaults setInteger:1337 forKey:#"testEntry"];
[groupDefaults synchronize];
Read stuff:
NSUserDefaults *groupDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.de.myApp.sharedGroup"];
NSInteger testEntry = [groupDefaults integerForKey:#"testEntry"];
NSLog(#"testEntry: %ld", (long)testEntry);

Related

Unrecognized selector occurring after Lightweight Core Data migration

I'm getting an unrecognized selector on a newly added field to my Core Data (SQLite) db on iOS. I added a new model version as directed using Xcode's Editor menu, and then verified that new version is the current one. I made sure also that the .h and .m files for the modified table were updated, though I did this by hand (how to have these generated for you?). Nothing unusual though, just a String type field.
The problem seems to be that the lightweight migration never takes place by the time code is run that tries to reference the database object. Tring to access newFieldName gives:
-[MyEntity newFieldName]: unrecognized selector sent to instance 0x852b510
2014-03-27 13:26:21.734 ASSIST for iPad[41682:c07] *** Terminating app due to
uncaught exception 'NSInvalidArgumentException', reason: '-[MyEntity newFieldName]:
unrecognized selector sent to instance 0x852b510'
The line of code that generates the above error is the only line in the for loop below:
DataStoreCoreData *dStore = [[DataStoreCoreData alloc] initWithDataItemDescription:did];
for (MyEntity *myEnt in [dStore objects])
NSString *name = [myEnt newFieldName];
As mentioned, when I examine the SQLite db it has no new field in it, which makes sense given the error. So I also stepped through the execution of the code that is supposed to do the migration and it seems to work fine. Success. It looks like the following:
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"ASSIST.sqlite"]];
// handle db upgrade
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error])
NSLog(#"\r\n Fail. [error localizedDescription]: %#", [error localizedDescription]);
else
NSLog(#"\r\n Success");
This is the 6th db version upgrade. All have been lightweight migrations with no unusual problems. Should not the above code force the SQLite db to reflect the new schema? How do I get it to do so, or is there another problem here?
I finally discovered the answer. The code I provided with the question is only part of the code required to be modified when making minimal changes to the database (lightweight migration). You also need to specify the new version when you create the object model. Notice "MyNewVersion" in the code below. You are supposed to update this parameter to reflect the new version you created and then selected as the current Model Version:
NSString *path = [[NSBundle mainBundle] pathForResource:#"MyNewVersion" ofType:#"mom" inDirectory:#"ASSIST.momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];

How to exit from Map App in IOS 6 i.e. return control back to app requesting directions?

I am using MKMapItem to launch Map app from within my app after the user taps 'Directions' button. The Map app comes up fine showing directions from current location to a address. But how do I go back to my app, If I no longer want to see Directions? Below is my code attached to IBAction.
Code:
- (IBAction)pressDirectionsButton:(id)sender {
Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)])
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
NSString *addressString = [NSString stringWithFormat:#"%#,%#,%#,%#,%#",
self.currentlySelectedTemple.houseNumber,
self.currentlySelectedTemple.street,
self.currentlySelectedTemple.city,
self.currentlySelectedTemple.state,
self.currentlySelectedTemple.country];
[geocoder geocodeAddressString:addressString completionHandler:^(NSArray *placemarks, NSError *error) {
//Convert CLPlacemark to an MKPlacemark
CLPlacemark *geocodedPlacemark = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc]
initWithCoordinate:geocodedPlacemark.location.coordinate addressDictionary:geocodedPlacemark.addressDictionary];
//Create a map item for geocoded address to pass to Maps app
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:geocodedPlacemark.name];
//Set Directions mode to Driving
NSDictionary *launchOptions = #{MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving};
//Get the "Current User Locations" MKMapItem
MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation];
//Pass the current location and destination map items to Maps app
[MKMapItem openMapsWithItems:#[currentLocationMapItem, mapItem] launchOptions:launchOptions];
}];
}
}
There is no way to go back to your app (programatically), as you are no longer in your app, but in the Maps app. After executing openMapsWithItems:launchOptions:, your App Delegate's applicationdDidEnterBackground: method is called.
Currently there is no way to use Mapkit to embed a map with directions within your app.

bundle sqlite database with app and stringByAppendingPathComponent error

There are probably many other questions I don't even know to ask yet since I'm new to app programming.
I initially created the database from within the app, copied it to my working folder (which is probably not where it should ultimately reside), then appended my records (about 1,000 of them) from a text file.
The first two questions that come to mind are:
- what folder should the database be in?
- how does it get deployed with the app?
I found quite few examples using the following lines in persistentStoreCoordinator function:
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"myDatabase.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
But the first line gives me the pre-compile error: "Receiver type 'NSURL' for instance message does not declare a method with selector 'stringByAppendingPathComponent:'. Why is it not working for me?
And is this in fact the best way to bundle my database with the rest of the app?
Thanks!
Easiest solution is to use NSUrl instead of NSString. SO user #trapper already provided a solution in the below link.
importing sqlite to coredata
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Database.sqlite"];
// If the database doesn't exist copy in the default one
if (![storeURL checkResourceIsReachableAndReturnError:NULL])
{
NSURL *defaultStoreURL = [[NSBundle mainBundle] URLForResource:#"Database" withExtension:#"sqlite"];
if ([defaultStoreURL checkResourceIsReachableAndReturnError:NULL])
{
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager copyItemAtURL:defaultStoreURL toURL:storeURL error:NULL];
}
}

Please explain me how to control a Table View in Cocoa?

I search Google to find a good answer but the most tutorials are shown in previous Xcode Versions...
Also, I don't want to drag-n-drop cells from the Interface Builder, but to control the Table View programmatically (from an NSObject subclass file).
What I currently do is this: 1. Create a file named tableController.h that is a subclass of NSObject.
2. I create an NSObject Object in my Nib File (and set it as a subclass of tableController).
3. I drag a Table View to my window.
4. I CTRL+Drag from the Table View to my tableController.h so to create the outlet "tableView"
5. I create these functions in the interface file:
-(int)numberOfRowsInTableView:(NSTableView *)cocoaTV;
-(id)tableView:(NSTableView *)cocoaTV:objectValueForTableCollumn:(NSTableColumn *)tableCollumn row:(int)row;
6. I implement the functions like this:
-(int)numberOfRowsInTableView:(NSTableView *)cocoaTV{
return 5;
}
-(id)tableView:(NSTableView *)cocoaTV:objectValueForTableCollumn:(NSTableColumn *)tableCollumn row:(int)row{
NSArray *tvArray = [[NSArray alloc]initWithObjects:#"1",#"2",#"3",#"4",#"5", nil];
NSString *v = [tvArray objectAtIndex:row];
return v;
}
Then I CTRL+Drag from the Object in the Interface Builder to the Table View to set the dataSource and to set it as delegate.
When I build and Run the App it shows that it has created the 5 Rows but in every cell in every column it says "Table View Cell".
Any help would be appreciated....
-(id)tableView:(NSTableView *)cocoaTV:objectValueForTableCollumn:(NSTableColumn *)tableCollumn row:(int)row is wrong.. i'm not sure how it compiles, to be honest (unless there was an error copy/pasting it). the method should look like:
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSArray *tvArray = [[NSArray alloc]initWithObjects:#"1",#"2",#"3",#"4",#"5", nil];
NSString *v = [tvArray objectAtIndex:row];
return v;
}

sqlite database resets on each compile and run

I have built up a basic database application in which user can insert records onto a table and app displays them on a TableView.
Everything is working as it is supposed to be. For example, the new records do display even if we kill the app from app switcher and relaunch it from the springboard.
BUT every time I build and run using Xcode, the database just goes to default records! The new records are just not there.
Is it normal?... but what if I want to test my app for new records? Any fix?
BTW, JFYI, below is the code I use to make editable DB.
-(NSString *)createEditableDatabase{
// Check if DB already exists
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *writableDB = [documentsDir stringByAppendingPathComponent:#"Database.db"];
success = [fileManager fileExistsAtPath:writableDB];
//The editable DB already exists
if (success) {
return writableDB;
}
//The editable DB does not exist
//Copy the default DB into App's Doc Dir.
NSString *defaultPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Database.db"];
success = [fileManager copyItemAtPath:defaultPath toPath:writableDB error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable DB file: '%#'", [error localizedDescription]);
}
return writableDB;
}
While digging deeper, I noticed that the database modify date in finder was not updating when I inserted a record. So, I found out that I was still using old path to perform DB operations (not the Documents one). :) Now everything working fine. Anyways thanks Nick

Resources