Convert UIView configured as a File Owner from the Xib-file to Swift source code.

Source Xib Source

When UIView configured as an File Owner, the connection to IBActions and IBOutlets established from special Proxy object, present in a Xib or Storyboard file, called File's Owner.

Here is how the Xib-file may look like:

The Simple Xib-file.

It has the two UILabels bound to IBOutlets and one UIButton bound to IBAction.

public class View: UIView {

   @IBOutlet private weak var titleLabel: UILabel! {
      didSet {
         titleLabel.textColor = .blue
      }
   }
   @IBOutlet private weak var numberOfTapsLabel: UILabel!

   private var numberOfTaps = 0

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

In iOS Simulator this Xib-file looks as shown below:

The Simple Xib-file in Xcode Playground Live View.

Tapping on UIButton will change title of the second label.

Generated code

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

public class Generated: UIView {

   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(frame: CGRect())
      setupUI()
      setupLayout()
   }

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

   private func setupUI() {

      addSubview(stackView)

      autoresizingMask = [.flexibleWidth, .flexibleHeight]
      backgroundColor = UIColor.systemBackground
      frame = CGRect(x: 0, y: 0, width: 320, height: 568)

      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: safeAreaLayoutGuide.leadingAnchor, constant: 16).isActive = true
      safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 16).isActive = true
      stackView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 16).isActive = true
   }
}

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

In iOS Simulator the generated UIView looks as shown below:

The Generated code in Xcode Playground Live View.

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

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

Such issues are expected. Here is why:

  1. The label color was set in didSet setter for titleLabel. This setter is not available when no Xib-file is involved and no IBOutlet is present.
  2. The IBAction is not called because there is no IBOutlet and 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 setup and remaining source code.

@@ -1,5 +1,5 @@
 import UIKit
 
-public class Generated: UIView {
+public class Adjusted: UIView {
 
    private lazy var titleLabel = UILabel()
@@ -8,4 +8,6 @@
    private lazy var stackView = UIStackView()
 
+   private var numberOfTaps = 0
+
    public init() {
       super.init(frame: CGRect())
@@ -18,4 +20,9 @@
    }
 
+   @objc private func onIncrement(_ sender: UIButton) {
+      numberOfTaps += 1
+      numberOfTapsLabel.text = "The button tapped \"\(numberOfTaps)\" times."
+   }
+
    private func setupUI() {
 
@@ -48,4 +55,6 @@
       button.translatesAutoresizingMaskIntoConstraints = false
       button.setTitle("Tap", for: .normal)
+      // 2️⃣ Button connected to the existing function (which was previously an IBAction).
+      button.addTarget(self, action: #selector(onIncrement(_:)), for: .touchUpInside)
 
       titleLabel.contentMode = .left
@@ -56,4 +65,5 @@
       titleLabel.text = "Hi, This is an example UI!"
       titleLabel.translatesAutoresizingMaskIntoConstraints = false
+      titleLabel.textColor = .blue // 1️⃣ Moved from `didSet`
    }

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

The Generated code in iOS Simulator after maintenance.

Summary

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

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

Downloads