SherlockForms - An elegant SwiftUI Form builder to create a searchable Settings and DebugMenu screens for iOS

Related tags

Form SherlockForms
Overview

🕵️‍♂️ SherlockForms

What one man can invent Settings UI, another can discover its field.

-- Sherlock Forms

An elegant SwiftUI Form builder to create a searchable Settings and DebugMenu screens for iOS.

(Supports from iOS 14, except .searchable works from iOS 15)

Overview

Normal Searching Context Menu
UserDefaults App Info Device Info

This repository consists of 3 modules:

  1. SherlockForms: SwiftUI Form builder to enhance cell findability using iOS 15 .searchable.
    • Various form cells to automagically interact with .searchable, including Text, Button, Toggle, Picker, NavigationLink, etc.
    • "Copy text" from context menu by long-press
  2. SherlockDebugForms: Useful app/device info-views and helper methods, specifically for debugging purpose.
    • App Info view
    • Device Info view
    • UserDefaults Viewer
      • TODO: Editor
    • TODO: File Browser
    • TODO: Console Logger
  3. SherlockHUD: Standalone, simple-to-use Notification View (Toast) UI used in SherlockForms

Examples

SherlockForms & SherlockDebugForms

From SherlockForms-Gallery app:

import SwiftUI
import SherlockDebugForms

/// NOTE: Each view that owns `SherlockForm` needs to conform to `SherlockView` protocol.
@MainActor
struct RootView: View, SherlockView
{
    /// NOTE:
    /// `searchText` is required for `SherlockView` protocol.
    /// This is the only requirement to define as `@State`, and pass it to `SherlockForm`.
    @State public var searchText: String = ""

    @AppStorage("username")
    private var username: String = "John Appleseed"

    @AppStorage("language")
    private var languageSelection: Int = 0

    @AppStorage("status")
    private var status = Constant.Status.online

    ... // Many more @AppStorage properties...

    var body: some View
    {
        // NOTE:
        // `SherlockForm` and `xxxCell` are where all the search magic is happening!
        // Just treat `SherlockForm` as a normal `Form`, and use `Section` and plain SwiftUI views accordingly.
        SherlockForm(searchText: $searchText) {

            // Simple form cells.
            Section {
                textCell(title: "User", value: username)
                arrayPickerCell(title: "Language", selection: $languageSelection, values: Constant.languages)
                casePickerCell(title: "Status", selection: $status)
                toggleCell(title: "Low Power Mode", isOn: $isLowPowerOn)

                sliderCell(
                    title: "Speed",
                    value: $speed,
                    in: 0.5 ... 2.0,
                    step: 0.1,
                    maxFractionDigits: 1,
                    valueString: { "x\($0)" },
                    sliderLabel: { EmptyView() },
                    minimumValueLabel: { Image(systemName: "tortoise") },
                    maximumValueLabel: { Image(systemName: "hare") },
                    onEditingChanged: { print("onEditingChanged", $0) }
                )

                stepperCell(
                    title: "Font Size",
                    value: $fontSize,
                    in: 8 ... 24,
                    step: 1,
                    maxFractionDigits: 0,
                    valueString: { "\($0) pt" }
                )
            }

            // Navigation Link Cell (`navigationLinkCell`)
            Section {
                navigationLinkCell(
                    title: "UserDefaults",
                    destination: { UserDefaultsListView() }
                )
                navigationLinkCell(
                    title: "App Info",
                    destination: { AppInfoView() }
                )
                navigationLinkCell(
                    title: "Device Info",
                    destination: { DeviceInfoView() }
                )
                navigationLinkCell(title: "Custom Page", destination: {
                    CustomView()
                })
            }

            // Buttons
            Section {
                buttonCell(
                    title: "Reset UserDefaults",
                    action: {
                        Helper.deleteUserDefaults()
                        showHUD(.init(message: "Finished resetting UserDefaults"))
                    }
                )

                buttonDialogCell(
                    title: "Delete All Contents",
                    dialogTitle: nil,
                    dialogButtons: { completion in
                        Button("Delete All Contents", role: .destructive) {
                            try? Helper.deleteAllFilesAndCaches()
                            showHUD(.init(message: "Finished deleting all contents"))
                            completion()
                        }
                        Button("Cancel", role: .cancel) {
                            print("Cancelled")
                            completion()
                        }
                    }
                )
            }
        }
        .navigationTitle("Settings")
        // NOTE:
        // Use `formCopyable` here to allow ALL `xxxCell`s to be copyable.
        .formCopyable(true)
    }
}

To get started:

  1. Conform your Settings view to protocol SherlockView
  2. Add @State var searchText: String to your view
  3. Inside view's body, use SherlockForm (just like normal Form), and use various built-in form cells:
    • Basic built-in cells
      • textCell
      • buttonCell
      • buttonDialogCell (iOS 15)
      • navigationLinkCell
      • toggleCell
      • arrayPickerCell
      • casePickerCell
      • sliderCell
      • stepperCell
    • More customizable cells (part of ContainerCell)
      • hstackCell
      • vstackCell
  4. (Optional) Attach .formCellCopyable(true) to each cell or entire form.
  5. (Optional) Attach .enableSherlockHUD(true) to topmost view hierarchy to enable HUD

To customize cell's internal content view rather than cell itself, use .formCellContentModifier which may solve some troubles (e.g. context menu) when customizing cells.

SherlockHUD

import SwiftUI
import SherlockHUD

@main
struct MyApp: App
{
    var body: some Scene
    {
        WindowGroup {
            NavigationView {
                RootView()
            }
            .enableSherlockHUD(true) // Set at the topmost view!
        }
    }
}

@MainActor
struct RootView: View
{
    /// Attaching `.enableSherlockHUD(true)` to topmost view will allow using `showHUD`.
    @Environment(\.showHUD)
    private var showHUD: (HUDMessage) -> Void

    var body: some View
    {
        VStack(spacing: 16) {
            Button("Tap") {
                showHUD(HUDMessage(message: "Hello SherlockForms!", duration: 2, alignment: .top))
                // alignment = top / center / bottom (default)
                // Can also attach custom view e.g. ProgressView. See also `HUDMessage.loading`.
            }
        }
        .font(.largeTitle)
    }
}

See SherlockHUD-Demo app for more information.

Acknowledgement

License

MIT

Comments
  • [Forms] Add `SimpleList` & `NestedList`

    [Forms] Add `SimpleList` & `NestedList`

    This PR adds SimpleList & NestedList which is derived from SwiftUI.List for searchable & copyable component.

    See example app for more info.

    Screencast

    https://user-images.githubusercontent.com/138476/181136608-c76a53e2-9bfd-4ff0-895d-455acb8c3820.MP4

    enhancement 
    opened by inamiy 0
  • [DebugForms] Fix UserDefaultsListView in iOS14

    [DebugForms] Fix UserDefaultsListView in iOS14

    This PR fixes UserDefaultsListView in iOS14 where Form's cells did not work as expected due to SwiftUI bug that can't mix Form-inner-view with .sheet.

    To workaround this, .background(EmptyView().sheet(...)) is used for iOS 14 in 4cc1bbf.

    | Before | After | |---|---| | | |

    This PR also adds Examples/SherlockForms-Gallery-iOS14.xcodeproj for iOS 14 simulator run in 072ffc9, where original .swiftpm example didn't run well due to the following error:

    dyld: can't resolve symbol _$sScMMa in /Users/xxx/Library/Developer/CoreSimulator/Devices/3A8F81A3-C365-40A8-98B4-1AC752313AE1/data/Containers/Bundle/Application/B4A105C3-3B65-45D2-92CD-CEAC1B4D3593/SherlockForms-Gallery.app/SherlockForms-Gallery
    
    because dependent dylib @rpath/libswift_Concurrency.dylib could not be loaded
    
    bug 
    opened by inamiy 0
  • [Forms] Add `FormCellIconWidthEnvironmentKey`

    [Forms] Add `FormCellIconWidthEnvironmentKey`

    This PR improves form-cell's icon & text alignment by adding FormCellIconWidthEnvironmentKey.

    Screenshot

    | Before | After | |---|---| | | |

    enhancement 
    opened by inamiy 0
  • [Forms] Improve `arrayPickerCell`'s `selection` by using `Hashable` value rather than `Int`

    [Forms] Improve `arrayPickerCell`'s `selection` by using `Hashable` value rather than `Int`

    Note: Breaking change.

    This PR improves arrayPickerCell's selection by using Hashable value rather than Int.

    To work as existing Int-based approach, developer will need mutual Binding conversions between Int and Value as shown in this PR's example.

    opened by inamiy 0
  • [Forms] Add `AsyncArrayPickerCell`

    [Forms] Add `AsyncArrayPickerCell`

    This PR adds AsyncArrayPickerCell for async-loading and then shows arrayPickerCell.

    https://user-images.githubusercontent.com/138476/154999950-79b28206-6c9c-41b1-b732-1d60e56b11e8.mp4

    Known issue

    When this cell appears at middle of the form after scroll, and accessory contains ProgressView, it may not animate correctly, possibly due to SwiftUI bug.

    enhancement 
    opened by inamiy 0
  • [DebugForms] Refactor `UserDefaultsListView` to allow editing

    [DebugForms] Refactor `UserDefaultsListView` to allow editing

    This PR refactors DebugForms's UserDefaultsListView to allow value-editing.

    Known issue

    • After presenting DatePicker's popup, showDetail doesn't work properly (possibly due to SwiftUI bug).
    enhancement 
    opened by inamiy 0
  • [Forms] Allow empty title for `textFieldCell` & `textEditorCell`

    [Forms] Allow empty title for `textFieldCell` & `textEditorCell`

    Improvement for:

    • #5
    • #7

    This PR allows empty title for textFieldCell & textEditorCell to use full-width text input. By this change, context-menu's "Copy" functionality needs some fixes, and also resolved in this PR.

    opened by inamiy 0
  • [Forms] Add `touchesEnd` override to hide keyboard

    [Forms] Add `touchesEnd` override to hide keyboard

    This PR adds a hack to override touchesEnd to hide currently presenting keyboard.

    This hacky approach is discussed in: https://developer.apple.com/forums/thread/127196

    which works nicer compared to tapGesture on top of form as discussed in: https://stackoverflow.com/questions/56491386/how-to-hide-keyboard-when-using-swiftui

    enhancement 
    opened by inamiy 0
  • [DebugForm] Support hierarchical search results

    [DebugForm] Support hierarchical search results

    This PR decouples SherlockDebugForm's:

    • UserDefaultsListView / AppInfoView / DeviceInfoView that holds @State var searchText

    into:

    • UserDefaultsSectionsListView / AppInfoSectionsView / DeviceInfoSectionsView with @Binding var searchText

    so that sections can be reused for showing search results in its ancestor view, and allows hierarchical search. (This behavior is similar to how iOS's Settings.app handles hierarchical searching)

    Screenshot

    enhancement 
    opened by inamiy 0
  • [Forms] Refactor `buttonCell` & `buttonDialogCell` to allow async action & cancellation

    [Forms] Refactor `buttonCell` & `buttonDialogCell` to allow async action & cancellation

    This PR supports async-action for buttonCell & buttonDialogCell with introducing ButtonDialogCell.DialogButton as a breaking change.

    Loading indicator will be prompted during async action, and can be cancelled by tapping it.

    Screencast

    https://user-images.githubusercontent.com/138476/154058323-cc29ccf1-4166-481d-9cfc-19bbeb1b5f4a.mp4

    enhancement 
    opened by inamiy 0
Releases(0.2.1)
  • 0.2.1(Jul 27, 2022)

    For Swift 5.6, Xcode 13.4.1.

    Milestone: https://github.com/inamiy/SherlockForms/milestone/3?closed=1

    What's Changed

    • [Forms] Add @ViewBuilder in List components by @inamiy in https://github.com/inamiy/SherlockForms/pull/18

    Full Changelog: https://github.com/inamiy/SherlockForms/compare/0.2.0...0.2.1

    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Jul 27, 2022)

    For Swift 5.6, Xcode 13.4.1.

    Milestone: https://github.com/inamiy/SherlockForms/milestone/2?closed=1

    What's Changed

    • [DebugForms] Add Helper.setAnimationSpeed by @inamiy in https://github.com/inamiy/SherlockForms/pull/16
    • [Forms] Add SimpleList & NestedList by @inamiy in https://github.com/inamiy/SherlockForms/pull/17

    Full Changelog: https://github.com/inamiy/SherlockForms/compare/0.1.0...0.2.0

    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Mar 10, 2022)

Owner
Yasuhiro Inami
Functional Programmer at @delyjp / KURASHIRU / クラシル. Interests: Swift / Haskell / PureScript / Elm / Rust / TypeScript / Category Theory
Yasuhiro Inami
Custom Field component with validation for creating easier form-like UI from interface builder.

#YALField Custom Field component with validation for creating easier form-like UI from interface builder. ##Example Project To run the example project

Yalantis 476 Sep 1, 2022
The most flexible and powerful way to build a form on iOS

The most flexible and powerful way to build a form on iOS. Form came out from our need to have a form that could share logic between our iOS apps and

HyperRedink 32 Aug 15, 2022
Declarative form building framework for iOS

Formalist Swift framework for building forms on iOS Formalist is a Swift framework for building forms on iOS using a simple, declarative, and readable

Seed 159 May 25, 2022
iOS validation framework with form validation support

ATGValidator ATGValidator is a validation framework written to address most common issues faced while verifying user input data. You can use it to val

null 51 Oct 19, 2022
APValidators - Codeless solution for form validation in iOS!

APValidators is a codeless solution for form validation. Just connect everything right in Interface Builder and you're done. Supports really complex and extendable forms by allowing to connect validators in tree.

Alty 131 Aug 16, 2022
Former is a fully customizable Swift library for easy creating UITableView based form.

Former is a fully customizable Swift library for easy creating UITableView based form. Submitting Issues Click HERE to get started with filing a bug r

Ryo Aoyama 1.3k Dec 27, 2022
ObjectForm - a simple yet powerful library to build form for your class models.

ObjectForm A simple yet powerful library to build form for your class models. Motivations I found most form libraries for swift are too complicated to

jakehao 175 Nov 2, 2022
Some cells to Form a Pod

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

null 0 Nov 2, 2021
XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift & Obj-C.

XLForm By XMARTLABS. If you are working in Swift then you should have a look at Eureka, a complete re-design of XLForm in Swift and with more features

xmartlabs 5.8k Jan 6, 2023
SwiftForms is a small and lightweight library written in Swift that allows you to easily create forms.

SwiftForms is a powerful and extremely flexible library written in Swift that allows to create forms by just defining them in a couple of lines. It also provides the ability to customize cells appearance, use custom cells and define your own selector controllers.

Miguel Ángel Ortuño 1.3k Dec 27, 2022
Discover new programming concepts and more new SwiftUI 2 features in this section

Africa-Zoo One thing is sure, we will discover new programming concepts and more new SwiftUI 2 features in this section. TOPICS ARE COVERED: JSON with

Noye Samuel 2 Nov 8, 2022
Carbon🚴 A declarative library for building component-based user interfaces in UITableView and UICollectionView.

A declarative library for building component-based user interfaces in UITableView and UICollectionView. Declarative Component-Based Non-Destructive Pr

Ryo Aoyama 1.2k Jan 5, 2023
A framework to validate inputs of text fields and text views in a convenient way.

FormValidatorSwift The FormValidatorSwift framework allows you to validate inputs of text fields and text views in a convenient way. It has been devel

ustwo™ 500 Nov 29, 2022
SwiftyFORM is a lightweight iOS framework for creating forms

SwiftyFORM is a lightweight iOS framework for creating forms Because form code is hard to write, hard to read, hard to reason about. Has a

Simon Strandgaard 1.1k Dec 29, 2022
Boring-example - Using boring crate from iOS application

BoringSSL example Using boring crate from iOS application. Checkout git clone gi

Alexei Lozovsky 0 Dec 31, 2021
iOS Validation Library

Honour Validation library for iOS inspired by Respect/Validation. Validator.mustBe(Uppercase()).andMust(StartsWith("F")).validate("FOOBAR") ❗ If you w

Jean Pimentel 55 Jun 3, 2021
GrouponHeader - iOS TableView Header Animation, Swift/UIKit

GrouponHeader Description: iOS TableView Header Animation Technology: Swift, UIK

James Sedlacek 8 Dec 15, 2022
Meet CRRulerControl - Customizable Control for iOS

Customizable component, created by Cleveroad iOS developers, is aimed at turning a simple ruler into a handy and smart instrument

Cleveroad 112 Oct 2, 2022
Elegant iOS form builder in Swift

Made with ❤️ by XMARTLABS. This is the re-creation of XLForm in Swift. 简体中文 Overview Contents Requirements Usage How to create a Form Getting row valu

xmartlabs 11.6k Jan 1, 2023
A light-weight, extensible package for building pixel-perfect iOS settings screens.

SettingsKit A light-weight, extensible package for easily building pixel-perfect iOS settings screens in a pinch. Installation SettingsKit can be inst

Seb Vidal 76 Nov 14, 2022