/ Swift 3

Protocol oriented image picker

Using the UIImagePickerController is pretty straightforward. The problem starts, when you have multiple controllers, and you need the picking ability in both of them. You can make a parent class with the core functionality, but that's old-school for a Swift developer, isn't it?

The final product

I imagine my protocol oriented image picker api something like this.

class ViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        self.showImagePicker()
    }
}

extension ViewController: ImagePickerPresentable {
    func selectedImage(data: Data?) {
        //here is your image data...
    }
}

The problem

TL;DR: @objc functions may not currently be in protocol extensions. You could create a base class instead, though that's not an ideal solution.

source: StackOverflow

The main idea is to have a protocol, that implements the UIImagePickerController-Delegate. This would be an easy thing, but the image picker protocol is an ancient one from the Objective-C times, so the system will never call your delegate methods if you do this. That's why we need a little trick, so we are going to use a helper class, but first, let's define our main protocol.

protocol ImagePickerPresentable: class {
    
    func showImagePicker()
    func selectedImage(data: Data?)
}

Next, we create a default implementation for the showImagePicker method, if our class is kind of a UIViewController. The helper class is going to be our delegate, so that instance will take care of the photo selection.

extension ImagePickerPresentable where Self: UIViewController {

    fileprivate func pickerControllerActionFor(for type: UIImagePickerControllerSourceType, title: String) -> UIAlertAction? {
        guard UIImagePickerController.isSourceTypeAvailable(type) else {
            return nil
        }
        return UIAlertAction(title: title, style: .default) { [unowned self] _ in
            let pickerController           = UIImagePickerController()
            pickerController.delegate      = ImagePickerHelper.shared
            pickerController.sourceType    = type
            pickerController.allowsEditing = true
            self.present(pickerController, animated: true)
        }
    }

    func showImagePicker() {
        ImagePickerHelper.shared.delegate = self
        
        let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
        
        if let action = self.pickerControllerActionFor(for: .camera, title: "Take photo") {
            optionMenu.addAction(action)
        }
        if let action = self.pickerControllerActionFor(for: .photoLibrary, title: "Select from library") {
            optionMenu.addAction(action)
        }

        optionMenu.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

        self.present(optionMenu, animated: true)
    }
}

As I mentioned before, we need a helper class. I really don't like singletons, but for this purpose one will do. It's going to be completely private, so no-one else can access it. After the photo is selected, you won't need this class in memory that's why I made a dispose method to clean it up after the job.

fileprivate class ImagePickerHelper: NSObject {
    
    weak var delegate: ImagePickerPresentable?
    
    fileprivate struct `Static` {
        fileprivate static var instance: ImagePickerHelper?
    }
    
    fileprivate class var shared: ImagePickerHelper {
        if ImagePickerHelper.Static.instance == nil {
            ImagePickerHelper.Static.instance = ImagePickerHelper()
        }
        return ImagePickerHelper.Static.instance!
    }
    
    fileprivate func dispose() {
        ImagePickerHelper.Static.instance = nil
    }
    
    func picker(picker: UIImagePickerController, selectedImage data: Data?) {
        picker.dismiss(animated: true, completion: nil)

        self.delegate?.selectedImage(data: data)
        self.delegate = nil
        self.dispose()
    }
}

Finally, we implement the UIImagePickerControllerDelegate methods, and tell the helper class to call back our protocol implementation with the final image data.

extension ImagePickerHelper: UIImagePickerControllerDelegate {
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        self.picker(picker: picker, selectedImage: nil)
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        guard let data = info[UIImagePickerControllerOriginalImage] as? UIImage else {
            return self.picker(picker: picker, selectedImage: nil)
        }

        self.picker(picker: picker, selectedImage: UIImagePNGRepresentation(data))
    }
    
    func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
        self.picker(picker: picker, selectedImage: UIImagePNGRepresentation(image))
    }
}

Yeah, almost forgot this... stupid thing, but we need this implementation as well.

extension ImagePickerHelper: UINavigationControllerDelegate {
    
}

From now on if your view controller conforms to the ImagePickerRepresentable protocol, you can pick images with just a few lines of code. I wanted to find an universal solution for this problem, but unfortunately the frameworks provided by Apple are not 100% Swift-ified, so I needed the singleton hack. I really hope that in the future we can just extend the UIImagePickerControllerDelegate protocol, and eliminate the need of this trick.