Make ComposableArchitecture work with UIKit

Related tags

UI ComposableUIKit
Overview

ComposableUIKit

The ComposableArchitecture (TCA) library provides a way of structuring Swift code with the Redux-pattern. It is highly optimized for SwiftUI, and works really well there. But UIKit is still highly relevant, and, as-is, it does not really lend itself to the Redux pattern. UIKit is not declarative like SwiftUI, but imperative.

This Swift package provides tools that enable you to build a bridge between TCA and UIKit. Right now, UINavigationController is supported.

Usage

UINavigationController

Provided you already have setup TCA with a State (e.g. UnauthenticatedState) and an Action (e.g. UnauthenticatedAction), you will need to add dismiss cases to your Action (e.g. dismissLogin) which sets the corresponding substate to nil (e.g. state.login = nil).

Create an enum (e.g. UnauthenticatedNavSegmentID), with one case for each sub-state (=segment) you want to display. It's best to have one segment for every sub-reducer.

Create a function that makes a UINavigationController, and an extension for the segment id enum like this:

import ComposableArchitecture
import ComposableUIKit

func makeNavigationController(
    store: Store
   
) 
   -> UINavigationController {
    
   UINavigationController()
        .
   composable
        .
   bind(
            
   store: unauthenticatedStore,
            
   makeRootSegment: { unauthenticatedStore 
   in
                
   makeUnauthenticatedRootSegment(
                    
   store: unauthenticatedStore,
                    
   dependencies: dependencies.
   decorated
                )
            },
            
   makeDismissAction: \.
   dismissAction
        )
}


   extension 
   UnauthenticatedNavSegmentID {
    
   var dismissAction: UnauthenticatedAction
   ? {
        
   switch 
   self {
        
   case .
   root
   :
            
   return 
   nil
        
   case .
   login
   :
            
   return .
   dismissLogin
        
   case .
   registration
   :
            
   return .
   dismissRegistration
        
   case .
   resetPassword
   :
            
   return .
   login(.
   dismissResetPassword)
        }
    }
}
  

The resulting UINavigationController can be used however you like, like in a modal or in a UITabbar. Just make sure to not call any methods on the navigation controller that change its view controller hierarchy - all manipulation must be done via the TCA state and the binding.

The binding on the navigation controller is active as long as the navigation controller exists, or until navigationController.composable.bindingLifetime.cancel() is called, or until .composable.bind(...) is called again.

The root segment could look like this:

private func makeUnauthenticatedRootSegment(
    store: Store
   
) 
   -> NavSegment
   
     {
    
    NavSegment(
        
    id: .
    root,
        
    store: store,
        
    viewControllers: { store 
    in
            NavSegment.
    ViewController(
                
    store: store,
                
    make: UnauthenticatedRootViewController.
    init(
    viewStore:)
            )
        },
        
    nextSegment: { store 
    in
            NavSegment.
    Next(
                
    store: store,
                
    toLocalState: \.
    login,
                
    fromLocalAction: UnauthenticatedAction.
    login,
                
    make: makeLoginSegment
            )
            NavSegment.
    Next(
                
    store: store,
                
    toLocalState: \.
    registration,
                
    fromLocalAction: UnauthenticatedAction.
    registration,
                
    make: makeRegistrationSegment
            )
        }
    )
}

   
  

As you can see, multiple NavSegment.Next elements can be added to each segment, even though only one of them can be active. If both are active (because .login and registration are both non-nil), then only the first (login) will be used.

The number of displayed viewControllers (NavSegment.ViewController) is not restricted, so each TCA sub-state can display as many view controllers as necessary.

NavSegment.ViewController.init() lets you either use the current TCA state level (e.g. UnauthenticatedState), or a sub-state. Additionally, a showIf parameter lets you conditionally add/remove view controllers depending on the TCA state.

NavSegments can be arbitrarily deeply stacked, and they are created only if their parent segment is active. When the user dismisses a view controller (e.g. via back button, back swipe, or long-press on back button), then .dismissAction will be used to notify the TCA Store, starting with the deepest-nested sub-reducer.

Contact

🐦 Contact me via Twitter @manuelmaly

0.1.0

  • Added UINavigationController bridge (UINavigationController.composable.bind(...))
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

πŸ“ Declarative UIKit in 10 lines of code.
πŸ“ Declarative UIKit in 10 lines of code.

Withable πŸ“ Declarative UIKit in 10 lines of code. See corresponding article at Declarative UIKit with 10 lines of code A simple extension instead of

🧩 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

Navigation helpers for SwiftUI applications build with ComposableArchitecture
Navigation helpers for SwiftUI applications build with ComposableArchitecture

Swift Composable Presentation πŸ“ Description Navigation helpers for SwiftUI applications build with ComposableArchitecture. More info about the concep

This app is a sample app that recognizes specific voice commands such as
This app is a sample app that recognizes specific voice commands such as "make it red", "make it blue", "make it green", and "make it black" and change the background color of the view in the frame.

VoiceOperationSample This app is a sample app that recognizes specific voice commands such as "make it red", "make it blue", "make it green", and "mak

A script for focusing on work, blocking non-work stuff.

A script for focusing on work, blocking non-work stuff. The idea is to forbid mindless app/website context-switching while you're focused. Once you're

Work-hours-mac - Simple app that tracks your work hours from the status bar
Work-hours-mac - Simple app that tracks your work hours from the status bar

Track Your Work Hours Simple app that tracks your work hours from status bar. Fe

GraphQLite is a toolkit to work with GraphQL servers easily. It also provides several other features to make life easier during iOS application development.
GraphQLite is a toolkit to work with GraphQL servers easily. It also provides several other features to make life easier during iOS application development.

What is this? GraphQLite is a toolkit to work with GraphQL servers easily. It also provides several other features to make life easier during iOS appl

DGPreview - Make UIKit project enable preview feature of SwiftUI
DGPreview - Make UIKit project enable preview feature of SwiftUI

DGPreview Make UIKit project enable preview feature of SwiftUI Requirements iOS

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.

Controls-Practice-UIKit- - Controls Practice (UIKit)
Controls-Practice-UIKit- - Controls Practice (UIKit)

Controls Practice (UIKit) Change a number 0 to 255 different ways: Button (+1) I

RSA public/private key encryption, private key signing and public key verification in Swift using the Swift Package Manager. Works on iOS, macOS, and Linux (work in progress).

BlueRSA Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux

A Github's like work contribution timeline. 🀸🏻
A Github's like work contribution timeline. 🀸🏻

Workaholic 🀸🏻 A Github's like work contribution timeline. ToDo[s] Remove Dependancies of 3rd Party classes. You can watch to Workaholic to see conti

How Swift standard types and classes were supposed to work.
How Swift standard types and classes were supposed to work.

How Swift standard types and classes were supposed to work. A collection of useful extensions for the Swift Standard Library, Foundation, and UIKit.

Fully-wrapped UITextField made to work entirely in SwiftUI
Fully-wrapped UITextField made to work entirely in SwiftUI

iTextField ⌨️ A fully-wrapped `UITextField` that works entirely in SwiftUI. πŸ¦… Get Started | Examples | Customize | Install | Get Started Install iTex

Owner
Manuel Maly
Manuel Maly
Work in progress gallery of controls available to Catalyst apps using Optimized for Mac

Catalyst Controls Gallery Very simple work-in-progress demonstration of many common controls available to Mac Catalyst as of macOS 11. Provided moreso

Steven Troughton-Smith 163 Sep 18, 2022
A way to quickly add a notification badge icon to any view. Make any view of a full-fledged animated notification center.

BadgeHub A way to quickly add a notification badge icon to any view. Demo/Example For demo: $ pod try BadgeHub To run the example project, clone the r

Jogendra 772 Dec 28, 2022
Twinkle is a Swift and easy way to make any UIView in your iOS or tvOS app twinkle.

Twinkle ✨ Twinkle is a Swift and easy way to make any UIView in your iOS or tvOS app twinkle. This library creates several CAEmitterLayers and animate

patrick piemonte 600 Nov 24, 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
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