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
@ViewBuilderclosure for a value type with thedestination(for:)view modifier - In a child view, resolve the destination by using
DestinationViewwith 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
ResolvableDestinationprotocol - Register a value of this type with the
destination(_:)view modifier - In a child view, resolve the destination by using
DestinationViewwith 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) }
}
}
}