Trying to load an image after the view loads, the model object driving the view (see MovieDetail below) has a urlString. Because a SwiftUI View
element has no life cycle methods (and there's not a view controller driving things) what is the best way to handle this?
The main issue I'm having is no matter which way I try to solve the problem (Binding an object or using a State variable), my View doesn't have the urlString
until after it loads...
// movie object struct Movie: Decodable, Identifiable { let id: String let title: String let year: String let type: String var posterUrl: String private enum CodingKeys: String, CodingKey { case id = "imdbID" case title = "Title" case year = "Year" case type = "Type" case posterUrl = "Poster" } }
// root content list view that navigates to the detail view struct ContentView : View { var movies: [Movie] var body: some View { NavigationView { List(movies) { movie in NavigationButton(destination: MovieDetail(movie: movie)) { MovieRow(movie: movie) } } .navigationBarTitle(Text("Star Wars Movies")) } } }
// detail view that needs to make the asynchronous call struct MovieDetail : View { let movie: Movie @State var imageObject = BoundImageObject() var body: some View { HStack(alignment: .top) { VStack { Image(uiImage: imageObject.image) .scaledToFit() Text(movie.title) .font(.subheadline) } } } }
Thanks in advance.
SwiftUI gives us equivalents to UIKit's viewDidAppear() and viewDidDisappear() in the form of onAppear() and onDisappear() . You can attach any code to these two events that you want, and SwiftUI will execute them when they occur.
viewDidLoad is called when the ViewController has loaded its view hierarchy into memory. This is the point where you can perform your customized initialisation for your view controller. For instance, if your view controller has a UILabel and you want to set a custom text to it, this is the point where you do that.
SwiftUI builds the view hierarchy of a View by calling the body property to initialise its children. It will then call the body property of each of those children, which may then initialise their own children, which have their body property called, and so on.
I hope this is helpful. I found a blogpost that talks about doing stuff onAppear for a navigation view.
Idea would be that you bake your service into a BindableObject and subscribe to those updates in your view.
struct SearchView : View { @State private var query: String = "Swift" @EnvironmentObject var repoStore: ReposStore var body: some View { NavigationView { List { TextField($query, placeholder: Text("type something..."), onCommit: fetch) ForEach(repoStore.repos) { repo in RepoRow(repo: repo) } }.navigationBarTitle(Text("Search")) }.onAppear(perform: fetch) } private func fetch() { repoStore.fetch(matching: query) } }
import SwiftUI import Combine class ReposStore: BindableObject { var repos: [Repo] = [] { didSet { didChange.send(self) } } var didChange = PassthroughSubject<ReposStore, Never>() let service: GithubService init(service: GithubService) { self.service = service } func fetch(matching query: String) { service.search(matching: query) { [weak self] result in DispatchQueue.main.async { switch result { case .success(let repos): self?.repos = repos case .failure: self?.repos = [] } } } } }
Credit to: Majid Jabrayilov
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With