Swift, Core Data, Optional Integer16 and keyPath - swift

I have an entity in CoreData which has an optional property of type Integer 16. It can genuinely be nil and in my application I want to refer to it as an Int? type. As Int? (or for that matter Int16?) isn't a recognised Objective-C type, the compiler throws a bit of a wobbly. I want to avoid using code like NSNumber?.intValue throughout so I've actually set up my ManagedObject type with custom accessors for this property. My question relates to identifying the property through #keyPath rather than a static string. In Core Data the field is named 'pin' on entity 'User'. Here's the code I have:
class User: NSManagedObject {
// MARK: - Properties
static let pinKey = "pin"
#NSManaged internal(set) var name: String
#NSManaged fileprivate var primitivePin: NSNumber?
internal(set) var pin: Int? {
get {
willAccessValue(forKey: #keyPath(pin)) // THIS LINE ERRORS
let value: Int? = primitivePin.map { $0.intValue }
didAccessValue(forKey: User.pinKey)
return value
}
set {
willChangeValue(forKey: User.pinKey)
primitivePin = newValue.map { NSNumber(value: Int16($0)) }
didChangeValue(forKey: User.pinKey)
}
}
}
The line in error is what I 'want' to achieve but of course the var pin isn't an obj-c type and the compiler complains, so I have defined the static constant pinKey as you can see. #keyPath feels like the right way to go about it, and the entity does have a field called pin, but in this scenario is the only option open to me to use a static value?

In #keyPath you have to specify property name. If you don't have defined property called pin, you will receive an error. In your case you have to use #keyPath(User.primitivePin). I believe this should work.
Also, i guess, calling map is redundant here. You can write directly let value = primitivePin?.intValue and so on.

The answer is....with custom properties/accessors #keyPath can't used as there is no defined #NSManaged property for it - as Maksym points out. However, you can't use the defined primitive for it either, instead using the property name as a String as also shown in the code above (User.pinKey)

Related

How to pass a class object to a function but prevent mutation?

I can't see where in the Swift language is the facility to pass a class object to a function yet prevent that function from mutating the object by either calling functions that will implicitly mutate it or setting public variables. I'm gathering that this facility just does not exist, can anyone confirm?
That is to say, all objects are always mutable everywhere they can be seen.
This is extremely common throughout Cocoa. You create an immutable class and a mutable subclass. For examples, see AVComposition/AVMutableComposition, CBService/CBMutableService, CNContact/CNMutableContact.
In ObjC, this is common practice with collections as well (arrays, dictionaries, etc), but since those are value types in Swift, there's no need to use the classes (NSArray/NSMutableArray).
In Swift, rather than creating two classes, you create an immutable protocol and a class:
protocol Person: AnyObject {
var name: String { get }
var address: String { get }
}
class MutablePerson: Person {
var name: String = ""
var address: String = ""
}
Now, any function that accept Person will have an immutable object, and any function that accepts MutablePerson will be able to mutate it. This is a general pattern you can use to give different parts of your program access to different slices of the object's API. It's much more general and flexible than just const.
That said, this is not as common a pattern in Swift as it is in ObjC, since in most cases where this is useful, the type should be a struct anyway. But it is absolutely available if needed.
To your question about doing this with two classes, as in ObjC, it's possible, as long as you define both in the same file. It's just a bit tedious:
public class Person {
public fileprivate(set) var name: String = ""
public fileprivate(set) var address: String = ""
}
public class MutablePerson: Person {
public override var name: String {
get { super.name }
set { super.name = newValue }
}
public override var address: String {
get { super.address }
set { super.address = newValue }
}
}
It's possible a property wrapper could improve this, but I haven't been able to figure out how.
There's no way I can think of to allow usage of methods, but properties are no problem**. Just use an Immutable as a function parameter.
final class Class {
var property = true
}
var object = Immutable(Class())
object.property = false // Cannot assign to property: 'object' is immutable
/// An affordance for accessing the properties of an object
/// without the ability to mutate them.
#dynamicMemberLookup
public struct Immutable<Object: AnyObject> {
private let object: Object
}
// MARK: - public
public extension Immutable {
init(_ object: Object) {
self.object = object
}
subscript<Value>(dynamicMember keyPath: KeyPath<Object, Value>) -> Value {
object[keyPath: keyPath]
}
}
** The getters could be mutating, and they could return mutating closures. 😜 But that's an issue with the protocol approach as well. The best that we can do right now is a generally accurate hack.
What you are looking for are value types (such as structs). If you mutate any properties of a value type, you mutate the instance itself.
This means that when you pass a value type to a function, the function won't be able to mutate any of the properties of said value type.
On the other hand, classes are reference types, so mutating any of their properties doesn't mutate the class instance itself. Because of this, you cannot ban functions from modifying mutable properties of the class (unless you make them setter of said properties private).

In Swift do you not have to use a getter and setter when creating an object type, such as class, that conforms to a protocol requiring so?

protocol UserType {
var name: String { get }
var age: Int { get set }
struct Person: UserType {
var name: String //<- why is this okay?
var age: Int //<- why is this okay? dont we need a getter setter
}
let somePerson = Person(name: "Billy", age: 22)
Why in the above examples, we dont use get constructs or set constructs for age and a get construct for name? Is it because in the protocol when the property is said to "{ get set }" that essentially means it has to be able to be read and has to be able to be changed, which can be done through a declaration in the syntax of a stored property?
Thanks!
protocol UserType {
var name: String { get }
var age: Int { get set }
}
The important thing to understand is that { get } and { get set } are merely notations. They have nothing to do with "getter and setter". They are merely ways of describing to the compiler what the protocol's requirements are. (You could argue that the notation is confusing or misleading, but it's what we've got, so we have to live with it.)
So, basically, all this says is:
An adopter must declare a name String instance property.
An adopter must also declare an age Int instance property and that property must be writable.
That's all it means. Well, you have satisfied those requirements in your adopter (Person). Your code is legal. The end.
One thing to note first: Every property has an implicit getter and setter unless otherwise stated. e.g. a simple var age: Int implicitly has a get and set, and let age: Int implicitly has a get. When you declare a manual getter and setter, you're overriding something, not creating something that didn't otherwise exist.
As for protocols, in explicit terms:
var name: String { get } means the adhering Type must have a property with a signature of name:String that can be read, AKA. get
var age: Int { get set } means the adhering Type must have a property with a signature of age:String that can be read or assigned, AKA get and set
Note that these rules are inclusive not exclusive. The requirements don't care how they are satisfied, and don't disallow anything else.
This means with your given example, a property of let name: String would work because it only requires access, but let age: Int wouldn't work, because it cannot be changed. It also doesn't care about internal details, so using computed properties, private setters, etc. is fine as long as they have the necessary access.

Is it possible to get the KVC-String from Swift 4 KeyPath?

For a project I am currently working on, it would be very useful to get the KVC-String from a KeyPath instance my method is receiving. Short example:
struct Person {
var name: String
}
let propertyCache = ["name": "something"]
func method<T>(_ keypath: KeyPath<Person, T>) -> T? {
let kvcName = keypath.kvc
return propertyCache[kvcName]
}
This might seem not very useful, but in my project it is :) I found a property on KeyPath called _kvcKeyPathString which is also public, but it returns nil every time I tried.
Or is their maybe a possibility to use reflection there? Thanks in advance for ideas/solutions!
I don't know of a pure Swift way to get the name of the property as a string yet.
But, if you add the #objc attribute to the property then _kvcKeyPathString will actually have a value instead of always being nil. Also, since Swift structs can't be represented in Objective-C, this method only works for classes.
A minimal working example usage:
class SomeClass {
#objc var someProperty = 5
}
let keyPath = \SomeClass.someProperty
print(keyPath._kvcKeyPathString)

How to reflect not managed properties of NSManagedObject

I am searching how to get not managed property names and types of a NSManagedObject subclass.
here is few sample code to help me to ask my question :
#objc(Operation)
public class Operation : NSManagedObject {
#NSManaged var name: String
#NSManaged var amount: NSNumber
}
#objc(Account)
public class Account: NSManagedObject {
#NSManaged var bic: String
#NSManaged var number: String
#NSManaged var operations: Set<Operation>
#NSManaged var servicesSubscriptions: Set<ServiceSubcription>
// and more.
}
extension Account
{
public var lastOperation : Operation {
get
{
return self.operations.last
}
set(value)
{
self.operations.insert(value)
}
}
}
I have found many ways to get property names using reflect() function. reflect() do not work with NSManagedObject at all. (like this simple one)
edit
I have found examples with class_copyPropertyList function, that retrieve correctly property names, but don't found yet how to get types. Thank to Tom Harrington comment. (see that sample)
I have found many ways to get Attributes (or relations) of managed objects using NSEntityDescription. (like this one). Which work and get back bic and number, but not lastOperation.
edited
updated code sample to match better to reality
So my question is :
How to get back my lastOperation property, and its type, dynamically at run time ?
edit, what i am trying to do
I am parsing json, dnamically using reflection.
I need the type (or type name) of a property knowing only its name (i have "lastOperation", and need to get back Operation, or "Operation"). Once i get the type i can instanciate an object, then populate its own properties, using same mechanism (recursively).
Thank you for any help
When you get the list of properties using class_copyPropertyList, you can iterate through the list to look at each property in turn:
var propertyCount : UInt32 = 0
let properties = class_copyPropertyList(Account.self, &propertyCount)
for var i=0; i<Int(propertyCount); i++ {
let property = properties[i]
let propertyName = String(UTF8String: property_getName(property))
....
}
The type of each property is contained in one of the property attributes, as a string:
let propertyType = property_copyAttributeValue(property, "T")
let propertyTypeString = String(UTF8String: propertyType)
For your lastOperation property the string will look something like #\"Operation\". You'll have to clean up that string a little to get Operation.
I wrote a blog post a while ago describing something similar to what you're trying to do. The code is in Objective-C but all the functions, methods, etc are the same.
There is no way that I know of when it comes to a NSManagedObject. However, I would suggest creating title as a transient property inside of your model and then it will show up as part of the entity description.

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)