📐 Declarative UIKit in 10 lines of code.

Overview

Withable

📐 Declarative UIKit in 10 lines of code.

See corresponding article at Declarative UIKit with 10 lines of code A simple extension instead of libraries for more.

How to use

With a single extension on AnyObject you can do things like this.

class ContentViewController: UIViewController {
    
    ...
    
    lazy var titleLabel = UILabel()
        .with {
            $0.text = viewModel.title
            $0.textColor = .label
            $0.font = .preferredFont(forTextStyle: .largeTitle)
        }
    
    ...
}

With any kind of object, really.

lazy var submitButton = UIButton()
    .with {
        $0.setTitle("Submit", for: .normal)
        $0.addTarget(self, action: #selector(didTapSubmitButton), for: .touchUpInside)
    }
present(
    DetailViewController()
        .with {
            $0.modalTransitionStyle = .crossDissolve
            $0.modalPresentationStyle = .overCurrentContext
        },
    animated: true
)
present(
    UIAlertController(title: title, message: message, preferredStyle: .alert)
        .with {
            $0.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
        },
    animated: true
)
let today = DateFormatter()
    .with {
        $0.dateStyle = .medium
        $0.locale = Locale(identifier: "en_US")
    }
    .string(from: Date())
lazy var displayLink = CADisplayLink(target: self, selector: #selector(update))
    .with {
        $0.isPaused = true
        $0.preferredFramesPerSecond = 120
        $0.add(to: RunLoop.main, forMode: .common)
    }

Even value types as well (after conforming to Withable).

extension PersonNameComponents: Withable { }

let name = PersonNameComponents()
    .with {
        $0.givenName = "Geri"
        $0.familyName = "Borbás"
    }

Not to mention 3D stuff (ARKit, RealityKit, SceneKit).

view.scene.addAnchor(
    AnchorEntity(plane: .horizontal)
        .with {
            $0.addChild(
                ModelEntity(
                    mesh: MeshResource.generateBox(size: 0.3),
                    materials: [
                        SimpleMaterial(color: .green, isMetallic: true)
                    ]
                )
            )
        }
)

How it works

It is implemented in this with method. 💎

public extension Withable {
    
    func with(_ closure: (Self) -> Void) -> Self {
        closure(self)
        return self
    }
}

The method implements pretty classic patterns. You can think of it as something between an unspecialized/parametric builder, or a decorator with customizable/pluggable decorating behaviour. See Withable.swift for all details (generics, value types).

UIKit benefits

The package contains a couple of convinient extensions of UIKit classes what I use (probably will be moved to their own package as they grow). I left them here intentionally as they may exemplify how you can create your own extensions tailored for your codebases' needs.

For example, you may create a convenient text decorator for UILabel.

extension UILabel {
    
    func with(text: String?) -> Self {
        with {
            $0.text = text
        }
    }
}

Furthermore, you can condense your styles to simple extensions like this.

extension UILabel {
    
    var withTitleStyle: Self {
        with {
            $0.textColor = .label
            $0.font = .preferredFont(forTextStyle: .largeTitle)
        }
    }
    
    var withPropertyStyle: Self {
        with {
            $0.textColor = .systemBackground
            $0.font = .preferredFont(forTextStyle: .headline)
            $0.setContentCompressionResistancePriority(.required, for: .vertical)
        }
    }
    
    var withPropertyValueStyle: Self {
        with {
            $0.textColor = .systemGray
            $0.font = .preferredFont(forTextStyle: .body)
        }
    }
    
    var withParagraphStyle: Self {
        with {
            $0.textColor = .label
            $0.numberOfLines = 0
            $0.font = .preferredFont(forTextStyle: .footnote)
        }
    }
}

With extensions like that, you can clean up view controllers.

class ContentViewController: UIViewController {
    
    let viewModel = Planets().earth
    
    private lazy var body = UIStackView().vertical(spacing: 10).views(
        UILabel()
            .with(text: viewModel.title)
            .withTitleStyle,
        UIStackView().vertical(spacing: 5).views(
            UIStackView().horizontal(spacing: 5).views(
                UILabel()
                    .with(text: "size")
                    .withPropertyStyle
                    .withBox,
                UILabel()
                    .with(text: viewModel.properties.size)
                    .withPropertyValueStyle,
                UIView.spacer
            ),
            UIStackView().horizontal(spacing: 5).views(
                UILabel()
                    .with(text: "distance")
                    .withPropertyStyle
                    .withBox,
                UILabel()
                    .with(text: viewModel.properties.distance)
                    .withPropertyValueStyle,
                UIView.spacer
            ),
            UIStackView().horizontal(spacing: 5).views(
                UILabel()
                    .with(text: "mass")
                    .withPropertyStyle
                    .withBox,
                UILabel()
                    .with(text: viewModel.properties.mass)
                    .withPropertyValueStyle,
                UIView.spacer
            )
        ),
        UIImageView()
            .with(image: UIImage(named: viewModel.imageAssetName)),
        UILabel()
            .with(text: viewModel.paragraphs.first)
            .withParagraphStyle,
        UILabel()
            .with(text: viewModel.paragraphs.last)
            .withParagraphStyle,
        UIView.spacer
    )
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(body)
        view.backgroundColor = .systemBackground
        body.pin(
            to: view.safeAreaLayoutGuide,
            insets: UIEdgeInsets(top: 30, left: 30, bottom: 30, right: 30)
        )
    }
}

I recommend to read the corresponding article at Declarative UIKit with 10 lines of code A simple extension instead of libraries to read more about the background and more examples.

Used by Apple

Later on, I found out that on occasions Apple uses the very same pattern to enable decorating objects inline. These decorator functions are even uses the same with naming convention.

These examples below are in vanilla UIKit. 🍦

let arrow = UIImage(named: "Arrow").withTintColor(.blue)
let mail = UIImage(systemName: "envelope").withRenderingMode(.alwaysTemplate)
let color = UIColor.label.withAlphaComponent(0.5)

Stored properties in extensions

In addition, the package contains an NSObject extension that helps creating stored properties in extensions. I ended up including it because I found extending UIKit classes with stored properties is a pretty common usecase. See NSObject+Extensions.swift and UIButton+Extensions.swift for more.

You can do things like this.

extension UITextField {
    
    var nextTextField: UITextField? {
        get {
            associatedObject(for: "nextTextField") as? UITextField
        }
        set {
            set(associatedObject: newValue, for: "nextTextField")
        }
    }
}

Declare constraints inline

One more secret weapon is the UIView.onMoveToSuperview extension, which is simply a closure called (once) when the view gets added to a superview. With that, you can declare the constraints in advance using this closure at initialization time, then they are added/activated later on at runtime by the time when the view has a superview. See Keyboard Avoidance repository for usage examples.

License

Licensed under the MIT License.

You might also like...
Create descriptive UIKit screens, faster!
Create descriptive UIKit screens, faster!

Columbina's DeclarativeUIKit Create descriptive UIKit screens, faster! Get rid of constraints manipulation and use declarative language to create your

Application to test MVVM architecture with Combine and UIKit librarys.

Application to test MVVM architecture with Combine and UIKit librarys.

🎨 View instance initializing sugar for Swift & UIKit
🎨 View instance initializing sugar for Swift & UIKit

🎨 View instance initializing sugar for Swift & UIKit

xTensions is a collection of useful class extensions for UIKit.

xTensions Intro xTensions is a collection of useful class extensions for UIKit. Swift Package Manager Note: Instructions below are for using SwiftPM w

A simple and elegant UIKit for iOS.
A simple and elegant UIKit for iOS.

HamsterUIKit A simple and elegant UIKit(Chart) for iOS, written in Swift. 📊 Curve and bar Charts. 💡 Protocols are designed based on UIKit(UITableVie

🧩 Easy scrollable layouts in UIKit
🧩 Easy scrollable layouts in UIKit

Easy scrollable layouts in UIKit Create complex scrollable layout using UIViewControllers or plain UIViews and simplify your code! ScrollStackControll

An easy way to add a shimmering effect to any view with just one line of code. It is useful as an unobtrusive loading indicator.
An easy way to add a shimmering effect to any view with just one line of code. It is useful as an unobtrusive loading indicator.

LoadingShimmer An easy way to add a shimmering effect to any view with just single line of code. It is useful as an unobtrusive loading indicator. Thi

Lightweight touch visualization library in Swift. A single line of code and visualize your touches!
Lightweight touch visualization library in Swift. A single line of code and visualize your touches!

TouchVisualizer is a lightweight pure Swift implementation for visualising touches on the screen. Features Works with just a single line of code! Supp

NotSwiftUI is designed to help you create UI components quickly and efficiently with code!
NotSwiftUI is designed to help you create UI components quickly and efficiently with code!

NotSwiftUI NotSwiftUI is designed to help you create UI components quickly and efficiently with code! Capitalizing on the idea that most of the UI ele

Owner
Geri Borbás
iOS / Unity / Design / UI / UX / Geometry. Open for Remote Contracts 💬
Geri Borbás
A set of UIKit helpers that simplify the usage of UIKit view's and controller's in SwiftUI.

A set of UIKit helpers that simplify the usage of UIKit view's and controller's in SwiftUI. Many of these helpers are useful even in a pure UIKit project.

SwiftUI+ 6 Oct 28, 2022
Declarative, configurable & highly reusable UI development as making Lego bricks.

LeeGo is a lightweight Swift framework that helps you decouple & modularise your UI component into small pieces of LEGO style's bricks, to make UI dev

WANG Shengjia 969 Dec 29, 2022
Neumorphism framework for UIKit.

NeumorphismKit is neumorphism framework for UIKit. Requirements iOS 12.0+ Swift 5.1+ Versions NeumorphismKit version Xcode version 1.0.0 Xcode 11+ 1.1

y-okudera 42 Dec 13, 2022
Easily use UIKit views in your SwiftUI applications. Create Xcode Previews for UIView elements

SwiftUIKitView Easily use UIKit views in SwiftUI. Convert UIView to SwiftUI View Create Xcode Previews from UIView elements SwiftUI functional updatin

Antoine van der Lee 682 Dec 29, 2022
Make ComposableArchitecture work with UIKit

ComposableUIKit The ComposableArchitecture (TCA) library provides a way of structuring Swift code with the Redux-pattern. It is highly optimized for S

Manuel Maly 11 Nov 5, 2022
Combine publisher bridges for UIKit

Combine publisher bridges for UIKit

Combine Community 1.3k Jan 1, 2023
Swift extensions for UIKit.framework.

XUIKit Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installation XUIKit is a

FITZ 0 Oct 22, 2021
Convenient domain specific language for writing programmatic UI built over UIKit and more.

XYKit Swifty and convenient domain specific language for creating programmatic UI in a more declarative way and more than that. Built on top of UIKit

Denis Goloborodko 1 Nov 5, 2021
🎸🎸🎸 Common categories for daily development. Such as UIKit, Foundation, QuartzCore, Accelerate, OpenCV and more.

?????? Common categories for daily development. Such as UIKit, Foundation, QuartzCore, Accelerate, OpenCV and more.

77。 423 Jan 4, 2023
Example Catalyst app that is shown in a UIKit popover underneath an NSStatusItem

CatalystStatusItemPopoverExample Example Catalyst app that is shown in a UIKit popover underneath an NSStatusItem. References How to use macOS Specifi

Tom Irving 9 Sep 8, 2022