SkillAgentSearch skills...

UIKitPlus

🏰 Declarative UIKit with LivePreview for iOS9+ (best alternative to SwiftUI)

Install / Use

/learn @MihaelIsaev/UIKitPlus
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <a href="LICENSE"> <img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License"> </a> <a href="https://swift.org"> <img src="https://img.shields.io/badge/swift-5.2-brightgreen.svg" alt="Swift 5.5"> </a> <a href="https://swift.org"> <img src="https://img.shields.io/badge/iOS-9+-brightgreen.svg" alt="Swift 5.5"> </a> <img src="https://img.shields.io/badge/iPadOS+Catalyst-✓-brightgreen.svg" alt="iPadOS and Catalyst support"> <img src="https://img.shields.io/badge/macOS-✓-brightgreen.svg" alt="macOS support"> <a href="https://cocoapods.org/pods/UIKit-Plus"> <img src="https://img.shields.io/cocoapods/v/UIKit-Plus.svg" alt="Cocoapod"> </a> <a href="https://discord.gg/q5wCPYv"> <img src="https://img.shields.io/discord/612561840765141005" alt="Swift.Stream"> </a> </p> <p align="center">🚀❤️ YOU WILL LOVE <b>UIKIT</b> MORE THAN EVER ❤️🚀</p> <br/> <p align="center"><b>Nothing is impossible!</b></p> <p align="center">Build awesome responsive UIs even simpler than with SwiftUI <b>cause you already know everything</b>.</p> <br/> <p align="center">With. Live. Preview. iOS9+.</p> <br/> <p align="center"> <img src="https://user-images.githubusercontent.com/1272610/216769696-65cc09d1-2796-4b7e-b746-01e25fc70486.jpeg"> </p> <br/> <p align="center"><a href="https://github.com/MihaelIsaev/UIKitPlusExample" style="color:green;">EXAMPLES</a></p> <p align="center"><a href="https://discord.gg/q5wCPYv">OUR COMMUNITY IN DISCORD</a></p>

Requirements

Xcode 13.0+

Swift 5.5+

Good mood

Installation

With CocoaPods

Add the following line to your Podfile:

pod 'UIKit-Plus', '~> 2.3.0'

With Swift Package Manager

In Xcode 13.0+ go to File -> Swift Packages -> Add Package Dependency and enter there URL of this repo

https://github.com/MihaelIsaev/UIKitPlus

IMPORTANT!

Since version 2 there are a lot of advantages and fixes, and your project could look cleaner since there are no AppDelegate and SceneDelegate anymore, everything is under the hood like with SwiftUI, but it is very obvious and convenient to use any AppDelegate/SceneDelegate methods.

Check it out by creating a project with the new project template!

IMPORTANT!

To support iOS lower than 13 you have to set -weak_framework SwiftUI in Other Linker Flags in Build Settings.

Without that your app gonna crash on iOS lower than 13 because it will try to load SwiftUI without luck.

<img width="816" alt="Screenshot 2020-03-29 at 03 35 10" src="https://user-images.githubusercontent.com/1272610/77836323-bbd71e00-716e-11ea-88f8-3a6b135b99ec.png">

Project Template! 🍾

To simplify life with UIKitPlus you can download our template!

For that run the following commands in console

git clone https://github.com/MihaelIsaev/UIKitPlus.git
cp -R UIKitPlus/Templates ~/Library/Developer/Xcode/
rm -rf UIKitPlus

After that you will be able to go to File -> New -> Project and choose UIKitPlus app! 🚀

UIKitPlus App Template Screenshot

💡After project creation you have to install UIKitPlus manually either with Swift Package Manager or with CocoaPods

File Template

Together with project template you will get the file template 👍

Features

1. Delayed constraints

Declare all the constraints in advance before adding view to superview. Even by tags.

Button("Click me").width(300).centerInSuperview()

2. Declarativity

Build everything declarative way. Any view. Any control. Even layers, gestures, colors, fonts, etc.

UText("Hello world").color(.red).alignment(.center).font(.sfProMedium, 15)
// or
UText("Hello ".color(.red).font(.sfProMedium, 15), "world".color(.green).font(.sfProBold, 15)).alignment(.center)
// or even
"Hello world".color(.red).alignment(.center).font(.sfProMedium, 15)

3. Reactivity

Use @UState for any property, react on any thing, map states to different types, etc.

@UState var text = "Hello world"
UText($text)

@UState var number = 5
UText($number.map { "\($0)" })

@UState var bool = false
UText($bool.map { $0 ? "enabled" : "disabled" })

4. Purity

Everything is pretty clear. Clean short code without magic.

5. SwiftUI-like but still beloved UIKit

Declare subviews like in SwiftUI (but don't forget that we're still in UIKit and use autolayout)

body {
    View1()
    View2()
    View3()
    // btw it is NOT limited to 10
}

6. Reusable and extendable

Declare views or its styles in extensions. Subclass views. Use all the power of OOP.

7. All modern features

Diffable data-source (yes yes for iOS9+). Dynamic colors for light/dark mode. Stateable animations. Reactivity.

8. Everything and even more

Built-in ImageLoader, no need in huge 3rd party libs. Just set URL to Image. Fully customizable and overridable.

UImage(url: "")
UImage(url: "", defaultImage: UIImage(named: "emptyImage")) // set default image to show it while loading
UImage(url: "", loader: .defaultRelease) // release image before start loading
UImage(url: "", loader: .defaultImmediate) // immediate replace image after loading
UImage(url: "", loader: .defaultFade) // replace image with fade effect after loading
UImage(url: "", loader: ImageLoader()) // subclass from `ImageLoader` and set you custom loader here

Easy device model and type detection and ability to set values based on that.

UButton("Click me").width(400 !! iPhone6(300) !! .iPhone5(200))

Localizable strings

Localization.default = .en // set any localization as default to use it with not covered languages
Localization.current = .en // override current locale
String(.en("Hello"), .fr("Bonjour"), .ru("Привет"))

Custom trait collections.

9. Live Preview

Live preview provided by SwiftUI (available only since macOS Catalina).

The only problem we have is that since names of views are the same in UIKitPlus and SwiftUI we should use aliases like UButton for Button or UView for View, so everything with U prefix. It is only necessary if you want to use live previews, otherwise there is no need to import SwiftUI, so no name conflicts.

Preview single item

💡 You can create as many preview structs as you need

ViewController example

#if canImport(SwiftUI) && DEBUG
import SwiftUI
@available(iOS 13.0, *)
struct MyViewController_Preview: PreviewProvider, DeclarativePreview {
    static var preview: Preview {
        Preview {
            MainViewController()
        }
        .colorScheme(.dark)
        .device(.iPhoneX)
        .language(.fr)
        .rtl(true)
    }
}
#endif

View example

#if canImport(SwiftUI) && DEBUG
import SwiftUI
@available(iOS 13.0, *)
struct MyButton_Preview: PreviewProvider, DeclarativePreview {
    static var preview: Preview {
        Preview {
            UButton(String(.en("Hello"), .fr("Bonjour"), .ru("Привет")))
                .circle()
                .background(.blackHole / .white)
                .color(.white / .black)
                .height(54)
                .edgesToSuperview(h: 8)
                .centerYInSuperview()
        }
        .colorScheme(.dark)
        .layout(.fixed(width: 300, height: 64))
        .language(.fr)
        .rtl(true)
    }
}
#endif

Preview group 🔥

It is just convenient way to create multiple previews inside one struct

Limitations:

  • only 10 previews inside group
  • rtl and language properties can be set only to group, not to previews directly
#if canImport(SwiftUI) && DEBUG
import SwiftUI
@available(iOS 13.0, *)
struct MyPreviewGroup_Preview: PreviewProvider, DeclarativePreviewGroup {
    static var previewGroup: PreviewGroup {
        PreviewGroup { // 1 to 10 previews inside
            Preview {
                MainViewController()
            }
            .colorScheme(.dark)
            .device(.iPhoneX)
            Preview {
                MainViewController()
            }
            .colorScheme(.light)
            .device(.iPhoneX)
            Preview {
                // in this group title will be shown in `fr` language
                UButton(String(.en("Hello"), .fr("Bonjour"), .ru("Привет")))
                    .circle()
                    .background(.blackHole / .white)
                    .color(.white / .black)
                    .height(54)
                    .edgesToSuperview(h: 8)
                    .centerYInSuperview()
            }
            .colorScheme(.dark)
            .layout(.fixed(width: 300, height: 64))
        }
        .language(.fr) // limited to group
        .rtl(true) // limited to group
    }
}
#endif

Usage

import UIKitPlus

Even no need to import UIKit at all!

<details> <summary>Constraints</summary>

Solo

aspectRatio
/// 1:1
UView().aspectRatio()

/// 1:1 low priority
UView().aspectRatio(priority: .defaultLow)

/// 4:3
UView().aspectRatio(4 / 3)

/// 4:3 low priority
UView().aspectRatio(priority: .defaultLow)
width
/// 100pt
UView().width(100)

/// Stateable width
@UState var width: CGFloat = 100

UView().width($width)

/// Stateable but based on different type
@UState var expanded = false

UView().width($expanded.map { $0 ? 200 : 100 })

/// Different value for different devices
/// 80pt for iPhone5, 120pt for any iPad, 100pt for any other devices
UView().width(100 !! .iPhone5(80) !! .iPad(150))
height
/// 100pt
UView().height(100)

/// Stateable width
@UState var height: CGFloat = 100

UView().height($width)

/// Stateable but based on different type
@UState var expanded = false

UView().height($expanded.map { $0 ? 200 : 100 })

/// Different value for different devices
/// 

Related Skills

View on GitHub
GitHub Stars627
CategoryDevelopment
Updated4d ago
Forks37

Languages

Swift

Security Score

100/100

Audited on Apr 2, 2026

No findings