Resolve SwiftUI views trough a resolver injected in parent that is inspired by NavigationStack navigationDestination pattern.
This is the most basic way to use Destinations.
- Register a
@ViewBuilder
closure for a value type with thedestination(for:)
view modifier - In a child view, resolve the destination by using
DestinationView
with a matching value type
import Destinations
struct ParentView: View {
var body: some View {
ChildView()
.destination(for: Int.self) { value in
Text(100 + value, format: .number)
}
}
}
struct ChildView: View {
var body: some View {
DestinationView(value: 1)
}
}
Alternatively, you can register a custom ResolvableDestination
implementation. This way you can inject properties from the parent and hold some state with SwiftUI property wrappers.
- Implement a custom type that adopts the
ResolvableDestination
protocol - Register a value of this type with the
destination(_:)
view modifier - In a child view, resolve the destination by using
DestinationView
with a value type that matches the value type used in step 1
import Destinations
struct MyDestination: ResolvableDestination {
let base: Int
func body(value: Int) -> some View {
Text(base + value, format: .number)
}
}
struct ParentView: View {
var body: some View {
ChildView()
.destination(MyDestination(base: 100))
}
}
struct ChildView: View {
var body: some View {
DestinationView(value: 1)
}
}
The resolver pattern matches very well with uses in navigation. Destinations comes with support for custom navigation links and sheets.
struct MyDestination: ResolvableDestination {
func body(value: String) -> some View {
Text(value)
}
}
struct ContentView: View {
@State var isSheetPresented = false
var body: some View {
NavigationStack {
List {
DestinationNavigationLink(
"Present MyDestination with push transition",
value: "Hello, world!"
)
Button("Present MyDestination modally") {
isSheetPresented.toggle()
}
}
}
.sheet(
isPresented: $isSheetPresented,
value: "Hello, modal world!"
)
.destination(MyDestination())
}
}
Since types that conform to ResolvableDestination
also conform to DynamicProperty
, you can use SwiftUI state property wrappers like @Environment
and @State
in your destination types.
struct FileSizeLabelDestination: ResolvableDestination {
@State private var fileSize: Result<Int, Error>?
let calculateFileSize: (URL) throws -> Int
func body(value: URL) -> some View {
Group {
switch fileSize {
case .none:
ProgressView()
case let .success(size):
Text(
Measurement(value: Double(size), unit: UnitInformationStorage.bytes),
format: .byteCount(style: .file)
)
case let .failure(error):
Text(error.localizedDescription)
}
}
.task(id: value) {
fileSize = .init { try calculateFileSize(value) }
}
}
}