When I try to do this, the model is stored in the NSManagedObjectContext if I use the context, and without it it throws an error, but I'm not expecting the same result.
Is there an easy way to implement this?
class WordDal: NSManagedObject {
#nonobjc public class func fetchRequest() -> NSFetchRequest<WordDal> {
return NSFetchRequest<WordDal>(entityName: "WordDal")
}
#NSManaged public var word: String?
#NSManaged public var uuid: UUID?
}
struct WordPresentation {
let word: String
let uuid: UUID
}
func mappingNSManagedObject(_ wordPresentation: WordPresentation) -> WordDal {
let model = WordDal()
model.uuid = wordPresentation.uuid
model.word = wordPresentation.word
return model
}
Consider to redesign your model to have computed property for the new wrapper type that transforms the property value to and from the wrapper value.
Implementing a computed property in a Swift Core Data model is often a clear, more intuitive way to achieve what you need.
Here is an example implementation:
struct WordPresentation {
let word: String
let uuid: UUID }
class WordDal: NSManagedObject {
#nonobjc public class func fetchRequest() -> NSFetchRequest<WordDal> {
return NSFetchRequest<WordDal>(entityName: "WordDal")
}
#NSManaged public var word: String?
#NSManaged public var uuid: UUID?
var wordPresentation : WordPresentation {
get {
return WordPresentation(word: self.word, uuid: self.uuid)
}
set {
self.word = newValue.name
self.uuid = newValue.id
}
}
}
I solved the problem like this (I don't know why I put it off and didn't understand right away):
class WordDal: NSManagedObject {
#nonobjc public class func fetchRequest() -> NSFetchRequest<WordDal> {
return NSFetchRequest<WordDal>(entityName: "WordDal")
}
#NSManaged public var word: String?
#NSManaged public var uuid: UUID?
}
struct WordPresentation {
let word: String
let uuid: UUID
}
func removeFromStorage(by uuid: UUID) {
getDataFromStorage { [weak self] objects in
guard let self = self else { return }
if let objectForRemove = objects.first(where: { $0.uuid == uuid }) {
self.coreDataStack.mainContext.delete(objectForRemove)
self.coreDataStack.saveContext(self.managedObjectContext)
}
}
}
I'm creating a presentation level model with UUID!
And I delete only on him himself UUID.
Now I can walk both ways.
Related
I have a base class and a derived class... the base class has a class function userDefaultKeyPrefix() which indicates the user defaults key for user default storage using a wrapper
however the AdvancedManager class ends up with the BaseManager class value of userDefaultKeyPrefix() "BASE.someVar" instead of "ADVANCED.someVar"
Why is this happening and how to achieve the desired outcome so the keys are given by the class
The init of the someVar is called as the Base class, not the Advanced
class BaseManager {
class func userDefaultKeyPrefix() -> String {
return "BASE"
}
#UserDefault(userDefaultKeyPrefix() + ".someVar", defaultValue: false) var someVar: Bool
}
class AdvancedManager: BaseManager {
override class func userDefaultKeyPrefix() -> String {
return "ADVANCED"
}
}
#propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
//let oldValue = wrappedValue
UserDefaults.standard.set(newValue, forKey: key)
UserDefaults.standard.synchronize()
}
}
}
UPDATE: This bellow works (the key is the "lazy") as expected, but defeats the clean code of a property wrapper
func prefix() -> String {
return String(describing: type(of: self))
}
lazy var _someVar = UserDefault(self.prefix() + ".someVar", defaultValue: "DEFAULT")
var someVar: String {
get {
return _someVar.wrappedValue
}
set {
_someVar.wrappedValue = newValue
}
}
Any better solutions are welcome as answer
I have written a simple protocol Data :
public protocol Data {
var state: [String: Any] { get set }
var objectId: String? { get set }
}
public extension Data {
var objectId: String? {
get {
return self.state["objectId"] as? String
}
set {
self.state["objectId"] = newValue
}
}
}
This way, I have created several types conforming to it:
public struct Person: Data {
public var state: [String : Any]
}
public struct Car: Data {
public var state: [String : Any]
}
// ...
Now what I want to do is to make each of these types Hashable, the problem is that I need to write this code in every type:
extension Car Hashable {
public static func == (lhs: Car, rhs: Car) -> Bool {
return lhs.objectId == rhs.objectId
}
public func hash(into hasher: inout Hasher) {
hasher.combine(self.objectId ?? "")
}
}
// ...
What I want to know is if it is possible to generically declare Data as Hashable from its objectId. As I am using a protocol, I couldn't find a way to do so.
Thank you for your help.
As #Leo Dabus mentioned in his comment, you should probably use another name for your protocol due to native Foundation.Data type existence.
Either way, using the following code you can implement Hashable protocol into your Data protocol:
public protocol Data: Hashable {
var state: [String: Any] { get set }
var objectId: String? { get set }
}
public extension Data {
var objectId: String? {
get {
return self.state["objectId"] as? String
}
set {
self.state["objectId"] = newValue
}
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.objectId == rhs.objectId
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.objectId ?? "")
}
}
Although this will have as a side effect that protocol Data can only be used as a generic constraint because it has Self or associated type requirements:
Meaning that you can now on use the Data protocol like this:
func myFunc<T: Data>(data: T) {
}
Is it a good practice to encapsulate a Core Data Model's data? just like you would in any other class, and implement getters and setters to retrieve and set data to it?
For example:
public class MyModel: NSManaged {
var name: String {
get { self._name }
set { self._name = newValue }
}
}
extension MyModel {
#nonobjc public class func fetchRequest() -> NSFetchRequest<MyModel> {
return NSFetchRequest<MyModel>(entityName: "MyModel")
}
#NSManaged fileprivate var _name: String
}
From within a property wrapper in Swift, can you someone refer back to the instance of the class or struck that owns the property being wrapped? Using self doesn't obviously work, nor does super.
I tried to pass in self to the property wrapper's init() but that doesn't work either because self on Configuration is not yet defined when #propertywrapper is evaluated.
My use case is in a class for managing a large number of settings or configurations. If any property is changed, I just want to notify interested parties that something changed. They don't really need to know which value just, so use something like KVO or a Publisher for each property isn't really necessary.
A property wrapper looks ideal, but I can't figure out how to pass in some sort of reference to the owning instance that the wrapper can call back to.
References:
SE-0258
enum PropertyIdentifier {
case backgroundColor
case textColor
}
#propertyWrapper
struct Recorded<T> {
let identifier:PropertyIdentifier
var _value: T
init(_ identifier:PropertyIdentifier, defaultValue: T) {
self.identifier = identifier
self._value = defaultValue
}
var value: T {
get { _value }
set {
_value = newValue
// How to callback to Configuration.propertyWasSet()?
//
// [self/super/...].propertyWasSet(identifier)
}
}
}
struct Configuration {
#Recorded(.backgroundColor, defaultValue:NSColor.white)
var backgroundColor:NSColor
#Recorded(.textColor, defaultValue:NSColor.black)
var textColor:NSColor
func propertyWasSet(_ identifier:PropertyIdentifier) {
// Do something...
}
}
The answer is no, it's not possible with the current specification.
I wanted to do something similar. The best I could come up with was to use reflection in a function at the end of init(...). At least this way you can annotate your types and only add a single function call in init().
fileprivate protocol BindableObjectPropertySettable {
var didSet: () -> Void { get set }
}
#propertyDelegate
class BindableObjectProperty<T>: BindableObjectPropertySettable {
var value: T {
didSet {
self.didSet()
}
}
var didSet: () -> Void = { }
init(initialValue: T) {
self.value = initialValue
}
}
extension BindableObject {
// Call this at the end of init() after calling super
func bindProperties(_ didSet: #escaping () -> Void) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if var child = child.value as? BindableObjectPropertySettable {
child.didSet = didSet
}
}
}
}
You cannot do this out of the box currently.
However, the proposal you refer to discusses this as a future direction in the latest version:
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
For now, you would be able to use a projectedValue to assign self to.
You could then use that to trigger some action after setting the wrappedValue.
As an example:
import Foundation
#propertyWrapper
class Wrapper {
let name : String
var value = 0
weak var owner : Owner?
init(_ name: String) {
self.name = name
}
var wrappedValue : Int {
get { value }
set {
value = 0
owner?.wrapperDidSet(name: name)
}
}
var projectedValue : Wrapper {
self
}
}
class Owner {
#Wrapper("a") var a : Int
#Wrapper("b") var b : Int
init() {
$a.owner = self
$b.owner = self
}
func wrapperDidSet(name: String) {
print("WrapperDidSet(\(name))")
}
}
var owner = Owner()
owner.a = 4 // Prints: WrapperDidSet(a)
My experiments based on : https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
protocol Observer: AnyObject {
func observableValueDidChange<T>(newValue: T)
}
#propertyWrapper
public struct Observable<T: Equatable> {
public var stored: T
weak var observer: Observer?
init(wrappedValue: T, observer: Observer?) {
self.stored = wrappedValue
}
public var wrappedValue: T {
get { return stored }
set {
if newValue != stored {
observer?.observableValueDidChange(newValue: newValue)
}
stored = newValue
}
}
}
class testClass: Observer {
#Observable(observer: nil) var some: Int = 2
func observableValueDidChange<T>(newValue: T) {
print("lol")
}
init(){
_some.observer = self
}
}
let a = testClass()
a.some = 4
a.some = 6
The answer is yes! See this answer
Example code for calling ObservableObject publisher with a UserDefaults wrapper:
import Combine
import Foundation
class LocalSettings: ObservableObject {
static var shared = LocalSettings()
#Setting(key: "TabSelection")
var tabSelection: Int = 0
}
#propertyWrapper
struct Setting<T> {
private let key: String
private let defaultValue: T
init(wrappedValue value: T, key: String) {
self.key = key
self.defaultValue = value
}
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
public static subscript<EnclosingSelf: ObservableObject>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, T>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Setting<T>>
) -> T {
get {
return object[keyPath: storageKeyPath].wrappedValue
}
set {
(object.objectWillChange as? ObservableObjectPublisher)?.send()
UserDefaults.standard.set(newValue, forKey: object[keyPath: storageKeyPath].key)
}
}
}
I have a subclass of NSManagedObject. I'm using a protocol to create a "wrapper" class. In my controller the data can be either: Items or Item1. To be able to use my function I'll have to add the protocol ItemInfo to Items but that means I'll have to add
var items: Items { return self }
in Items, which seems a bit redundant. I've tried creating a base class but that didn't work.
Question:
Is there a better way to let my function accept both Items and Item1 as parameter like using generics?
NSManagedObject:
class Items: NSManagedObject {
#NSManaged var name: String
#NSManaged var code: String
}
Protocol:
protocol ItemInfo {
var item: Items { get }
}
extension ItemInfo {
var name : String { return item.name }
var code : String { return item.code }
}
Wrapper:
class Item1: ItemInfo {
let item: Items
init(item: Items) { self.item = item }
}
function:
func handleItem(item: ItemInfo) {
print(item.name)
print(item.code)
}
I could use:
func handleItem<T>(item: T) {
if let a = item as? Items {
print(a.name)
print(a.code)
}
if let a = item as? ItemInfo {
print(a.name)
print(a.code)
}
}
But this doesn't seem the right way ...
If I understand correctly what you are trying to achieve (function accepting two kind of items), I would use protocol as type accepted by function, refer the code below
class Items: NSManagedObject, ItemInfo {
#NSManaged var name: String
#NSManaged var code: String
}
class Item1: NSManagedObject, ItemInfo {
#NSManaged var name: String
#NSManaged var code: String
}
protocol ItemInfo {
var name: String {get set}
var code: String {get set}
}
and function would look like this
func handle(item: ItemInfo) {
print(item.code)
print(item.name)
}