Using @IBSegueAction with UINavigationController

To achieve the result which you want I recreated a simple project which I will link on my GitHub.

To start off, you need to get rid of the segue you have created to your Navigation controller and recreate it.

Recreate the Segue

Next select present Modally and name your segue with an identifier

Present modally

Segue Identifier

Next you need to define the IBSegueAction in your first ViewController

ViewController.swift

import UIKit

class ViewController: UITableViewController {
    
    let persons = ["Robert", "Peter", "Dave"]
    var selectedPerson = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    @IBSegueAction
    private func showPerson(coder: NSCoder, sender: Any?, segueIdentifier: String?)
        -> PersonViewController? {
        return PersonViewController(coder: coder, personDetails: persons[selectedPerson])
    }
    
    
    //MARK:- TableView Methods
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return persons.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "personCell", for: indexPath)
        cell.textLabel?.text = persons[indexPath.row]
        return cell
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedPerson = indexPath.row
        performSegue(withIdentifier: "ShowPerson", sender: self)
    }
}

Then add the implementation of init?(coder: NSCoder, personDetails: String) in the PersonViewController as follows. Xcode will yell that you need to implement required init?(coder: NSCoder) Just tap fix.

PersonViewController.swift

import UIKit

class PersonViewController: UIViewController {
    //Your data passed in below as non optional constant
    let personDetails: String
    
    //The received data gets initialized below
    init?(coder: NSCoder, personDetails: String) {
      self.personDetails = personDetails
      super.init(coder: coder)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @IBOutlet weak var personLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        personLabel.text = personDetails
    }
}

The next step is to select the ViewController's top part in the storyboard so you can see the three icons.

Select ViewController from which you pass data

Following that, you have to select the Segue which goes out from the second UINavigationController to your destination ViewController here PersonViewController and drag back from the segue to the first yellow icon of the ViewController you want to send information from like on the screenshot below. Xcode will automatically recognize the IBSegueAction's name which you created in code.

StoryBoard

That's it.

You should get a result like this once you tap any of the cells in the first View Controller

Sample

Here is the sample project GitHub

I also found more information about IBSegueAction here


I had the same problem but managed to get it working. Instead of setting your segue action selector on your Show Detail segue, set it on Navigation Controller's Relationship Segue, as in the following screenshot:

right clicking navigation controller's relationship segue