setting property of nsobject subclass by means of selector setProperty - swift

I have subclass of NSObject and I can set value of my subclass with:
performSelector(onMainThread: Selector("setNameOfProperty:"), with: "myName", waitUntilDone: true)
Actually, I did not write any method named setMethodProperty of course.
So here is my simple class:
class Post: NSObject {
var name: String?
var statusText: String?
var profileImageName: String?
var statusImageName: String?
var numLikes: NSNumber?
var numComments: NSNumber?
var location: Location?
override func setValue(_ value: Any?, forKey key: String) {
if key == "location" {
location = Location()
location?.setValuesForKeys(value as! [String: AnyObject])
} else {
super.setValue(value, forKey: key)
}
}
}
A then from another class I just invoke method performSelector:
let samplePost = Post()
samplePost.performSelector(onMainThread: Selector("setStatusText:"), with: "myName", waitUntilDone: true)
I am looking for any information about that interesting thing, but I couldn't. Maybe someone has the link about it or just know what is this behavior. If you can write about it to clarify situation.

Read more about key-value coding in About Key-Value Coding, specifically:
Objects typically adopt key-value coding when they inherit from NSObject (directly or indirectly), which both adopts the NSKeyValueCoding protocol and provides a default implementation for the essential methods. Such an object enables other objects, through a compact messaging interface, to do the following:
Access object properties.
The protocol specifies methods, such as the generic getter valueForKey: and the generic setter setValue:forKey:, for accessing object properties by their name, or key, parameterized as a string. The default implementation of these and related methods use the key to locate and interact with the underlying data, as described in Accessing Object Properties.
By subclassing NSObject, Post class implements NSKeyValueCoding.
Basically it means that the properties defined in Post generate corresponding getter and setter methods, which means that they can be accessed using performSelector. This Key-Value coding allows you to perform selectors for getting or setting even properties which names you don't know during compilation - selector can be created from a string variable.
In case you will decide to migrate the project to Swift 4, note that you will have to either mark each property that you want to access this way using #objc, or use #objcMembers annotation on the whole class.

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).

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.

Swift, Core Data, Optional Integer16 and keyPath

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)

Storing an array of custom objects in Realm

I have an object called List which subclasses Realm's Object class:
class List: Object {
dynamic var brandListItems: [BrandListItem] = []
}
and another object, BrandListItem which also subclasses to Object:
class BrandListItem: Object {
dynamic var brandID: String?
dynamic var name: String?
}
My app is crashing with the following error
'Property 'brandListItems' is declared as 'NSArray', which is not a supported RLMObject property type. All properties must be primitives, NSString, NSDate, NSData, NSNumber, RLMArray, RLMLinkingObjects, or subclasses of RLMObject.
I tried doing something like RLMArray<BrandListItem>() with no luck. How do I successfully save these objects to Realm?
You need to use Realm's List<T> property. Note that it's not marked dynamic
https://realm.io/docs/swift/latest/#to-many-relationships
class List: Object {
let brandListItems = RealmSwift.List<BrandListItem>()
}
Note that it's necessary to qualify Realm Swift's List<T> with its module to disambiguate it from your newly-declared List class.

What's the difference between class methods and instance methods in Swift?

protocol NoteProtocol {
var body: NSString? { get set }
var createdAt: NSDate? { get set }
var entityId: NSString? { get set }
var modifiedAt: NSDate? { get set }
var title: NSString? { get set }
// class methods
class func insertNewNoteInManagedObjectContext(managedObjectContext: NSManagedObjectContext!) -> NoteProtocol
class func noteFromNoteEntity(noteEntity: NSManagedObject) -> NoteProtocol
// instance methods
func update(#title: String, body: String)
func deleteInManagedObjectContext(managedObjectContext: NSManagedObjectContext!)
}
Hi
This is a piece of code I found on GitHub. In this protocol, what is the main difference between class methods and instance methods? How they are defined?
Can anyone help me?
Some text from the documentation:
Instance Methods
Instance methods are functions that belong to instances of a particular class, structure, or enumeration. They support the functionality of those instances, either by providing ways to access and modify instance properties, or by providing functionality related to the instance’s purpose.
ie. An Instance of the class has to call this method. Example :
var a:classAdoptingNoteProtocol=classAdoptingNoteProtocol()
a.update()
Class Methods
Instance methods, as described above, are methods that are called on an instance of a particular type. You can also define methods that are called on the type itself. These kinds of methods are called type methods. You indicate type methods for classes by writing the keyword class before the method’s func keyword, and type methods for structures and enumerations by writing the keyword static before the method’s func keyword.
They are what are called as Static methods in other languages.To use them, this is what I would do:
var b=classAdoptingNoteProtocol.noteFromNoteEntity(...)
This will return a instance of a class which adopts NoteProtocol. ie. you don't have to create a instance of the class to use them.
Below the definition of instance methods and class methods (called type methods in Swift).
For more details you can browse the method section of the Swift documentation
Instance methods:
Instance methods are functions that belong to instances of a particular class, structure, or enumeration. They support the functionality of those instances, either by providing ways to access and modify instance properties, or by providing functionality related to the instance’s purpose. Instance methods have exactly the same syntax as functions
Type methods:
Instance methods, as described above, are methods that are called on
an instance of a particular type. You can also define methods that are
called on the type itself. These kinds of methods are called type
methods. You indicate type methods for classes by writing the keyword
class before the method’s func keyword, and type methods for
structures and enumerations by writing the keyword static before the
method’s func keyword.
Basically you can call type method (class method) without instance:
var myNoteProtocol = NoteProtocolAdoptImplClass.noteFromNoteEntity(...);
While you need to instantiate for instance methods:
var myNoteProtocol = NoteProtocolAdoptImplClass()
myNoteProtocol.update(...)