I'm building an app that prompts the users for their contacts. I'm brand new to Swift but making decent progress.
I'm invoking this open function from a parent call site and want to receive the response value back from the contactPicker function. I'm a bit unfamiliar with the implied nature of the contactPicker invocation, and was expecting to have to invoke it directly from within the presentContactPicker function, but the documentation seemed to suggest this approach is preferable.
As it stands now, this implementation correctly prompts the Contact Picker UI, and prints the selected contacts to the console. But I need assistance actually passing the contacts to the callback function.
The issue that I have is that I'm not able to access the callback function from inside the contactPicker. I have an intuition that maybe I could attach the callback to the parent class with a technique like self.callback = callback in the open function and then call self.callback() in the contactPicker itself, but it didn't work.
import Foundation
import UIKit
import ContactsUI
#objc(ContactsPicker)
class ContactsPicker : NSObject, CNContactPickerDelegate {
#objc static func requiresMainQueueSetup() -> Bool {
return false
}
#objc func open(_ options: NSDictionary, callback: RCTResponseSenderBlock) -> Void {
DispatchQueue.main.async {
self._presentContactPicker(options: options)
}
}
func _presentContactPicker(options: NSDictionary) -> Void {
let contactPickerVC = CNContactPickerViewController()
contactPickerVC.delegate = self
let controller = RCTPresentedViewController()
controller?.present(contactPickerVC, animated: true)
}
func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
print("+++++++++++++++++++++++++++")
print(contacts)
print("+++++++++++++++++++++++++++")
### But how can I access the callback from the exposed open method?
}
}
maybe I could attach the callback to the parent class with a technique like self.callback = callback in the open function and then call self.callback() in the contactPicker itself
This is the right idea. You can declare a property to store the callback like this:
private var callback: RCTResponseSenderBlock?
#objc func open(_ options: NSDictionary, callback: #escaping RCTResponseSenderBlock) -> Void {
self.callback = callback
DispatchQueue.main.async {
self._presentContactPicker(options: options)
}
}
Note that the since the callback "escapes" into the class, you should mark the parameter as #escaping.
func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
callback(someParameters)
}
Related
I don't clear about these two, Nowadays the world is shifting to the closure types. But I'm not clearly understanding this. Can someone explain me with a real-time example?
So a real life example of both would be something like this:
protocol TestDelegateClassDelegate: class {
func iAmDone()
}
class TestDelegateClass {
weak var delegate: TestDelegateClassDelegate?
func doStuff() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.delegate?.iAmDone()
}
}
}
class TestClosureClass {
var completion: (() -> Void)?
func doStuff() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.completion?()
}
}
}
class ViewController: UIViewController, TestDelegateClassDelegate {
func iAmDone() {
print("TestDelegateClassDelegate is done")
}
override func viewDidLoad() {
super.viewDidLoad()
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
let testingClosure = TestClosureClass()
testingClosure.completion = {
print("TestClosureClass is done")
}
testingClosure.doStuff()
}
}
Here we have 2 classes TestDelegateClass and TestClosureClass. Each of them have a method doStuff which waits for 3 seconds and then reports back to whoever is listening where one uses delegate procedure and the other one uses closure procedure.
Although they do nothing but wait you can easily imagine that they for instance upload an image to server and notify when they are done. So for instance you might want to have an activity indicator running while uploading is in progress and stop it when done. It would look like so:
class ViewController: UIViewController, TestDelegateClassDelegate {
#IBOutlet private var activityIndicator: UIActivityIndicatorView?
func iAmDone() {
print("TestDelegateClassDelegate is done")
activityIndicator?.stopAnimating()
}
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator?.startAnimating()
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
activityIndicator?.startAnimating()
let testingClosure = TestClosureClass()
testingClosure.completion = {
self.activityIndicator?.stopAnimating()
print("TestClosureClass is done")
}
testingClosure.doStuff()
}
}
Naturally you would only use one of the two procedures.
You can see there is a huge difference in code. To do a delegate procedure you need to create a protocol, in this case TestDelegateClassDelegate. A protocol is what defines the interface of a listener. And since an iAmDone method is defined it must be defined in ViewController as well as long as it is defined as TestDelegateClassDelegate. Otherwise it will not compile. So anything declared as TestDelegateClassDelegate will have that method and any class can call it. In our case we have weak var delegate: TestDelegateClassDelegate?. That is why we can call delegate?.iAmDone() without caring what delegate actually is. For instance we can create another class:
class SomeClass: TestDelegateClassDelegate {
func iAmDone() {
print("Something cool happened")
}
init() {
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
}
}
So a good example for instance is an UITableView that uses a delegate and dataSource (both are delegates, just properties are named differently). And table view will call the methods of whatever class you set to those properties without needing to know what that class is as long as it corresponds to the given protocols.
Same could be achieved with closures. A table view could have been defined using properties giving closures like:
tableView.onNumberOfRows { section in
return 4
}
But that would most likely lead into one big mess of a code. Also closures would in this case be giving many programmers headaches due to potential memory leaks. It is not that closures are less safe or anything, they just do a lot of code you can't see which may produce retain cycles. In this specific case a most likely leak would be:
tableView.onNumberOfRows { section in
return self.dataModel.count
}
and a fix to it is simply doing
tableView.onNumberOfRows { [weak self] section in
return self?.dataModel.count ?? 0
}
which now looks overcomplicated.
I will not go into depths of closures but in the end when you have repeated call to callbacks (like in case of table view) you will need a weak link either at delegate or in closure. But when the closure is called only once (like uploading an image) there is no need for a weak link in closures (in most but not all cases).
In retrospective use closures as much as possible but avoid or use caution as soon as a closure is used as a property (which is ironically the example I gave). But you would rather do just this:
func doSomethingWithClosure(_ completion: #escaping (() -> Void)) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
completion()
}
}
And use it as
doSomethingWithClosure {
self.activityIndicator?.stopAnimating()
print("TestClosureClass is done")
}
This has now removed all potential risks. I hope this clears a thing or two for you.
In Swift/obj-c the term delegate is used to refer to a protocol that responds to specific selectors.
Thing about it just like calling a method on an object.
E.g.
protocol CalculatorDelegate : class { // : class so it can be made 'weak'
func onCalculation(result: Int) -> Void
}
Now if we have a Calculator class, to use the delegate we'd do something like
class Calculator() {
weak var delegate: CalculatorDelegate?
func calculate(_ a: Int, _ b: Int) -> Int {
let result = a + b
self.delegate?.onCalculation(result: result)
return result
}
}
Then in some other class (e.g. in iOS - a View Controller) we might do:
class MyClass : CalculatorDelegate {
func onCalculation(result: Int) {
print("Delegate method on calculation called with result \(result)")
}
func someButtonPress() {
let calculator = Calculator()
calculator.delegate = self
calculator.calculate(42, 66)
}
}
So you can see how the setup is quite elaborate.
Now closures are simply blocks of code that can be called in other places, so you could change all of the code like so:
class Calculator2() {
weak var delegate: CalculatorDelegate?
func calculate(_ a: Int, _ b: Int, onCalculation: (#escaping (Int) -> Void) -> Int)?) {
let result = a + b
onCalculation?(result)
return result
}
}
class MyClass {
func someButtonPress() {
let calculator = Calculator2()
calculator.calculate(42, 66, onCalculation: { (result: Int) in
print("Closure invoked with \(result)")
})
}
}
However, with closures you need to be aware that it is a lot easier to shoot yourself in the foot by capturing variables (e.g. self) strongly, which will lead to memory leaks even under ARC regime.
Closures are first-class objects so that they can be nested and passed around
Simply,
In swift, functions are primitive data types like int, double or character that is why you can pass a function in the function parameter. In swift mechanism simplify in closures syntax like lambda expression in other languages.
E.g. If you want to call rest API through URSSession or Alamofire and return response data then you should use completionHandler(it's closure).
Void closure : - {(paramter:DataType)->Void}
Return closure : - {(paramter:DataType)->DataType}
e.g. (int, int) -> (int)
https://docs.swift.org/swift-book/LanguageGuide/Closures.html
I'm trying to create an observable sequence to indicate the status of Bluetooth on device. I'm using ReplaySubject<CBManagerState>, but am curious if there is something better, as I hear bad things about using onNext()
What is the appropriate way to connect callback delegates to the RxSwift observable domain?
class BluetoothStatusMonitor: NSObject, CBPeripheralManagerDelegate {
let bluetoothStatusSequence = ReplaySubject<CBManagerState>.create(bufferSize: 1)
var bluetoothPeripheralManager: CBPeripheralManager?
func checkBluetoothStatus()
{
//silently check permissions, without alert
let options = [CBCentralManagerOptionShowPowerAlertKey:0]
bluetoothPeripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: options)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
bluetoothStatusSequence.onNext(peripheral.state)
}
}
This is exactly the kind of things that Subjects are good for. They exist primarily to convert non-Rx code into Rx code. That said, RxCocoa has the DelegateProxy type that is designed to handle a lot of the work necessary to do delegates right. It's still hard to figure out exactly how to implement one, but once you get the hang of it they are quite useful...
I have to admit that most of the code is black magic to me, but it does work. I try to explain as much as I can in comments below.
import RxSwift
import RxCocoa
import CoreBluetooth
// The HasDelegate protocol is an associated type for the DelegateProxyType
extension CBPeripheralManager: HasDelegate {
public typealias Delegate = CBPeripheralManagerDelegate
}
class CBPeripheralManagerDelegateProxy
: DelegateProxy<CBPeripheralManager, CBPeripheralManagerDelegate>
, DelegateProxyType
, CBPeripheralManagerDelegate {
init(parentObject: CBPeripheralManager) {
super.init(parentObject: parentObject, delegateProxy: CBPeripheralManagerDelegateProxy.self)
}
deinit {
_didUpdateState.onCompleted()
}
static func registerKnownImplementations() {
register { CBPeripheralManagerDelegateProxy(parentObject: $0) }
}
// a couple of static functions for getting and setting a delegate on the object.
static func currentDelegate(for object: CBPeripheralManager) -> CBPeripheralManagerDelegate? {
return object.delegate
}
static func setCurrentDelegate(_ delegate: CBPeripheralManagerDelegate?, to object: CBPeripheralManager) {
object.delegate = delegate
}
// required delegate functions must be implemented in the class. This is where Subjects come in.
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
_didUpdateState.onNext(peripheral.state)
}
fileprivate let _didUpdateState = PublishSubject<CBManagerState>()
}
extension Reactive where Base: CBPeripheralManager {
var delegate: CBPeripheralManagerDelegateProxy {
return CBPeripheralManagerDelegateProxy.proxy(for: base)
}
var state: Observable<CBManagerState> {
return delegate._didUpdateState
}
var didUpdateState: Observable<Void> {
return delegate._didUpdateState.map { _ in }
}
// optional methods are setup using the `methodInvoked` function on the delegate
var willRestoreState: Observable<[String: Any]> {
return delegate.methodInvoked(#selector(CBPeripheralManagerDelegate.peripheralManager(_:willRestoreState:)))
.map { $0[1] as! [String: Any] }
}
var didStartAdvertising: Observable<Error?> {
return delegate.methodInvoked(#selector(CBPeripheralManagerDelegate.peripheralManagerDidStartAdvertising(_:error:)))
.map { $0[1] as? Error }
}
// I didn't implement all of the optionals. Use the above as a template to implement the rest.
}
As far as I can tell, the methodInvoked function performs some meta-programming magic on the object to install the method at runtime. This is done because many of the iOS classes that have delegates actually behave differently depending on whether or not the method was defined on the delegate (regardless of what the method does,) so we don't want to simply give the proxy every method in the protocol.
Of course, once you have the above in place. You can do all the standard RX stuff with your peripheral manager:
bluetoothManager.rx.state
.subscribe(onNext: { state in print("current state:", state) })
.disposed(by: disposeBag)
bluetoothManager.rx.didStartAdvertising
.subscribe(onNext: { error in
if let error = error {
print("there was an error:", error)
}
}
.disposed(by: disposeBag)
I'm wanting to use a multicast delegate to inform multiple objects when things change. The tutorials I've read that explain this, have a protocol that only has one function that is called directly on the array of delegates. That works fine when there is only one function defined. My Protocol has 6 functions. I want to avoid creating 6 separate functions and reuse a single function that can be applied to my array of delegates.
Quick example: (I understand this is none working, but I just want to get my idea across.
protocol MyProtocol {
func method1()
func method2()
func method3()
}
class TestClass {
var delegates = [MyProtocol]()
func invokeDelegates(delegateMethod: () -> ()) {
for delegate in delegates {
delegate.delegateMethod()
}
}
}
The obvious problem is the compiler complains that "delegateMethod" isn't defined in the original protocol. Is there a way that I cast the method as being part of MyProtocol and the compiler will trust me?
Is this even possible?
Here is a gist of an Multicast Delegate pattern that I use in my projects. It also prevents from having strong reference cycles (memory leaks). WeakWrapper handles this.
Ok. In some of the solutions I see mistakes (strong retain cycles, race conditions, ...)
Here is what I combine based on 1 day research. For the stack of delegates I used NSHashTable, so all the delegates are having weak reference.
class MulticastDelegate <T> {
private let delegates: NSHashTable<AnyObject> = NSHashTable.weakObjects()
func add(delegate: T) {
delegates.add(delegate as AnyObject)
}
func remove(delegate: T) {
for oneDelegate in delegates.allObjects.reversed() {
if oneDelegate === delegate as AnyObject {
delegates.remove(oneDelegate)
}
}
}
func invoke(invocation: (T) -> ()) {
for delegate in delegates.allObjects.reversed() {
invocation(delegate as! T)
}
}
}
func += <T: AnyObject> (left: MulticastDelegate<T>, right: T) {
left.add(delegate: right)
}
func -= <T: AnyObject> (left: MulticastDelegate<T>, right: T) {
left.remove(delegate: right)
}
How to set delegate:
object.delegates.add(delegate: self)
How to execute function on the delegates:
instead of
delegate?.delegateFunction
you use
delegates.invoke(invocation: { $0.delegateFunction })
You need to change the signature of invokeDelegates to take a closure of type (MyProtocol) -> (), and then you need to pass each delegate to the closure.
protocol MyProtocol {
func method1()
func method2()
func method3()
}
class TestClass {
var delegates = [MyProtocol]()
func invokeDelegates(delegateMethod: (MyProtocol) -> ()) {
for delegate in delegates {
delegateMethod(delegate)
}
}
}
The closure should just invoke the appropriate delegate method on its argument. Swift can infer the argument and return types of the closure, and you can use the shorthand $0 to refer to the argument, so the closure can be quite short:
let tester = TestClass()
tester.invokeDelegates(delegateMethod: { $0.method1() })
On the other hand, you could just use Collection.forEach directly on the delegates array (if it's accessible) and skip the invokeDelegates method:
tester.delegates.forEach { $0.method1() }
So I have a singleton class in which I have implemented the delegate of my module. However when that delegate method gets executed in this singleton I call a method in another class of the App and it crashes because all the variables previously set in that class are empty.
class Player: Jukebox, JukeboxDelegate {
static let sharedInstance = Player()
func setDelegate(){
jukebox = Jukebox(delegate: self)
}
func play(Link: String) {
jukebox.setIT([JukeboxItem(URL: NSURL(string: Link)!)])
jukebox.setImage(currentImage)
jukebox?.play()
}
func audioPlayerDidFinishPlaying(player: Jukebox, successfully flag: Bool) {
if pltype == PlayerType.Playlist {
MyMusicVC.continuePlaying() { () -> () in
}
}
}
func jukeboxStateDidChange(state: Jukebox) {
}
func jukeboxPlaybackProgressDidChange(jukebox: Jukebox) {
}
func jukeboxDidLoadItem(jukebox: Jukebox, item: JukeboxItem) {
}
func jukeboxDidUpdateMetadata(jukebox: Jukebox, forItem: JukeboxItem) {
}
}
Any ideas on why could this be happening?
Its hard to understand what is going on. But you are subclassing a Jukebox class. Then making this subclass a delegate of the Jukebox itself. Then I also notice you're setting a Jukebox property that appears to be optional but not.
For example. Why in the play() function is jukebox. used twice but the last line is jukebox?. Is jukebox optional? Or implicitly unwrapped? And if thats the case, why are you using it unwrapped twice and then optionally the third time.
Have you tried not making this singleton a subclass of jukebox. Instead just make it conform to the JukeBoxDelegate protocol and see if that accomplishes the same goal?
I have two viewControllers: LoginViewController and NextViewController. Now when the app runs the LoginViewController runs a completion handler that takes care of the authentication with HTTP request, and after it's done, it calls another closure that gets the necessary user data.
I don't know how to pass that data that i get back from the callback function into the NextViewController to display it because I have no way of knowing when the data becomes available as it is running an HTTP request in the background.
So how should I present the data when it becomes available?
I know I can just call the second callback method for getting the user information inside the nextViewController, but that makes the app slower.
Here's example code:
class Methods: NSObject {
//Singleton
class func sharedInstance() -> Methods {
struct Singleton {
static var sharedInstance = Methods()
}
return Singleton.sharedInstance
}
private func GETMethod(callBackMethod: (Success: Bool) -> Void) {
//Do the authentication
}
private func retriveUserData(callBackMethod: (data: String, Success: Bool) -> Void) {
//Gets the data and passes processed data back in a callBackMethod
}
func doAuthentication(callBackMethod: (Success: Bool) -> Void){
GETMethod { (Success) in
if Success {
self.retriveUserData({ (data, Success) in
data // <- HOW DO I GET THIS INTO LOGIN VIEW CONTROLLER?
callBackMethod(Success: true)
})
}
}
}
}
class LoginViewController: UIViewController {
func loginButtonPressed(){
Methods.sharedInstance().doAuthentication { (Success) in
}
}
}
class NextViewController: UIViewController {
//Present data when it becomes available
}
Consider using a MVC pattern, especially the "model" part. Create an object that serves as the shared data model for your application. Update it when you have new data. Depending on the timing of updates vs. controller loading, it can either send notifications when data changes or provide an API that the interested objects (controllers) can query to find out the current state.