KVO or how to listen for a property changes from outside in Swift - objective-c

I have a instance with a property which I want to listen for updates from other instance.
For example class Menu has a property badgeCount, I want to listen for any updates for badgeCount for example when badgeCount is changed. I want my ViewController to have callback after badgeCount is modified to know actual data.
In objective was KVO that I can use for listed property, how can I use KVO in Swift. I am new in Swift.

If you want to use KVO in swift, there are two requirements :
The class you want to do KVO on must inherit from NSObject (or any NSObject subclass)
The property you need to observe must be marked as dynamic
a code example would be:
class Menu: NSObject {
dynamic var badgeCount: Int = 0
}
And then, you can use the usual menuInstance.addObserver(self, forKeyPath: "badgeCount", options: NSKeyValueObservingOptions(), context: nil)
But this solution is not very much swifty.
Better solutions are (not an exhaustive list):
Use swift's didSet/willSet to call some callback
class Menu {
var badgeCount: Int = 0 {
didSet {
badgeCountChangedListener(badgeCount)
}
}
init(badgeCountChangedListener: (Int -> Void)) {
self.badgeCountChangedListener = badgeCountChangedListener
}
private let badgeCountChangedListener: (Int -> Void)
}
Use RxSwift's Variable type
class Menu {
let badgeCount = Variable(0)
}
// and from where you observe
menuInstance.badgeCount.subscribeNext { badgeCount in
print(badgeCount)
}

Related

Alternative to load method in Swift

I am working on developing an application in Swift. I wanted to design a system for the application that allowed for loose coupling between objects, and one strategy (which I have used successfully in other languages) was to create something I call an instance factory. It is pretty simple and here is the basic implementation I came up with in Swift:
import Foundation
private var typeGenerators = Dictionary<String, InstanceFactory.GeneratorCallback>()
public class InstanceFactory: NSObject {
public typealias GeneratorCallback = () -> AnyObject!
public class func registerGeneratorFor(typeName: String, callback: GeneratorCallback) {
typeGenerators[typeName] = callback
}
public class func instanceOf(typeName: String) -> AnyObject! {
return typeGenerators[typeName]?()
}
}
The idea is that when an object instance needs access to another object instance, rather than creating that instance outright which would more tightly couple the two objects, the first object would defer to the factory to provide the needed instance by calling the instanceOf method. The factory would know how to provide various instance types because those types would register with the factory and provide a closure that could generate the instance.
The trick is how to get the classes to register with the factory. I had previously made a similar factory in Objective-C and the way I got registration to work was to override the +load method for each class that needed to register with the factory. This worked great for Objective-C, and I figured it could work for Swift as well since I would be restricting the factory to only provide objects that are derived from NSObject. It appeared I got this to work and I spent a significant about of effort designing classes to make use of the factory.
However, after upgrading to Xcode 6.3, I discovered Apple has disallowed the usage of the load class method in Swift. Without this, I am unaware of a mechanism to allow classes to automatically register themselves with the factory.
I am wondering if there some other way to get the registration to work.
What alternatives are available that could allow classes to register with the factory, or what other techniques could be use to accomplish the same kind of loose coupling the factory provides?
I've found a possible solution to your problem after I wanted to register all ViewControllers that would be implementing a certain Protocol in my application and I ran into both this question and a possible answer.
The original was posted here: How to list all classes conforming to protocol in Swift?
I adapted it to Swift 3 and made it a bit more Swift-y and generic:
import UIKit
class ContextRoute: NSObject {
}
#objc protocol ContextRoutable {
static var route: ContextRoute { get }
}
class ContextRouter: NSObject {
private static var storedRoutes: [ContextRoute]?
static var routes: [ContextRoute] {
get {
if let storedRoutes = storedRoutes {
return storedRoutes
}
let routables: [ContextRoutable.Type] = classes(implementing: ContextRoutable.self)
let newRoutes = routables.map { routable in routable.route }
storedRoutes = newRoutes
return newRoutes
}
}
private class func classes<T>(implementing objcProtocol: Protocol) -> [T] {
let classes = classList().flatMap { objcClass in objcClass as? T }
return classes
}
private class func classList() -> [AnyObject] {
let expectedClassCount = objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))
let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
let actualClassCount:Int32 = objc_getClassList(autoreleasingAllClasses, expectedClassCount)
var classes = [AnyObject]()
for i in 0 ..< actualClassCount {
if let currentClass: AnyClass = allClasses[Int(i)],
class_conformsToProtocol(currentClass, ContextRoutable.self) {
classes.append(currentClass)
}
}
allClasses.deallocate(capacity: Int(expectedClassCount))
return classes
}
}
I tried it in my application and it works. I clocked it in the simulator and it takes 0.05s for an application that has about 12000 classes.
Consider taking the Swift approach using a protocol instead. I think the solution is actually simpler than the Objective-C approach. There are variations of this with Self constraints which are even better if you have more control over the classes.
// define a protocol to create an instance of a class
protocol FactoryInstantiable {
static func makeFactoryInstance() -> AnyObject
}
// Factory for generating new instances
public class InstanceFactory: NSObject {
public class func instanceOf(typeName: String) -> AnyObject! {
if let ProductType = NSClassFromString(typeName) as? FactoryInstantiable.Type {
return ProductType.makeFactoryInstance()
} else {
return nil
}
}
}
// your class which generally could be defined somewhere else
class MyClass {
var counter : Int
init(counter: Int) {
self.counter = 0
}
}
// extension of your class to conform to the FactoryInstantiable protocol
extension MyClass : FactoryInstantiable {
static func makeFactoryInstance() -> AnyObject {
return MyClass(counter: 0)
}
}

Implementing a generic weak storage box which knows when the value becomes nil

Not long after I posted this question, I got an idea of how a generic weak storage box could be implemented, that knows and sends an event when the weak value it holds is deallocked.
Here's what I mean by that (an example implementation that doesn't work):
class WeakValue<T: AnyObject> {
weak var value: T? {
didSet { /* Send event here when value is changed __or becomes nil__ */ }
}
}
This doesn't work. For some reason, property observers are not triggered when they observe a weak var and it becomes nil (missing feature, anyone?).
So, here's the idea I had:
private class Watcher {
weak var delegate: WeakValue<AnyObject>?
init(delegate d: WeakValue<AnyObject>) { delegate = d }
deinit { delegate?.watcherWillDisappear() }
}
public class WeakValue<T: AnyObject> {
private let storage = NSMapTable.strongToWeakObjectsMapTable()
public init() {}
public init(value v: T?) { value = v; reloadValue() }
public weak var value: T? { didSet { reloadValue() } }
private func reloadValue() {
storage.removeAllObjects()
if let v = value { storage.setObject(v, forKey: Watcher(delegate: unsafeBitCast(self, WeakValue<AnyObject>.self))) }
}
private func watcherWillDisappear() {
/* Event triggered here */
}
}
The idea was to use the functionality of NSMapTable to my advantage. Here's how it should work:
When a value is set, a strong-key/weak-value pair is added to NSMapTable. The key is a Watcher class that only NSMapTable holds a reference to (that's why it has to be strong). The value is the actual value that is to be stored.
Whenever the value is deallocked, NSMapTable removes the key/value pair from its storage, which in turn deallocks the Watcher class (set as the key and which only NSMapTable holds a reference to), which, when doing so, warns the WeakValue class.
My question is twofold:
This doesn't seem to work (I didn't test this on a Playground, I tested it on a real project): the test class I feed to WeakValue is deallocked, but watcherWillDisappear is not called. Why doesn't it work?
This is not very efficient, right? (creating a new NSMapTable for every weak value I want to store and for which I need an alert when deallocked)

Computed property of closures

I would like to use a closure as a computed property. I mean something like the code below.
class MyClass {
typealias myFuncType = () -> (Void)
private var _myAction:myFuncType
var myAction:myFuncType = {
set(newAction){
self._myAction = newAction
}
}
}
Is it possible or will the compiler think that as I opened a bracked it must be the closure definition?
Closures (and functions) are advertised as first class citizens in swift, so you can store them in variables and properties like any other data type.
That said, your code is almost good, you just have to remove the '=' because otherwise it's considered as a stored property with inline initialization. The correct code is:
var myAction:myFuncType {
set(newAction) {
self._myAction = newAction
}
get { // see notes below
return _myAction
}
}
Some notes:
there's no need to use a computed property backed by a stored property - your code is equivalent to:
class MyClass {
typealias myFuncType = () -> (Void)
var myAction: myFuncType
}
if you need to make additional processing when setting the property, make use of Property Observers: willSet and didSet
in a computed property, if you implement a setter, you must also provide a getter

Accessor methods in Swift not being called

In Objective-C I am capable of doing the following on properties:
#property (nonatomic, strong) Person *currentPerson;
Then intellisense will reveal that I can add the following method:
- (void)setCurrentPerson:(Person *)currentPerson {
...
}
When that properties method is set, that method is then called.
Is there an equivalent way of having setter methods fired in Swift? I'm noticing that there's no intellisense and adding the same method in Swift never gets called.
func setCurrentPerson(currentPerson: Person) {} // never called
You can achieve the same result by using a pseudo-private property embedded into a computed property, as follows:
class Test {
var _data: String = ""
var data: String {
get { return self._data}
set { self._data = newValue }
}
}
the _data property is still publicly accessible (there's no private/protected/private access modifier in swift yet) - that's the reason why I prefix it with an underscore, as a reminder for me that's a pseudo-private property.
With the data computed property you have getter and setter you can use to override the standard behavior.
This method is a workaround mimicking properties in objective c. willSet and didSet property observers can still be used as outlined by #gotnull in his answer.
As defined in the Swift Properties guide:
var currentPerson: Person? {
willSet(currentPerson) {
// ...
}
didSet {
// ...
}
}

What is the Swift equivalent of respondsToSelector?

I've googled but not been able to find out what the swift equivalent to respondsToSelector: is.
This is the only thing I could find (Swift alternative to respondsToSelector:) but isn't too relevant in my case as its checking the existence of the delegate, I don't have a delegate I just want to check if a new API exists or not when running on the device and if not fall back to a previous version of the api.
As mentioned, in Swift most of the time you can achieve what you need with the ? optional unwrapper operator. This allows you to call a method on an object if and only if the object exists (not nil) and the method is implemented.
In the case where you still need respondsToSelector:, it is still there as part of the NSObject protocol.
If you are calling respondsToSelector: on an Obj-C type in Swift, then it works the same as you would expect. If you are using it on your own Swift class, you will need to ensure your class derives from NSObject.
Here's an example of a Swift class that you can check if it responds to a selector:
class Worker : NSObject
{
func work() { }
func eat(food: AnyObject) { }
func sleep(hours: Int, minutes: Int) { }
}
let worker = Worker()
let canWork = worker.respondsToSelector(Selector("work")) // true
let canEat = worker.respondsToSelector(Selector("eat:")) // true
let canSleep = worker.respondsToSelector(Selector("sleep:minutes:")) // true
let canQuit = worker.respondsToSelector(Selector("quit")) // false
It is important that you do not leave out the parameter names. In this example, Selector("sleep::") is not the same as Selector("sleep:minutes:").
There is no real Swift replacement.
You can check in the following way:
someObject.someMethod?()
This calls the method someMethod only if it's defined on object someObject but you can use it only for #objc protocols which have declared the method as optional.
Swift is inherently a safe language so everytime you call a method Swift has to know the method is there. No runtime checking is possible. You can't just call random methods on random objects.
Even in Obj-C you should avoid such things when possible because it doesn't play well with ARC (ARC then triggers warnings for performSelector:).
However, when checking for available APIs, you can still use respondsToSelector:, even if Swift, if you are dealing with NSObject instances:
#interface TestA : NSObject
- (void)someMethod;
#end
#implementation TestA
//this triggers a warning
#end
var a = TestA()
if a.respondsToSelector("someMethod") {
a.someMethod()
}
Update Mar 20, 2017 for Swift 3 syntax:
If you don't care whether the optional method exists, just call delegate?.optionalMethod?()
Otherwise, using guard is probably the best approach:
weak var delegate: SomeDelegateWithOptionals?
func someMethod() {
guard let method = delegate?.optionalMethod else {
// optional not implemented
alternativeMethod()
return
}
method()
}
Original answer:
You can use the "if let" approach to test an optional protocol like this:
weak var delegate: SomeDelegateWithOptionals?
func someMethod() {
if let delegate = delegate {
if let theMethod = delegate.theOptionalProtocolMethod? {
theMethod()
return
}
}
// Reaching here means the delegate doesn't exist or doesn't respond to the optional method
alternativeMethod()
}
If the method you are testing for is defined as an optional method in a #objc protocol (which sounds like your case), then use the optional chaining pattern as:
if let result = object.method?(args) {
/* method exists, result assigned, use result */
}
else { ... }
When the method is declare as returning Void, simply use:
if object.method?(args) { ... }
See:
“Calling Methods Through Optional Chaining”
Excerpt From: Apple Inc. “The Swift Programming Language.”
iBooks. https://itun.es/us/jEUH0.l
It seems you need to define your protocol as as subprotocol of NSObjectProtocol ... then you'll get respondsToSelector method
#objc protocol YourDelegate : NSObjectProtocol
{
func yourDelegateMethod(passObject: SomeObject)
}
note that only specifying #objc was not enough. You should be also careful that the actual delegate is a subclass of NSObject - which in Swift might not be.
For swift3
If you just want to call the method, run the code below.
self.delegate?.method?()
Functions are first-class types in Swift, so you can check whether an optional function defined in a protocol has been implemented by comparing it to nil:
if (someObject.someMethod != nil) {
someObject.someMethod!(someArgument)
} else {
// do something else
}
In Swift 2,Apple introduced a new feature called API availability checking, which might be a replacement for respondsToSelector: method.The following code snippet comparison is copied from the WWDC2015 Session 106 What's New in Swift which I thought might help you,please check it out if you need to know more.
The Old Approach:
#IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
if dropButton.respondsToSelector("setSpringLoaded:") {
dropButton.springLoaded = true
}
}
The Better Approach:
#IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
if #available(OSX 10.10.3, *) {
dropButton.springLoaded = true
}
}
For swift 3.0
import UIKit
#objc protocol ADelegate : NSObjectProtocol {
#objc optional func hi1()
#objc optional func hi2(message1:String, message2:String)
}
class SomeObject : NSObject {
weak var delegate:ADelegate?
func run() {
// single method
if let methodHi1 = delegate?.hi1 {
methodHi1()
} else {
print("fail h1")
}
// multiple parameters
if let methodHi2 = delegate?.hi2 {
methodHi2("superman", "batman")
} else {
print("fail h2")
}
}
}
class ViewController: UIViewController, ADelegate {
let someObject = SomeObject()
override func viewDidLoad() {
super.viewDidLoad()
someObject.delegate = self
someObject.run()
}
// MARK: ADelegate
func hi1() {
print("Hi")
}
func hi2(message1: String, message2: String) {
print("Hi \(message1) \(message2)")
}
}
Currently (Swift 2.1) you can check it using 3 ways:
Using respondsToSelector answered by #Erik_at_Digit
Using '?' answered by #Sulthan
And using as? operator:
if let delegateMe = self.delegate as? YourCustomViewController
{
delegateMe.onSuccess()
}
Basically it depends on what you are trying to achieve:
If for example your app logic need to perform some action and the delegate isn't set or the pointed delegate didn't implement the onSuccess() method (protocol method) so option 1 and 3 are the best choice, though I'd use option 3 which is Swift way.
If you don't want to do anything when delegate is nil or method isn't implemented then use option 2.
As I started to update my old project to Swift 3.2, I just needed to change the method from
respondsToSelector(selector)
to:
responds(to: selector)
I just implement this myself in a project, see code below. As mentions by #Christopher Pickslay it is important to remember that functions are first class citizens and can therefore be treated like optional variables.
#objc protocol ContactDetailsDelegate: class {
optional func deleteContact(contact: Contact) -> NSError?
}
...
weak var delegate:ContactDetailsDelegate!
if let deleteContact = delegate.deleteContact {
deleteContact(contact)
}
another possible syntax by swift..
if let delegate = self.delegate, method = delegate.somemethod{
method()
}
I use guard let else, so that can do some default stuff if the delegate func is not implemented.
#objc protocol ViewController2Delegate: NSObjectProtocol {
optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnVoid string: String)
optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnString string: String) -> String
}
class ViewController2: UIViewController {
weak var delegate: ViewController2Delegate?
#IBAction func onVoidButtonClicked(sender: AnyObject){
if (delegate != nil && delegate!.respondsToSelector(Selector("viewController2:didSomethingWithStringAndReturnVoid:"))) {
NSLog("ReturnVoid is implemented")
delegate!.viewController2!(self, didSomethingWithStringAndReturnVoid: "dummy")
}
else{
NSLog("ReturnVoid is not implemented")
// Do something by default
}
}
#IBAction func onStringButtonClicked(sender: AnyObject){
guard let result = delegate?.viewController2?(self, didSomethingWithStringAndReturnString: "dummy") else {
NSLog("ReturnString is not implemented")
// Do something by default
return
}
NSLog("ReturnString is implemented with result: \(result)")
}
}
I guess you want to make a default implementation for delegate. You can do this:
let defaultHandler = {}
(delegate?.method ?? defaultHandler)()
Swift 3:
protocol
#objc protocol SomeDelegate {
#objc optional func method()
}
Object
class SomeObject : NSObject {
weak var delegate:SomeObject?
func delegateMethod() {
if let delegateMethod = delegate?.method{
delegateMethod()
}else {
//Failed
}
}
}
The equivalent is the ? operator:
var value: NSNumber? = myQuestionableObject?.importantMethod()
importantMethod will only be called if myQuestionableObject exists and implements it.