Converting UIViewController from the simple Xib-file to Swift source code.

Source Xib file

The simple Xib file – is a Xib file which holds design bound to the single UIViewController. Think about it as a "one to one" relationship between UI and Code.

Here is how the simple Xib file may look like:

The structure of the simple Xib file with UIViewController.

It has the UILabel bound to IBOutlet and UIButton bound to IBAction.

public class ViewController: UIViewController {

   @IBOutlet private weak var titleLabel: UILabel!
   @IBOutlet private weak var numberOfTapsLabel: UILabel!

   private var numberOfTaps = 0

   public override func viewDidLoad() {
      super.viewDidLoad()
      titleLabel.textColor = .blue
   }

   @IBAction private func onIncrement(_ sender: UIButton) {
      numberOfTaps += 1
      numberOfTapsLabel.text = "The button tapped \"\(numberOfTaps)\" times."
   }
}

In Simulator this Xib-file looks as shown below:

The simple UIViewController in Simulator before converting Xib file to Swift code.

Tapping on UIButton will change text of the second UILabel.

Generated code

The source code generated by Decode.app looks like below:

public class Generated: UIViewController {

   private lazy var titleLabel = UILabel()
   private lazy var button = UIButton(type: .system)
   private lazy var numberOfTapsLabel = UILabel()
   private lazy var stackView = UIStackView()

   public init() {
      super.init(nibName: nil, bundle: nil)
      setupUI()
      setupLayout()
   }

   required init?(coder: NSCoder) {
      fatalError("Please use this class from code.")
   }

   private func setupUI() {

      view.addSubview(stackView)

      view.backgroundColor = UIColor.systemBackground

      stackView.addArrangedSubview(titleLabel)
      stackView.addArrangedSubview(button)
      stackView.addArrangedSubview(numberOfTapsLabel)

      stackView.alignment = .top
      stackView.axis = .vertical
      stackView.spacing = 10
      stackView.translatesAutoresizingMaskIntoConstraints = false

      numberOfTapsLabel.contentMode = .left
      numberOfTapsLabel.font = UIFont.systemFont(ofSize: 17)
      numberOfTapsLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal)
      numberOfTapsLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .vertical)
      numberOfTapsLabel.text = "The button tapped \"0\" times."
      numberOfTapsLabel.textAlignment = .natural
      numberOfTapsLabel.translatesAutoresizingMaskIntoConstraints = false

      button.contentVerticalAlignment = .center
      button.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .medium)
      button.titleLabel?.lineBreakMode = .byTruncatingMiddle
      button.translatesAutoresizingMaskIntoConstraints = false
      button.setTitle("Tap", for: .normal)

      titleLabel.contentMode = .left
      titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
      titleLabel.numberOfLines = 0
      titleLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal)
      titleLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .vertical)
      titleLabel.text = "Hi, This is an example UI!"
      titleLabel.translatesAutoresizingMaskIntoConstraints = false
   }

   private func setupLayout() {

      stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16).isActive = true
      stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16).isActive = true
      view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 16).isActive = true
   }
}

The generated code contains instances of UILabel and UIButton, as in original source file.

In Simulator the generated UIViewController looks as shown below:

The Swift source code generated by Decode.app from Xib file running in Simulator.

Almost like in the original view, but there is few issues:

  1. The title label has a black color instead of blue.
  2. Tapping on button not changing a title of the second UILabel. The IBAction connection is broken.

Such issues are expected. Here is why:

  1. The label color was set in viewDidLoad method. This method is not present in generated code. We have to manually move it.
  2. The IBAction is not called because there is no IBOutlet and the Xib file anymore. We need to restore this connection manually.

Resolving issues

The diff-file below shows what was changed in order to restore connection between UI elements and source code.

@@ -1,5 +1,5 @@
 import UIKit
 
-public class Generated: UIViewController {
+public class Adjusted: UIViewController {
 
    private lazy var titleLabel = UILabel()
@@ -8,4 +8,6 @@
    private lazy var stackView = UIStackView()
 
+   private var numberOfTaps = 0
+
    public init() {
       super.init(nibName: nil, bundle: nil)
@@ -18,4 +20,14 @@
    }
 
+   public override func viewDidLoad() {
+      super.viewDidLoad()
+      titleLabel.textColor = .blue
+   }
+
+   @objc private func onIncrement(_ sender: UIButton) {
+      numberOfTaps += 1
+      numberOfTapsLabel.text = "The button tapped \"\(numberOfTaps)\" times."
+   }
+
    private func setupUI() {
 
@@ -46,4 +58,6 @@
       button.translatesAutoresizingMaskIntoConstraints = false
       button.setTitle("Tap", for: .normal)
+      // 1️⃣ Button connected to the existing function (which was previously an IBAction).
+      button.addTarget(self, action: #selector(onIncrement(_:)), for: .touchUpInside)
 
       titleLabel.contentMode = .left

The generated view after maintenance in Simulator looks as shown below:

The Swift source code generated by Decode.app from Xib file running in Simulator after maintenance.

Summary

An example above shows that converting the simple Xib file to Swift source code is easy doable task. The few things requires attention:

  1. View settings made in viewDidLoad method have to be moved to generated code.
  2. The connections from controls made via IBAction have to be reestablished manually via target-action assignments.

Downloads