Setting the delegate on initialization of object in conforming "header" - ios

I am getting an error trying to set the delegate of my ParseController, which is a property on my VC.
protocol ParseControllerDelegate: NSObjectProtocol {
var parseController: ParseController! {get set}
//TODO:- Instead of this function, i should use a closure, but couldnt get the sytnax right!
func updateUI()
}
class ParseController: NSObject {
weak var delegate: ParseControllerDelegate!
var dataArray:[AnyObject]!
init(delegate:ParseControllerDelegate!) {
self.delegate = delegate
}
Then when I try to set this in my VC "header" I get an error.
var parseController:ParseController! = ParseController(delegate: self)
I am able to set in viewDidLoad with this method, but ideally I would like to set the delegate where it is. The error I get is
Type 'MainVC -> () -> MainVC!' does not conform to protocol 'ParseControllerDelegate'
MainVC is my viewController.
Things I tried:
Casting (self as ParseControlleDelegate)
self()
self!
Im sure it is something simple, but I can't figure out the proper syntax.

Figured it out!
Proper syntax is:
lazy var parseController:ParseController = ParseController(delegate: self)
*Note: I had to remove the delegate requirement from my protocol, as I got a compiler warning that "lazy" isn't allowed on a protocol requirement, and the delegate requirement was not met once I declared it lazy in my VC.

Related

How to move data in VC using Delegates?

How can I move the data stored in data into the next VC and append it in my list when the sendDate is tapped ? Here is my code of the sending class :
protocol DataSentDelegate{
func userDidEnterData(data: String)
}
class SecondViewController: UIViewController{
var delegate: DataSentDelegate!
#IBAction func addItem(_ sender: Any)
{
let data = textField.text
delegate?.userDidEnterData(data: data)
}
Here is the code of recieving class :
class SecondPageViewController: UIViewController, DataSentDelegate{
func userDidEnterData(data: String) {
}
#IBAction func sendDate(_ sender: UIDatePicker) {
}
How do I implement list.append(data!) where data holds the value of textField.text
Do you not have to set the delegate in SecondPageViewController to self (normally in ViewDidLoad?
You declared it but don't seem to have assigned it.
You can share data through whole project using Singleton Pattern.
A singleton class is initialised for only once.
Look at these answers:
Swift - set delegate for singleton
What about you just add an array variable in your SecondPageViewController which will hold a list of strings, then append a new string each time sendDate delegate method gets called?
A few other remarks, there is no need to declare your delegate var as implicitly unwrapped if you're using optional chaining anyway, just declare it as an optional. Secondly, because SecondPageViewController is a class, it's better to make your delegate protocol class bound as such: protocol DataSentDelegate: class { func userDidEnterData(data: String) }, thirdly to avoid possible strong reference cycles make your delegate var a weak one.

Updating a UILabel via protocol results in crash (found nil)

I want to implement the MVP pattern for a new app. So the View shouldn't have any logic besides one that exclusively concerns UI elements. Therefore I want to request initial data from an "Interpreter" (interpreting user input in later code), which in turn requests data from my model and gives it to the "Presenter". The presenter holds a protocol with functions of the view.
The problem is: Calling updateUIData() from the presenter results in a
fatal error: unexpectedly found nil while unwrapping an Optional value
while calling the function from within the View at the same position is working just fine.
I suspect the error comes from the initialization of the specific MainViewController in the init of the presenter, but I don't know how to resolve this, if my guess is right.
Here's my (relevant) code:
MainViewController:
class MainViewController: UIViewController {
lazy var interpreter = Interpreter() // lazy needed b/c Interpreter holds Presenter which holds MainViewController
#IBOutlet var dateLabel: UILabel!
#IBOutlet var totalTimeLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// updateUIData()
requestData()
}
func requestData() {
interpreter.requestData()
}
}
extension MainViewController: MainViewSetters {
func updateUIData() {
dateLabel.text = "Data"
totalTimeLabel.text = "loaded"
}
}
MainViewSetters (Protocol):
protocol MainViewSetters {
func updateUIData()
}
Interpreter:
class Interpreter {
let presenter = Presenter()
func requestData() {
// normally: get data from model and pass it to presenter
presenter.presentData()
}
}
Presenter:
class Presenter {
let mainView: MainViewSetters
init(withMainViewController mainVC: MainViewSetters = MainViewController()) {
mainView = mainVC
}
func presentData() {
mainView.updateUIData()
}
}
Your problem here is that you are not passing the reference to MainViewController to your instance of Presenter.
This code :
lazy var interpreter = Interpreter()
Should be more like this : (Type is needed here because with lazy the compiler can't infer properly)
lazy var interpreter: Interpreter = Interpreter(for: self)
You then have to create a special initializer in Interpreter which will pass the viewController instance to its presenter property :
class Interpreter {
let presenter: Presenter
init(for viewController: MainViewSetters) {
presenter = Presenter(withMainViewController: viewController)
}
func requestData() {
// normally: get data from model and pass it to presenter
presenter.presentData()
}
}
I also highly suggest you to remove the default value to Presenter's init method, it's very unlikely you'll want to assign a random instance of MainViewController as mainView of any Presenter object.
Finally, please note that this code is creating a retain cycle and neither your MainViewController instance nor your Presenter instance will be deallocated. This is due to the fact the Presenter class holds a strong reference to the MainViewController instance with its property mainView. To fix this you have to mark the mainView as weak as well as making it optional.
Please see the fixed implementation below :
class Presenter {
weak var mainView: MainViewSetters?
init(withMainViewController mainVC: MainViewSetters) {
mainView = mainVC
}
func presentData() {
mainView?.updateUIData()
}
}
For weak to be acceptable on a property of type MainViewSetters (which is not a real type but only a protocol) you have to specify that its a protocol that will only be applied to classes :
protocol MainViewSetters: class {
func updateUIData()
}
You are initializing interpreter passing a default MainViewController().
Change that code from:
lazy var interpreter = Interpreter()
to
lazy var interpreter = Interpreter(withMainViewController: self)

Trying to understand the implementation of delegates with protocols in Swift

After doing loads of research I am still a little confused about how to use and implement delegates. I have tried writing my own, simplified example, to aid my understanding - however it does not working - meaning I must be a little lost.
//the underlying protocol
protocol myRules {
func sayName(name: String);
}
//the delegate that explains the protocols job
class myRulesDelegate: myRules {
func sayName(name: String){
print(name);
}
}
//the delegator that wants to use the delegate
class Person{
//the delegator telling which delegate to use
weak var delegate: myRulesDelegate!;
var myName: String!;
init(name: String){
self.myName = name;
}
func useDels(){
//using the delegate (this causes error)
delegate?.sayName(myName);
}
}
var obj = Person(name: "Tom");
obj.useDels();
I have read and watched so many tutorials but am still struggling. I no longer get error (cheers guys). But still get no output from sayName.
which demonstrates I must be misunderstanding how delegate patterns work.
I would really appreciate a corrected version of the code, with a simple explanation as to why it works, and why it is helpful.
I hope this helps others too. Cheers.
In Swift you omit the first parameter's external name, so your function call should be delegate.sayName("Tom")
Also, it is dangerous to use an implicitly unwrapped optional for your delegate property, as you have found. You should use a weak optional:
//the underlying protocol
protocol MyRulesDelegate: class {
func sayName(name: String)
}
//the delegator that wants to use the delegate
class Person {
//the delegator referencing the delegate to use
weak var delegate: MyRulesDelegate?
var myName: String
init(name: String){
self.myName = name
}
func useDels() {
//using the delegate
delegate?.sayName(myName)
}
}
Finally, your delegate must be an object, so you can't use a delegate in the way you have shown; you need to create another class that can set an instance of itself as the delegate
class SomeOtherClass: MyRulesDelegate {
var myPerson: Person
init() {
self.myPerson = Person(name:"Tom")
self.myPerson.delegate = self
}
func sayName(name: String) {
print("In the delegate function, the name is \(name)")
}
}
var something = SomeOtherClass()
something.myPerson.useDels()
Output:
In the delegate function, the name is Tom

Cannot assign self as delegate in property initializer

I am getting a strange error message when I try to access self when initializing a property on my class. It seems like my class has a weird type.
This is the error:
Cannot assign a value of type 'ViewController -> () ViewController' to a value of type 'ModelControllerDelegate?'
This is the code, simple example in a playground:
import Foundation
import UIKit
protocol ModelControllerDelegate
{
func modelControllerDidFinishTask()
}
class ModelController
{
var delegate : ModelControllerDelegate?
}
class ViewController : UIViewController, ModelControllerDelegate
{
var modelController : ModelController =
{
let controller = ModelController()
controller.delegate = self
return controller
}()
func modelControllerDidFinishTask()
{
}
}
You are trying to use self when not fully initialised. Might work if you use a lazy initialisation:
lazy var modelController : ModelController = { ...
When initialising a property with a closure like this the closure is run before the instance is fully initialised. This means there are a few things you can't do here:
Access other property values
Use self
Call any instance methods
To get around this you could either initialise modelController entirely in the ViewController's init, or at least defer the setting of its delegate until then.

Protocols and delegate trouble in swift

All,
I have create a swift file and put a protocol in it, like this :
protocol PayButtonProtocol {
func enablePayButton()
func disablePayButton()
}
I have made my viewcontroller conform to the protocol like this :
class ViewController: UIViewController, PayButtonProtocol
I have also created the functions in the ViewController so it conforms like this
func enablePayButton() {
println("Button enabled")
PAYBarButton.enabled = true
}
func disablePayButton() {
PAYBarButton.enabled = false
}
And in another class, I have set the delegate and I want to execute the enablePayButton when something is pressed like this :
var delegate:PayButtonProtocol?
and in the function i want to execute one of the functions by :
delegate?.enablePayButton()
but it doesn't execute, what am I missing please ?
More than likely delegate is nil. Add a breakpoint and check the value of delegate before that line executes. Or change the "?" to a "!" and it will crash if delegate is nil, letting you know what's wrong.
Your code in the other class:
var delegate:PayButtonProtocol?
defines a variable named delegate that is of type PayButtonProtocol?.
The variable delegate will contain nil until you assign something to it:
delegate = <someObjectThatConformsToPayButtonProtocol>

Resources