From af9d77e0701eb618491bf32c79fac0385fcc2bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Fri, 29 Apr 2022 14:31:37 +0200 Subject: [PATCH 1/7] Link to blog ... --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b82f9ff..3ffe356 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,15 @@ ViewController's for SwiftUI. -WIP. - The core idea is that the `ViewController` is owning, or at least driving, the View(s). Not the other way around. +Blog entry explaining all the things: +[Model View Controller for SwiftUI](http://www.alwaysrightinstitute.com/viewcontroller/) -## How to Use +## Quick: How to Use -More details will be posted but to get started. +Just the basic to get started quickly. ### Step A: Setup Project and Root VC From ef4e85a3e8a827d542288c25a1355440852cbb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Fri, 29 Apr 2022 14:59:30 +0200 Subject: [PATCH 2/7] Typo ... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ffe356..b68baf1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Blog entry explaining all the things: ## Quick: How to Use -Just the basic to get started quickly. +Just the basics to get started quickly. ### Step A: Setup Project and Root VC From ffbf9ed32f8736995da1d7a786be92692c97515a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 30 Apr 2022 14:42:21 +0200 Subject: [PATCH 3/7] Tie the `PushLink` destination to the specific VC Previously this was just checking the VC class, which could match for multiple links. --- .../NavigationLink/PushLink.swift | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Sources/ViewController/NavigationLink/PushLink.swift b/Sources/ViewController/NavigationLink/PushLink.swift index 977c2b7..6571eab 100644 --- a/Sources/ViewController/NavigationLink/PushLink.swift +++ b/Sources/ViewController/NavigationLink/PushLink.swift @@ -171,16 +171,29 @@ public struct PushLink: View } @ViewBuilder private var destination: some View { - if let presentedVC = parentViewController - .presentedViewController(of: VC.self, mode: mode) - { - contentView - .controlled(by: presentedVC) - .environment(\.viewControllerPresentationMode, .navigation) - .navigationTitle(presentedVC.navigationTitle) + if let activeVC = childViewController { + if let presentedVC = activeVC as? VC { + if let presentation = + parentViewController.activePresentation(for: presentedVC), + presentation.mode == mode + { + contentView + .controlled(by: presentedVC) + .environment(\.viewControllerPresentationMode, .navigation) + .navigationTitle(presentedVC.navigationTitle) + } + else { + SwiftUI.Label("Error: The linked VC is not being presented as a link", + systemImage: "exclamationmark.triangle") + } + } + else { + SwiftUI.Label("Error: The linked VC has an unexpected type!", + systemImage: "exclamationmark.triangle") + } } else { - SwiftUI.Label("Error: Missing/wrong presented VC", + SwiftUI.Label("Linked VC is not yet being presented.", systemImage: "exclamationmark.triangle") } } From 5034037a3c3d29e2345d5f995077dba1e160d3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 30 Apr 2022 14:43:33 +0200 Subject: [PATCH 4/7] Add more documentation, comments Minor API change: - `presentedViewController(of:mode:)` drops the `of` label --- .../NavigationLink/PushLink.swift | 30 +++++ .../Presentations/AutoPresentation.swift | 4 +- .../Presentations/Presentation.swift | 107 +++++++++++++----- .../Presentations/PresentationMode.swift | 10 +- Sources/ViewController/ViewController.swift | 36 ++++++ 5 files changed, 151 insertions(+), 36 deletions(-) diff --git a/Sources/ViewController/NavigationLink/PushLink.swift b/Sources/ViewController/NavigationLink/PushLink.swift index 6571eab..8795e11 100644 --- a/Sources/ViewController/NavigationLink/PushLink.swift +++ b/Sources/ViewController/NavigationLink/PushLink.swift @@ -158,6 +158,28 @@ public struct PushLink: View logger.debug("PushLink[dismiss]: done: \(activeVC.description)") } + /** + * Returns a `Binding` that handles presentation and dismiss of an + * associated ``ViewController``. + * + * That is: + * - It returns `true` if the link has an associated ``ViewController`` + * that is being presented. + * - If it is set to `true`: + * - If a ``ViewController`` is already presented by this link, it stays + * presented. + * - If no ``ViewController`` is being presented yet, this will construct + * and present a new associated ``ViewController`` + * - If it is set to `false` (i.e. the user navigated away from the + * destination), the link's ViewController gets dismissed. + * + * Note: When switching between two `NavigationLink`s, SwiftUI can set the + * Binding for the new controller to `true`, before the Binding of the + * old controller was set to `false`. PushLink deals w/ that and + * dismisses old VCs before presenting a new. + * + * - Returns: A Binding that controls whether the PushLink is active. + */ private var isActiveBinding: Binding { Binding( get: { @@ -170,6 +192,14 @@ public struct PushLink: View ) } + /** + * Returns a View representing the destination of the `NavigationLink`. + * + * Which is usually going to be the `contentView` of the destination + * ``ViewController``, bound to the same. + * This also pushes the ``ViewController/navigationTitle`` to the + * SwiftUI environment. + */ @ViewBuilder private var destination: some View { if let activeVC = childViewController { if let presentedVC = activeVC as? VC { diff --git a/Sources/ViewController/Presentations/AutoPresentation.swift b/Sources/ViewController/Presentations/AutoPresentation.swift index e0deee7..c2076d7 100644 --- a/Sources/ViewController/Presentations/AutoPresentation.swift +++ b/Sources/ViewController/Presentations/AutoPresentation.swift @@ -15,7 +15,9 @@ import SwiftUI * It watches the current VC to detect presentation changes, * and binds the sheet/navlink to the respective mode. */ -struct AutoPresentationViewModifier: ViewModifier where VC: ViewController { +internal struct AutoPresentationViewModifier: ViewModifier + where VC: ViewController +{ @ObservedObject var viewController : VC diff --git a/Sources/ViewController/Presentations/Presentation.swift b/Sources/ViewController/Presentations/Presentation.swift index 8542406..3801cb5 100644 --- a/Sources/ViewController/Presentations/Presentation.swift +++ b/Sources/ViewController/Presentations/Presentation.swift @@ -23,21 +23,72 @@ public extension ViewController { } } + +// MARK: - Lookup Presentations public extension _ViewController { - + + /** + * Returns the active ``ViewControllerPresentation`` for a given + * ``ViewController/PresentationMode`` (or the first, if no mode + * is specified. + * + * - Parameter mode: An optional presentation mode that has to match. + * - Returns: An active presentation for the mode, if there is one. + * Or the first active presentation if `mode` is `nil`. + */ + @inlinable func activePresentation(for mode: PresentationMode?) -> ViewControllerPresentation? { guard let mode = mode else { return activePresentations.first } return activePresentations.first(where: { $0.mode == mode }) } + + /** + * Returns the active ``ViewControllerPresentation`` for a specific + * ``ViewController`` object. + * + * - Parameter presentedViewController: The ``ViewController`` to check for. + * - Returns: A presentation for the ``ViewController``, if it is indeed + * being presented. + */ + @inlinable func activePresentation(for presentedViewController: _ViewController) -> ViewControllerPresentation? { activePresentations.first { $0.viewController === presentedViewController } } - // MARK: - Bindings + /** + * Lookup a presented ``ViewController`` of a particular type. Returns nil + * if there is none such (or the mode doesn't match). + * + * Example: + * ```swift + * let settingsVC = presentedViewController(Settings.self) + * ``` + * + * - Parameters: + * - type: The type of the ViewController to lookup + * - mode: Optionally the mode the viewcontroller is presented in + * (e.g. `sheet`, `navigation` or `custom`) + * - Returns: A ``ViewController`` of the specified type, if one exists. + */ + @inlinable + func presentedViewController(_ type: VC.Type, + mode: ViewControllerPresentationMode?) + -> VC? + where VC: ViewController + { + guard let presentation = activePresentation(for: mode) else { return nil } + if let mode = mode, mode != presentation.mode { return nil } + return presentation.viewController as? VC + } +} + + +// MARK: - Bindings +public extension _ViewController { /** * This allows us to check whether a particular VC is being presented, @@ -83,8 +134,17 @@ public extension _ViewController { } /** - * Only checks whether a specific mode is active. This is used for the - * internally supported "auto" modes (`.sheet` and `.navigation`). + * A Binding that represents whether a presentation in a particular mode is + * active (e.g. `sheet`, `navigation` or `custom`). + * + * Used for the internally supported "auto" modes (`.sheet` and + * `.navigation`). + * + * - Parameters: + * - mode: The mode the viewcontroller is presented in + * (e.g. `sheet`, `navigation` or `custom`) + * - Returns: A `Bool` `Binding` that can be used w/ an `isActive` parameter + * of a `sheet` or `NavigationLink`. */ @inlinable func isPresentingMode(_ mode: ViewControllerPresentationMode) @@ -133,34 +193,22 @@ public extension _ViewController { } ) } - - /** - * Lookup a presented ``ViewController`` of a particular type. Returns nil - * if there is none such (or the mode doesn't match) - * - * E.g. this is used by the SheetPresentation. - */ - @inlinable - func presentedViewController(of type: VC.Type, - mode: ViewControllerPresentationMode?) - -> VC? - where VC: ViewController - { - guard let presentation = activePresentation(for: mode) else { return nil } - if let mode = mode, mode != presentation.mode { return nil } - return presentation.viewController as? VC - } - /** - * This allows us to check whether a particular type of VC is being presented, - * e.g. in case the presentation should be done differently (e.g. sheet vs - * navigation). + * A Binding that represents whether a particular type of ``ViewController`` + * is being presented. * * CAREFUL: This only checks the type, there could be multiple presentations * with the same type! (leading to multiple Bindings being true, * and different ContentViews being active, potentially capturing the * wrong environment). + * + * - Parameters: + * - type: The type of the ViewController to lookup + * - mode: Optionally the mode the viewcontroller is presented in + * (e.g. `sheet`, `navigation` or `custom`) + * - Returns: A `Bool` `Binding` that can be used w/ an `isActive` parameter + * of a `sheet` or `NavigationLink`. */ func isPresenting(_ controllerType: VC.Type, mode: ViewControllerPresentationMode?) @@ -169,9 +217,10 @@ public extension _ViewController { { isPresenting(mode: mode) { $0 is VC } } +} - - // MARK: - API Methods +// MARK: - API Methods +public extension _ViewController { @inlinable func show(_ viewController: VC) { @@ -208,10 +257,6 @@ public extension _ViewController { defaultPresent(viewController, mode: mode) } - /** - * Present a ``ViewController`` that doesn't have a - * ``ViewController/ContentView`` assigned. - */ @inlinable func present(_ viewController: VC) where VC.ContentView == DefaultViewControllerView diff --git a/Sources/ViewController/Presentations/PresentationMode.swift b/Sources/ViewController/Presentations/PresentationMode.swift index 5769aac..fda0621 100644 --- a/Sources/ViewController/Presentations/PresentationMode.swift +++ b/Sources/ViewController/Presentations/PresentationMode.swift @@ -30,14 +30,16 @@ public enum ViewControllerPresentationMode: Hashable { // FIXME: Used in two different ways, for accessing the actual presentation, // and for deciding what presentation to use. - /// The ``ViewController`` will decide on an appropriate presentation mode. + /** + * The ``ViewController`` will decide on an appropriate presentation mode. + */ case automatic /** * The ``ViewController`` won't do the presentation automagically, - * the user needs to handle the presentation explicitly. - * E.g. using `presentAsSheet()` or `presentInNavigation()`, or in a - * completely manual way. + * the user needs to handle it explicitly + * (e.g. using the `.sheet` modifier or a programmatic `NavigationLink` with + * the `isActive` bound to the `presentedViewController`). */ case custom diff --git a/Sources/ViewController/ViewController.swift b/Sources/ViewController/ViewController.swift index 3ce2beb..fadf7a3 100644 --- a/Sources/ViewController/ViewController.swift +++ b/Sources/ViewController/ViewController.swift @@ -212,11 +212,47 @@ public protocol ViewController: _ViewController, ObservableObject, Identifiable func present(_ viewController: VC) where VC.ContentView == DefaultViewControllerView + /** + * Present the ``ViewController`` in a context aware mode. + * E.g. if it is within a ``NavigationController``, it'll get presented as + * a navigation. + * By default ``ViewController``s are presented as sheets. + * + * - Parameter viewController: The ``ViewController`` to present. + */ func show(_ viewController: VC) + /** + * Present a ``ViewController`` that doesn't specify an explicit + * ``ViewController/ContentView`` type (i.e. doesn't implement `view` or + * typealias/nest a `ContentView` type). + * Unless specified otherwise in the presentationMode, this will end up in + * a ``ViewControllerPresentationMode/custom`` (i.e. the user has to deal + * with the presentation himself). + * + * - Parameter viewController: The ``ViewController`` to present. + */ func show(_ viewController: VC) where VC.ContentView == DefaultViewControllerView + /** + * Present the ``ViewController`` in a context aware, "detail", mode. + * + * If the container ViewController doesn't support an explicit "detail" mode, + * this acts like ``ViewController/show``. + * + * - Parameter viewController: The ``ViewController`` to present. + */ func showDetail(_ viewController: VC) + /** + * Present a ``ViewController`` that doesn't specify an explicit + * ``ViewController/ContentView`` type (i.e. doesn't implement `view` or + * typealias/nest a `ContentView` type). + * Unless specified otherwise in the presentationMode, this will end up in + * a ``ViewControllerPresentationMode/custom`` (i.e. the user has to deal + * with the presentation himself). + * + * - Parameter viewController: The ``ViewController`` to present. + */ func showDetail(_ viewController: VC) where VC.ContentView == DefaultViewControllerView From 7e2ebac722c1a07665972a5f7ad1c5d814dee470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 30 Apr 2022 14:44:49 +0200 Subject: [PATCH 5/7] Drop the explicit sheet/nav presentations As discussed in Issue #3. Users of the framework will mostly use automatic presentations. And if they want to do something custom, they still can. Just use the bindings available for the active VC and attach those to the `isActive` binding of the View presentation. --- .../Presentations/PushPresentation.swift | 165 ----------------- .../Presentations/SheetPresentation.swift | 168 ------------------ ViewController.xcodeproj/project.pbxproj | 12 -- 3 files changed, 345 deletions(-) delete mode 100644 Sources/ViewController/Presentations/PushPresentation.swift delete mode 100644 Sources/ViewController/Presentations/SheetPresentation.swift diff --git a/Sources/ViewController/Presentations/PushPresentation.swift b/Sources/ViewController/Presentations/PushPresentation.swift deleted file mode 100644 index a24fc36..0000000 --- a/Sources/ViewController/Presentations/PushPresentation.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// PushPresentation.swift -// ViewController -// -// Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. All rights reserved. -// - -import SwiftUI - -public extension View { - - /** - * Controls how a specific ViewController is being presented in `.custom` - * mode. - * - * It does NOT do the actual presentation! I.e. `.present(MyViewController)` - * still has to be called. - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) { - * NavigationView { - * ChildView() - * } - * .navigationViewStyle(.stack) - * } - * .presentInNavigation(AddWidgetVC.self) { - * AddWidgetVC.ContentView() - * } - * .presentInNavigation(ViewConfigVC.self) { - * AddWidgetVC.ContentView() - * } - * ``` - * - * Note: This is using a `background` for the `NavigationLink`. - * Use a ``PushLink`` for a real ``NavigationLink``. - */ - func presentInNavigation(_ vc: VC.Type, - @ViewBuilder content: @escaping () -> V) - -> some View - where VC: ViewController, V: View - { - // Note: The explicit specialization is NECESSARY, otherwise the wrong ones - // are picked up! - self.modifier(PushPresentation(destination: content)) - } - - /** - * Controls how a specific ``ViewController`` is being presented in `.custom` - * mode. - * - * It does NOT do the actual presentation! I.e. `.present(MyViewController)` - * still has to be called. - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) - * .presentInNavigation(AddWidgetVC.self) - * .presentInNavigation(ViewConfigVC.self) - * ``` - */ - @inlinable - func presentInNavigation(_ vc: VC.Type) -> some View { - presentInNavigation(vc, content: { RenderContentView() }) - } - - /** - * Helper to avoid using ``presentInNavigation`` with a ``ViewController`` - * that doesn't have a proper ``ContentView``. - */ - @available(*, unavailable, - message: "The ViewController needs a proper `ContentView`") - func presentInNavigation(_ vc: VC.Type) -> some View - where VC.ContentView == DefaultViewControllerView - { - presentInNavigation(vc, content: { RenderContentView() }) - } -} - -/** - * Controls how a specific ViewController is being presented in `.custom` mode. - * It does NOT do the actual presentation! - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) { - * NavigationView { - * ChildView() - * } - * .navigationViewStyle(.stack) - * } - * .presentInNavigation(AddWidgetVC.self) { - * AddWidgetVC.ContentView() - * } - * .presentInNavigation(ViewConfigVC.self) { - * AddWidgetVC.ContentView() - * } - * ``` - * - * Note: This is using a `background` for the `NavigationLink`. - * Use a ``PushLink`` for a real ``NavigationLink``. - */ -@usableFromInline -struct PushPresentation: ViewModifier - where DestinationVC: ViewController, V: View -{ - - @EnvironmentObject var parent : AnyViewController - - private let destination : () -> V - private let mode : ViewControllerPresentationMode - - public init(destination: @escaping () -> V) { - self.destination = destination - self.mode = .custom - } - @usableFromInline - internal init(mode: ViewControllerPresentationMode, - destination: @escaping () -> V) - { - self.destination = destination - self.mode = mode - } - - @usableFromInline - func body(content: Content) -> some View { - content - .background( - NavigationLink( - // TODO: This needs an optional extra condition - isActive: parent.isPresenting(DestinationVC.self, mode: mode), - destination: { - if let presentedVC = - parent.presentedViewController(of: DestinationVC.self, - mode: mode) - { - destination() - .environment(\.viewControllerPresentationMode, .navigation) - .controlled(by: presentedVC) - .navigationTitle(presentedVC.navigationTitle) - } - else { - TypeMismatchInfoView( - parent: parent, expectedMode: mode - ) - } - }, - label: { Color.clear } // TBD: EmptyView? - ) - ) - } -} diff --git a/Sources/ViewController/Presentations/SheetPresentation.swift b/Sources/ViewController/Presentations/SheetPresentation.swift deleted file mode 100644 index a7a8367..0000000 --- a/Sources/ViewController/Presentations/SheetPresentation.swift +++ /dev/null @@ -1,168 +0,0 @@ -// -// SheetPresentation.swift -// ViewController -// -// Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. All rights reserved. -// - -import SwiftUI - -public extension View { - - /** - * Controls how a specific ViewController is being presented in `.custom` - * mode. - * - * It does NOT do the actual presentation! I.e. `.present(MyViewController)` - * still has to be called. - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) { - * NavigationView { - * ChildView() - * } - * .navigationViewStyle(.stack) - * } - * .presentInSheet(AddWidgetVC.self) { - * AddWidgetVC.ContentView() - * } - * .presentInSheet(ViewConfigVC.self) { - * AddWidgetVC.ContentView() - * } - * ``` - */ - func presentInSheet(_ vc: VC.Type, - @ViewBuilder content: @escaping () -> V) - -> some View - where VC: ViewController, V: View - { - // Note: The explicit specialization is NECESSARY, otherwise the wrong ones - // are picked up! - /* - * This has issues in the content closure: - * .presentInSheet(ConfigVC.self) { - * // Passing parameters (like `widget`) here, ends up w/ the "first" - * // widget of the type. - * ConfigVC.ContentView() - * } - * Environment objects do seem to be fine though? So something strange - * happening w/ the capture here. - */ - self.modifier(SheetPresentation(destination: content)) - } - - /** - * Controls how a specific ``ViewController`` is being presented in `.custom` - * mode. - * - * It does NOT do the actual presentation! I.e. `.present(MyViewController)` - * still has to be called. - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) - * .presentInSheet(AddWidgetVC.self) - * .presentInSheet(ViewConfigVC.self) - * ``` - */ - @inlinable - func presentInSheet(_ vc: VC.Type) -> some View { - presentInSheet(vc, content: { RenderContentView() }) - } - - /** - * Helper to avoid using ``presentInSheet`` with a ``ViewController`` - * that doesn't have a proper ``ContentView``. - */ - @available(*, unavailable, - message: "The ViewController needs a proper `ContentView`") - func presentInSheet(_ vc: VC.Type) -> some View - where VC.ContentView == DefaultViewControllerView - { - presentInSheet(vc, content: { RenderContentView() }) - } -} - -/** - * Controls how a specific ViewController is being presented in `.custom` mode. - * It does NOT do the actual presentation! - * - * This peeks into the ``ViewController/presentedVC`` of the current - * ``ViewController`` (which is stored in the environment!) - * - * How to use: - * ``` - * ContentView() - * .presentInSheet(WidgetViewVC.self) { - * NavigationView { - * ChildView() - * } - * .navigationViewStyle(.stack) - * } - * .presentInSheet(AddWidgetVC.self) { - * AddWidgetVC.ContentView() - * } - * .presentInSheet(ViewConfigVC.self) { - * AddWidgetVC.ContentView() - * } - * ``` - */ -@usableFromInline -struct SheetPresentation: ViewModifier - where DestinationVC: ViewController, V: View -{ - - @EnvironmentObject private var parent : AnyViewController - - private let destination : () -> V - private let mode : ViewControllerPresentationMode - - public init(destination: @escaping () -> V) { - self.destination = destination - self.mode = .custom - } - @usableFromInline - internal init(mode: ViewControllerPresentationMode, - destination: @escaping () -> V) - { - self.destination = destination - self.mode = mode - } - - private func isActive(_ vc: _ViewController) -> Bool { - // TODO: This needs an optional extra condition - vc is DestinationVC - } - - @usableFromInline - func body(content: Content) -> some View { - content - .sheet(isPresented: parent.isPresenting(mode: mode, isActive)) { - if let presentedVC : DestinationVC = parent - .presentedViewController(of: DestinationVC.self, mode: mode) - { - // TODO: This is tricky. The destination here can capture - // the incorrect VC, because the sheet presentation condition - // only checks the type! - destination() - .environment(\.viewControllerPresentationMode, .sheet) - .controlled(by: presentedVC) - } - else { - TypeMismatchInfoView( - parent: parent, expectedMode: mode - ) - } - } - } -} diff --git a/ViewController.xcodeproj/project.pbxproj b/ViewController.xcodeproj/project.pbxproj index c62d3f1..e1ccee0 100644 --- a/ViewController.xcodeproj/project.pbxproj +++ b/ViewController.xcodeproj/project.pbxproj @@ -19,10 +19,6 @@ E80050272816EE3400E4805C /* ViewControllerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80050252816EE3400E4805C /* ViewControllerInfo.swift */; }; E8283B432819629E00467F10 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8283B422819629E00467F10 /* ContentView.swift */; }; E8283B442819629E00467F10 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8283B422819629E00467F10 /* ContentView.swift */; }; - E836E5A0280EEA870001B85E /* PushPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E59F280EEA870001B85E /* PushPresentation.swift */; }; - E836E5A1280EEA870001B85E /* PushPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E59F280EEA870001B85E /* PushPresentation.swift */; }; - E836E5A3280EEACA0001B85E /* SheetPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E5A2280EEACA0001B85E /* SheetPresentation.swift */; }; - E836E5A4280EEACA0001B85E /* SheetPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E5A2280EEACA0001B85E /* SheetPresentation.swift */; }; E836E5AD280EECD50001B85E /* PushLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E5AC280EECD50001B85E /* PushLink.swift */; }; E836E5AE280EECD50001B85E /* PushLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E836E5AC280EECD50001B85E /* PushLink.swift */; }; E83ADA662814267600D98D82 /* Containment.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83ADA652814267600D98D82 /* Containment.swift */; }; @@ -77,8 +73,6 @@ E80050252816EE3400E4805C /* ViewControllerInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerInfo.swift; sourceTree = ""; }; E8283B422819629E00467F10 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; E836E59E280EEA0A0001B85E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - E836E59F280EEA870001B85E /* PushPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushPresentation.swift; sourceTree = ""; }; - E836E5A2280EEACA0001B85E /* SheetPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetPresentation.swift; sourceTree = ""; }; E836E5AC280EECD50001B85E /* PushLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushLink.swift; sourceTree = ""; }; E83ADA652814267600D98D82 /* Containment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Containment.swift; sourceTree = ""; }; E83ADA77281444B000D98D82 /* TypeMismatchInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeMismatchInfoView.swift; sourceTree = ""; }; @@ -150,8 +144,6 @@ E8BA6EBE2815914F00FA6C5C /* ViewControllerPresentation.swift */, E8E7066827F2014F00F50160 /* PresentationMode.swift */, E800501F2816C40600E4805C /* AutoPresentation.swift */, - E836E59F280EEA870001B85E /* PushPresentation.swift */, - E836E5A2280EEACA0001B85E /* SheetPresentation.swift */, ); path = Presentations; sourceTree = ""; @@ -354,7 +346,6 @@ E88DCE802812D6A700CD5203 /* Subscriptions.swift in Sources */, E8BA6EBF2815914F00FA6C5C /* ViewControllerPresentation.swift in Sources */, E88DCE702812C83C00CD5203 /* ReExports.swift in Sources */, - E836E5A3280EEACA0001B85E /* SheetPresentation.swift in Sources */, E83ADA662814267600D98D82 /* Containment.swift in Sources */, E800501D2816B09A00E4805C /* DebugOverlay.swift in Sources */, E83ADA78281444B000D98D82 /* TypeMismatchInfoView.swift in Sources */, @@ -366,7 +357,6 @@ E88DCE862812DF8200CD5203 /* RepresentedObject.swift in Sources */, E88DCE832812DD8B00CD5203 /* Title.swift in Sources */, E80050202816C40600E4805C /* AutoPresentation.swift in Sources */, - E836E5A0280EEA870001B85E /* PushPresentation.swift in Sources */, E800501A2816B00E00E4805C /* DebugMode.swift in Sources */, E88DCE892812E9CD00CD5203 /* Presentation.swift in Sources */, E88DCE7D2812D3A300CD5203 /* ViewControllerStorage.swift in Sources */, @@ -391,7 +381,6 @@ E88DCE812812D6A700CD5203 /* Subscriptions.swift in Sources */, E8BA6EC02815914F00FA6C5C /* ViewControllerPresentation.swift in Sources */, E88DCE712812C83C00CD5203 /* ReExports.swift in Sources */, - E836E5A4280EEACA0001B85E /* SheetPresentation.swift in Sources */, E83ADA672814267600D98D82 /* Containment.swift in Sources */, E800501E2816B09A00E4805C /* DebugOverlay.swift in Sources */, E83ADA79281444B000D98D82 /* TypeMismatchInfoView.swift in Sources */, @@ -403,7 +392,6 @@ E88DCE872812DF8200CD5203 /* RepresentedObject.swift in Sources */, E88DCE842812DD8B00CD5203 /* Title.swift in Sources */, E80050212816C40600E4805C /* AutoPresentation.swift in Sources */, - E836E5A1280EEA870001B85E /* PushPresentation.swift in Sources */, E800501B2816B00E00E4805C /* DebugMode.swift in Sources */, E88DCE8A2812E9CD00CD5203 /* Presentation.swift in Sources */, E88DCE7E2812D3A300CD5203 /* ViewControllerStorage.swift in Sources */, From e9386e50c9b373745f0c2bbd90279711af9056b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 30 Apr 2022 14:53:05 +0200 Subject: [PATCH 6/7] GHActions: Add non-SPM test targets for static lib Make sure those work. --- .github/workflows/swift.yml | 41 ++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 1d91170..71b3cf4 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -7,7 +7,7 @@ on: - cron: "0 9 * * 1" jobs: - nextstep: + SwiftPackage: runs-on: macos-latest steps: - name: Select latest available Xcode @@ -20,3 +20,42 @@ jobs: run: swift build -c debug - name: Build Swift Release Package run: swift build -c release + iOS: + runs-on: macos-latest + steps: + - name: Select latest available Xcode + uses: maxim-lobanov/setup-xcode@v1.2.1 + with: + xcode-version: 12.4 + - name: Checkout Repository + uses: actions/checkout@v2 + - name: Prerequisites + run: gem install xcpretty + - name: Build + run: set -o pipefail; xcodebuild -scheme ViewController-iOS build | xcpretty --color + iOS15: + runs-on: macos-latest + steps: + - name: Select latest available Xcode + uses: maxim-lobanov/setup-xcode@v1.2.1 + with: + xcode-version: 13.2 + - name: Checkout Repository + uses: actions/checkout@v2 + - name: Prerequisites + run: gem install xcpretty + - name: Build + run: set -o pipefail; xcodebuild -scheme ViewController-iOS build | xcpretty --color + NeXTstep: + runs-on: macos-latest + steps: + - name: Select latest available Xcode + uses: maxim-lobanov/setup-xcode@v1.2.1 + with: + xcode-version: 12.4 + - name: Checkout Repository + uses: actions/checkout@v2 + - name: Prerequisites + run: gem install xcpretty + - name: Build + run: set -o pipefail; xcodebuild -scheme ViewController-macOS build | xcpretty --color From b8dd0cec6a4792c9c9e208dc2a2feec1f9a5194a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 30 Apr 2022 14:59:08 +0200 Subject: [PATCH 7/7] GHActions: Bump requirement to Xcode 13.2 This is necessary, code doesn't build otherwise. We could consider a backport? PRs welcome! --- .github/workflows/swift.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 71b3cf4..9002e64 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -21,19 +21,6 @@ jobs: - name: Build Swift Release Package run: swift build -c release iOS: - runs-on: macos-latest - steps: - - name: Select latest available Xcode - uses: maxim-lobanov/setup-xcode@v1.2.1 - with: - xcode-version: 12.4 - - name: Checkout Repository - uses: actions/checkout@v2 - - name: Prerequisites - run: gem install xcpretty - - name: Build - run: set -o pipefail; xcodebuild -scheme ViewController-iOS build | xcpretty --color - iOS15: runs-on: macos-latest steps: - name: Select latest available Xcode @@ -52,7 +39,7 @@ jobs: - name: Select latest available Xcode uses: maxim-lobanov/setup-xcode@v1.2.1 with: - xcode-version: 12.4 + xcode-version: 13.2 - name: Checkout Repository uses: actions/checkout@v2 - name: Prerequisites