In most cases, setting a delegate is as simple as implementing the delegate protocol in a class and declaring an instance of that class as the delegate for an instance of whatever you're using.
I actually used this same basic concept for the SFSpeechRecognizer which belongs to the same speech framework in my code. (Pseudocode example):
class myViewControllerClass: SFSpeechRecognizerDelegate{
let mySpeechRecognizer = SFSpeechRecognizer(...)
viewDidLoad(){
mySpeechRecognizer.delegate = self
}
...
//SFSpeechRecognizerDelegate Functions here
...
}
//This works as expected, woo!
However, it seems that SFSpeechRecognitionTask has no delegate property which can be set. I tried implementing the 'SFSpeechRecognitionTaskDelegate' protocol in my class in hopes that it would just magically work. However the delegate functions don't seem to ever be called. Which kind of makes sense, because it has no way of knowing that my view controller should be the delegate so why would it!?
The apple documentation covers the protocol itself and how to use it:
https://developer.apple.com/reference/speech/sfspeechrecognitiontaskdelegate
But the documentation for the task itself doesn't identify any delegate property:
https://developer.apple.com/reference/speech/sfspeechrecognitiontask
Also for reference here's the SFSpeechRecognizer documentation which has the protocol AND identifies a delegate property as you'd expect:
https://developer.apple.com/reference/speech/sfspeechrecognizer
Is there some alternative way I'm supposed to be setting the delegate for an SFSpeechRecognitionTask? Or is it handled in some completely different way?
In SFSpeechRecognizer there is a method
func recognitionTask(with request: SFSpeechRecognitionRequest,
delegate: SFSpeechRecognitionTaskDelegate) -> SFSpeechRecognizerTask
Where you can pass on the delegate for the SFSpeechRecognizerTask.
I did it like this in ViewController:
recognitionTask = speechRecognizer?.recognitionTask(with: speechRecognitionRequest, delegate: self)
Related
Example: I have a SpeechSynthesizer class that needs to update something in my UIView when it’s done uttering a piece of text. Since the SpeechSynthesizer class conforms to protocol AVSpeechSynthesizerDelegate, it is the one that receives the didFinish signal when the uttering has been completed. The idea here is to keep the ViewController from having too many delegate methods and a long list of protocols to conform to. The workaround I found was to have the ViewController passed in as a SpeechSynthesizer initialization parameter. This way I get to access the ViewController connected to the UIView I want to update from inside the SpeechSynthesizer class. The thing I don’t like about it is that it looks kind of ugly to have the ViewController passed in as a parameter to every single class that needs to use it. So I wonder, which other way I could accomplish this.
I suppose another way to ask the question is: How can I make the function
private func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance)
return something to a ViewController since it's not "called" by it?
I added a reply on Quora. Copying it here:
After doing some research and testing on code of my own here are 2 solutions to this problem.
Solution 1: The Delegate Pattern:
Create a custom delegate protocol in the ViewController
protocol ViewControllerDelegate:class {
func getViewLayer() -> CALayer
}
The ViewController must conform to this newly created protocol and therefore implement all the functions defined by it, so somewhere in the class ViewController you add:
public func getViewLayer() -> CALayer {
return self.view.layer
}
Then on my custom class, ReadTextMachine, I added a variable of the ViewControllerDelegate type
private weak var viewControllerDelegate: ViewControllerDelegate?
The variable must be weak and protocol must be of type class in order to solve a “retain cycle” problem (since both the custom class and the ViewController will point to each other)
You’ll notice now that the function call inside the ViewController is already “callable” from the custom class, so in my ReadTextMachine I added:
let viewLayer = self.viewControllerDelegate?.getViewLayer()
self.cameraPreview = CameraPreview(session: self.camera.getSession(), container: viewLayer!)
self.cameraPreview?.addPreview()
In the above case, my CameraPreview (yes, a 3rd class in this example) simply adds a camera preview layer on the UIView. For that it needed access to the main View’s layer.
The above code still doesn’t work because our original viewController’s instance hasn’t been passed as reference anywhere in our code. For that we add the following function in ReadTextMachine:
public func setViewControllerDelegate(viewController: ViewController) { // call this from the ViewController so that ViewController can be accessed from here.
self.viewControllerDelegate = viewController
}
The above piece of code will have to be called from the ViewController, after we instantiate our custom class (ReadTextMachine), so that the viewControllerDelegate inside it points to the ViewController. So in our ViewController.swift:
operatingMode = ReadTextMachine()
operatingMode.setViewControllerDelegate(viewController: self)
Another example and explanation can be found in this video from LetsBuildThatApp. I derived my solution mostly from it.
My current app in development applying the above solution can be found here: agu3rra/World-Aloud
Solution 2: Notifications and Observers pattern
This one is a bit easier to understand and follow. The general idea is to have your custom class broadcast a message which triggers a function call on your ViewController since it has an observer setup, waiting to hear that message.
So to give an example, in the context I used it, I have a CameraCapture class which uses AVFoundation to capture a photo. The capture photo trigger cannot immediately return an image, since iOS has a set of steps to execute before actually generating an image. I wanted my ReadTextMachine to resume an activity after CameraCapture had a photo available. (To apply this in the context of the CustomClass triggers ViewController event is basically the same, since both are actual classes in an iOS app as well).
So the 1st thing I did was create a broadcast function since I would use it in many places in my app. I simply placed it in a Utilities.swift file in the Xcode project.
public func broadcastNotification(name: String) {
let notification = Notification.Name(rawValue: name)
NotificationCenter.default.post(name: notification, object: nil)
}
The above function takes a string, which must be a unique notification identifier, and broadcasts it thru NotificationCenter.
In my CameraCapture class, I added a static constant to reference the unique identifier of the message:
static let NOTIFY_PHOTO_CAPTURED = "agu3rra.worldAloud.photo.captured"
For those who know AVFoundation, a photo is available when event didFinishProcessingPhoto gets executed, so at the end of that I added:
broadcastNotification(name: CameraCapture.NOTIFY_PHOTO_CAPTURED)
The above is a call to my previously defined utility function.
For my ReadTextMachine class to be able to catch that notification, I added the following on its init() and deinit routines:
override init() {
super.init()
// Setup event observers
let notification1 = Notification.Name(rawValue: CameraCapture.NOTIFY_PHOTO_CAPTURED)
NotificationCenter.default.addObserver(self,
selector: #selector(self.processingDoneTakingPhoto),
name: notification1,
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self) // cleanup observer once instance no longer exists
}
Removing the observer is important at deinit so that when your object is deallocated from memory, the observer isn’t left lingering around. The above configured observer triggers a function call inside ReadTextMachine:
#IBAction private func processingDoneTakingPhoto() {
// does my stuff
}
That’s it! Again, the entire Xcode project can be downloaded from my project’s Git repository: agu3rra/World-Aloud
Hope this can be of use to others.
Cheers!
Having my first crack at POP. In this case I want to decorate some UIViewControllers so that any that they automatically raise a 'Page viewed' analytics event.
So I created a protocol, and and extension for that protocol:
protocol ReportPageViewedEvent {
func reportPageViewed()
var pageName : String? { get set }
}
extension ReportPageViewedEvent where Self: UIViewController
{
func reportPageViewed()
{
guard let pageName = self.pageName else
{
fatalError("UIViewController implements ReportPageViewEvent protocol but did not set pageName property")
}
let eventBusiness = EventBusiness.sharedInstance
eventBusiness.logUserViewedPage(pageName)
}
}
This works as I want, if I decorate a UIViewController with ReportPageViewedEvent like this:
class HomeView: UIViewController, ReportPageViewedEvent {
I get a compiler error unless I set 'pageName' which is exactly what I want.
Where I am getting unstuck is where and how to call the actual reportPageViewed() method. I really want it to be called from viewDidLoad which means I either have to modify every 'viewDidLoad' in every controller that uses it, or subclass and call the method in the super class which defies the point of using POP in the first place.
Is there a nice way to achieve this. I can't find an example like this in any tutorial/blog.
Basically, there is always some behaviour shared by all the screens of your app. So it is appropriate to create a class called (for example) BaseViewController so all the other view controllers will inherit from it.
In BaseViewController's viewDidLoad you can call the reportPageViewed() method.
However, this approach makes the Protocol Oriented Programming not needed. Protocols are useful when you need to assign some same behaviour to objects that have nothing in common (which is not the case for app screens).
Hi so I have a protocol called DTNavigationControllerDataSource which is defined as (leaving out the methods)
protocol DTNavigationControllerDataSource: NSObjectProtocol
Then, in my own custom UINavigationController subclass I have an instance variable of this protocol as follows.
var dataSource: DTNavigationControllerDataSource?
Basically I need to know when an object becomes the dataSource. i.e The dataSource property is set. So I can call the dataSource when it is initially set, to setup some state. I've thought about using KVO however NSObjectProtocol doesn't define any methods for doing so. I'm not sure if this is even possible or if there is an alternative way outside of using KVO to know when the dataSource property inside DTNavigationController is set. I've also tried didSet and willSet but Xcode threw errors at me, as I think that is only available for primitives.
The didSet approach should work fine actually, had no issue with the following code in a playground (Xcode 7 GM):
class DTNavigationController : UINavigationController {
var dataSource: DTNavigationControllerDataSource? {
didSet {
print("new data source: \(dataSource)")
}
}
}
I want a class with all class methods. I would like to use a delegate so my view controllers (conforming to the protocol) can call AlertFactory.passwordResetSucess() and display the alert.
Is there a way to make this this work? A way to use the delegate from this class?
Is this bad practice? Bad form? Why?
What's a good way to make this happen? There will be other class methods used in several views.
Thanks!
protocol AlertFactoryDelegate
{
func showAlert(alert: UIAlertController)
}
class AlertFactory: NSObject {
let delegate: AlertFactoryDelegate!
class func passwordResetSuccess()
{
var alert = UIAlertController(title: "Success!", message: "Yay", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Continue", style: UIAlertActionStyle.Default, handler: nil))
delegate.showAlert(alert)
}
}
You can set delegate to be static, and access it with AlertFactory.delegate
class AlertFactory: NSObject {
static weak var delegate: AlertFactoryDelegate?
class func passwordResetSuccess() {
...
self.delegate?.showAlert(alert)
}
}
To create a delegate:
class SomeClass: AlertFactoryDelegate {
... // Implement everything the protocol requires
}
// Asssing a delegate
AlertFactory.delegate = SomeClass()
You cannot set an instance variable on a class without instantiating the class first. Is there a reason you don't want to instantiate the class, and just want to use class methods instead? If you feel that you have good reason to use class methods over instance methods, then you have two potential options:
Pass a delegate as a parameter to the class method.
Use Completion Blocks instead of Delegation. Here's a decent example in swift.
In my personal experience I find blocks to be preferable.
EDIT: I haven't used Swift that much lately, but as #Skrundz pointed out you can indeed use a static variable on a class in Swift.
You could use the SINGLETON command pattern, its a very common practice. You should read up on it, but basically means there is only 1 ever instance of it and you can call methods off of it when needed (or send things to it as needed). Common examples are EventSystem objects or GlobalObserver objects, Factories, and ContextManagers.
Here is information on it:
https://sourcemaking.com/design_patterns/singleton
Using a SINGLETON has tradeoffs, but can be very useful in many situations. This seems like a good pattern for your problem.
You need to initialize your delegate when your app starts. From then on, when another view controller needs to set that delegate you can assign it: (this assumes you made delegate public and its a SINGLETON)
_myViewController.actionThatRequiresDelegate = AlertFactory.delegate
As mentioned in one of the other answers, using Swifts trailing syntax closure system is a great way to use anonymous functions and you could do your logic in there, or communicate with your delegate in there.
update
Initialize in the AppDelegate.swift:
override init()
{
AlertFactory.init()
}
Then in your AlertFactory
public static AlertFactoryDelegate myDelegate
public static func init()
{
myDelegate = new AlertFactoryDelegate()
}
I've declared this protocol
protocol ReactorDelegate {
func ReactorUpdateUI()
}
In my Model I have a delegate optional property and checking if isn't nill, unwrap it to call the protocol methode provided by the VC.
var delegate:ReactorDelegate?
if delegate {delegate?.ReactorUpdateUI()}
My VC follows the protocol and has the ReactorUpdateUI methode
class VC_Reactor: UIViewController, ReactorDelegate
I'm trying to create an instance of the Model in the VC but this fails
let reactorCore=Reactor()
ERROR: 'Reactor' is not constructible with '()' what let's me know that not all properties have an initial value.
I can work-around it by making my delegate in the VC an implicitly unwrapped optional
var reactorCore:Reactor!
and in the ViewDidLoad with a custom init: reactorCore=Reactor(delegate:self)
I don't understand why I need a custom init(delegate:ReactorDelegate) if I declare the delegate property optional.
You need to mark the method as optional in your delegate protocol:
#objc protocol ReactorDelegate {
optional func ReactorUpdateUI()
}
Then mark your class as #objc and use optional chaining on the method, too:
delegate?.ReactorUpdateUI?()
Note: When writing a protocol, it's easiest to keep everything required and stub out the methods in your class, then when everything works go back and make what you want optional be optional. The error messages you get are pretty opaque, so this way you're working with fewer issues at a time.
You have to declare your protocol like this:
protocol ReactorDelegate: class {
func ReactorUpdateUI()
}
And after, your delegate:
weak var delegate: ReactorDelegate
delegate?.ReactorUpdateUI()