might be a silly question, but I'm trying to understand better why I can't do this. I recall this working in Swift 5.6.1, but I recently updated to Swift 5.7.2.
Before asking, I want to note that I did see this question: Swift passing self as argument in class init, but it didn't quite answer my question. Or maybe I just want to see if these are the only solutions...
I have a couple of classes that's something like this.
class Bar {
weak var delegate: FooDelegate?
init(delegate: FooDelegate) {
self.delegate = delegate
}
}
class Foo: FooDelegate {
var bar: Bar
init() {
self.bar = Bar(delegate: self)
}
}
Before I updated, I don't remember this throwing any errors. Now I'm getting the error
Variable 'self.bar' used before being initialized.
Is there a way to set this up so that I'm passing the delegate correctly?
Thanks all!
You can solve this by breaking it up in two steps, create the Bar object and then set delegate
init() {
bar = Bar()
bar.delegate = self
}
Of course this requires a new init for the Bar class
I created an extension swift file, and in that file I wrote extension UIViewController{ ... } in my project, rather than writing all lines in every view controller. Everything worked ok. Later on I created a public func(i.e myFunc), with the same name inside each of the 30 view controllers, and having different outputs. I am trying to call this common named function from the extension file, but I get an error "Value of type 'UIViewController' has no member 'myFunc'. On the extension file, how can I call this common named function which executes different outputs on each different view controller?
Extension file Code:
extension UIViewController {
func sampleFunc {
let viewController = self.navigationController?.visibleViewController {
........
viewController.myFunc() }. *//error: Value of type 'UIViewController?' has no member 'myFunc'*
}
}
Note: I don't want to use like this:
if let viewController = self as? ViewController1 {viewController.myFunc()}
if let viewController = self as? ViewController2 {viewController.myFunc()}
if let ... 3,
4....30
Or maybe is there a way to check whether the function exists, and if it exists then execute command without receiving that kind of errors ?
use of unresolved identifier
or
value of type 'UIViewController?' has no member 'myFunc'
The viewController variable in the extension is could be any VC, couldn't it? Not just the 30 you created. You can't guarantee that any random VC will have a method called myFunc, so you can't call it in the extension.
One way to resolve this problem is to create a protocol that your 30 VCs all implement:
protocol MyFuncable : UIViewController { // please come up with a better name
func myFunc ()
}
This is an example of how you implement the protocol:
class ViewController: UIViewController, MyFuncable {
func myFunc() {
// do whatever you want...
print("myFunc executed")
}
}
Now we guarantee that everything that implements myFuncable will have a method called myFunc, so now in your extension, you can check whether the VCs are MyFuncable:
extension UIViewController {
func sampleFunc() {
// here is where the checking happens:
if let viewController = self.navigationController?.visibleViewController as? MyFuncable {
viewController.myFunc()
}
}
}
Try this code below.
extension UIViewController {
func sampleFunc() {
if let viewController = self.navigationController?.visibleViewController {
print("Class name: \(NSStringFromClass(type(of: viewController)))")
let anyObject = viewController as AnyObject
if anyObject.responds(to: #selector(anyObject.myFunc)) {
anyObject.myFunc()
}
}
}
}
I'm trying to achieve the following but am running into issues :-)
create a protocol that UIViewController and UIView subclass can adopt
which contain one static method to be called on this class (call it
configuration
I then want to use the objectiveC runtime to find the classes that adopt this protocol
On each of those class I want to call the configuration method
The configuration method is to return a dictionary (key: a description string, value: a selector to be called on the class)
So far I was able to create the protocol, find the class implementing the protocol but i'm running into compiling issues.
Here is the protocol
#objc public protocol MazeProtocol: NSObjectProtocol{
#objc static func configurations() -> NSDictionary
}
Here is the extension to adopt the protocol on one of my class:
extension MapCoordinatorViewController: MazeProtocol {
static func configurations() -> NSDictionary {
let returnValue = NSMutableDictionary()
returnValue.setObject(#selector(test), forKey: "test" as NSString)
return returnValue
}
#objc static func test() {
print("test")
}}
and here is the code i'm using to try to call the selector returned from the configuration method:
let selectorKey = controllerClass.configurations().allKeys[indexPath.row]
let selector = controllerClass.configurations().object(forKey: selectorKey)
controllerClass.performSelector(selector) <================ error here
ControllerClass is declared as let controllerClass: MazeProtocol.Type
I get the following compile warning:
Instance member 'performSelector' cannot be used on type 'MazeProtocol'
What am I missing?
You can technically force this to work. Please don't. This is horrible Swift. To get this to work, you have to undermine everything Swift is trying to do. But yes, with warnings, you can technically get this to compile and work. Please, please don't.
First, you need to make selector be a Selector. You're using an NSDictionary, which is terrible in Swift, and so you get Any? back. But, yes, you can as! cast it into what you want:
let selector = controllerClass.configurations().object(forKey: selectorKey) as! Selector
And then, defying all the type gods, you can just declare that classes are actually NSObjectProtocol, because why not?
(controllerClass as! NSObjectProtocol).perform(selector)
This will throw a warning "Cast from 'MapCoordinatorViewController.Type' to unrelated type 'NSObjectProtocol' always fails", but it will in fact succeed.
After all that "don't do this," how should you do this? With closures.
public protocol MazeProtocol {
static var configurations: [String: () -> Void] { get }
}
class MapCoordinatorViewController: UIViewController {}
extension MapCoordinatorViewController: MazeProtocol {
static let configurations: [String: () -> Void] = [
"test": test
]
static func test() {
print("test")
}
}
let controllerClass = MapCoordinatorViewController.self
let method = controllerClass.configurations["test"]!
method()
Consider the following code, which adds a gesture recognizer to a view.
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
let gesture = UITapGestureRecognizer(target: self, action: #selector(handleGesture(gesture:)))
let test1 = self
#objc func handleGesture(gesture: UITapGestureRecognizer) {
// some code
print("hello")
}
override func viewDidLoad() {
let test2 = self
super.viewDidLoad()
imageView.addGestureRecognizer(gesture)
}
}
As per this question, the above code does not work because I'm trying to use self (in the gesture recognizer's initializer) when not fully initialized, and this is so because of Swift's two-phase initialization.
I'm not interested in the easy fix to make this work, but this triggers a couple of questions:
1) Why does the compiler allow us to use self here if self is not ready to be used? Shouldn't I get a compiler error if I'm trying to use self too soon?
2) We can't directly inspect the type of self with alt+click in XCode. However, we can inspect the types of my ad hoc variables test1 and test2. While test2's type is ViewController, as expected, test1's type is (ViewController) -> () -> ViewController (i.e., a closure that takes a ViewController and returns a closure that takes nothing and returns a ViewController). What is that and why does self have two different types within the same class?
1)
Shouldn't I get a compiler error if I'm trying to use self too soon?
I do agree. You may send a bug report to swift.org.
Why does the compiler allow us to use self here if self is not ready to be used?
Unfortunately, there's another self in the descendants of NSObject, the method self() of NSObject.
2)
What is that and why does self have two different types within the same class?
The current Swift interprets the initial value expression in the class context, not in the instance context.
You know method names can be used as closures in Swift:
class ViewController: UIViewController {
//..
func aMethod() {
//...
}
func anInstanceMethod() {
let meth = aMethod // () -> ()
}
}
Swift can also refer to an instance method in the class context, which generates a so-called unapplied method reference (see SE-0042), which currently returns a curried function:
class ViewController: UIViewController {
//...
func aMethod() {
//...
}
class func aClassMethod() {
let meth = aMethod // (ViewController) -> () -> ()
}
}
The method self() as well.
Generally we do not need self() method and this behavior should be changed, I think.
This is interesting behaviour that works for Objective-C objects. Let's take these three examples:
class Object: NSObject {
let test = self // compiles
}
class NonNSObject {
// let test = self // errors
lazy var lazyTest = self // compiles
}
struct NonClass {
// let test = self // errors
lazy var lazyTest = self // errors
}
NonNSObject exhibits what you'd escape:
The object cannot reference itself until it is fully initialized, and let bindings must all be initialized before full initialization, so this failed.
However, NSObject happens to have an Objective-C method - (instancetype)self; which returns self. We can model this on NonNSObject as so:
func returnSelf() -> NonNSObject {
return self
}
This is where we start to see the answer to 2).
If we reference this method returnSelf on the Class we get the signature (NonNSObject) -> () -> NonNSObject. You can do this with any instance method as so:
let test = NonNSObject.returnSelf
The signature makes sense in this context:
The argument is the object we actually want to call the method on
Then we "apply" the function (with no arguments, in this case)
And we finally get our return value
let curriedFunction = NonNSObject.returnSelf // (Self) -> () -> Self
let readyToCall = curriedFunction(NonNSObject()) // () -> Self
let finallyApplied = readyToCall() // Self
Putting all the pieces together, we can see that in the case of ViewController (which inherits from UIViewController which way up the chain inherits from NSObject) there is an instance method self which the compiler is assuming you meant, so it uses that instead of the instance itself (as that would be an error). Its signature is thus a natural consequence of using an instance method on the class itself—it needs an instance, which is the first argument.
In summary:
1) Instead of assuming you made an error, the Swift compiler finds a function self on NSObject and returns the curried form.
2) This is the curried form of a function, in particular, an instance method which returns its own type.
2.5) It's still highlighted in pink because Swift-ObjC interop is mildly hacky, and self is both a method and, well, self.
As a bonus, the struct cannot reference itself at all, even lazily.
If so, are there any key differences that weren't otherwise present when using key-value observation in Objective-C?
You can use KVO in Swift, but only for dynamic properties of NSObject subclass. Consider that you wanted to observe the bar property of a Foo class. In Swift 4, specify bar as dynamic property in your NSObject subclass:
class Foo: NSObject {
#objc dynamic var bar = 0
}
You can then register to observe changes to the bar property. In Swift 4 and Swift 3.2, this has been greatly simplified, as outlined in Using Key-Value Observing in Swift:
class MyObject {
private var token: NSKeyValueObservation
var objectToObserve = Foo()
init() {
token = objectToObserve.observe(\.bar) { [weak self] object, change in // the `[weak self]` is to avoid strong reference cycle; obviously, if you don't reference `self` in the closure, then `[weak self]` is not needed
print("bar property is now \(object.bar)")
}
}
}
Note, in Swift 4, we now have strong typing of keypaths using the backslash character (the \.bar is the keypath for the bar property of the object being observed). Also, because it's using the completion closure pattern, we don't have to manually remove observers (when the token falls out of scope, the observer is removed for us) nor do we have to worry about calling the super implementation if the key doesn't match. The closure is called only when this particular observer is invoked. For more information, see WWDC 2017 video, What's New in Foundation.
In Swift 3, to observe this, it's a bit more complicated, but very similar to what one does in Objective-C. Namely, you would implement observeValue(forKeyPath keyPath:, of object:, change:, context:) which (a) makes sure we're dealing with our context (and not something that our super instance had registered to observe); and then (b) either handle it or pass it on to the super implementation, as necessary. And make sure to remove yourself as an observer when appropriate. For example, you might remove the observer when it is deallocated:
In Swift 3:
class MyObject: NSObject {
private var observerContext = 0
var objectToObserve = Foo()
override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext)
}
deinit {
objectToObserve.removeObserver(self, forKeyPath: #keyPath(Foo.bar), context: &observerContext)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
// do something upon notification of the observed object
print("\(keyPath): \(change?[.newKey])")
}
}
Note, you can only observe properties that can be represented in Objective-C. Thus, you cannot observe generics, Swift struct types, Swift enum types, etc.
For a discussion of the Swift 2 implementation, see my original answer, below.
Using the dynamic keyword to achieve KVO with NSObject subclasses is described in the Key-Value Observing section of the Adopting Cocoa Design Conventions chapter of the Using Swift with Cocoa and Objective-C guide:
Key-value observing is a mechanism that allows objects to be notified of changes to specified properties of other objects. You can use key-value observing with a Swift class, as long as the class inherits from the NSObject class. You can use these three steps to implement key-value observing in Swift.
Add the dynamic modifier to any property you want to observe. For more information on dynamic, see Requiring Dynamic Dispatch.
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
Create a global context variable.
private var myContext = 0
Add an observer for the key-path, and override the observeValueForKeyPath:ofObject:change:context: method, and remove the observer in deinit.
class MyObserver: NSObject {
var objectToObserve = MyObjectToObserve()
override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: "myDate", options: .New, context: &myContext)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &myContext {
if let newValue = change?[NSKeyValueChangeNewKey] {
print("Date changed: \(newValue)")
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
deinit {
objectToObserve.removeObserver(self, forKeyPath: "myDate", context: &myContext)
}
}
[Note, this KVO discussion has subsequently been removed from the Using Swift with Cocoa and Objective-C guide, which has been adapted for Swift 3, but it still works as outlined at the top of this answer.]
It's worth noting that Swift has its own native property observer system, but that's for a class specifying its own code that will be performed upon observation of its own properties. KVO, on the other hand, is designed to register to observe changes to some dynamic property of some other class.
(Edited to add new info): consider whether using the Combine framework can help you accomplish what you wanted, rather than using KVO
Yes and no. KVO works on NSObject subclasses much as it always has. It does not work for classes that don't subclass NSObject. Swift does not (currently at least) have its own native observation system.
(See comments for how to expose other properties as ObjC so KVO works on them)
See the Apple Documentation for a full example.
Both yes and no:
Yes, you can use the same old KVO APIs in Swift to observe Objective-C objects.
You can also observe dynamic properties of Swift objects inheriting from NSObject.
But... No it's not strongly typed as you could expect Swift native observation system to be.
Using Swift with Cocoa and Objective-C | Key Value Observing
No, currently there is no builtin value observation system for arbitrary Swift objects.
Yes, there are builtin Property Observers, which are strongly typed.
But... No they are not KVO, since they allow only for observing of objects own properties, don't support nested observations ("key paths"), and you have to explicitly implement them.
The Swift Programming Language | Property Observers
Yes, you can implement explicit value observing, which will be strongly typed, and allow for adding multiple handlers from other objects, and even support nesting / "key paths".
But... No it will not be KVO since it will only work for properties which you implement as observable.
You can find a library for implementing such value observing here:
Observable-Swift - KVO for Swift - Value Observing and Events
Yes.
KVO requires dynamic dispatch, so you simply need to add the dynamic modifier to a method, property, subscript, or initializer:
dynamic var foo = 0
The dynamic modifier ensures that references to the declaration will be dynamically dispatched and accessed through objc_msgSend.
An example might help a little here. If I have an instance model of class Model with attributes name and state I can observe those attributes with:
let options = NSKeyValueObservingOptions([.New, .Old, .Initial, .Prior])
model.addObserver(self, forKeyPath: "name", options: options, context: nil)
model.addObserver(self, forKeyPath: "state", options: options, context: nil)
Changes to these properties will trigger a call to:
override func observeValueForKeyPath(keyPath: String!,
ofObject object: AnyObject!,
change: NSDictionary!,
context: CMutableVoidPointer) {
println("CHANGE OBSERVED: \(change)")
}
In addition to Rob's answer. That class must inherit from NSObject, and we have 3 ways to trigger property change
Use setValue(value: AnyObject?, forKey key: String) from NSKeyValueCoding
class MyObjectToObserve: NSObject {
var myDate = NSDate()
func updateDate() {
setValue(NSDate(), forKey: "myDate")
}
}
Use willChangeValueForKey and didChangeValueForKey from NSKeyValueObserving
class MyObjectToObserve: NSObject {
var myDate = NSDate()
func updateDate() {
willChangeValueForKey("myDate")
myDate = NSDate()
didChangeValueForKey("myDate")
}
}
Use dynamic. See Swift Type Compatibility
You can also use the dynamic modifier to require that access to members be dynamically dispatched through the Objective-C runtime if you’re using APIs like key–value observing that dynamically replace the implementation of a method.
class MyObjectToObserve: NSObject {
dynamic var myDate = NSDate()
func updateDate() {
myDate = NSDate()
}
}
And property getter and setter is called when used. You can verify when working with KVO. This is an example of computed property
class MyObjectToObserve: NSObject {
var backing: NSDate = NSDate()
dynamic var myDate: NSDate {
set {
print("setter is called")
backing = newValue
}
get {
print("getter is called")
return backing
}
}
}
Currently Swift does not support any built in mechanism for observing property changes of objects other than 'self', so no, it does not support KVO.
However, KVO is such a fundamental part of Objective-C and Cocoa that it seems quite likely that it will be added in the future. The current documentation seems to imply this:
Key-Value Observing
Information forthcoming.
Using Swift with Cocoa and Objective-C
One important thing to mention is that after updating your Xcode to 7 beta you might be getting the following message:
"Method does not override any method from its superclass". That's because of the arguments' optionality. Make sure that your observation handler looks exactly as follows:
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject : AnyObject]?, context: UnsafeMutablePointer<Void>)
This may be prove helpful to few people -
// MARK: - KVO
var observedPaths: [String] = []
func observeKVO(keyPath: String) {
observedPaths.append(keyPath)
addObserver(self, forKeyPath: keyPath, options: [.old, .new], context: nil)
}
func unObserveKVO(keyPath: String) {
if let index = observedPaths.index(of: keyPath) {
observedPaths.remove(at: index)
}
removeObserver(self, forKeyPath: keyPath)
}
func unObserveAllKVO() {
for keyPath in observedPaths {
removeObserver(self, forKeyPath: keyPath)
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let keyPath = keyPath {
switch keyPath {
case #keyPath(camera.iso):
slider.value = camera.iso
default:
break
}
}
}
I had used KVO in this way in Swift 3. You can use this code with few changes.
Overview
It is possible using Combine without using NSObject or Objective-C
Availability: iOS 13.0+, macOS 10.15+, tvOS 13.0+, watchOS 6.0+, Mac Catalyst 13.0+, Xcode 11.0+
Note: Needs to be used only with classes not with value types.
Code:
Swift Version: 5.1.2
import Combine //Combine Framework
//Needs to be a class doesn't work with struct and other value types
class Car {
#Published var price : Int = 10
}
let car = Car()
//Option 1: Automatically Subscribes to the publisher
let cancellable1 = car.$price.sink {
print("Option 1: value changed to \($0)")
}
//Option 2: Manually Subscribe to the publisher
//Using this option multiple subscribers can subscribe to the same publisher
let publisher = car.$price
let subscriber2 : Subscribers.Sink<Int, Never>
subscriber2 = Subscribers.Sink(receiveCompletion: { print("completion \($0)")}) {
print("Option 2: value changed to \($0)")
}
publisher.subscribe(subscriber2)
//Assign a new value
car.price = 20
Output:
Option 1: value changed to 10
Option 2: value changed to 10
Option 1: value changed to 20
Option 2: value changed to 20
Refer:
https://developer.apple.com/documentation/combine
https://developer.apple.com/documentation/combine/receiving_and_handling_events_with_combine
https://developer.apple.com/documentation/combine/published
Another example for anyone who runs into a problem with types such as Int? and CGFloat?. You simply set you class as a subclass of NSObject and declare your variables as follows e.g:
class Theme : NSObject{
dynamic var min_images : Int = 0
dynamic var moreTextSize : CGFloat = 0.0
func myMethod(){
self.setValue(value, forKey: "\(min_images)")
}
}