Swift 3: CoreData FetchRequest - core-data

I was using the following in Swift 2.3
class func getRecord(tableName: String) -> [AnyObject] {
let request = NSFetchRequest(entityName: tableName)
let requestResult = MyModel.executeRequest(request);
}
where MyModel is some model.
The above code was working fine in Swift 2. Now I have to convert it in Swift 3.
This is what I do in swift3,
let request: NSFetchRequest<NSFetchRequestResult> = tableName.fetchRequest()
But surely a string type doesn't have a fetchRequest member. So I am stuck on this stage. Please guide me in this regard.

In Swift 3, NSFetchRequest became a generic type. And you can use your entity's class's static method fetchRequest to get a NSFetchRequest<Self>.
For example, if your entity is called MyModel, you can get a fetch request like this:
let request: NSFetchRequest<MyModel> = MyModel.fetchRequest()
Then, you can execute it by calling execute on a managed object context.
try? context.execute(request)

Simple use your modelname.fetchRequset. In your case you have to do as follows,
let request: NSFetchRequest<MyModel> = MyModel.fetchRequest()

Related

Implementing ForEach.onMove() with Core Data

I am trying to implement ForEach.onMove using Core Data. I have two entities, Parent with a To-Many relationship to Child, with the relationship marked as Ordered. The view is something like this:
struct ParentView : View {
#ObservedObject var parent : Parent
var body : some View {
List {
ForEach($parent.children! as! [Child]) { child in
ChildView(child)
}.onMove {
parent.managedObjectContext!.performAndWait { indices, to in
(parent.children! as! NSMutableOrderedSet).moveObjects(at: indices, to: to)
try! parent.managedObjectContext!.save()
parent.objectWillChange.send()
}
}
}
}
}
Results are:
No errors of any kind.
During debug of the onMove function, I can see that the items are re-ordered as required
The managedObjectContext.updatedObjects is empty at the same debug step, before the call to save()
When reloading the app, the re-ordering is obviously not saved (apparently because the updatedObjects set was empty at #3.)
What am I doing wrong? How can I make the MOC realize the re-ordering change?
I fixed this, apparently with a simple solution. I am posting this for future generations of google searchers. :D
The problem is with this line:
(parent.children! as! NSMutableOrderedSet).moveObjects(at: indices, to: to)
Apparently, taking the immutable version and making it mutable works, but doesn't update the managed object. This works:
parent.mutableOrderedSetValue(forKey: "children").moveObjects(at: indices, to: to)

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.

Calling function with callback defined as string

var method = 'serviceName.MethodName'
I Just want to call it like
serviceName.methodName(function(output callback){
});
Is there any approach to call it.thanks
There are two methods that I can think of now.
JS eval
You can use the javascript eval function to convert any string into code snippet like below. Although eval is a quick solution but should not be used unless you dont have any other option by your side.
var method = 'UserService.getData';
eval(method)();
Factory pattern
Use a below pattern to get the service
You would need to define the services in such a manner that you can access them using a pattern.
var Services = {
// UserService and AccountsService are again objects having some callable functions.
UserService : {getData: function(){}, getAge: function(){}},
AccountsService : {getData: function(){}, getAge: function(){}},
// getService is the heart of the code which will get you the required service depending on the string paramter you pass.
getService : function(serviceName){
var service = '';
switch(serviceName){
case 'User':
service = this.UserService;
break;
case 'Accounts':
service = this.AccountsService;
break;
}
return service;
}
}
You can use get the required service with below code
Services.getService('User')
I'm not aware of any way you can resolve the serviceName part of that string to an object, without using eval. So obviously you need to be extremely careful.
Perhaps:
if (method.match(/^[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+$/) {
var servicePart = eval(method.split('.')[0]);
var methodPart = method.split('.')[1];
servicePart[methodPart](...)
}
There are two separate problems in your question:
How to access object property by property name (string)?
How to access object by it's name (string)?
Regarding the first problem - it is easy to access object property by string using the following notation:
const myObject = {
myProp: 1,
};
console.log(myObject['myProp']);
And regarding the second problem - it depends on what serviceName is:
if it is a property of some other object, then use someObject['serviceName']['MethodName']
if it is a local variable, consider using a Map (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) to associate strings with objects;

delete core data managed object with Swift 3

Swift 3 has migrated my code and changed:
context.deleteObject(myManagedObject)
to
context.delete(myManagedObject)
this is compiling fine (XCode 8b3) but at runtime complaining that the context does not have a function/selector delete(managedObject)
Here is the runtime error:
[NSManagedObjectContext delete:]: unrecognized selector sent to instance
My code is very basic:
func delete()
{
let appDel: AppDelegate = UIApplication.shared().delegate as! AppDelegate
if let context: NSManagedObjectContext = appDel.managedObjectContext
{
context.delete(exerciseData)
appDel.saveContext()
}
}
Why is it no longer working?
Thanks
Greg
From the Xcode 8 beta 3 - Release Notes
Known Issues in Xcode 8 beta 3 – Swift Compiler
Attempting to use NSManagedObjectContext's delete(:) method may result in calling the UIKit-added delete(:) method on NSObject instead (part of the UIResponderStandardEditActions category) if the argument is optional (including ImplicitlyUnwrappedOptional). (27206368)
Workaround: Manually unwrap the optional value using if let or !.
You need to check if this holds true in your case.

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.

Resources