2

I am trying to save certain views as png's inside my application (MacOS with SwiftUI). I have the methods below which take in some view, and then render the view and save it.

In my specific use case, whenever this function is called, the view is not being displayed on the current UI, thus I really shouldn't need this to run on the main thread, however it's required by the ImageRenderer.

Because of that, when I am using this to save images, my UI becomes unresponsive and frozen when trying to perform other actions. Is there any way I can avoid this and find a way to save the data for the view (creating the view as image) on a background thread?

 func savePNG(image: NSImage, path: URL) {
        
        let imageRepresentation = NSBitmapImageRep(data: image.tiffRepresentation!)
        let pngData = imageRepresentation?.representation(using: .png, properties: [:])
        do {
            try pngData!.write(to: path)
        } catch {
            print(error)
        }
    }
    
    @MainActor
    func saveOne(dataone: some View, filePathIn: String, renderBump: CGFloat = 1.0) {
            let render = ImageRenderer(content: dataone)
            render.scale = renderBump
            let new = render.nsImage
            if let alpha = new {
            let url = URL(filePath: "\(filePathIn).png")
            savePNG(image: alpha, path: url)
                
            }
    }

Some more context: Basically there are API calls happening in the background. Based on the response, there is some view instantiated, and then sent to the image saver. This is happening frequently enough that even though it's quick rendering, it is briefly freezing the view many times which net effect is too much to ignore.

So if I have a super basic view like this

struct viewToSave: View {
    let textIn: String
    var body: some View {
        VStack {
            Text(textIn)
        }.background(Color.black)
            .frame(maxWidth: 950, maxHeight: 540)
            
    }
}

And then some method in view model like this

@MainActor
   func fetchDataAndMakeView() {
       Task {
           do {
               let returned: someCodableType = try await network.getData()
               let viewBasedOnReturned: viewToSave = viewToSave(textIn: returned.textProperty)
               imgMain.shared.saveOne(dataone: viewBasedOnReturned, filePathIn: "somePath")
           } catch {
               print("error: \(error.localizedDescription)")
           }
       }
       
   }

I feel like this would make no sense to have to block the main thread to perform my ultimate goal. Is there some other way of converting view to image in swift without doing on main thread with ImageRenderer?

apod
  • 145
  • 1
  • 6
  • Does this answer your question? [Trouble running async functions in background threads (concurrency)](https://stackoverflow.com/questions/73538764/trouble-running-async-functions-in-background-threads-concurrency) – lorem ipsum Mar 10 '23 at 00:43
  • First of all you need to identify what's taking time - maybe it's rendering itself, or maybe it's saving. So put 2 signposts to measure the time. If it's saving, then once you are past `let new = render.nsImage`, you can move the remaining work from main thread to `DistpatchQueue.global` - problem solved. If it's rendering though, then you need to reconsider your approach. – timbre timbre Mar 10 '23 at 00:57
  • It's not taking that much time, I just want to be able to add continuous input while image rendering is happening. So if I am typing in a text editor for example, this would freeze it briefly while image renderer is working. – apod Mar 10 '23 at 13:41
  • well, like I said: move everything after `let new = render.nsImage` to background thread, and see if it's any better. If not, there's nothing much you can do (other than showing some spinner, or changing your approach completely) – timbre timbre Mar 10 '23 at 18:23
  • Gotcha thx. Do you have any advice for how to change approach? Could I create a whole separate application to render the images? In this case do you have any recommendations on how to pass the data out from one application into the other locally. – apod Mar 11 '23 at 11:29
  • @apod, have you found a solution to this problem yet? If so, please post it. – KeithB Jun 24 '23 at 17:58

0 Answers0