Viewmodel query with a pointer

I have successfully migrated from objC to swift but I am new to SwiftUI and have been struggling a bit with the view models.

From what I gather I am not allowed the following query because it requires a try:

 @StateObject var viewModel =  PFTour.query("creator" == User.current)
        .order([.descending("date")])
        .include("route")
        .include("creator")
        .include("photos")
        .viewModel

So I need to implement a custom viewmodel object? Most of the examples I’ve seen use live query which I don’t need, so any pointers would be appreciated. thanks.

Just put the query inside of a custom view model:

A number of different examples in that project. The view model from a query is meant to be a simple and quick way, but it’s not the only way.

Thanks I will dig into this.

You can have view models inside of view models. You can put the query view model inside of your custom view model and have it publish. You can then set it during initialization so you can use try. It will look something like:

// PFTour is the Model

// View
struct ContentView: View {
  @StateObject var viewModel = CustomViewModel()
}

// View Model
class CustomViewModel: ObservableObject {
  @Published var tourQueryViewModel: QueryViewModel<PFTour>?
  // Any other properties or view models here  

  init() {
    do {
      tourQueryViewModel = try PFTour.query("creator" == User.current)
        .order([.descending("date")])
        .include("route")
        .include("creator")
        .include("photos")
        .viewModel
    } catch {
       // Handle error
    }
  }
}

This is pretty slick. Here’s the view model I came up with for displaying an image from a ParseFile. Sharing in hope this might help others or let me know if there’s a better way to accomplish this:

class ParsePhotoPublisher: ObservableObject {
    var parseFile: ParseFile? {
        didSet {
            Task {
                await fetchImage()
            }
        }
    }
    @Published var image: UIImage = UIImage(systemName: "arrow.down.to.line")!
    @Published var downloaded = false
    
    func fetchImage() async {
            Task{
                do {
                    let fetchedFile = try await parseFile?.fetch()
                    if let localURL = fetchedFile?.localURL, let uiImage = UIImage(contentsOfFile: localURL.relativePath){
                        Task { @MainActor in
                            image = uiImage
                            downloaded = true
                        }
                    }
                }
                catch {
                    print("unable to fetch file")
                }
            }
    }
}
struct PhotoView: View {
    let photoFile: ParseFile
    @StateObject var imageViewModel = ParsePhotoPublisher()

    var body: some View {
        VStack{
            if self.imageViewModel.downloaded {
                Image(uiImage: self.imageViewModel.image)
                    .resizable()
                    .scaledToFill()
                    .shadow(radius: 3)
                    .cornerRadius(3.0)
            }else {
                HStack{
                    Spacer()
                    ProgressView()
                    Spacer()
                }
            }
        }
        .frame(width: UIScreen.main.bounds.size.width - 150, height: UIScreen.main.bounds.size.width - 150, alignment: .leading)
        .onAppear{
            self.imageViewModel.parseFile = photoFile
        }
    }
}

Your way looks good. It’s all about what you need for your app. In the SnapCat example app, I show an example on how you can customize the standard QueryViewModel and Subscription to fetch images automatically based on the timeline/news-feed query. More details:

This view model does a standard find when initialized and then automatically subscribes to updates using live query:

1 Like