PresentationLink broken in Xcode 11 beta 3

PresentationLink (in SwiftUI) seems to be broken in Xcode 11 beta 3.

It succeeds in presenting a modal view controller the first time, but stops working thereafter.

As noted in this StackOverflow post, this seems to occur when embedding a PresentationLink within a NavigationView, with the following warning printed to console:

[WindowServer] display_timer_callback: unexpected state (now:1abc3d3ccc7 < expected:1abc3d91a0f)

This bug has existed since Xcode 11 beta 1 (back when PresentationLink was called PresentationButton), which is quite alarming given the fact that we’re on beta 3 now.

I’ve written a quick and dirty workaround for this:

private enum SetPresentedViewKey: EnvironmentKey {
    static var defaultValue: (AnyView?) -> () {
        fatalError()
    }
}

private extension EnvironmentValues {
    var setPresentedView: (AnyView?) -> () {
        get {
            self[SetPresentedViewKey.self]
        } set {
            self[SetPresentedViewKey.self] = newValue
        }
    }
}

/// A replacement for the buggy (as of Xcode 11 b3) `PresentationLink`.
public struct PresentationLink2<Destination: View, Label: View>: View {
    public let destination: Destination
    public let label: Label

    @Environment(\.setPresentedView) private var setPresentedView
    @State private var presentedView: AnyView? = nil

    public init(destination: Destination, @ViewBuilder _ label: () -> Label) {
        self.destination = destination
        self.label = label()
    }

    private struct _Body<Destination: View, Label: View>: View {
        @Environment(\.setPresentedView) private var setPresentedView

        let destination: Destination
        let label: Label

        init(destination: Destination, label: Label) {
            self.destination = destination
            self.label = label
        }

        var body: some View {
            Button(action: present, label: { label })
        }

        func present() {
            setPresentedView(AnyView(destination))
        }
    }

    public var body: some View {
        _Body(destination: destination, label: label)
            .environment(\.setPresentedView, { self.presentedView = $0 })
            .presentation(presentedView.map {
                Modal($0, onDismiss: { self.presentedView = nil })
            })
    }
}

Simply swap out PresentationLink for PresentationLink2, and your modal view presentations should start working consistently.

 
3
Kudos
 
3
Kudos

Now read this

Losing My Touch

I’d hate to think that I’m losing my touch, With reality, with thoughts—it’s just too much. The dynamics, to me, are quite unclear Who, or what, am I supposed to be here? I hate these emotions that I suddenly feel I’m aware of it now, I’... Continue →