How to use Struct as Observer in NotificationCenter - ios

In NotificationCenter Class , Why apple has created Observer of type Any?
func addObserver(Any, selector: Selector, name: NSNotification.Name?, object: Any?)
My Reasoning.
If observer is struct then on assigning inside as function parameter, It will be copied then how my observer can receive the notification.
I can't write any function which uses #objc prefix in Struct.
Selector is always be type of #objc.
So What is the use of Any in addObserver.....
It should always be of type AnyObject.
Secondly we already known that NotificationCenter keep the weak copy of observer, And we can't use weak modifier for type Any. Then how apple is managing all this?
Any help in understanding this concept is highly appreciated.

No one chose to make this parameter Any. It's just what they got by default. It's automatically bridged from ObjC:
- (void)addObserver:(id)observer
selector:(SEL)aSelector
name:(nullable NSNotificationName)aName
object:(nullable id)anObject;
The default way that id is bridged is Any. It hasn't been specially refined for Swift. In practice, you can't really use structs meaningfully here. The fact that the compiler won't stop you from calling it in an unhelpful way doesn't imply that it's intended to be used that way.

Why type Any? - because in Objective C it is type id.
Why you can't mark your function as #obj - #obc is the keyword for Swift code which indicates what compiler should add this method to a header file for this Class, yes you can make headers only for Classes.
Selector also is the objective-c term, it just says which function to invoke, similar to msg_send

In NotificationCenter Class , Why apple has created Observer of type Any.
Because all Objective-C id declarations are translated into Swift as Any.
You might object that this really should be AnyObject, because only a class will work here. And indeed, that's the way id used to be translated into Swift. But nowadays you can pass anything where an id is expected, because if it's something Objective-C can't understand, it will be boxed up as a class instance (e.g. as a _SwiftValue) so that it can make the round-trip into Objective-C and back again to Swift. Therefore id is translated as Any.
However, just because you can pass a struct here doesn't mean you should. It won't work, as you've discovered. Objective-C cannot introspect a Swift struct.
There are lots of situations like this, where Cocoa gives you enough room to hang yourself by passing the wrong thing. The contents of a CALayer is typed as Any, but if you pass anything other than a CGImage, nothing will happen. The layerClass if a UIView is typed as AnyClass, but you'd better pass a CALayer subclass. I could go on and on.

Related

Swift: A function with a Selector argument returns an unmanaged<AnyObject>?

What is the use of -
func perform(_ aSelector: Selector!) -> unmanaged<AnyObject>!
in iOS?
To call a method of a class?
To add a selector method?
To add a class delegate?
To define a class? (I doubt it's this)
I was originally thinking that it it was to add a selector method, but after looking at it some more I think it may be "to call a method of a class." Selectors are used for target/action paradigms where I kick something off and when the event fires or finishes then it wants to fire off some kind of action method.
In this example do I pass it a parameter of a selector function then at the end of this "perform" function, I am returning an unmanaged object of any type? Does that even make sense?
Thanks!
In Swift this is basically useless. It's bridged from Objective-C, where it used to be very useful (before ARC), but now it's a bit tricky.
The point of it is to send a message by name and get a result. Generally speaking that translates to calling a method of that name.

Swift 4 Using KVO to listen to volume changes

I just updated to Swift 4 and Xcode 9 and got a (swiftlint) warning for the following code telling me that I should use KVO now:
Warning:
(Block Based KVO Violation: Prefer the new block based KVO API with
keypaths when using Swift 3.2 or later. (block_based_kvo))
The old code:
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
if keyPath == "outputVolume"{
guard let newKey = change?[NSKeyValueChangeKey.newKey] as? NSNumber else {
fatalError("Could not unwrap optional content of new key")
}
let volume = newKey.floatValue
print("volume " + volume.description)
}
}
My attempt to fix:
let audioSession = AVAudioSession.sharedInstance()
audioSession.observe(\.outputVolume) { (av, change) in
print("volume \(av.outputVolume)")
}
Apple claims here that most of the properties should be dynamic (I know that this is AVPlayer and not AVAudioSession). I looked it up but couldn't find any dynamic statements inside AVPlayer properties and was wondering how that could possibly work (If I'm not mistaken those are required for KVO to work).
EDIT:
I'm not certain if it doesn't trigger because it simply doesn't work or if it's due to what I try to archive. In general I'll want to get notified on volume changes triggered by pushing the hardware-volume-rockers.
I assume you're referring to the line:
You can use Key-value observing (KVO) to observe state changes to many of the player’s dynamic properties...
This use of "dynamic" isn't the same thing as Objective-C's #dynamic or Swift's dynamic. The docs just mean "properties that change" in this context, and they're telling you that the AVPlayer is generally very KVO-compliant and intended to be observed that way. "KVO compliant" means it follows the change notification rules. There are many ways to achieve that, both automatic and manual. The docs are just promising that AVPlayer does.
(An important point about Cocoa that distinguishes it from many other systems is that Cocoa handles many things "by convention". There's no way to say in code "this is KVO compliant" and there is no way for the compiler to enforce it, but Cocoa developers tend to be very good about following the rules. When ARC was developed, it relied heavily on the fact that Cocoa developers had for years named methods following very specific rules that indicate how memory management is handled. It just added complier enforcement of the rules Cocoa developers had always followed by hand. This is why Cocoa developers get very noisy about naming conventions and capitalization. There are major parts of Cocoa that rely entirely on following consistent naming rules.)
Remembering that the AVPlayer interface is an Objective-C API that happens to be bridged to Swift, there's no equivalent of the Swift keyword dynamic in that case. That's a keyword that tells Swift that this property may be observed and so its accessors can't be optimized to static dispatch. That's not something Objective-C requires (or can do; all ObjC properties are "dynamic" in this sense).
The Objective-C #dynamic is a completely different thing, only weakly related to KVO (though it comes up in a lot of KVO-heavy contexts like Core Data). It just means "even though you can't find an accessor implementation for this property anywhere, trust me, by the time this runs an implementation will be available." This relies on the ability of ObjC's runtime to generate implementations dynamically or dispatch in programmer-controlled ways (this still kind of exists in Swift by manipulating the ObjC runtime, but it isn't really a "Swift" feature).
As for how KVO works, it's one of the few true "magic tricks" in Cocoa. For a quick intro, see Key-Value Observing Implementation Details. The short version is:
When you observe an object, a subclass for that object is dynamically created (yes, a new class is invented at runtime).
The subclass adds calls to willChangeValue... and didChangeValue... around all calls to the superclass's property accessors.
The object is "ISA-swizzled" to be that new class.
Magic! (Ok, not really magic; it's just code, but it's quite a trick.)
EDIT: The original question never mentioned that it wasn't working. The reason it's not working is because you're not assigning the returned NSKeyValueObservation in a property; you're just throwing it away. I'm surprised there's not a warning about that; I may open a radar.
When the returned NSKeyValueObservation deallocates, the observation goes away, so this creates an observation and immediately destroys it. You need to store it in a property until you want the observation to go away.
Solution by OP.
It needs to be stored in a property. Not a variable, not _ but a property. Otherwise it won't work. Like this:
class YourViewController: UIViewController {
var obs: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
let audioSession = AVAudioSession.sharedInstance()
self.obs = audioSession.observe( \.outputVolume ) { (av, change) in
print("volume \(av.outputVolume)")
}
}
}

Cannot assign a value of type 'AnyDataSource<NSManagedObjectSubclass>' to a value of type 'AnyDataSource<NSManagedObject>'

I'm stumped.
The title of this question the compiler error.
I am creating an object that basically marries the delegates of NSFetchedResultsController and UITableViewDataSource. The type parameter is used to indicate which NSManagedObject subclass should be used in the fetch requests.
Here is an example where Swift lacks dynamism and we end up writing all sorts of crazy code, (OR, I'm new to Swift and dislike being told what I'm not allowed to do)
So, on a UITableViewController subclass, i'd like to have a property
var dataSource: AnyDataSource<NSManagedObject>?
when I try to create one of these with a subclass of NSManagedObject and assign it to that property, the compiler complains. There seems to be nothing I can do that will succeed without a warning.
You would think that I should be able to use NSManagedObject or any of its subclasses, so I'm a little confused.
Any ideas? I'm using typical "type erasure" patterns that can be found on the internet, like via realm.io.
Ultimately I found that this approach was not possible. I mean, to achieve these with completely protocol-based programming.
I defined a few base protocols with no associated type, implemented an abstract baseclass that implements that protocol, then introduced generic type parameters in subclasses, that implement further protocols that have associated type.
I'll post my generalized solution on github when it's finished.

Is it possible to hash a Swift protocol?

I am trying to write a registry in Swift that maps from API's (Protocols) to Implementations (Classes). I would like to be able to provide the registry an API and receive back an instance of the class that implements it. In Objective-C this was fairly trivial - just call NSStringFromProtocol on the protocol and then use that as a key for a dictionary containing the classes that implement them. In Swift, however, we do not have this introspective capability. When I try to do the same I am told that MyAPI.protocol does not have a member "mirrorType". My question to you is how, in Swift, without using #objc protocols, I can map from a protocol itself to the class that implements it. Thanks!
By now it's not possible without using #objc. The solution I've found in this case is using the protocol name (string) as a key for the dictionary for this implementations (In my case I'll always have only one instance per Protocol).
Using #objc will force you to have all your implementations returning AnyObject the equivalent (id) in objective-C (if your function does not return a native objective-C type).
Hope that helps.

Swift equivalent for #protocol(DelegateType)

I'm working with ReactiveCocoa in Swift. I need to use the following method:
rac_signalForSelector(selector: Selector, fromProtocol: Protocol?)
Passing the selector works fine with Selector("method:"), but I cannot find how to pass the delegate protocol to the fromProtocol parameter.
What is the proper way to pass the Protocol type from a delegate to a method signature like this?
EDIT: Adding method documentation and best attempt
The documentation for this method is as follows:
selector - The selector for whose invocations are to be observed. If it doesn’t exist, it will be implemented using information from protocol, and may accept non-object arguments and return a value. This cannot have C arrays or unions as arguments or return type.
protocol - The protocol in which selector is declared. This will be used for type information if the selector is not already implemented on the receiver. This must not be NULL, and selector must exist in this protocol.
I have tried sending in DelegateType.self, and I end up receiving this error:
Could not find an overload for rac_signalForSelector that accepts the supplied arguments
Did you use #objc when you declared your protocol?
I believe SomeProtocol.self is right right way to pass it but since you're passing it into an obj-c API it needs to be prefixed with #objc like this example from the docs:
#objc protocol HasArea {
var area: Double { get }
}
Edit: Turns out the protocol in question is from a library (written in objective-c, so already compatible with objective-c), not defined in Swift.
That being the case, it's probably a compiler bug, so before you do anything else make sure you're using the latest version of Xcode (beta 3 at the time of writing).
If that doesn't work out, I think Tommy's idea to use NSProtocolFromString is the best recourse until the compiler bug is fixed.

Resources