1

I have this code for presenting a SafariView

struct ContentView: View {
    @ObservedObject var viewModel: ContentViewModel
    @State var url: URL?
    @State var toShowSafari: Bool = false
    
    var body: some View {
        VStack {
            Button(action: {
                url = URL(string: "https://www.google.com")
                toShowSafari = true
            }, label: {
                Text("Tap me")
            }).fullScreenCover(isPresented: $toShowSafari) {
                let _ = print("url - \(url)")
                if let url = url {
                    SafariView(url: url)
                }
            }
        }
    }
}

Why the result of the print here is "url - nil" and how can I make those changes be executed in order?

  • 2
    This is a known thing. You should use `fullScreenCover(item:onDismiss:content:)` the `item` being the `url`. The `Content` of the `fullScreenCover` is drawn when the `Button` is drawn therefore keeping the "old" value. Presenting keeps the `ContentView` from redrawing. Another alternative is to have `SafariView` take the `url` as an `@Binding` you could present the way you are doing it now. – lorem ipsum Jun 05 '21 at 16:18
  • 1
    Does this answer your question? [Multiple sheet(isPresented:) doesn't work in SwiftUI](https://stackoverflow.com/questions/58837007/multiple-sheetispresented-doesnt-work-in-swiftui) – lorem ipsum Jun 05 '21 at 16:22
  • @loremipsum About that link, no. Argument item requires URL to conform to Identifiable. Is making some wrapper for url is a good solution? –  Jun 05 '21 at 18:10
  • 1
    No wrapper needed just a simple `extension` to `URL` so it conforms. Pick a variable that will be unique or create a `UUID` – lorem ipsum Jun 05 '21 at 19:42

2 Answers2

1

Why this is happening I don't know, but this is what I usually do to avoid this issue:

class Selected: ObservableObject {
    @Published var url: URL?
}

struct ContentView: View {
    @ObservedObject var viewModel: ContentViewModel
    @StateObject var selected = Selected()  // <---
    @State var toShowSafari: Bool = false
    
    var body: some View {
        VStack {
            Button(action: {
                selected.url = URL(string: "https://www.google.com") 
                toShowSafari = true
            }, label: {
                Text("Tap me")
            }).fullScreenCover(isPresented: $toShowSafari) {
                let _ = print("url - \(selected.url)")
                if let url = selected.url {
                    SafariView(url: url)
                }
            }
        }
    }
}
  • When you initialize an `ObservableObject` like that you should use `@StateObject` not `@ObservedObject` https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app – lorem ipsum Jun 05 '21 at 16:12
1
struct URLFullCoverView: View {
    @State var url: URL?
    
    var body: some View {
        VStack {
            Button(action: {
                url = URL(string: "https://www.google.com")
            }, label: {
                Text("Tap me")
            })
            //This initializer will give you the current value
            .fullScreenCover(item: $url, content: {newUrl in
                Text(newUrl.absoluteString)
            })
        }
    }
}
//With this extension you can make it conform to Identifiable
extension URL : Identifiable{
    public var id: String {
        //UUID().uuidString
        //or
        self.absoluteString
    }
}
lorem ipsum
  • 21,175
  • 5
  • 24
  • 48