1

I have a UITableView. When a cell is tapped, my app generates a relevant image. The image generation is done on a background thread, as some images make time a considerable time to render. Once an image has rendered, UIImageWriteToSavedPhotosAlbum is called on the main thread.

As any number of UIImageWriteToSavedPhotosAlbum tasks may be underway in any order, I need a way to track each task. I wish to store the indexPath of the selected cell, as contextInfo, passing it to UIImageWriteToSavedPhotosAlbum.

When image:didFinishSavingWithError:contextInfo: is called, I need to get the indexPath out of contextInfo.

But, the vast majority of online examples of UIImageWriteToSavedPhotosAlbum pass nil for contextInfo.

I have read the official docs, the Swift 3 pointer migration guide, the header file, Objective-C code that uses bridging and casting, and a great many SO threads on the subject of UnsafeMutableRawPointer/UnsafeRawPointer.

End result: I am confused by the myriad options, and do not know whether I am missing something obvious, whether I should be performing load() on pointer data, should call unsafeBitCast(), or should sacrifice goats to the gods.

Or, reevaluate my career, and become a carpenter.

Q1. How can I store an instance of IndexPath in a form suitable for contextInfo?

Q2. Once image:didFinishSavingWithError:contextInfo has been called, how do I form an IndexPath out of contextInfo, so as to get to my stored data? Thank you.

Womble
  • 4,607
  • 2
  • 31
  • 45

1 Answers1

6

Finally, thankfully stumbled across a correct answer on SO:

Swift 2 - UnsafeMutablePointer<Void> to object

A solution follows, based on the above answers. Any improvements or suggestions are welcomed.

For ease of use and debugging, I wrapped my data in a reference type:

class ImageWritingContext : NSObject
{
    var indexPath = IndexPath()
    // ...
}

The data needs to be held strongly. In this case, in an array property.

fileprivate var imageWritingContexts = [ImageWritingContext]()

When it comes time to draw, we stick the data in the wrapper and add the wrapper to the array.

let context = ImageWritingContext()
context.indexPath = indexPath
self.imageWritingContexts.insert(context, at: 0)

Now for the magic.

let contextRawPointer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self.imageWritingContexts[0]).toOpaque())

Are we there yet, are we there yet?

let completionTarget: AnyObject = self
let completionSelector = #selector(self.image(_:didFinishSavingWithError:contextInfo:))

UIImageWriteToSavedPhotosAlbum(image,
    completionTarget,
    completionSelector,
    contextRawPointer)

The callback:

func image(_ image: UIImage,
    didFinishSavingWithError error: Error?,
    contextInfo: UnsafeRawPointer)
{
    // Perform error checking here.

    // Extract the data. Pop some valium first.
    let context: ImageWritingContext = Unmanaged<ImageWritingContext>
        .fromOpaque(contextInfo)
        .takeUnretainedValue()
    let indexPath = context.indexPath

    // Do something with the data.

    // Remember to remove the context from self.imageWritingContexts!
}
Community
  • 1
  • 1
Womble
  • 4,607
  • 2
  • 31
  • 45