1

I am trying to use a delegate to pass an array of data to the previous view controller. I have decided to use a delegate as my unwind segues are not working (more on that in a previous Stack Overflow post). I have created the delegate performing all of the necessary steps for setting it up.

I have tried some solutions from this Stack Overflow question but a lot of the answers are for things I have already done. Things I have tried:

  • Renaming the protocol.
  • Renaming the delegate.
  • Making the protocol not of type class.
  • Force unwrapping the delegate (this one is interesting - it caused my app to crash even though I know the delegate exists).

I have created the protocol in ViewControllerB:

protocol ViewControllerBDelegate: class {
    func viewControllerB(_ controller: UIViewController, didSelectArray array: [String])
}

I create a variable for the delegate with a weak reference cycle:

weak var viewControllerBDelegate: ViewControllerBDelegate?

I have also called a function from my delegate in didSelectRowAt:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let strArray = ["Hello", "World"]
    viewControllerBDelegate?.viewControllerB(self, didSelectArray: strArray)
}

Then on ViewControllerA:

class ViewControllerA: UIViewController, ViewControllerBDelegate

In the ViewControllerB delegate function:

func viewControllerB(_ controller: UIViewController, didSelectArray array: [String]) {
    print("An array of strings: \(array)")

    controller.dismiss(animated: true, completion: nil)
}

In the prepare for segue function:

if let viewControllerB = segue.destination as? ViewControllerB {
    viewControllerB.viewControllerBDelegate = self
}

I expected the delegate to print the contents of the array that was passed over by ViewControllerB. It did not work though, and unlike what my code was aiming for, the view controller did not dismiss like it was supposed to.

  • A bit of constructive criticism? I just commented on your other question - is there a reason you are using Xcode 11 beta? You want to use `UIKit`, correct? Both delegates and Storyboard unwinds work for me in Xcode 10. I don't know (and may be wrong about this) of any reason to *not* use Xcode 10 if you aren't targeting `UIKit`. I feels to me that you are over-complicating things. Get something working in Xcode 10 and iOS 12 (or earlier) and then work with the betas. –  Jul 31 '19 at 00:34
  • 2
    “Force unwrapping the delegate ... caused my app to crash” ... OK, so you’ve narrowed it down to the fact that `viewControllerBDelegate` is obviously `nil`. So the question is why. So, add a breakpoint in your `prepare(for:sender:)` and make sure you’re hitting that line. If not, perhaps you presented the second view controller rather than performing a segue to it? Perhaps the second view controller was embedded in a navigation controller? It’s impossible to tell on the basis of the information provided, but it’s likely to be something like that. – Rob Jul 31 '19 at 00:49
  • Unrelated, you’re calling `controller.dismiss(animated: true, completion: nil)`. Note that, technically, one should call `dismiss` on the presenting view controller, not the presented view controller. "The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.” So, you can simplify and just call `dismiss(animated: true)`, not `controller.dismiss(animated: true)`. (Note, this is unrelated to the problem at hand, but just a FYI.) – Rob Jul 31 '19 at 00:54
  • @dfd I am using the Xcode 11 beta because I want to prepare for iOS 13. I need to use the iOS 13 SDKs. I might just have to switch back to Xcode 10 if the unwind segues work there. –  Jul 31 '19 at 13:05
  • 1
    @Rob I forgot to mention that ViewControllerB is presented modally and is embedded in a navigation controller. When I was performing the segue to ViewControllerB, I forgot to include code about the navigation controller. That fixed it, –  Jul 31 '19 at 13:12

2 Answers2

0

You create a weak reference to delegate. If nothing else doesn't reference on the delegate (include UIKit staff), it will be removed by ARC. If it's true, you should remove weak modifier.

You can read about ARC here https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html

The follow situation can be appeared in the your app:

  1. App presents viewControllerA
  2. App makes the transition to viewControllerB
  3. App makes viewControllerA as the delegate viewControllerB
  4. App dismises viewControllerA

now, the delegate of viewControllerB equals nil

Axazeano
  • 890
  • 9
  • 23
0

You said:

Force unwrapping the delegate ... caused my app to crash

That narrows it down to the fact that viewControllerBDelegate is obviously nil. So the question is why. So, add a breakpoint in your prepare(for:sender:) and make sure you’re hitting that line. If not, perhaps you presented the second view controller rather than performing a segue to it? Perhaps the second view controller was embedded in a navigation controller? It’s impossible to tell on the basis of the information initially provided, but it’s likely to be something like that.

In the comments, you have confirmed that the second view controller was embedded within a navigation controller. I know you’ve figured this out, but for the sake of future readers, in that case the prepare(for:sender:) might look like:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let nav = segue.destination as? UINavigationController,
        let controller = nav.topViewController as? ViewControllerB {
            controller.delegate = self
    }
}

Forgive my changing the name of the delegate property to simply delegate. As a matter of convention, I alway use delegate except in those rare cases where I need to disambiguate between two different delegate protocols. But hopefully this illustrates the idea.

Rob
  • 415,655
  • 72
  • 787
  • 1,044