CouchbaseLite 1.3.0 view querying id of doctype - couchbase-lite

I am using couchbase lite 1.3.0 for an ios app. I am saving the model objects as docs in the db. However I am changing the 'doctype' dynamically while saving. Upon querying with 'docType' id , the cbl view emits wrong/unmatching 'docType'. Couldn't figure out how to fix this.
[view setMapBlock: MAPBLOCK({
if ([doc[#"docType"] isEqual: #"docType1"])
{
emit(doc, nil);
}
})
version: #"1"];
CBLQuery* query = [view createQuery];
query.descending = NO;
CBLQueryEnumerator* result = [query run: &error];
for (CBLQueryRow* row in result) {
//The rows emitted doc of 'doctype2'
}

Changing docType is OK I think, the problem is you map function is hard coded against a query parameter which is not encouraged and also you emit a full doc as well.
I can achieve what you are trying to do with this:
[view setMapBlock: MAPBLOCK({
emit(doc[#"docType"], nil);
})
version: #"1"];
and then query using the docType1 as a parameter, if you need the doc just set the preFetch totrue`
I'm aware that this will introduce 'redundancy' to what you want, but I think it solves the problem

Related

NSManagedObjectContext(): `init()` was deprecated in iOS 9.0: Use -initWithConcurrencyType

I was working through Core Data Stack in Swift - Demystified but when I got to the line
self.context = NSManagedObjectContext()
I got the warning
`init()` was deprecated in iOS 9.0: Use -initWithConcurrencyType: instead
I see that I can do one of the following for self.context =
NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.ConfinementConcurrencyType)
NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)
NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
but since ConfinementConcurrencyType is also deprecated now that leaves me MainQueueConcurrencyType and PrivateQueueConcurrencyType. What is the difference between these two and how should I choose which one to use? I read this documentation, but I didn't really understand.
You essentially will always have at least 1 context with NSMainQueueConcurrencyType and many contexts with NSPrivateQueueConcurrencyType. NSPrivateQueueConcurrencyType is used typically for saving or fetching things to core data in the background (like if attempting to sync records with a Web Service).
The NSMainQueueConcurrencyType creates a context associated with the main queue which is perfect for use with NSFetchedResultsController.
The default core data stack uses a single context with NSMainQueueConcurrencyType, but you can create a much better app by leveraging multiple NSPrivateQueueConcurrencyType to do any work that does not affect the UI.
Replace these two function with the following one:
lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// 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.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}

Parse.com Query: Cache is always empty (iOS)

I'm writing an iOS-App using Parse.com as a backend.
In the - (PFQuery)queryForTable method of my PFQueryTableViewController I'm retrieving a set of data from Parse, but I'm unable to cache this query in order to support functionality when the device is currently offline.
The method looks as followed:
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
[query whereKey:#"city" equalTo:[[NSUserDefaults standardUserDefaults] objectForKey:#"city"]];
// userMode is active when a user is logged in and is about to edit coins
if (self.userModeActive) {
[query whereKey:self.textKey equalTo:self.user[#"location"]];
}
// dateFilter is active when view is pushed from an event
if (self.dateFilterActive) {
[self createDateRangeForFilter];
[query whereKey:#"date" greaterThan:[[self createDateRangeForFilter] objectAtIndex:0]];
[query whereKey:#"date" lessThan:[[self createDateRangeForFilter] objectAtIndex:1]];
} else {
// Add a negative time interval to take care of coins when it's after midnight
[query whereKey:#"date" greaterThanOrEqualTo:[[NSDate date] dateByAddingTimeInterval:-(60 * 60 * 6)]];
[query orderByAscending:self.dateKey];
}
// locationFilter is active when view is pushed from a location profile
if (self.locationFilterActive) {
[query whereKey:#"location" equalTo:self.locationToFilter];
}
// If no objects are loaded in memory, look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
if ([query hasCachedResult]) {
NSLog(#"hasCache");
} else {
NSLog(#"chache empty");
}
return query;
}
[query hasCachedResults] always returns false in this case.
In another class, I'm doing the almost exactly same query (on a different Parse-Class) and it caches automatically. The only difference is, that this other query contains PFFiles.
This may be a dumb question but I'm stuck with it for days now.
Thanks for any help and let me know if I can give you more information.
The code guards the setting of cache policy with a conditional if (self.objects.count == 0). It would seem that you're using the cache when there are zero objects and not using it after the query has succeeded. Since the default is to not use the cache, the code is arranged to never use it.
Just remove the conditional, or use the cache conditionally upon [query hasCachedResult]
EDIT - It is still the case that the cache policy can/should be set unconditionally, but a query can have hasCachedResults only if its criteria don't change after the find (I don't see a place in the docs confirming this, but it stands to reason). To insure that a query can return cached results, leave its criteria unchanged after the find.
The [NSDate date] avoid the cache of PFQuery. Here is a work-around:
do not query NSDate at viewDidLoad
but do it in viewDidAppear
The code:
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
// 1. load from cache only when viewDidLoad
// setup query WITHOUT NSDate "where" condition
if (self.shouldQueryToNetwork) {
// 2. update objects with date condition only when view appeared
[query whereKey:#"date" greaterThan:[NSDate date]];
}
return query;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.shouldQueryToNetwork = YES;
// Sync objects with network
[self loadObjects];
}

NSManagedObjectContext save unsuccessful, but returns nil error

I am developing an OS X Application that uses a single-threaded Core Data model without nested contexts.
I am creating objects on the main thread in the defaultContext and try to save them after creation, but the save fails without returning an error. I have not overwritten any methods in my CoreData objects, but I am using the latest version of MagicalRecord.
The code that fails:
// pseudocode for createOrFetchWithData:inContext:
// fetch object from value in objectDict
// if(!object) create project in context
// [object importValuesForKeysWithObject:objectData] // MR method
// return object
MyObject *object = [MyObject createOrFetchWithData:objectData
inContext:[NSManagedObjectContext defaultContext]];
if(!object) return; // just to emphasise that I am sure the object is not nil.
[[NSManagedObjectContext defaultContext] saveOnlySelfWithCompletion:^(BOOL saveSuccessful, NSError *error) {
if(saveSuccessful) {
NSLog(#"yay");
} else {
NSLog(#"nay");
}
}];
The return value of [NSManagedObjectContext defaultContext] is not nil and I have verified that the code is executed on the main thread.
I have a relationship that isn't set in the MyObject *object, but it is marked as optional in the data model.
Any idea what might cause this simple operation to fail? I have other entities that save just fine, but this particular case fails.
Note: I am running OS X Mavericks DP 8.
In the end, it was my misunderstanding of MagicalRecord's (and possibly CoreData's) implementation of the save: methods: in case there is no change to the context ([context hasChanges] == NO) AND the parent context (!!), the save will be aborted, and the completion block is called with NO for successful and no error object.

Unable to use reference from a Make New command in Core-data app with AppleScript support

I am able to support the Make New command of AppleScript for my app, however the returned 'specified object' (an NSUniqueIDSpecifier) for the core data managed object is useless. The following AppleScript returns the error message:
error "SpellAnalysis got an error: Invalid key form." number -10002 from level id "x-coredata:///Levels/tC5A49E01-1CE1-4ED6-8F6B-BC0AE90E279A2"
tell application "SpellAnalysis"
set thisLevel to make new «class Slev» with properties {«class Saln»:3}
get properties of thisLevel
end tell
So the newly created Levels object can not be acted upon in AppleScript. I've combed the Web for a solution to this and the closest thing I have found is Bill Cheeseman's example app, "WareroomDemo" which specifically deals with Cocoa Scriptability for Core Data apps (the Sketch example does not use Core Data). Unfortunately, it is a dated example, running only on pre-64-bit XCode and I can't actually run it--I can only look at the code. His app's Make Command may have the same limitations for all I know.
The returned 'objectSpecifier' is unable to refer to the created object either as a safe-guard against corrupting Core Data's organizing scheme, or perhaps because the returned object is an un-cashed 'fault'. I think the latter possibility is unlikely because I can force the fault to cash (by getting a property value on the managed object) , yet I get the same error message with the AppleScript.
Here is the method that creates my class:
- (id)newScriptingObjectOfClass:(Class)class forValueForKey:(NSString *)key withContentsValue:(id)contentsValue properties:(NSDictionary *)properties { // Creates a new Lesson object in response to the AppleScript 'make' command.
// Documentation for 'newScriptingObject…' states that to create a new class object when using Core Data, you intercede using the following method (or you can subclass the NSCreateCommand's 'performDefaultImplementation' method and put your NSManagedObject init code there):
if (class == [Levels class]) {
//NSLog(#"class: %#",class);
NSEntityDescription *levelsEntity = [NSEntityDescription
entityForName:#"Levels"
inManagedObjectContext:levelsDBase];
NSManagedObject *levelObject = [[NSManagedObject alloc] initWithEntity:levelsEntity insertIntoManagedObjectContext:levelsDBase];
SLOG(#"lessonObject: %#", lessonObject);
NSString *levelNumberString = [[properties objectForKey:#"levelNumber"] stringValue];
SLOG(#"levelNumberString: %#", levelNumberString);
[levelObject setValue:levelNumberString forKey:#"levelNumber"];
return levelObject; // When using Core Data, it seems that you return the newly created object directly
}
return [super newScriptingObjectOfClass:(Class)class forValueForKey:(NSString *)key withContentsValue:(id)contentsValue properties:(NSDictionary *)properties];
}
Here is my object specifier method:
- (NSScriptObjectSpecifier *)objectSpecifier {
// This NSScriptObjectSpecifiers informal protocol returns a unique ID specifier specifying the absolute string of the URI representation of this managed object. // AppleScript return value: 'level id <id>'.
// The primary container is the application.
NSScriptObjectSpecifier *containerRef = nil; // I understand that if the application is the container, this is value you use for the container reference
NSString *uniqueID = [[[self objectID] URIRepresentation] absoluteString];
return [[[NSUniqueIDSpecifier alloc] initWithContainerClassDescription:[NSScriptClassDescription classDescriptionForClass:[NSApp class]] containerSpecifier:containerRef key:#"levelsArray" uniqueID:uniqueID] autorelease];
}
The problem lies with the specifier method. The Sketch example actually uses the technique that I needed. I overlooked it many times because I didn't see how it would apply to Core Data managed objects. Instead of returning the objects uniqueID, you make it return the managedObject index using the 'indexOfObjectIdenticalTo:' method as follows:
- (NSScriptObjectSpecifier *)objectSpecifier {
NSArray *levelsArray = [[NSApp delegate] levelsArray]; // Access your exposed to-many relationship--a mutable array
unsigned index = [levelsArray indexOfObjectIdenticalTo:self]; // Determin the current objects index
if (index != (unsigned)NSNotFound) {
// The primary container is the document containing this object's managed object context.
NSScriptObjectSpecifier *containerRef = nil; // the appliation
return [[[NSIndexSpecifier allocWithZone:[self zone]] initWithContainerClassDescription:[NSScriptClassDescription classDescriptionForClass:[NSApp class]] containerSpecifier:containerRef key:#"levelsArray" index:index] autorelease];
} else {
return nil;
}
}
Note that this method resides within a subclass of your Core Data managedObject--in this case, the 'Levels' class. The 'self' within the 'indexOfObjectIndenticalToSelf:' method refers to the current managedObject ('Levels') being handled. Also, be sure to provide the specifier (accessor) type to your 'sdef' file, like this:
<element type="level">
<cocoa key="levelsArray"/>
<accessor style="index"/>
</element>

Manual Core Data schema migration without "document changed" warning?

The data model for my Core Data document-based app (10.5 only) is in a
framework, so automatic schema upgrades using a Core Data mapping
model don't appear to work. It appears that the Core Data machinery
doesn't find the appropriate data models or mapping model when they
are not in the app's main bundle. So, instead of using the automatic
migration, I'm running a migration manually in
configurePersistentStoreCoordinatorForURL:ofType:... in my
NSPersistenDocument subclass (code below). I migrate the persistent
store to a temporary file and then overwrite the existing file if the
migration succeeds. The document then presents an error with the
message "This document's file has been changed by another application
since you opened or saved it." when I try to save. As others on this
list have pointed out, this is due to my modification of the
document's file "behind its back". I tried updating the document's
file modification date, as shown below, but I then get an error dialog
with the message "The location of the document "test.ovproj" cannot be
determined." when I try to save. I'm less sure of the reason for this
error, but trading one unnecessary message (in this case) for an other
isn't quite what I was going for.
Can anyone offer some guidance? Is there a way to manually upgrade the
schema for a document's persistent store without triggering one of
these (in this case unnecessary) warnings?
code for upgrading the data store in my subclasses
-configurePersistentStoreCoordinatorForURL:ofType:... :
if(upgradeNeeded) {
NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:VUIModelBundles() orStoreMetadata:meta];
if(sourceModel == nil) {
*error = [NSError errorWithDomain:VUIErrorDomainn ode:VUICoreDataErrorCode localizedReason:BWLocalizedString(#"Unable to find original data model for project.")];
return NO;
}
NSManagedObjectModel *destinationModel = [self managedObjectModel];
NSMigrationManager *migrationManager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:destinationModel];
NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:VUIModelBundles() forSourceModel:sourceModel destinationModel:destinationModel];
if(mappingModel == nil) {
*error = [NSError errorWithDomain:VUIErrorDomain code:VUICoreDataErrorCode localizedReason:BWLocalizedString(#"Unable to find mapping model to convert project to most recent project format.")];
return NO;
}
#try {
//move file to backup
NSAssert([url isFileURL], #"store url is not a file URL");
NSString *tmpPath = [NSString tempFilePath];
id storeType = [meta objectForKey:NSStoreTypeKey];
if(![migrationManager migrateStoreFromURL:url
type:storeType
options:storeOptions
withMappingModel:mappingModel
toDestinationURL:[NSURLfileURLWithPath:tmpPath]
destinationType:storeType
destinationOptions:storeOptions
error:error]) {
return NO;
} else {
//replace old with new
if(![[NSFileManager defaultManager] removeItemAtPath:[url path] error:error] ||
![[NSFileManager defaultManager] moveItemAtPath:tmpPath toPath:[url path] error:error]) {
return NO;
}
// update document file modification date to prevent warning (#292)
NSDate *newModificationDate = [[[NSFileManager defaultManager] fileAttributesAtPath:[url path] traverseLink:NO] bjectForKey:NSFileModificationDate];
[self setFileModificationDate:newModificationDate];
}
}
#finally {
[migrationManager release];
}
}
}
return [super configurePersistentStoreCoordinatorForURL:url ofType:fileType modelConfiguration:configuration storeOptions:storeOptions error:error];
I haven't run across this particular situation, but I have a few guesses. First, instead of using -removeItemAtPath: and -moveItemAtPath: when you want to switch files, use the FSExchangeObjects() function instead. NSDocument uses FSRefs to track the file and unless you use FSExchangeObjects(), it'll realize that it's looking at a completely different file.
Second, you can manually set your document's managed object model by overriding -managedObjectModel, in particular using the +mergedModelFromBundles: method to load the models from your framework. According to the docs, it should by default merge any models in the main bundle and in all linked frameworks, so this should only be necessary for dynamically loaded bundles. Don't know why that's not working for you, but I haven't tried this. To figure out what bundles to search, NSBundle's +bundleForClass: method is your friend.
Beware FSExchangeObjects()! It does not support all volumes types, see bSupportsFSExchangeObjects. I'm looking for a replacement myself. Option seem to be MoreFilesX's FSExchangeObjectsCompat or 10.5's FSReplaceObjects().
10 years later...
I encountered the same issue, and with the new API for NSDocument, you can update the document's fileModificationDate with the new date of the updated file after doing the migration
migrate()
if let newModificationDate = try? NSFileManager.defaultManager().attributesOfItemAtPath(url.path!)[NSFileModificationDate] as? NSDate {
self.fileModificationDate = newModificationDate
}
after that you can call super.configurePersistentStoreCoordinatorForURL...
That's because NSDocument is setting the file modification date even before calling readFromURL:ofType
See Document Initialization Message Flow

Resources