Then you need to do this in a 2 step process. First, you need to set up your app to receive custom URLs from your widget. This is shockingly well explained by Apple here. Once you have your app's custom url scheme set up, it is time to set up your widget. Essentially what you are going to do is send a URL with a query that is the URL you want to open. Back in your app, you receive that URL, parse it out, and then call openUrl()
with the URL you want to open, and that app will open.
Your code above is close. Following Apple's example above, try this:
In your widget create a deep link URL:
func createDeeplinkForCalc() -> URL {
var components = URLComponents()
components.scheme = "myphotoapp"
components.host = "com.example.myphotoapp"
components.path = "/calc"
components.queryItems = [
URLQueryItem(name: "open", value: "calc://")
]
return components.url!
}
Then, in .widgetURL
, pass this:
.widgetURL(createDeeplinkForCalc())
In your main app:
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
handleURL(url: URL)
}
}
}
func handleURL(_ url:URL) {
// This makes sure you got the correct URL
guard url.scheme == "myphotoapp",
url.host == "com.example.myphotoapp"
else { return }
let query = parseQuery(url: url)
guard let urlString = query["open"],
!urlString.isEmpty else { return } // make sure something exists in the value
if let calcURL = URL(string: urlString) {
openURL(calcURL) // this calls "calc://" and opens the calculator
}
private func parseQuery(url: URL) -> Query {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems
else { return ["":""] }
return queryItems.reduce(into: Query()) { (result, item) in
result[item.name] = item.value
}
}
}
The above has not been tested, but should work.