Swiftpack.co - Package - iDara09/BDUIKnit


Swift Package Manager Twitter


BDUIKnit is a collection of SwiftUI custom reusable UI components and extensions packed in a Swift Package. BDUIKnit is completely written in Swift with no dependencies. The package is fully documented with some exceptions of internal objects.


  • To collect my personal custom reusable UI components and extensions and put them in one place.
  • To create custom reusable UI components and share them.
  • To explore, learn new techniques, and share what I learnt building these components & extensions.

Get Started


To add BDUIKnit to your project:

  • Open your project in Xcode
  • Go to File > Swift Packages > Add Package Dependency...
  • Search for BDUIKnit and follow Xcode's installation dialogs.

Quick Introduction

BDUIKnit follows MVVM design pattern; therefore, most Views will have their corresponding View Models. View models are either class or struct, so use the appropriate @ObservedObject, @State, or @Binding as needed.

New to MVVM? Fear not. Try to read the below codes, if you can make sense of what they are doing, you are ready to use BDUIKnit.

// create a view model that controls the tray view
let trayViewModel = BDButtonTrayViewModel()
trayViewModel.mainItem = createTrayMainItem()
trayViewModel.items = createTrayItems()

trayViewModel.expanded = true
trayViewModel.shouldDisableMainItemWhenExpanded = true

trayViewModel.trayColor = Color(.systemBackground)
trayViewModel.itemActiveColor = Color.accentColor

// pass the view model to the tray view to render
BDButtonTrayView(viewModel: trayViewModel)

// while the tray view is displaying, update the view model
trayViewModel.expanded = false

// the tray view is now collapsed



A tray-like view that is normally pinned to the bottom-trailing of a scene.

Tray item now supports more animations.

Regular Vertical Size Class


Compact Vertical Size Class


Tray Item Animations


Quick Start:

// Add tray view to a view when content ignores safe area

struct SomeView: View {

    @State private var trayViewModel = BDButtonTrayViewModel()

    var body: some View {
        ZStack {
                .overlay(trayView, alignment: .bottomTrailing)
        .onAppear(perform: setupOnAppear)

    var trayView: some View {
        BDButtonTrayView(viewModel: trayViewModel).padding(16)

    func setupOnAppear() {
        // setup tray view model...

For sample code, see ButtonTrayViewPreview


A text field view intended to be used as a modal presentation sheet when need to get inputs from user.


Quick Start:

For sample code, see ModalTextFieldPreview


A text view intended to be used as a modal presentation sheet when need to get inputs from user.


Quick Start:

For sample code, see ModalTextViewPreview

BDPersist Property Wrapper

A property wrapper that stores value in a given store. For example, UserDefaults.

Quick Start:

// Store username in UserDefaults

@BDPersist(in: .userDefaults, key: "username", default: "")
var username: String
// Add post notification when username changed

static let nUsernameDidChange = Notification.Name("nUsernameDidChange")

@BDPersist(in: .userDefaults, key: "username", default: "", post: nUsernameDidChange)
var username: String
// Use optional value and NSUbiquitousKeyValueStore
// see docs for how to enable ubiquitous store

@BDPersist(in: .ubiquitousStore, key: "highScore", default: nil)
var highScore: Int?
// Use type-safe key

// create an enum
// conform to BDPersistKey
// implement the required prefix property
enum Keys: BDPersistKey {
    var prefix: String { "some.prefix." }
    case autoplay
    case autosave

// the key is 'some.prefix.autoplay'
@BDPersist(in: .userDefaults, key: Keys.autoplay, default: true)
var autoplay: Bool

// the key is 'some.prefix.autosave'
@BDPersist(in: .userDefaults, key: Keys.autosave, default: false)
var autosave: Bool
// Use custom store

// conform to BDPersistStorable
// implement required methods
class CustomStore: BDPersistStorable {
    // implementations...

@BDPersist(in: .custom(CustomStore()), key: "username", default: "")
var username: String

For sample code, see PersistPropertyWrapperPreview


An object used to present sheet. It provide an easy way to store previous dismissed sheet if needed.

Quick Start

// Example code with Enum conforms to BDPresentationSheetItem

struct UserProfileView: View {

    // conform to BDPresentationSheetItem or Identifiable
    enum Sheet: BDPresentationSheetItem {
        case modalTextField
        case modalTextView

    @State private var sheet = BDPresentationItem<Sheet>()

    var body: some View {
        Form {
            Button("Edit Username") {

            Button("Edut UserBio") {
                self.sheet.current = .modalTextView
        .onAppear(perform: setupOnAppear)
        .sheet(item: $sheet.current, onDismiss: sheetDismissed, content: presentationSheet)

    func setupOnAppear() {
        // if need to access sheet.previous on dismissed
        sheet.shouldStorePrevious = true

    func presentationSheet(for sheet: Sheet) -> some View {
        switch sheet {
        case .modalTextField: return AnyView(...)
        case .modalTextView: return AnyView(...)

    func sheetDismissed() {
        if sheet.previous == .modalTextField {
            // do something

For sample code, see PresentationItemPreview


// Create color from hex

Color(hex: "BDA12A") // a Color

UIColor(hex: "#bda12a") // a UIColor

UIColor(hex: "purple") // fatal error: create color with invalid hex: 'purple'


Stars: 11


Used By

Total: 0


BDUIKnit 1.2.5 - 2020-05-21 20:26:25


  • new properties for BDModalTextFieldModel
    • commitButtonTitle
    • characterLimit
    • characterLimitColor
    • characterLimitWarningColor
  • new property for BDModalTextViewModel
    • commitButtonTitle