Home > Enterprise >  SwiftUI view API patterns
SwiftUI view API patterns

Time:11-14

I am writing a SwiftUI framework package where I want to have some APIs that allow presentation of a view. For these views, I always want them presented above the presenting view as a modal sheet view (I want to disallow having these views embedded as a subview in an app's view).

Apple appears to have very few system APIs like this, but I have found this API - which behaves as I expect:

public func fileImporter(isPresented: Binding<Bool>, allowedContentTypes: [UTType], onCompletion: @escaping (_ result: Result<URL, Error>) -> Void) -> some View

https://developer.apple.com/documentation/swiftui/form/fileimporter(ispresented:allowedcontenttypes:allowsmultipleselection:oncompletion:)

Note that Apple has implemented this as a View extension, so you can simply use like so:

Button("Import..") {
    self.showingFileImporter = true
}
.fileImporter(isPresented: $showingFileImporter, allowedContentTypes: [.pdf]) { result in
    // got result
}

So, I'm trying to use this pattern to implement my own API, something like:

func mySomethingSelector(isPresented: Binding<Bool>, completion: @escaping () -> Void) -> some View

However, I'm unsure how to start the implementation. The first thing I noticed is that fileImporter actually returns some View, but this is not the screen that's actually presented. So, we need to return some kind of wrapper view, that then presents above the wrapper view? (Note that I assume that Apple has actually implemented fileImporter by bridging to UIKit - and possibly out of process. But I just want to use standard SwiftUI for my implementation).

Anyway, hopefully this makes sense. Any guidance appreciated.

CodePudding user response:

A very simple implementation might look like this, using a sheet to display your custom View:

struct ContentView: View {
    @State private var isPresented: Bool = false
    var body: some View {
        Button("Present") {
            isPresented.toggle()
        }
        .mySomethingSelector(isPresented: $isPresented) {
            print("Done")
        }
    }
}

extension View {
    func mySomethingSelector(isPresented: Binding<Bool>, completion: @escaping () -> Void) -> some View {
        self.sheet(isPresented: isPresented, onDismiss: completion) {
            Text("My View")
        }
    }
}
  • Related