KVO notification with Core Data in Swift - core-data

When I generate an NSManagedObject subclass with swift, the property types are all #NSManaged, meaning I can't observe them. This is a problem when using bindings in a Cocoa application because updating the property frequently requires other properties to be 'updated'.
For example, if I add this method to my NSManagedObject subclass:
dynamic var ratePerPoint: Double {
guard pointsPerMonth > 0 else { return 0 }
return monthlyRate / Double(pointsPerMonth)
}
Then it's important that whenever I update the pointsPerMonth variable, which is part of the core data object, that I send a didChangeValueForKey("ratePerPoint") message.
If I don't, then the UI bindings don't update properly.

If ratePerPoint is a calculated property you have to implement keyPathsForValuesAffectingRatePerPoint in your NSManagedObject subclass.
+ (NSSet *)keyPathsForValuesAffectingRatePerPoint {
return [NSSet setWithObjects:#"monthlyRate", #"pointsPerMonth", nil];
}
Documentation: Registering Dependent Keys

Related

Does Core Data bypass security?

My iOS 13.2 Swift app is a type of SceneKit editor which uses Core Data to persist user edits. I'm wondering if Core Data might be associated with the message below since NSManagedObject is a subclass of NSObject and since a test app I created without Core Data does not cause the message to be displayed.
Although I haven't subclassed any SceneKit classes which support NSSecureCoding, and no other classes in the app use NSSecureCoding, the following message is displayed when a SCNScene is displayed in a SCNView:
The Displayed Message:
[general] NSSecureCoding allowed classes list contains [NSObject class], which bypasses security by allowing any Objective-C class to be implicitly decoded. Consider reducing the scope of allowed classes during decoding by listing only the classes you expect to decode, or a more specific base class than NSObject.
This message is displayed only once even though a SCNScene can be reopened multiple times to reflect the user's edits.
Possible Causes
1. Some of the Core Data entities contain binary data properties used to display thumbnail images. However, when I comment-out the code associated with creating/displaying the thumbnails, the above message is still displayed. The thumbnail data is created with the following code which returns an optional data object. I wonder about this, because Swift bridges to NSData which is a subclass of NSObject.
static func createNonCachedItemThumbnailData(item: Item) -> Data? {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let dataSource = appDelegate.dataSource
guard let selectedDesign = dataSource.selectedDesign else { return nil }
let resourceSubdirectoryName = selectedDesign.uniqueID!
guard let bundleURL = Bundle.main.url(forResource: item.uniqueID!, withExtension: "png", subdirectory: resourceSubdirectoryName + ".scnassets") else { return nil }
guard let imageSource = CGImageSourceCreateWithURL(bundleURL as CFURL, nil) else { return nil }
/*
maxDimension is the lesser of the width or height of the UIImageView in ItemSCNNodeSelectionViewCell.
*/
let maxDimension: CGFloat = 64.0
let options: [NSString: Any] = [
kCGImageSourceThumbnailMaxPixelSize: maxDimension,
kCGImageSourceCreateThumbnailFromImageAlways: true]
guard let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { return nil }
return UIImage(cgImage: scaledImage).pngData()
}
One of the Core Data entities uses NSKeyedArchiver/NSKeyedUnarchiver to archive SCNMaterial as binary data. I didn't use the transformable type for this property, because I read that the transformable type doesn't notice changes when the context is saved. This code seems far from the problem, but the compiler may have noticed it.
Any help would be appreciated.

How to create instance of WCMUsePojo in my Prosper spec?

I have a functioning WCMUsePojo Groovy class which is called from a sightly html component. I am trying to create an instance of my WCMUsePojo class for testing based on the content from the Prosper setup method.
It's basically the same type of question as How can I create an instance of WCMUsePojo in a servlet for a specific page? But I don't see it answered and this is specifically about how to unit test methods in WCMUsePojo classes within the Prosper framework. Is there a Java or Groovy equivalent to Sightly's data-sly-use attribute?
def setupSpec() {
pageBuilder.content {
page_with_new_gridwrapper {
'jcr:content'{
'gridpar' {
'my_gridwrapper'('sling:resourceType':'my/components/my_gridwrapper') {
}
}
}
}
}
}
def "Test Page with New Grid Container"(){
Page page = pageManager.getPage("/content/page_with_new_gridwrapper")
// the following 2 lines return null :-(
// but I would prefer these to return an object with the type MyGridContainerHelper
MyGridContainerHelper cmp = page.getContentResource().getChild("gridpar/my_gridwrapper").adaptTo(MyGridContainerHelper.class)
Component cmp2 = WCMUtils.getComponent(page.getContentResource().getChild("gridpar/my_gridwrapper"))
expect:
page != null //passes
page.getContentResource().getChild("gridpar/my_gridwrapper") != null //passes
cmp != null // fails
cmp2 != null // fails
cmp.resourceType == "my/components/my_gridwrapper" // fails
}
To adapt an instance of MyGridContainerHelper from a resource object, you can implement your helper class using Sling Models rather than extending WCMUsePojo. Prosper supports registration of #org.apache.sling.models.annotations.Model-annotated classes by using the following syntax in the setupSpec block of your specification:
slingContext.addModelsForPackage("helper.class.package.name")
This eliminates the need to manually construct a Bindings object and initialize the POJO; the Sling model factory does all the work for you. Since Sightly's "use" attribute supports any class that is adaptable from a Resource or SlingHttpServletRequest, no additional changes are required for your existing Sightly template.
I ended up instantiating the object and calling the init method passing in a SimpleBindings object containing the resource I was testing with. This seems to work well for my purposes.
MyGridContainerHelper gridContainer = new MyGridContainerHelper();
SimpleBindings bindings = new SimpleBindings()
bindings.put("resource", page.getContentResource().getChild("gridpar/my_gridwrapper"))
gridContainer.init(bindings)

Unable to instantiate NSFetchedResultController with generic type AnyObject in Swift 3

I'm experimenting with CoreData in Swift 3 and have come up against a very bizarre circular compiler error in Xcode 8 beta.
NSFetchedResultsController needs a generic type parameter and AnyObject has worked fine up until now. The compiler throws the error:
Type 'AnyObject' does not conform to protocol 'NSFetchRequestObject'
To make me extra confused, if you delete the type parameter, XCode then says:
Reference to generic type NSFetchedResultsController requires argument in `<...>`
and helpfully suggests a fix using <AnyObject>....and the cycle repeats.
This looks very much like a bug. Any ideas before I report it?
If you take a look into NSFetchedResultsController, you can clearly see that it has a parameter with name ResultType which conforms to NSFetchRequestResult. So you should pass a type which conforms to NSFetchRequestResult.
So if you take a look into NSFetchRequestResult, you can see that it conforms to NSObjectProtocol. Also NSDictionary, NSManagedObject and NSManagedObjectID conforms to NSFetchRequestResult.
public protocol NSFetchRequestResult : NSObjectProtocol {
}
extension NSDictionary : NSFetchRequestResult {
}
extension NSManagedObject : NSFetchRequestResult {
}
extension NSManagedObjectID : NSFetchRequestResult {
}
So it clear that you should pass a type from any of these three NSDictionary or NSManagedObject or NSManagedObjectID.
Create your instance of NSFetchedResultsController like this.
let resultsController : NSFetchedResultsController<NSManagedObject>!
or like this
let resultsController : NSFetchedResultsController<NSManagedObjectID>!
or like this
let resultsController : NSFetchedResultsController<NSDictionary>!
Any entity in your Core Data model maps as a subclass of NSManagedObject generated in your code so they all can be used to replace AnyObject, they all conform indirectly to NSFetchRequestResult protocol. You should see which entity/class is being fetch by your FetchRequest connected to this FetchedResultsController and that's the type you should use there.

Passing A Delegate In prepareForSegue Method

I am having a problem passing an object from a TableView to a ViewController in an IOS app. I am using storyboard and have elected ARC and passing the delegate in my "prepareForSegue" method.
Here is my code in my TableView which segues via a push to another ViewController:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NextViewController *vc = (NextViewController *)[segue destinationViewController];
vc.managedObjectContext = managedObjectContext;
if ([[segue identifier] isEqualToString:#"EditCategory"])
{
NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
// I get a red warning which states "Cast of 'NSInteger' (aka 'int') to 'Entity' is disallowed with ARC" on this line:
[vc setEntity:(Entity *)selectedIndex];
}
}
Does anybody have any suggestions for how I can pass my object from the TableView to the ViewController? I am new to programming and have tried various expressions but nothing seems to work.
The error you're getting has to do with types.
The class NextViewController apparently has a method called -setEntity: that takes an object of type Entity *. The error is because you're trying to give -setEntity: an argument of the wrong type. You're trying to give it an NSInteger (which is a number like 0, 5, -999), but it wants an Entity.
You're on the right track for passing data from the table view to the NextViewController. You just need to do one of the following:
pass an Entity to -setEntity: (does the Entity class perhaps
have a constructor that takes an NSInteger?)
add a method to NextViewController that takes an NSInteger, and call that instead of -setEntity:

add<Key>Object vs insertNewObjectForEntityForName Core Data Relationships

Hi,
Although I have a lot of experience in database development, I'm having a hard time conceptualizing linking relationships in Core Data. As I understand it, the many relationship is an NSSet attached to the one file. After reading the documentation, I've understood part of it and gotten it to work in the first import in my code below.
I have a data model into which I perform two separate imports using the XMLParser. The first import loads Events and Categories from the same XML file within the same import like so:
if (thisTagIsForOneTable) {
// Insert object for the one-entity (Events)
self.eventsObject = [NSEntityDescription insertNewObjectForEntityForName:#"Events" inManagedObjectContext:xmlManagedObjectContext];
return;
}
if (thisTagIsForManyTable) {
// Insert object for many-entity (EventCategories)
self.categoriesObject = [NSEntityDescription insertNewObjectForEntityForName:#"EventCategories" inManagedObjectContext:xmlManagedObjectContext];
return;
}
......
// Set attribute values depending upon whether the tag is for the one-entity or the many-entity.
[self.xxxObject setValue:trimmedString forKey:attributeName];
......
// Set the relationship. This works great!
if (thisTagIsForManyTable) {
NSMutableSet *manyRecordSet = [self.eventsObject mutableSetValueForKey:#"categories"]; // My relationship name.
[manyRecordSet addObject:self.categoriesObject];
}
// Save the context. Voila.
The above works fine. The second import loads EventLocations separately in a different part of the application so I need to set it's to-one relationship to Events. This is where I'm not too sure. Should the steps be?
// Step A) Create (insert) new EventLocations object.
self.eventLocationsObject = [NSEntityDescription insertNewObjectForEntityForName:#"EventLocations" inManagedObjectContext:xmlManagedObjectContext];
// Step B) Locate and get a reference to the the related one-entity's object (Events) by ID? I have a custom class for Events.
// This seems to work but I'm taking a performance hit and getting a compiler warning below. The method returnObjectForEntity does a query and returns an NSManagedObject. Is this correct?
Events *event = (Events *)[self returnObjectForEntity:#"Events" withID:[oneRecordObject valueForKey:#"infIDCode"]];
// Step C) Set the relationship to the Events entity.
if (event) {
[event addLocationsObject:self.eventLocationsObject];
// Compiler warning: incompatible Objective-C types 'struct NSManagedObject *', expected 'struct EventLocations *' when passing argument 1 of 'addLocationsObject:' from distinct Objective-C type
}
// Save the context.
I'm not too sure about steps B and C. Any help would be appreciated. Thanks.
Step B: Please tell us the compiler warning.
To Speed things up you could create a cache (NSDictionary) with all Events and their #"infIDCode" as Keys before starting the import. This would speed things up as long as you can make sure no Events are added/deleted/changed within the import phase.
Step C:
self.eventLocationsObject should probably be declared as EventLocations*.
In general your import should work this way

Resources