IBInspectable
Swift
Enum
iOS Development
Programming Tutorial

How to create an IBInspectable of type enum

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

IBInspectable is useful when you want designers or developers to tweak a custom view from Interface Builder. The catch is that Interface Builder only understands a small set of property types, so a Swift enum has to be exposed through a supported raw value.

Why A Direct Enum Property Does Not Work

You can write a normal Swift enum for your runtime code, but @IBInspectable cannot render that enum as a custom control in the storyboard inspector. The common workaround is:

  1. expose an Int or String property as the inspectable field
  2. convert that raw value into the enum inside your view
  3. keep the rest of the class strongly typed

An integer-backed enum is usually the simplest bridge.

swift
1import UIKit
2
3@objc enum BorderStyle: Int {
4    case none = 0
5    case thin = 1
6    case thick = 2
7}

Marking the enum with @objc helps when the value needs Objective-C runtime compatibility, but the important part for Interface Builder is still the raw Int.

Bridge The Raw Value Inside The View

The view exposes an integer for Interface Builder and a typed enum for the rest of the implementation:

swift
1import UIKit
2
3@IBDesignable
4final class CardView: UIView {
5    @IBInspectable var borderStyleRawValue: Int = BorderStyle.none.rawValue {
6        didSet {
7            borderStyle = BorderStyle(rawValue: borderStyleRawValue) ?? .none
8        }
9    }
10
11    private(set) var borderStyle: BorderStyle = .none {
12        didSet {
13            applyBorderStyle()
14        }
15    }
16
17    override func awakeFromNib() {
18        super.awakeFromNib()
19        borderStyle = BorderStyle(rawValue: borderStyleRawValue) ?? .none
20        applyBorderStyle()
21    }
22
23    override func prepareForInterfaceBuilder() {
24        super.prepareForInterfaceBuilder()
25        borderStyle = BorderStyle(rawValue: borderStyleRawValue) ?? .none
26        applyBorderStyle()
27    }
28
29    private func applyBorderStyle() {
30        switch borderStyle {
31        case .none:
32            layer.borderWidth = 0
33            layer.borderColor = UIColor.clear.cgColor
34        case .thin:
35            layer.borderWidth = 1
36            layer.borderColor = UIColor.systemGray.cgColor
37        case .thick:
38            layer.borderWidth = 3
39            layer.borderColor = UIColor.systemBlue.cgColor
40        }
41    }
42}

Inside Interface Builder, you edit borderStyleRawValue as a number. Inside Swift code, the view uses the BorderStyle enum, which is much easier to read and maintain.

Make The Swift API Strongly Typed

The integer property should exist mainly for design-time tooling. When your app uses the view in code, it should work with the enum directly.

swift
1extension CardView {
2    var typedBorderStyle: BorderStyle {
3        get { BorderStyle(rawValue: borderStyleRawValue) ?? .none }
4        set { borderStyleRawValue = newValue.rawValue }
5    }
6}

Now callers stay strongly typed:

swift
let card = CardView(frame: .zero)
card.typedBorderStyle = .thick

This keeps the raw integer at the edge of the design system instead of letting it leak through the whole API.

Keep Design-Time Rendering Predictable

@IBDesignable tells Xcode to render the view in the canvas. That is helpful, but it also means your setup code runs in a design-time environment. Keep the rendering logic lightweight and deterministic.

The applyBorderStyle() helper matters because it centralizes the appearance update. Whether the property changes in code, from a storyboard, or during Interface Builder preview, the same logic runs in one place.

Another practical detail is fallback behavior. Storyboards can contain stale or invalid raw values, especially after refactoring. Using BorderStyle(rawValue:) ?? .none gives the view a safe default instead of leaving it in an undefined visual state.

Common Pitfalls

  • Declaring @IBInspectable var style: BorderStyle and expecting Interface Builder to support it directly.
  • Not providing a fallback when the stored raw value is invalid.
  • Changing the raw property without reapplying the visual update method.
  • Doing heavy runtime work in prepareForInterfaceBuilder.
  • Exposing only the integer bridge to the rest of the codebase instead of a typed Swift API.

Summary

  • 'IBInspectable supports basic property types, not arbitrary Swift enums.'
  • Bridge the enum through an Int or another supported raw type.
  • Keep the real view logic typed by converting the raw value back into the enum.
  • Centralize style application and provide safe fallbacks for invalid storyboard values.

Course illustration
Course illustration