I have a swift protocol, but it never fires.
I have 1 class which is an instance, and the other is a class where I want to manage an object;
protocol TurnDelegate: class {
func turnIsCompleted()
}
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
override init() {
super.init()
delegate?.turnIsCompleted()
}
}
class ClassTwo: NSObject, TurnDelegate {
static var instance = ClassTwo()
func turnIsCompleted() {
print ("Turn is completed")
}
}
let c2:ClassTwo = ClassTwo.instance
let c1:ClassOne = ClassOne.init()
My issue is that the protocol never fires and does not output "turn is completed"
How can I resolve this?
Edit: How do I set the delegate?
Many thanks
In case you have describe create custom init.
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
init(with delegate: TurnDelegate?) {
self.delegate = delegate
delegate?.turnIsCompleted()
}
}
Than:
let c2:ClassTwo = ClassTwo.instance
let c1:ClassOne = ClassOne.init(with: c2)
Output:
Turn is completed
You forgot to set the delegate.
Usually the delegate is set in an init method. The method in the protocol is called later in another method for example
protocol TurnDelegate: class {
func turnIsCompleted()
}
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
init(delegate: TurnDelegate?) {
self.delegate = delegate
}
func turnSomething()
{
delegate?.turnIsCompleted()
}
}
class ClassTwo: NSObject, TurnDelegate {
static let instance = ClassTwo()
func turnIsCompleted() {
print ("Turn is completed")
}
}
let c2 = ClassTwo.instance
let c1 = ClassOne(delegate: c2)
c1.turnSomething()
However for this purpose especially in conjunction with a singleton I'd prefer a callback closure rather than protocol / delegate. The benefit is less overhead and the callback is directly connected to the calling method.
class ClassOne : NSObject {
func turnSomething()
{
let c2 = ClassTwo.instance
c2.turn {
print ("Turn is completed")
}
}
}
class ClassTwo: NSObject {
static let instance = ClassTwo()
func turn(completion: ()->()) {
// do heavy work
completion()
}
}
let c1 = ClassOne()
c1.turnSomething()
Delegates in all their glory do have their drawbacks too. One of them is that relationships between objects and their delegates have to be established explicitly. In Cocoa there are typically two ways of doing this. One is connecting a delegate IBOutlet in InterfaceBuilder, the other is doing it programmatically. As #OlegGordiichuck points out you could do it in the initializer, but generally in Cocoa delegates tend to be properties. In your case this would boil down to instantiate objects of ClassTwo and ClassOne and then manually set the delegate of c2 as in
c2.delegate = c1
This however defeats your notification mechanism and you would have to have a separate method for notifying the delegate (Which is again typical, as usually your delegate cannot know about is significant other during its construction. Moreover the construction of the originator is usually not something the delegate would have to know about).
Related
I have a main view titled NoteTakerViewController. I have a weatherGetterController class with a protocol that returns 5 days of weather with a protocol function called getMyWeather. However the protocol function is not being called that returns the weather data to NoteTakerViewController. I am certain I have everything set up correctly with the delegates but perhaps I do not.
This is really not a duplicate of Swift Delegate Not Being Called as the solution on that page did not work.
Any help you could provide would be great.
Here's the relevant code snippets:
My weatherGetterController class:
protocol WeatherGetterControllerDelegate {
func getMyWeather(weather: [FiveDayForecast])
}
class WeatherGetterController: UIViewController, CLLocationManagerDelegate {
var weatherGetterDelegate: WeatherGetterControllerDelegate?
And in the WeatherGetterController "getWeather" function call. The line
self.weatherGetterDelegate?.getMyWeather(weather: myForecast)
is not being called.
func getWeather() {
...
getNetWeather { (fetchedInfo) in
if let fetchedInfo2 = fetchedInfo {
//self.updateUI(mainWeather: fetchedInfo2)
//need to call delegate here
let myForecast = self.figureFive(weather: fetchedInfo2)
//return the forecast
print(myForecast)
self.weatherGetterDelegate?.getMyWeather(weather: myForecast)
}
}
Finally the function implementation in NoteTakerViewController:
class NoteTakerViewController: UIViewController, ..., UITextFieldDelegate, WeatherGetterControllerDelegate
func getMyWeather(weather: [FiveDayForecast]) {
print("get my weather")
print(weather)
}
Through debugging I can see that "weatherGetterDelegate" is set to nil and I don't know why.
Thanks
First you need to make WeatherGetterController a property in NoteTakerViewController or a local variable where you call it but I will use the first option.
class NoteTakerViewController: UIViewController, ..., WeatherGetterControllerDelegate {
var weatherGetterController: WeatherGetterController?
//...
func viewDidLoad() {
//....
weatherGetterController = WeatherGetterController()
weatherGetterController?.delegate = self
}
//And then in some other part of your code you do
func someFunc() {
self.weatherGetterController?.getWeather()
I am curious why WeatherGetterController is define to be a viewcontroller, is that really correct?
class WeatherGetterController: UIViewController, CLLocationManagerDelegate {
Personally I would remove that part
class WeatherGetterController: CLLocationManagerDelegate {
Put this in init of WeatherGetterController
public weak var weatherGetterDelegate: WeatherGetterControllerDelegate?
/* if you are not initializing it anywhere else
and if you are initializing it anywhere else then make sure you are
initializing WeatherGetterController there too and then put delegate
of WeatherGetterController to NoteTakerViewController object
else write the below code */
var model = NoteTakerViewController()
public override init() {
self. weatherGetterDelegate = model
}
I have my viewcontroller that implements one Protocol,
import UIKit
class FirstScreenViewController: UIViewController, mainViewProtocol {
var presenter: mainPresenterProtocol?
//Protocol Functions
static func showSmallHeadline(textToShow: String) {
<#code#>
}
func showHeadline(textToShow: String) {
<#code#>
}
}
I have my presenter that implement second Protocol
import Foundation
class MainPresenter: mainPresenterProtocol {
var screenViewController: mainViewProtocol?
//confirms protocol
static func presenterProtocolFuncOne() {
<#code#>
}
func presenterProtocolFuncTwo(numOne: Int, numTwo: Int, sucssesMessage: String, failMessage: String) -> String {
<#code#>
}
func presenterProtocolFucThree() -> Bool {
<#code#>
}
}
how do I call the functions in my presenter (that implements them through the protocol) from my viewcontroller,
and how do I call the functions in my viewcontroller (that implements them through the protocol) from my presenter ?
Thank you !
The key thing here - strong relationship between controller and presenter. You should avoid that by adding weak to presenter property in controller.
protocol MainViewProtocol {
func controllerProtocolFunc()
}
protocol MainPresenterProtocol: class {
func presenterProtocolFunc()
}
class FirstScreenViewController: UIViewController, MainViewProtocol {
weak var presenter: MainPresenterProtocol?
func controllerProtocolFunc() { }
}
class MainPresenter: MainPresenterProtocol {
var screenViewController: MainViewProtocol?
func presenterProtocolFunc() { }
}
Next step, when you need to call presenter(controller) func from controller(presenter), simply call it with optional chaining:
presenter?.presenterProtocolFunc()
// or
screenViewController?.controllerProtocolFunc()
P.S. Also, note from docs:
Because protocols are types, begin their names with a capital letter
(such as FullyNamed and RandomNumberGenerator) to match the names of
other types in Swift (such as Int, String, and Double).
Add properties to each class referencing the other object by the type of the protocol.
class FirstScreenViewController: UIViewController, mainViewProtocol {
var presenter: mainPresenterProtocol?
// rest of the code
}
class MainPresenter: mainPresenterProtocol {
var screenViewController: mainViewProtocol?
// rest of the code
}
Assign the properties after constructing both objects. The ? after the protocol type makes them optional, allowing you delay the assignment to the point in time when both objects exist. Alternatively, you can swap out the ? for a ! if you are certain the properties will be assigned non-nil values, so you can save a lot of nil-checking and dereferencing.
let viewController = FirstScreenViewController()
let mainPresenter = MainPresenter()
viewController.presenter = mainPresenter
presenter.screenViewController = viewController
I have a delegation/initialization problem I can't seem to solve. Basically I have a storyboard with a few View controllers. Inside the storyboard there is this "View controller" which consists of a UITableview that I have connected with a DeviceListViewController class so that it populates the information. In here I have declared the following protocol:
protocol DeviceListViewControllerDelegate: UIAlertViewDelegate {
var connectionMode:ConnectionMode { get }
func connectPeripheral(peripheral:CBPeripheral, mode:ConnectionMode)
func stopScan()
func startScan()
}
and inside the class itself I have a init method like this (which is probably wrong but I didn't know what else I could do at this point):
convenience init(aDelegate: DeviceListViewControllerDelegate) {
self.init()
self.delegate = aDelegate
}
Then there is this second class that is not attached to any view controller called BLEMainViewController. It should be a singleton handling all the bluetooth actions. This means I should be able to delegate some stuff between DevicelistViewController and BLEMainViewController.
In the BLEMainViewController I have inherited the DeviceListViewControllerDelegate:
class BLEMainViewController: NSObject, DeviceListViewControllerDelegate {
var deviceListViewController:DeviceListViewController!
var delegate: BLEMainViewControllerDelegate?
static let sharedInstance = BLEMainViewController()
}
override init() {
super.init()
// deviceListViewController.delegate = self
deviceListViewController = DeviceListViewController(aDelegate: self)
}
The problem is that BLEMainViewController is not attached to any View Controller (and it shouldn't IMO) but it needs to be initialized as a singleton in order to handle all the BLE actions. Can anyone point me in the right direction (with an example preferably) on how to work around this?
I think you simply used wrong code architecture.
The BLEManager is a shared-instance, you can call it from everywhere, set it properties, and call its methods.
Its can delegate your view-controller with any predefine events you will add to its protocol and provide proper implementation
Here is some code, hope it helps
protocol BLEManagerDelegate{
func bleManagerDidStartScan(manager : BLEManager)
}
class BLEManager: NSObject {
static let sharedInstance = BLEManager()
var delegate: BLEManagerDelegate?
var devices : [AnyObject] = []
func startScan(){
delegate?.bleManagerDidStartScan(self)
//do what ever
}
func stopScan(){
}
}
In my app I have one screen divided between two ViewControllers - LadderViewController and GameHistoryTableViewController, which lies in a container. I want user to be able to filter the data in the table by tapping on something in the LadderView. I tried to solve this using delegates:
LadderViewController:
delegate = GameHistoryTableViewController()
func imageTapped(imageIndex: Int) {
delegate?.selectedHeroNumber(imageIndex)
}
GameHistoryTableViewController: (conforms to the delegate protocol and implemets a function from it)
func selectedHeroNumber(heroNumber: Int) {
let filteredGames = filterGamesFromHeroNumber(heroNumber)
tableDataSource = filteredGames
self.tableView.reloadData()
}
That doesn't work, though, because the delegate I declare in LadderViewConroller is another instance of GameHistoryTableViewController, not the (to the user) shown one. I don't know how to access the "visible" instance (table) of GameHistoryTableViewController though... So, how should be delegating used here? Or should I use another approach (and if so, what kind)? I basically need to change the table's data source according to on what the user taps, one can say "from outside" (dataSource is a property in my GameHistoryTableViewController class).
Here is an example with delegation like you want to do. It's a better solution than singleton in this case ;)
declare a new protocol call HeroInfo:
protocol HeroInfo: class {
func selectedHeroNumber(heroNumber: Int);
}
LadderViewController:
//create the delegation
weak var delegate:HeroInfo?
func imageTapped(imageIndex: Int) {
//call the delegate method
delegate?.selectedHeroNumber(imageIndex)
}
GameHistoryTableViewController:
// Here get the protocol HeroInfo inheritance
class userTableViewController: UITableViewController, HeroInfo {
override func viewDidLoad() {
super.viewDidLoad()
//Here get your Ladder view in a splitView
if let split = self.splitViewController {
let controllers = split.viewControllers
self.ladderViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? ladderViewController
//register it to delegate
self.ladderViewController?.delegate = self
}
}
...
// Here is your method of your protocol that you must conform to
func selectedHeroNumber(heroNumber: Int) {
let filteredGames = filterGamesFromHeroNumber(heroNumber)
tableDataSource = filteredGames
self.tableView.reloadData()
}
...
}
There are a few ways to achieve this, I have a similar setup for which I use a model class with a singleton to store the relevant data.
For instance you could have the following
class dataModel {
static let sharedInstance = dataModel()
private var _heroNumber = Int()
private init() {}
var heroNumber: Int = {
return _heroNumber
}
func setHero(hero: Int) -> Int {
return _heroNumber
}
}
}
You can then can access this model from each of your controllers using dataModel.sharedInstance.heroNumber etc...
I have a class that needs to call out to a delegate when one of its properties changes. Here are the simplified class and protocol for the delegate:
protocol MyClassDelegate: class {
func valueChanged(myClass: MyClass)
}
class MyClass {
weak var delegate: MyClassDelegate?
var currentValue: Int {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: Int) {
currentValue = initialValue
}
}
This all works just fine. But, I want to make this class generic. So, I tried this:
protocol MyClassDelegate: class {
func valueChanged(genericClass: MyClass)
}
class MyClass<T> {
weak var delegate: MyClassDelegate?
var currentValue: T {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: T) {
currentValue = initialValue
}
}
This throws two compiler errors. First, the line declaring valueChanged in the protocol gives: Reference to generic type 'MyClass' requires arguments in <...>. Second, the call to valueChanged in the didSet watcher throws: 'MyClassDelegate' does not have a member named 'valueChanged'.
I thought using a typealias would solve the problem:
protocol MyClassDelegate: class {
typealias MyClassValueType
func valueChanged(genericClass: MyClass<MyClassValueType>)
}
class MyClass<T> {
weak var delegate: MyClassDelegate?
var currentValue: T {
didSet {
if let actualDelegate = delegate {
actualDelegate.valueChanged(self)
}
}
}
init(initialValue: T) {
currentValue = initialValue
}
}
I seem to be on the right path, but I still have two compiler errors. The second error from above remains, as well as a new one on the line declaring the delegate property of MyClass: Protocol 'MyClassDelegate' can only be used as a generic constraint because it has Self or associated type requirements.
Is there any way to accomplish this?
It is hard to know what the best solution is to your problem without having more information, but one possible solution is to change your protocol declaration to this:
protocol MyClassDelegate: class {
func valueChanged<T>(genericClass: MyClass<T>)
}
That removes the need for a typealias in the protocol and should resolve the error messages that you've been getting.
Part of the reason why I'm not sure if this is the best solution for you is because I don't know how or where the valueChanged function is called, and so I don't know if it is practical to add a generic parameter to that function. If this solution doesn't work, post a comment.
You can use templates methods with type erasure...
protocol HeavyDelegate : class {
func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R
}
class Heavy<P, R> {
typealias Param = P
typealias Return = R
weak var delegate : HeavyDelegate?
func inject(p : P) -> R? {
if delegate != nil {
return delegate?.heavy(self, shouldReturn: p)
}
return nil
}
func callMe(r : Return) {
}
}
class Delegate : HeavyDelegate {
typealias H = Heavy<(Int, String), String>
func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R {
let h = heavy as! H // Compile gives warning but still works!
h.callMe("Hello")
print("Invoked")
return "Hello" as! R
}
}
let heavy = Heavy<(Int, String), String>()
let delegate = Delegate()
heavy.delegate = delegate
heavy.inject((5, "alive"))
Protocols can have type requirements but cannot be generic; and protocols with type requirements can be used as generic constraints, but they cannot be used to type values. Because of this, you won't be able to reference your protocol type from your generic class if you go this path.
If your delegation protocol is very simple (like one or two methods), you can accept closures instead of a protocol object:
class MyClass<T> {
var valueChanged: (MyClass<T>) -> Void
}
class Delegate {
func valueChanged(obj: MyClass<Int>) {
print("object changed")
}
}
let d = Delegate()
let x = MyClass<Int>()
x.valueChanged = d.valueChanged
You can extend the concept to a struct holding a bunch of closures:
class MyClass<T> {
var delegate: PseudoProtocol<T>
}
struct PseudoProtocol<T> {
var valueWillChange: (MyClass<T>) -> Bool
var valueDidChange: (MyClass<T>) -> Void
}
Be extra careful with memory management, though, because blocks have a strong reference to the object that they refer to. In contrast, delegates are typically weak references to avoid cycles.