-1

I have the following function that suppose to return [CIImage] for my purpose - displaying some metadata of photos in tableView.

func getCIImages() -> [CIImage] {
    var images = [CIImage]()
    let assets = PHAsset.fetchAssetsWithMediaType(.Image, options: nil)

    for i in 0..<assets.count {
        guard let asset = assets[i] as? PHAsset else {fatalError("Cannot cast as PHAsset")}  
        let semaphore = dispatch_semaphore_create(0)

        asset.requestContentEditingInputWithOptions(nil) { contentEditingInput, _ in
            //Get full image
            guard let url = contentEditingInput?.fullSizeImageURL else {return}
            guard let inputImage = CIImage(contentsOfURL: url) else {return}
            images.append(inputImage)
            dispatch_semaphore_signal(semaphore)
        }
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
    }
    return images
}

but it stucks in semaphore wait and didn't go further. I have walked through many tutorials but other variants of GCD don't works. I think it's because of simulator, I don't know, can't test on real device. Please help.

sandy
  • 1,072
  • 2
  • 12
  • 31
Andrey M.
  • 3,021
  • 29
  • 42
  • 2
    Why do you force a quasi-synchronous request at all? – vadian Aug 22 '16 at 16:34
  • @vadian because i want to display some metadata of `CIImages` in `tableView` and it is can be done by synchronous manner with one `tableView.reloadData()` call. – Andrey M. Aug 23 '16 at 10:48

1 Answers1

6

guards inside requestContentEditingInputWithOptions callback closure prevents signal sent to semaphore. In such cases (when you need cleanup actions) it is good to use defer. In your case:

asset.requestContentEditingInputWithOptions(nil) { contentEditingInput, _ in
    defer { dispatch_semaphore_signal(semaphore) }

    //Get full image
    guard let url = contentEditingInput?.fullSizeImageURL else {return}
    guard let inputImage = CIImage(contentsOfURL: url) else {return}
    images.append(inputImage)
}

UPDATE

Apart from cleanup bug there is another one. Completion closure of requestContentEditingInputWithOptions called on main thread. Which means that if you blocking main thread with semaphore: completion closure is blocked form executing as well. To fix blocked semaphore issue you need call getCIImages on a different thread than main.

Anyway making asynchronous things synchronous is wrong. You should think of different approach.

Silmaril
  • 4,241
  • 20
  • 22
  • 2
    Exactly the case why `defer` has been introduced! – Sulthan Aug 22 '16 at 16:29
  • None one of the `guard` does not calls `else` statement. This code produces the same behavior like my code - programm freezes – Andrey M. Aug 23 '16 at 05:41
  • @Silmaril Even if I make the call asynchronous how I can return the all images? – Andrey M. Aug 24 '16 at 09:41
  • 1
    @AndreyM. It depends on your requirements. But I created a simple example for you: https://gitlab.com/snippets/24749. Hope this will be enough to understand the idea. – Silmaril Aug 24 '16 at 11:04