diff --git a/README.md b/README.md index 2548958..b82f9ff 100644 --- a/README.md +++ b/README.md @@ -23,16 +23,13 @@ More details will be posted but to get started. class HomePage: ViewController { - struct ContentView: View { - - var body: some View { - VStack { - Text("Welcome to MWC!") - .font(.title) - .padding() - - Spacer() - } + var view: some View { + VStack { + Text("Welcome to MWC!") + .font(.title) + .padding() + + Spacer() } } } @@ -58,16 +55,13 @@ More details will be posted but to get started. class Settings: ViewController { - struct ContentView: View { - - var body: some View { - VStack { - Text("Welcome to Settings!") - .font(.title) - .padding() - - Spacer() - } + var view: some View { // the View being controlled + VStack { + Text("Welcome to Settings!") + .font(.title) + .padding() + + Spacer() } } } @@ -82,24 +76,19 @@ More details will be posted but to get started. show(Settings()) // or `present(Settings())` } - struct ContentView: View { - - @EnvironmentObject private var viewController : HomePage - - var body: some View { - VStack { - Text("Welcome to MWC!") - .font(.title) - .padding() - - Divider() - - Button(action: viewController.configureApp) { - Label("Configure", systemImage: "gear") - } + var view: some View { + VStack { + Text("Welcome to MWC!") + .font(.title) + .padding() - Spacer() + Divider() + + Button(action: self.configureApp) { + Label("Configure", systemImage: "gear") } + + Spacer() } } } @@ -144,20 +133,17 @@ inline a `NavigationLink`, use `PushLink`, which wraps that. class HomePage: ViewController { - struct ContentView: View { - - var body: some View { - VStack { - Text("Welcome to MWC!") - .font(.title) - .padding() - - Divider() + var view: some View { + VStack { + Text("Welcome to MWC!") + .font(.title) + .padding() - PushLink("Open Settings", to: Settings()) - - Spacer() - } + Divider() + + PushLink("Open Settings", to: Settings()) + + Spacer() } } } diff --git a/Sources/ViewController/AnyViewController.swift b/Sources/ViewController/AnyViewController.swift index 69bc40e..bd2c834 100644 --- a/Sources/ViewController/AnyViewController.swift +++ b/Sources/ViewController/AnyViewController.swift @@ -34,17 +34,20 @@ import SwiftUI */ public final class AnyViewController: ViewController { - public var id : ObjectIdentifier { ObjectIdentifier(viewController) } + public var id : ObjectIdentifier { ObjectIdentifier(viewController) } - public let viewController : _ViewController - private var subscription : AnyCancellable? + public let viewController : _ViewController + private var subscription : AnyCancellable? @usableFromInline - init(_ viewController: VC) where VC: ViewController { + internal let contentView : () -> AnyView + + public init(_ viewController: VC) where VC: ViewController { assert(!(viewController is AnyViewController), "Attempt to nest an AnyVC into another \(viewController)") self.viewController = viewController + self.contentView = { AnyView(viewController.view) } subscription = viewController.objectWillChange.sink { [weak self] _ in self?.objectWillChange.send() @@ -54,9 +57,9 @@ public final class AnyViewController: ViewController { /** * An initializer that avoids nesting `AnyViewController`s into themselves. */ - @usableFromInline init(_ viewController: AnyViewController) { self.viewController = viewController.viewController + self.contentView = viewController.contentView // TBD: Can't unwrap this? subscription = viewController.objectWillChange.sink { @@ -70,9 +73,8 @@ public final class AnyViewController: ViewController { // Those are typed erased by the base protocol already (_ViewController). @inlinable - public var contentView : AnyView { anyContentView } - @inlinable - public var anyContentView : AnyView { viewController.anyContentView } + @ViewBuilder public var view : AnyView { contentView() } + @inlinable public var controlledContentView : AnyView { anyControlledContentView } @inlinable diff --git a/Sources/ViewController/ContainerViewControllers/NavigationController.swift b/Sources/ViewController/ContainerViewControllers/NavigationController.swift index 30a5c11..07216e7 100644 --- a/Sources/ViewController/ContainerViewControllers/NavigationController.swift +++ b/Sources/ViewController/ContainerViewControllers/NavigationController.swift @@ -145,7 +145,7 @@ open class NavigationController: ViewController, _NavigationController private func markAsPresentingViewController() { rootViewController.presentingViewController = self - activePresentations.append(TypedViewControllerPresentation( + activePresentations.append(ViewControllerPresentation( viewController: _rootViewController, mode: .custom // not .navigation, that would activate the bg link! )) @@ -161,18 +161,11 @@ open class NavigationController: ViewController, _NavigationController // MARK: - View - public struct ContentView: View { - - @EnvironmentObject private var viewController : NavigationController - - public init() {} - - public var body: some View { - NavigationView { - viewController._rootViewController.contentView - .controlled(by: viewController._rootViewController) - .navigationTitle(viewController._rootViewController.navigationTitle) - } + public var view: some View { + NavigationView { + _rootViewController.view + .controlled(by: _rootViewController) + .navigationTitle(_rootViewController.navigationTitle) } } } diff --git a/Sources/ViewController/MainViewController.swift b/Sources/ViewController/MainViewController.swift index 1286bad..5e0d0c2 100644 --- a/Sources/ViewController/MainViewController.swift +++ b/Sources/ViewController/MainViewController.swift @@ -13,8 +13,6 @@ import SwiftUI * i.e. one which starts a new VC hierarchy. * Usually only one root VC is used per scene. * - * Checkout the ``View/main`` modifier for the more general solution. - * * E.g. this could be used in the `ContentView` of an app like this: * ```swift * struct ContentView: View { @@ -47,7 +45,7 @@ public struct MainViewController: View where VC: ViewController { } public var body: some View { - VC.ContentView() - .controlled(by: viewController) + viewController + .controlledContentView } } diff --git a/Sources/ViewController/NavigationLink/PushLink.swift b/Sources/ViewController/NavigationLink/PushLink.swift index 3246a6b..977c2b7 100644 --- a/Sources/ViewController/NavigationLink/PushLink.swift +++ b/Sources/ViewController/NavigationLink/PushLink.swift @@ -19,8 +19,8 @@ import SwiftUI * * Content View Example: * ```swift - * struct ContentView: View { - * var body: some View { + * class HomePage: ViewController { + * var view: some View { * PushLink("Preferences…", to: PreferencesPage()) * } * } @@ -28,8 +28,8 @@ import SwiftUI * * Explicit View Example: * ```swift - * struct ContentView: View { - * var body: some View { + * class HomePage: ViewController { + * var view: some View { * PushLink(to: PreferencesPage(), using: Text("Prefs!") { * Text("Preferences…") * } @@ -200,7 +200,7 @@ public struct PushLink: View extension PushLink { /** - * Create a ``PushLink`` that is using the ``ViewController/ContentView`` + * Create a ``PushLink`` that is using the ``ViewController/view`` * as the destination. * * Example: @@ -221,7 +221,7 @@ extension PushLink { } /** - * Create a ``PushLink`` that is using the ``ViewController/ContentView`` + * Create a ``PushLink`` that is using the ``ViewController/view`` * as the destination. * * Example: diff --git a/Sources/ViewController/Presentations/AutoPresentation.swift b/Sources/ViewController/Presentations/AutoPresentation.swift index 83eacb6..e0deee7 100644 --- a/Sources/ViewController/Presentations/AutoPresentation.swift +++ b/Sources/ViewController/Presentations/AutoPresentation.swift @@ -18,6 +18,28 @@ import SwiftUI struct AutoPresentationViewModifier: ViewModifier where VC: ViewController { @ObservedObject var viewController : VC + + fileprivate struct Present: View { + + @ObservedObject var presentingViewController : VC + let mode : ViewControllerPresentationMode + + var body: some View { + if let presentation = + presentingViewController.activePresentation(for: mode) + { + let presentedViewController = presentation.viewController + presentedViewController.anyControlledContentView + .environment(\.viewControllerPresentationMode, mode) + .navigationTitle(presentedViewController.navigationTitle) + } + else { + TypeMismatchInfoView( + parent: presentingViewController, expectedMode: mode + ) + } + } + } func body(content: Content) -> some View { // Note: Also used internally during presentation. @@ -27,34 +49,14 @@ struct AutoPresentationViewModifier: ViewModifier where VC: ViewController { .sheet( isPresented: viewController.isPresentingMode(.sheet), content: { - if let presentation = viewController.activePresentation(for: .sheet) { - presentation.contentView() - .environment(\.viewControllerPresentationMode, .sheet) - .navigationTitle(presentation.viewController.navigationTitle) - } - else { - TypeMismatchInfoView( - parent: viewController, expectedMode: .sheet - ) - } + Present(presentingViewController: viewController, mode: .sheet) } ) .background( NavigationLink( isActive: viewController.isPresentingMode(.navigation), destination: { - if let presentation = viewController - .activePresentation(for: .navigation) - { - presentation.contentView() - .environment(\.viewControllerPresentationMode, .navigation) - .navigationTitle(presentation.viewController.navigationTitle) - } - else { - TypeMismatchInfoView( - parent: viewController, expectedMode: .navigation - ) - } + Present(presentingViewController: viewController, mode: .navigation) }, label: { Color.clear } // TBD: EmptyView? ) diff --git a/Sources/ViewController/Presentations/Presentation.swift b/Sources/ViewController/Presentations/Presentation.swift index 1106b7e..8542406 100644 --- a/Sources/ViewController/Presentations/Presentation.swift +++ b/Sources/ViewController/Presentations/Presentation.swift @@ -102,7 +102,15 @@ public extension _ViewController { // helpers. guard !isShowing else { // isShowing=true would be activation - logger.warning("Attempt to activate VC via Binding, won't work!") + if self.activePresentations.contains(where: { $0.mode == mode }) { + logger.debug( + "Attempt to activate VC via Binding, mode already active!") + } + else { + // FIXME: This can sometimes be seen in sheets, figure out why + logger.warning( + "Attempt to activate VC via Binding, won't work \(self)!") + } return } @@ -279,7 +287,7 @@ public extension _ViewController { activePresentation.viewController.dismiss() } - activePresentations.append(TypedViewControllerPresentation( + activePresentations.append(ViewControllerPresentation( viewController: viewController, mode: mode )) diff --git a/Sources/ViewController/Presentations/PresentationMode.swift b/Sources/ViewController/Presentations/PresentationMode.swift index 46e5b38..5769aac 100644 --- a/Sources/ViewController/Presentations/PresentationMode.swift +++ b/Sources/ViewController/Presentations/PresentationMode.swift @@ -14,6 +14,15 @@ import SwiftUI * ```swift * public struct ContentView: View { * @Environment(\.viewControllerPresentationMode) private var mode + * + * var body: some View { + * if mode == .sheet { + * Text("I'm in a sheet!") + * } + * else { + * Text("I'm in a sheet, NOT!") + * } + * } * } * ``` */ diff --git a/Sources/ViewController/Presentations/ViewControllerPresentation.swift b/Sources/ViewController/Presentations/ViewControllerPresentation.swift index 93ae806..93fbcfd 100644 --- a/Sources/ViewController/Presentations/ViewControllerPresentation.swift +++ b/Sources/ViewController/Presentations/ViewControllerPresentation.swift @@ -16,38 +16,16 @@ import Foundation * * Note that a controller may hold multiple presentations, e.g. it may present * a detail in a `NavigationView` while also doing a presentation in a sheet. - * - * This is the type erased version, ``TypedViewControllerPresentation`` is - * created internally. - */ -public protocol ViewControllerPresentation { - - var viewController : _ViewController { get } - var mode : ViewControllerPresentationMode { get } - - var contentView : () -> AnyView { get } -} - -/** - * The concrete ``ViewControllerPresentation`` object used internally. */ -public struct TypedViewControllerPresentation - : ViewControllerPresentation -{ +public struct ViewControllerPresentation { public let viewController : _ViewController public let mode : ViewControllerPresentationMode - public let contentView : () -> AnyView - - init(viewController: VC, mode: ViewControllerPresentationMode) { + + init(viewController: VC, mode: ViewControllerPresentationMode) + where VC: ViewController + { self.viewController = viewController self.mode = mode - self.contentView = { - AnyView( - viewController.contentView - .controlled(by: viewController) - ) - } } } - diff --git a/Sources/ViewController/RenderContentView.swift b/Sources/ViewController/RenderContentView.swift index 13ee06e..68735f4 100644 --- a/Sources/ViewController/RenderContentView.swift +++ b/Sources/ViewController/RenderContentView.swift @@ -9,11 +9,11 @@ import SwiftUI /** - * Accesses the `contentView` of the current viewController of the specific - * class. + * Accesses the `view` of the current viewController of the specific + * ``ViewController`` class in the environment. * * User level code usually doesn't need to work with this. - * + * * Example: * ```swift * var body: some View { @@ -33,16 +33,16 @@ public struct RenderContentView: View { #if DEBUG @inlinable public var body: some View { - if viewController.contentView is EmptyView { + if viewController.view is EmptyView { Text(verbatim: "Embedding EmptyView?") .foregroundColor(.red) } - viewController.contentView + viewController.view } #else @inlinable public var body: some View { - viewController.contentView + viewController.view } #endif } diff --git a/Sources/ViewController/ViewController.swift b/Sources/ViewController/ViewController.swift index 222660b..3ce2beb 100644 --- a/Sources/ViewController/ViewController.swift +++ b/Sources/ViewController/ViewController.swift @@ -10,7 +10,9 @@ import SwiftUI import Combine /** - * A ViewController. + * A ``ViewController``. + * + * TODO: lotsa more documentation * * In WebObjects those would be called `WOComponent`s and are accessible * using the Environment (`WOContext` in WebObjects). @@ -19,15 +21,15 @@ import Combine * The lifecycle events also do not reflect whether the VC is "really" on * screen, just whether it has been presented. * - * There are two parts to presenting a ViewController: + * ### Custom Presentation + * + * There are two parts to presenting a ViewController in a custom way: * - Call `present` on the active viewController with the instance of the new, * child ViewController. The active VC can be accessed using * `@EnvironmentObject private var viewController : ViewController` * (or the specific VC subclass) * - To choose the presentation style, attach it to the View, for example: * `.presentInNavigation(ChildVC.self) { ChildVC.ContentView() }` - * - * TODO: lotsa more documentation */ public protocol ViewController: _ViewController, ObservableObject, Identifiable { @@ -44,9 +46,19 @@ public protocol ViewController: _ViewController, ObservableObject, Identifiable * E.g. there could be a different main View for macOS and for iOS. * * But having a single associated `ContentView` allows for more convenient - * API for that common case. + * APIs for that common case. * - * Example: + * Implicit View via `view` accessor: + * ```swift + * class Contacts: ViewController { + * + * var view: some View { + * Text("The Contacts!") + * } + * } + * ``` + * + * Implicit View, explicit class: * ```swift * class Contacts: ViewController { * @@ -61,12 +73,13 @@ public protocol ViewController: _ViewController, ObservableObject, Identifiable * } * ``` */ - associatedtype ContentView : ViewControllerView = DefaultViewControllerView + associatedtype ContentView : SwiftUI.View = DefaultViewControllerView /** * Dirty trick to let the user avoid the need to explicitly specify the * `ViewControllerView` when declaring Views within the scope of a * ViewController. + * * Example: * ```swift * class Contacts: ViewController { @@ -80,9 +93,36 @@ public protocol ViewController: _ViewController, ObservableObject, Identifiable typealias View = ViewControllerView /** - * Instantiates the ``ContentView`` associated with the ``ViewController``. + * Returns the ``ContentView`` associated with the ``ViewController``. + * + * One way to specify an associated ``View`` for the controller is by + * overriding this property, for example: + * ```swift + * class Contacts: ViewController { + * + * var view: some View { + * Text("The Contacts!") + * } + * } + * ``` + * + * Another way is to use a ``ViewControllerView`` (just a plain `View` w/ + * an `init` method w/o arguments, used to instantiate the `View`): + * ```swift + * class Contacts: ViewController { + * + * struct ContentView: View { + * + * @EnvironmentObject var viewController: Contacts + * + * var body: some View { + * Text("The Contacts!") + * } + * } + * } + * ``` */ - var contentView : ContentView { get } + @ViewBuilder var view : ContentView { get } // MARK: - Represented Object @@ -244,15 +284,3 @@ public protocol ViewController: _ViewController, ObservableObject, Identifiable */ func removeFromParent() } - -public extension ViewController { - - @inlinable - var contentView : ContentView { ContentView() } - - @inlinable - var controlledContentView : some SwiftUI.View { - contentView - .controlled(by: self) - } -} diff --git a/Sources/ViewController/ViewController/ContentView.swift b/Sources/ViewController/ViewController/ContentView.swift new file mode 100644 index 0000000..de332a6 --- /dev/null +++ b/Sources/ViewController/ViewController/ContentView.swift @@ -0,0 +1,51 @@ +// +// ContentView.swift +// ViewController +// +// Created by Helge Heß on 27.04.22. +// + +import SwiftUI + +public extension ViewController where ContentView: ViewControllerView { + + /** + * The default implementation of the user's ``ViewController`` doesn't + * implement the ``ViewController/view`` method, + * but _does_ have an associated ``View/ContentView`` specified. In this case + * the `ContentView` needs to be a ``ViewControllerView`` (which just adds + * the empty `init` to `View`). + */ + @inlinable + @ViewBuilder var view : ContentView { ContentView() } +} + +/** + * A view used to properly refresh the contentview on VC changes. + * + * The ``ViewController.ContentView`` doesn't necessarily subscribe the VC + * for changes, i.e. if used using the plain `var view` override. + * This makes sure the `view` actually gets re-evaluated. + */ +@usableFromInline +struct ControlWrapper: View { + + @ObservedObject fileprivate var viewController : VC + + @usableFromInline + init(viewController: VC) { self.viewController = viewController } + + @usableFromInline + var body : some View { + viewController.view + .controlled(by: viewController) + } +} + +public extension ViewController { + + @inlinable + var controlledContentView : some SwiftUI.View { + ControlWrapper(viewController: self) + } +} diff --git a/Sources/ViewController/ViewController/TypeErasure.swift b/Sources/ViewController/ViewController/TypeErasure.swift index bf9c41a..7f0d7a7 100644 --- a/Sources/ViewController/ViewController/TypeErasure.swift +++ b/Sources/ViewController/ViewController/TypeErasure.swift @@ -9,9 +9,6 @@ import SwiftUI public extension ViewController { - @inlinable - var anyContentView : AnyView { AnyView(contentView) } - @inlinable var anyControlledContentView : AnyView { AnyView(controlledContentView) } diff --git a/Sources/ViewController/ViewController/_ViewController.swift b/Sources/ViewController/ViewController/_ViewController.swift index 11327b4..aaf25f5 100644 --- a/Sources/ViewController/ViewController/_ViewController.swift +++ b/Sources/ViewController/ViewController/_ViewController.swift @@ -177,11 +177,6 @@ public protocol _ViewController: AnyObject, CustomStringConvertible { // MARK: - Type Erasure - /** - * Returns the type erased ``ContentView`` of the ``ViewController``. - */ - var anyContentView : AnyView { get } - /** * Returns the type erased ``ContentView`` of the ``ViewController``, * with the ``ViewController`` being applied as the ``controlled(by:)`` diff --git a/Sources/ViewController/ViewControllerEnvironment.swift b/Sources/ViewController/ViewControllerEnvironment.swift index 79ec7f5..c364b19 100644 --- a/Sources/ViewController/ViewControllerEnvironment.swift +++ b/Sources/ViewController/ViewControllerEnvironment.swift @@ -16,8 +16,10 @@ public extension EnvironmentValues { } /** - * Allows access to the ``ViewController``, w/o having the View refreshed if - * the VC changes. + * Allows access to the ``ViewController``, w/o having the `View` refreshed if + * the ViewController changes. + * + * I.e. an "unobserved object". * * Can be used like this: * ```swift @@ -75,22 +77,6 @@ public extension View { // Note: Also used internally during presentation. self .modifier(AutoPresentationViewModifier(viewController: viewController)) - .modifier(ControlledViewModifier(viewController: viewController)) - } -} - -// Push the VC into the environment by three means: -// - as an EnvironmentObject using its concrete class -// - type-erased, as an ``AnyViewController`` EnvironmentObject -// - as a plain `viewController` environment key (w/o state observation) -fileprivate struct ControlledViewModifier: ViewModifier - where VC: ViewController -{ - - let viewController : VC - - func body(content: Content) -> some View { - content .environmentObject(viewController) .environmentObject(AnyViewController(viewController)) .environment(\.viewController, viewController) diff --git a/ViewController.xcodeproj/project.pbxproj b/ViewController.xcodeproj/project.pbxproj index 583a1ef..c62d3f1 100644 --- a/ViewController.xcodeproj/project.pbxproj +++ b/ViewController.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ E80050242816EE1D00E4805C /* HierarchyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80050222816EE1D00E4805C /* HierarchyView.swift */; }; E80050262816EE3400E4805C /* ViewControllerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80050252816EE3400E4805C /* ViewControllerInfo.swift */; }; 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 */; }; @@ -73,6 +75,7 @@ E800501F2816C40600E4805C /* AutoPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoPresentation.swift; sourceTree = ""; }; E80050222816EE1D00E4805C /* HierarchyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HierarchyView.swift; sourceTree = ""; }; 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 = ""; }; @@ -172,6 +175,7 @@ E88DCE852812DF8200CD5203 /* RepresentedObject.swift */, E83ADA652814267600D98D82 /* Containment.swift */, E8FC0F11281848880051E640 /* TypeErasure.swift */, + E8283B422819629E00467F10 /* ContentView.swift */, ); path = ViewController; sourceTree = ""; @@ -368,6 +372,7 @@ E88DCE7D2812D3A300CD5203 /* ViewControllerStorage.swift in Sources */, E8E7066F27F2014F00F50160 /* PresentationMode.swift in Sources */, E88DCE9B28130B8300CD5203 /* RenderContentView.swift in Sources */, + E8283B432819629E00467F10 /* ContentView.swift in Sources */, E88DCE922812FA7A00CD5203 /* _ViewController.swift in Sources */, E836E5AD280EECD50001B85E /* PushLink.swift in Sources */, E88DCE732812CEE700CD5203 /* ViewController.swift in Sources */, @@ -404,6 +409,7 @@ E88DCE7E2812D3A300CD5203 /* ViewControllerStorage.swift in Sources */, E8E7067027F2014F00F50160 /* PresentationMode.swift in Sources */, E88DCE9C28130B8300CD5203 /* RenderContentView.swift in Sources */, + E8283B442819629E00467F10 /* ContentView.swift in Sources */, E88DCE932812FA7A00CD5203 /* _ViewController.swift in Sources */, E836E5AE280EECD50001B85E /* PushLink.swift in Sources */, E88DCE742812CEE700CD5203 /* ViewController.swift in Sources */,