diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 1d91170..9002e64 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,29 @@ 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: 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: 13.2 + - 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 diff --git a/README.md b/README.md index b82f9ff..b68baf1 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 basics to get started quickly. ### Step A: Setup Project and Root VC diff --git a/Sources/ViewController/NavigationLink/PushLink.swift b/Sources/ViewController/NavigationLink/PushLink.swift index 977c2b7..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,17 +192,38 @@ 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 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") } } 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/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/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 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 */,