If you are an iOS or macOS developer using Swift, you must be familiar with protocol conformance in order to hand off responsibilities to another class or structure. The Delegate Pattern is found all over Foundation, AppKit and UIKit which are the core frameworks for iOS and macOS development.
Such as UICollectionViewDelegate or URLSessionDelegate.
In order to prevent you from writing the boilerplate code of Delegate APIs, let’s make them Reactive using RxSwift.
The source code of this article is available here:
RxSwift version used: 4.3.1
If you’ve never used MapKit or any
MKLocalSearchCompleter in an application, this is a quite new API (since iOS 9.3) allowing you to get autocomplete address results based on an incomplete query request which is a simple String. The query string is passed through the
MKLocalSearchCompleter’s property queryFragment.
MKLocalSearchCompleterDelegate protocol is quite simple. It’s made of two optional methods
This one is called whenever the parent
MKLocalSearchCompleter results Array gets updated. This way you are always aware of the most recent results based on the last
queryFragment you passed to the parent
The results Array is available through the completer’s readonly results property which is an Array of
This one is the error callback called when the parent’s
MKLocalSearchCompleter is unable to generate a list of search results. This way, you can handle the error and let your users know what and why it went wrong.
The concrete RxCocoa’s DelegateProxy: RxMKLocalSearchCompleterDelegateProxy
Here we are building a concrete
DelegateProxy type that will be used by the upcoming
MKLocalSearchCompleter’s Reactive extension.
First we need to make
MKLocaSearchCompleter conform to the RxCocoa
HasDelegate protocol. We can achieve this with a simple extension.
Here we are setting the
HasDelegate’s associated type
Delegate to the concrete type
This allows us to use the RxCocoa’s
DelegateProxyType delegate setter and getter default implementation through the constraint extension of the
DelegateProxyType. Here is the extension mentioned:
So whenever the
ParentObject (associatedType) implements
Delegate (associatedType) is the same type as
HasDelegate protocol) you get the above two methods default implementation for free.
Next we have to create a concrete
RxMKLocalSearchCompleterDelegateProxy class that inherits from RxCocoa’s
DelegateProxy<ParentObject, Delegate> class. It also needs to implement RxCocoa’s
DelegateProxyType protocol as well as the
ParentObject will be of type
Delegate will be of type
localSearchCompleter is declared as weak to prevent retain cycles. It is readonly too.
init method stores the given
localSearchCompleter and then initializes the current object using RxCocoa’s superclass
DelegateProxy<ParentObject, Delegate> initializer.
registerKnownImplementations() method we call the superclass method
register<Parent> (its signature is below). In the “make” escaping closure we then return the
RxMKLocalSearchCompleterDelegateProxy initializer method with the
MKLocaSearchCompleter type as a parameter.
The “register” method is called by RxCocoa’s
DelegateProxyType in order to store our proxy subclass to its own proxy factory.
RxMKLocalSearchCompleterDelegateProxy is ready, let’s make
MKLocalSearchCompleter reactive 🚀.
Rx All the things 🙌🏻
The goal is to make
MKLocalSearchCompleter reactive. To do so, we are going to make an extension to the RxSwift’s
Reactive type, constraining its
Base type to be
Then we will need to declare a delegate property inside this extension which is of type
This delegate property is a computed property initialized by the previously created
RxMKLocalSearchCompleterDelegateProxy class using this method:
You must always call this method to instantiate a
DelegateProxyType object instead of directly instantiating
RxMKLocalSearchCompleterDelegateProxy through its initializer.
Indeed, this method takes care of creating and installing a proxy instance and then returns it or returns an existing proxy instance already installed.
The last task is to return observable sequences for each of the two
MKLocalSearchCompleterDelegate’s methods. First let’s see how we do it to recover the last updated
MKLocalSearchCompleter results using a computed property.
We use the delegate proxy using its
methodInvoked() passing the Selector of the corresponding delegate method to let the delegate proxy observe calls to this method returning results as an observable sequence.
We then map the result of
methodInvoked() which is an Array of Any representing the list of the delegate method parameter(s). In this case there is only one parameter of type
MKLocalSearchCompleter so the focus is made on the first parameter
You must be wondering what is the
castOrThrow(...) method 🤔. This is a method used all over RxCocoa, but not exposed outside of the RxSwift library. This simply tries to cast the given object (2nd parameter) into the given type (1st parameter) or throws a
RxCocoaError if it fails. Here is the implementation found in RxCocoa. You can add this to your file and make it public, fileprivate or private so it fits your needs.
didUpdateResults computed property then returns a
ControlEvent type is used to provide a safe representation of the observable sequence that represent UI events. The main properties of
ControlEvent sequences are:
- It never fails
- It won’t send any initial value on subscription
- It will Complete sequence on control being deallocated
- It never errors out
- It delivers events on the
This sequence type is appropriate to what we need here because updated
MKLocalSearchCompleter.results will be propagated to any UI element such as UITableView presenting the results to the user while he is typing his location query.
Let’s see how the failure delegate method is handled into an observable sequence through the
didFailWithError computed property.
This is really similar to the previous one, I’ll only address the
a syntax. It focuses on the second parameter of the given method invoked which is the actual
Error object, at index 1 in the corresponding Array of parameters.
queryFragment property is the entry point of the query mechanism. Whenever it’s value changes, a new request is made.
Let’s make it reactive too! So it can be bound to any text oriented UI like
Here it is, just like the text property of a
UILabel in RxCocoa we’ve made a
Binder which takes a
String and affects its value to the underlying
queryFragment property of the
As an example, we are going to bind a
UITextfield’s reactive text property to a
- Initialization of a
UITextfield’s reactive text property is unwrapped to be sure it’s not an optional
.orEmptyand then bound to our newly created reactive
- We then subscribe to the
didUpdateResultssequence to then print the address completion results.
So now, every user’s input on the
UITextfield is broadcasted to the
searchCompleter.queryFragment property triggering a new address completion request. The results are automatically forwarded to the
.didUpdateResults sequence allowing you to present the autocomplete list of results to your users. 👏🏻