How to handle ShareLink in SwiftUI - I get memory leaks when I update an image?

144 views Asked by At

I'll try to explain this as simple as possible. I've made an app that modifies an UIImage based on user actions.

To be able to share/save the resulting image, I added a ShareLink to my view.

Every time the user makes changes to the image the app consumes memory which is never released.

When I remove the ShareLink() the memory is released and works as expected.

Have I missed something regarding how ShareLink() works? I have read the Apple documentation, but I don't get any wiser.

Btw, I am using a @State-variable for my controller, which should be ok since it's not an ObservableObject.

This is the basic structure of my app, very simplified.

class ImageController() {
  func modifyImage(photo: UIImage?) -> async UIImage? {
    // do stuff here

    return resultingUIImage
  }
}

struct ContentView: View {
  @State private var photo: UIImage?
  @State private var imageController: ImageController()

  var body: some View {
    VStack {
      Image(uiImage: photo)

      HStack {
        Button("Modify the image") {
          Task {
            let newPhoto = await modifyImage(photo)
 
            DispatchQueue.main.async {
              self.photo = newPhoto
            }
          }
        }
        // when removing ShareLink() below the app doesn't leak memory
        ShareLink(item: Image(uiImage: photo), preview: SharePreview("Description", image: Image(uiImage: photo))
      }
    }
  }
}
1

There are 1 answers

0
irrbloss On

I have now solved it, but not sure if it's the correct way. This is what I did:

  1. Create a a sharable object:
struct ShareableJpeg {
    let imageData: Data
}

extension ShareableJpeg: Transferable {
    enum ShareError: Error {
        case failed
    }
    
    static var transferRepresentation: some TransferRepresentation {
        DataRepresentation(exportedContentType: .jpeg) { object in
            return object.imageData
        }
    }
}
  1. Create a state variable holding the image:
@State var shareableJpeg: ShareableJpeg?

  1. Implement ShareLink():
ShareLink(item: shareableJpeg, preview: SharePreview("Description", image: Image("AppIcon"))) {
  Label("Share", systemImage: "square.and.arrow.up")
}
  1. Assigning the shareableJpeg:
let resultingImage = await imageController.applyStuff(image)

if let jpegData = image.jpegData(compressionQuality: 0.9) {
    shareableJpeg = ShareableJpeg(imageData: jpegData)
}