diff --git a/.github/fastlane/Fastfile b/.github/fastlane/Fastfile index ab7b7e622..2f27c8b86 100644 --- a/.github/fastlane/Fastfile +++ b/.github/fastlane/Fastfile @@ -26,8 +26,8 @@ platform :ios do destination: [ 'platform=iOS Simulator,name=iPhone 8', 'platform=tvOS Simulator,name=Apple TV', - 'platform=watchOS Simulator,name=Apple Watch Series 5 - 44mm', - 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (4th generation)', + 'platform=watchOS Simulator,name=Apple Watch Series 8 (45mm)', + 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (5th generation)', 'platform=macOS', 'platform=macOS,variant=Mac Catalyst', ] @@ -44,8 +44,8 @@ platform :ios do destination: [ 'platform=iOS Simulator,name=iPhone 8', 'platform=tvOS Simulator,name=Apple TV', - 'platform=watchOS Simulator,name=Apple Watch Series 5 - 44mm', - 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (4th generation)', + 'platform=watchOS Simulator,name=Apple Watch Series 8 (45mm)', + 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (5th generation)', 'platform=macOS', 'platform=macOS,variant=Mac Catalyst', ] @@ -55,7 +55,7 @@ platform :ios do destination: [ 'platform=iOS Simulator,name=iPhone 8', 'platform=tvOS Simulator,name=Apple TV', - 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (4th generation)', + 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (5th generation)', 'platform=macOS,variant=Mac Catalyst', ] ) @@ -64,8 +64,8 @@ platform :ios do destination: [ 'platform=iOS Simulator,name=iPhone 8', 'platform=tvOS Simulator,name=Apple TV', - 'platform=watchOS Simulator,name=Apple Watch Series 5 - 44mm', - 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (4th generation)', + 'platform=watchOS Simulator,name=Apple Watch Series 8 (45mm)', + 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (5th generation)', 'platform=macOS', 'platform=macOS,variant=Mac Catalyst', ] @@ -75,10 +75,10 @@ platform :ios do destination: [ 'platform=iOS Simulator,name=iPhone 8', 'platform=tvOS Simulator,name=Apple TV', - 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (4th generation)', + 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (5th generation)', 'platform=macOS', 'platform=macOS,variant=Mac Catalyst', - 'platform=watchOS Simulator,name=Apple Watch Series 5 - 44mm', + 'platform=watchOS Simulator,name=Apple Watch Series 8 (45mm)', ] ) end diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 23b9613a7..d4b3da57f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -7,10 +7,10 @@ on: jobs: test: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Run TESTS @@ -31,10 +31,10 @@ jobs: retention-days: 90 platform_test: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Run Multiplatform TESTS @@ -42,10 +42,10 @@ jobs: working-directory: ${{ env.working-directory }} cli_test: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer GIT_SSH_COMMAND: "ssh -o StrictHostKeyChecking=no" steps: - uses: actions/checkout@v2 @@ -55,10 +55,10 @@ jobs: working-directory: ${{ env.working-directory }} build_for_swift_package_manager: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Validate SwiftPM BUILDs @@ -66,10 +66,10 @@ jobs: working-directory: ${{ env.working-directory }} build_for_cocoapods: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Validate Cocoapods Can Deploy (lib lint) @@ -77,10 +77,10 @@ jobs: working-directory: ${{ env.working-directory }} lint: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Swiftlint diff --git a/.github/workflows/PR_CI.yml b/.github/workflows/PR_CI.yml index eefeef1ff..42d1eedf2 100644 --- a/.github/workflows/PR_CI.yml +++ b/.github/workflows/PR_CI.yml @@ -4,10 +4,10 @@ on: [ pull_request, workflow_dispatch ] jobs: test: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Run TESTS @@ -28,10 +28,10 @@ jobs: retention-days: 90 platform_test: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Run Multiplatform TESTS @@ -39,10 +39,10 @@ jobs: working-directory: ${{ env.working-directory }} cli_test: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer GIT_SSH_COMMAND: "ssh -o StrictHostKeyChecking=no" steps: - uses: actions/checkout@v2 @@ -52,10 +52,10 @@ jobs: working-directory: ${{ env.working-directory }} build_for_swift_package_manager: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Validate SwiftPM BUILDs @@ -63,10 +63,10 @@ jobs: working-directory: ${{ env.working-directory }} build_for_cocoapods: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Validate Cocoapods Can Deploy (lib lint) @@ -74,10 +74,10 @@ jobs: working-directory: ${{ env.working-directory }} lint: - runs-on: macos-11 + runs-on: macos-12 env: working-directory: .github - DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.0.app/Contents/Developer steps: - uses: actions/checkout@v2 - name: Swiftlint diff --git a/ExampleApps/SwiftUIExample/Views/ContentView.swift b/ExampleApps/SwiftUIExample/Views/ContentView.swift index 2d2b48ec3..85b141135 100644 --- a/ExampleApps/SwiftUIExample/Views/ContentView.swift +++ b/ExampleApps/SwiftUIExample/Views/ContentView.swift @@ -21,7 +21,7 @@ struct ContentView: View, FlowRepresentable { @State var selectedTab: Tab = .map weak var _workflowPointer: AnyFlowRepresentable? var body: some View { - TabView(selection: $selectedTab) { // swiftlint:disable:this closure_body_length + TabView(selection: $selectedTab) { // NOTE: Using constant here guarantees the workflow cannot abandon, it stays launched forever. WorkflowView { WorkflowItem(MapFeatureOnboardingView.self) diff --git a/Sources/SwiftCurrent/Protocols/PassthroughFlowRepresentable.swift b/Sources/SwiftCurrent/Protocols/PassthroughFlowRepresentable.swift index 7605066b6..e1befaad7 100644 --- a/Sources/SwiftCurrent/Protocols/PassthroughFlowRepresentable.swift +++ b/Sources/SwiftCurrent/Protocols/PassthroughFlowRepresentable.swift @@ -7,7 +7,7 @@ // // swiftlint:disable:next missing_docs -public protocol _PassthroughIdentifiable { } +public protocol _PassthroughIdentifiable { } // swiftlint:disable:this type_name /// A `FlowRepresentable` that automatically captures data from the `Workflow` and passes it forward. public protocol PassthroughFlowRepresentable: FlowRepresentable, _PassthroughIdentifiable where WorkflowInput == AnyWorkflow.PassedArgs, WorkflowOutput == AnyWorkflow.PassedArgs { } diff --git a/Sources/SwiftCurrent_SwiftUI/Models/WorkflowViewModel.swift b/Sources/SwiftCurrent_SwiftUI/Models/WorkflowViewModel.swift index 868e4802e..a41274353 100644 --- a/Sources/SwiftCurrent_SwiftUI/Models/WorkflowViewModel.swift +++ b/Sources/SwiftCurrent_SwiftUI/Models/WorkflowViewModel.swift @@ -30,7 +30,10 @@ final class WorkflowViewModel: ObservableObject { extension WorkflowViewModel: OrchestrationResponder { func launch(to destination: AnyWorkflow.Element) { onFinishPublisher.send(nil) // We launched, so make sure nobody thinks we finished yet. - body = destination + DispatchQueue.main.async { [weak self] in + // Probably makes previews require running, but it's better than publishing during the view lifecycle + self?.body = destination + } } func proceed(to destination: AnyWorkflow.Element, from source: AnyWorkflow.Element) { diff --git a/Sources/SwiftCurrent_SwiftUI/Protocols/_WorkflowItemProtocol.swift b/Sources/SwiftCurrent_SwiftUI/Protocols/_WorkflowItemProtocol.swift index 9e749a4b4..8f09cb6dd 100644 --- a/Sources/SwiftCurrent_SwiftUI/Protocols/_WorkflowItemProtocol.swift +++ b/Sources/SwiftCurrent_SwiftUI/Protocols/_WorkflowItemProtocol.swift @@ -11,7 +11,7 @@ import SwiftCurrent /// :nodoc: Protocol is forced to be public, but it is an internal protocol. @available(iOS 14.0, macOS 11, tvOS 14.0, watchOS 7.0, *) -public protocol _WorkflowItemProtocol: View where FlowRepresentableType: FlowRepresentable & View { +public protocol _WorkflowItemProtocol: View where FlowRepresentableType: FlowRepresentable & View { // swiftlint:disable:this type_name associatedtype FlowRepresentableType var workflowLaunchStyle: LaunchStyle.SwiftUI.PresentationType { get } diff --git a/Sources/SwiftCurrent_SwiftUI/ViewModifiers/NavigationWrapper.swift b/Sources/SwiftCurrent_SwiftUI/ViewModifiers/NavigationWrapper.swift index 63c6c85f5..840a424fe 100644 --- a/Sources/SwiftCurrent_SwiftUI/ViewModifiers/NavigationWrapper.swift +++ b/Sources/SwiftCurrent_SwiftUI/ViewModifiers/NavigationWrapper.swift @@ -13,11 +13,15 @@ extension View { // PROBLEM: SwiftUI has a bug if isActive is defaulted to true on NavLinks. // See details here: https://stackoverflow.com/questions/68365774/nested-navigationlinks-with-isactive-true-are-not-displaying-correctly func navLink(to destination: D, isActive: Binding) -> some View { - background( - List { - NavigationLink(destination: destination, - isActive: isActive) { EmptyView() } - }.opacity(0.01) - ) + if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) { + return navigationDestination(isPresented: isActive) { destination } + } else { + return background( + List { + NavigationLink(destination: destination, + isActive: isActive) { EmptyView() } + }.opacity(0.01) + ) + } } } diff --git a/Sources/SwiftCurrent_SwiftUI/Views/WorkflowLauncher.swift b/Sources/SwiftCurrent_SwiftUI/Views/WorkflowLauncher.swift index 9790b1fe9..9ae0884de 100644 --- a/Sources/SwiftCurrent_SwiftUI/Views/WorkflowLauncher.swift +++ b/Sources/SwiftCurrent_SwiftUI/Views/WorkflowLauncher.swift @@ -31,9 +31,15 @@ public struct WorkflowLauncher: View { ViewBuilder { if isLaunched { if shouldEmbedInNavView { - NavigationView { - workflowContent - }.preferredNavigationStyle() + if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) { + NavigationStack { + workflowContent + } + } else { + NavigationView { + workflowContent + }.preferredNavigationStyle() + } } else { workflowContent } diff --git a/Sources/SwiftCurrent_Testing/MockOrchestrationResponder.swift b/Sources/SwiftCurrent_Testing/MockOrchestrationResponder.swift index 8c2a98793..2b7702b16 100644 --- a/Sources/SwiftCurrent_Testing/MockOrchestrationResponder.swift +++ b/Sources/SwiftCurrent_Testing/MockOrchestrationResponder.swift @@ -45,7 +45,7 @@ open class MockOrchestrationResponder: OrchestrationResponder { open var abandonCalled = 0 open var lastWorkflow: AnyWorkflow? - open var lastOnFinish:(() -> Void)? + open var lastOnFinish: (() -> Void)? open func abandon(_ workflow: AnyWorkflow, onFinish: (() -> Void)?) { lastWorkflow = workflow lastOnFinish = onFinish diff --git a/Sources/SwiftCurrent_UIKit/Extensions/UIViewControllerAdditions.swift b/Sources/SwiftCurrent_UIKit/Extensions/UIViewControllerAdditions.swift index d8215d587..ebd200b76 100644 --- a/Sources/SwiftCurrent_UIKit/Extensions/UIViewControllerAdditions.swift +++ b/Sources/SwiftCurrent_UIKit/Extensions/UIViewControllerAdditions.swift @@ -88,7 +88,7 @@ extension FlowRepresentable where Self: UIViewController { - Parameter onFinish: a callback after the workflow has been abandoned. - Note: In order to dismiss UIKit views, the workflow must have an `OrchestrationResponder` that is a `UIKitPresenter`. */ - public func abandonWorkflow(animated: Bool = true, onFinish:(() -> Void)? = nil) { + public func abandonWorkflow(animated: Bool = true, onFinish: (() -> Void)? = nil) { workflow?.abandon(animated: animated, onFinish: onFinish) } } diff --git a/Sources/SwiftCurrent_UIKit/Extensions/WorkflowUIKitAdditions.swift b/Sources/SwiftCurrent_UIKit/Extensions/WorkflowUIKitAdditions.swift index df1dff32f..e7a2a1e3c 100644 --- a/Sources/SwiftCurrent_UIKit/Extensions/WorkflowUIKitAdditions.swift +++ b/Sources/SwiftCurrent_UIKit/Extensions/WorkflowUIKitAdditions.swift @@ -18,7 +18,7 @@ extension Workflow { - Parameter onFinish: a callback after the workflow has been abandoned. - Important: In order to dismiss UIKit views the workflow must have an `OrchestrationResponder` that is a `UIKitPresenter`. */ - public func abandon(animated: Bool = true, onFinish:(() -> Void)? = nil) { + public func abandon(animated: Bool = true, onFinish: (() -> Void)? = nil) { AnyWorkflow(self).abandon(animated: animated, onFinish: onFinish) } } @@ -30,7 +30,7 @@ extension AnyWorkflow { - Parameter onFinish: a callback after the workflow has been abandoned. - Important: In order to dismiss UIKit views the workflow must have an `OrchestrationResponder` that is a `UIKitPresenter`. */ - public func abandon(animated: Bool = true, onFinish:(() -> Void)? = nil) { + public func abandon(animated: Bool = true, onFinish: (() -> Void)? = nil) { if let presenter = orchestrationResponder as? UIKitPresenter { presenter.abandon(self, animated: animated) { [self] in self._abandon()