create management subclass give errors - swift

If I go to editor > create management subclass and then let Xcode generate a subclass I always get errors.
What am I doing wrong here that I always get these errors?
extension Passport { //'Passport' is ambiguous for type lookup in this context
#nonobjc public class func fetchRequest() -> NSFetchRequest<Passport> {
return NSFetchRequest<Passport>(entityName: "Passport") //'Passport' is ambiguous for type lookup in this context
}
#NSManaged public var passportId: String? //#NSManaged only allowed on an instance property or method
#NSManaged public var person: Person? //#NSManaged only allowed on an instance property or method
}
How can is fix those errors or what do i wrong ?

You have to set the entity codegen to not to class definition but to Manual/None.
This configuration is the default Codegen configuration when you create an entity in the data model editor. With this configuration, Xcode will automatically generate the required NSManagedObject subclass as part of the project’s derived data.

Related

Swift/CoreData: -[:]: unrecognized selector sent to instance

I have a small iOS application which relies on CoreData.
I have at the moment two entities, "Product" and "StorageLocation".
There is a one-to-many relationship between them.
I generated the classes manually; the Model Editor also shows the correct class name.
When trying to save a new "Product" which contains a relationship to a "StorageLocation" I get this error:
-[StorageLocation addProductsObject:]: unrecognized selector sent to instance 0x60000389d280"
The generated NSManagedObject class is as follows:
import Foundation
import CoreData
extension StorageLocation {
#nonobjc public class func fetchRequest() -> NSFetchRequest<StorageLocation> {
return NSFetchRequest<StorageLocation>(entityName: "StorageLocation")
}
#NSManaged public var id: UUID?
#NSManaged public var name: String?
#NSManaged public var products: NSSet?
}
// MARK: Generated accessors for products
extension StorageLocation {
#objc(addProductsObject:)
#NSManaged public func addToProducts(_ value: Product)
#objc(removeProductsObject:)
#NSManaged public func removeFromProducts(_ value: Product)
#objc(addProducts:)
#NSManaged public func addToProducts(_ values: NSSet)
#objc(removeProducts:)
#NSManaged public func removeFromProducts(_ values: NSSet)
}
extension StorageLocation : Identifiable {
}
Problem, as error says, is the method "addToProducts" which I invoke like this:
let newItem = Product(context: viewContext)
newItem.id = id
newItem.name = name
newItem.qty = Int32(actualQty)
newItem.expDate = expDate
location.addToProducts(newItem)
I also checked if the model matches with the classes, no error found.
Any idea/hint?
Thanks
Marco
It's hard to be sure but since you generated the file manually, it might be that it didn't get added to the app target. That would mean the code exists but isn't being compiled as part of the app. Try this:
Click on the file in the file navigator on the left side of the window.
Bring up the "file inspector" tab on the right. That's the one you get if you press cmd-opt-1.
Look in the inspector where it says "target membership". This should list the app plus any test targets, as well as app extensions if you have any.
Make sure your app's name is checked in the target membership list.
If it wasn't checked, and you check it, your code will probably work.

How to publish an CoreData entity extension?

I'm using CoreData in my SwiftUI app. To have some better handling I added some wrappers to the CoreData classes like this:
extension Category {
public var wrappedName: String {
name ?? ""
}
}
This is working fine till now. But now I need to observe this wrappers also and I use the Category as an StateObject. Therefor I wanted to Publish it like this:
extension Category {
#Published public var wrappedName: String {
name ?? ""
}
}
This is triggering the error: Non-static property 'wrappedName' declared inside an extension cannot have a wrapper
If I remove the #Published I get an error for the #StateObject:
Property type 'Category?' does not match that of the 'wrappedValue' property of its wrapper type 'StateObject'
How to solve this issue?

Swift/iOS: How to access Object properties when type-casted from Protocol?

Some context first:
I am building a generic API for my CoreData Database. All Objects in my model live in pairs:
An NSManagedObject class that is stored in CoreData and can be converted into an NSObject with a protocol called ManagedObjectProtocol
An NSObject class that is actually used throughout my app and can be converted into an NSManagedObject with a protocol called DataObject
My ManagedObject Protocol
//MANAGED OBJECT PROTOCOL - Should be adhered to by all NSManagedObject classes
protocol ManagedObjectProtocol where Self: NSManagedObject {
//var managedObjectID: NSManagedObjectID { get set }
func populateRegularObject() -> DataObject
func populateRegularObjectFromRelated<T: TypeErasedDataObject>(relatedObject: T, at key: String) -> DataObject
}
In my API, I load the objects as follows:
let managedObject = API.shared.persistentContainer.newBackgroundContext().object(with: someObjectID) as! ManagedObjectProtocol
let toReturn = managedObject.populateRegulardObject() //<-- This Crashes
The problem:
This successfully loads my object. I should now be able to populate the DataObject that belongs to this ManagedObjectProtocol and use it in my app. But I can't because, apparently, typecasting to a Protocol loads the object differently than when I TypeCast it as a normal NSManagedObject. Immediately when I access a property of the loaded ManagedObject, my app crashes with error EXC_BAD_ACCESS.
Question:
How can I access my NSManagedObject's properties when I need to typecast it to a protocol?
To me, it would make sense to be able to do something like this:
extension NSManagedObject where Self: ManagedObjectProtocol {
func populateDataObject() -> DataObject
}
But this can't be done in swift. Can anyone suggest a solution? Any help would be highly appreciated.
The following post will help you better understand the issue
https://www.lesstroud.com/dynamic-dispatch-with-nsmanaged-in-swift/
Essentially, it seems that core data is unable to handle protocols which are unmanaged. It seems like core data rewrites the class definition to pass #NSManaged through proxy methods, but is unable to do so for protocols.
Adding the dynamic keyword to your property declaration will solve this issue.

Core Data Property Validation in Swift 3

I'm trying to set up custom property validation for a property belonging to an NSManagedObject subclass. According to the docs the general signature for such a method in Swift 3 is as follows:
func validate<Key>(value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws
My property is called amount, so I've got:
class Transaction: NSManagedObject {
#NSManaged var amount: Float
func validateAmount(value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
// custom validation logic...
}
}
As I understand it, when I create a new Transaction instance, set its amount property, and then try to save the managed object context, validateValue:forKey: should be called on the relevant instance, which should in turn locate and call my custom validation method. Unfortunately this isn't happening: validateValue:forKey: is called, but my method is ignored.
You can see all this for yourself in this sample project (written in Xcode 8.3.3), and I'd be grateful if someone can tell me where I'm going wrong.
func validateAmount(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws is called. Add a _ like in validateValue(_ value.

How can I make the memberwise initialiser public, by default, for structs in Swift?

I have a Swift framework that defines a struct:
public struct CollectionTO {
var index: Order
var title: String
var description: String
}
However, I can't seem to use the implicit memberwise initialiser from another project that imports the library. The error is:
'CollectionTO' cannot be initialised because it has no accessible initialisers
i.e. the default synthesized memberwise initialiser is not public.
var collection1 = CollectionTO(index: 1, title: "New Releases", description: "All the new releases")
I'm having to add my own init method like so:
public struct CollectionTO {
var index: Order
var title: String
var description: String
public init(index: Order, title: String, description: String) {
self.index = index;
self.title = title;
self.description = description;
}
}
... but is there a way to do this without explicitly defining a public init?
Quoting the manual:
"Default Memberwise Initializers for Structure Types
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Otherwise, the initializer has an access level of internal.
As with the default initializer above, if you want a public structure type to be initializable with a memberwise initializer when used in another module, you must provide a public memberwise initializer yourself as part of the type’s definition."
Excerpt from "The Swift Programming Language", section "Access Control".
While it is not possible to have the default memberwise initializer at least you can make one quickly with the following steps:
UPDATE: Xcode 11 and later
As mentioned by Brock Batsell on the comments, for Xcode 11 and later all you need to is this:
Right click the class or struct name and choose refactor ->
Generate Memberwise Initializer
Xcode 10 and earlier answer
Make the object a class temporarily instead of a struct
Save
Right click the class name and choose refactor ->
Generate Memberwise Initializer
Change it back to a struct
We now have a ruby gem 💎 to parse a complete swift data model file, line-by-line, and add public access modifiers, public member-wise default initializers, and other things into a separate auto-generated output swift file.
This gem is called swift_republic
Please check out the following documentation for running this gem:
https://github.com/mehul90/swift_republic
Sometimes it's really annoying having an initializer when you don't need one. If you're constantly updating the variables to the object, it becomes bothersome very quickly to update the variables in 3 places (variable declaration, initializer parameter, and initializer implementation).
A workaround I've used for this issue is to have a static variable on the struct to act as (or essentially wrap) the "initializer". For instance:
struct MyStruct {
static var empty = Self()
static func empty(name: String) -> Self {
.init(privateName: name)
}
private var identifier: String = ""
}
Then you can call it similar to how you would an initializer (with autocomplete and everything!):
func someFunction(_ value: MyStruct) { ... }
//someFunction(.init()) -> ERROR, invalid due to `private` variable
someFunction(.empty)
someFunction(.empty(name: "Dan IRL"))
let myObject = MyStruct.empty
let myObject2 = MyStruct.empty(name: "Monty Python")
You have to define public init by yourself, luckily starting from Xcode 14 🥳 there is an automatic initializer completion (source - 60399329)