Swift 使用async/await实现一句代码获取系统相册照片和视频

众所周知,想要获取体系相册相片和视频,也就是调起UIImagePickerController,创立控制器、遵守署理、完成署理办法、弹出/封闭控制器,拿张相片都如此繁琐。为了更快获取体系相册相片和视频,于是封装了一个体系相册的工具类ImagePicker,内部完成好署理和弹出/封闭的操作,方便自己平常调试。

Demo地址

  • ImagePicker封装的办法其一:打开相册,经过闭包回来相片图片:
func getPhoto() {
    ImagePicker.openAlbumForImage { [weak self] result in
        guard let self = self else { return }
        switch result {
        case let .success(image):
            self.setupImage(image)
        case let .failure(pickError):
            pickError.log()
        }
    }
}

这么一看如同已经够简练了,不过Swift现在已经有async/await的特性了,那是不是能够经过一句代码就能获取到图片呢?

答案是必定能够的啦。

从Swift中的async/await代码实例详解这篇文章中得知,能够经过withCheckedThrowingContinuation将【根据闭包异步处理成果】转换成【结构化并发同步处理成果】,完成一句代码获取体系相册相片:

func getPhoto() async {
    let image: UIImage? = try? await ImagePicker.openAlbum()
    imgView.image = image
}

这样看上去就真的超简练了~

  • 首要完成方法:
// MARK: - Pick object handle
private extension ImagePicker.Controller {
    // 曾经的方法:
    // - 保存闭包,直至署理办法的调起,然后经过该闭包以回来成果
    // - 外部调用:picker.pickObject() { result in ...... },经过闭包异步获取成果
    func pickObject(completion: @escaping ImagePicker.Completion<T>) {
        self.completion = completion
    }
    // 现在的方法:
    // - 经过`withCheckedThrowingContinuation`将【根据闭包异步处理成果】转换成【结构化并发同步处理成果】
    // - 外部调用:let object: T? = try? await picker.pickObject(),等待并同步获取成果
    func pickObject() async throws -> T {
        try await withCheckedThrowingContinuation { continuation in
            // 在曾经的方法中这个闭包是给外部使用的,现在的方法修改成【内部使用】:
      // 外部先卡住函数,等待署理办法的调起,然后经过`continuation`将成果回来给外部,完成同步处理。
            pickObject() { result in
                continuation.resume(with: result)
            }
        }
    }
    // MARK: - UIImagePickerControllerDelegate
    // 用户选择了相片/视频
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        // 1.获取成果
        let result: Result<T, ImagePicker.PickError>
        do {
            result = .success(try T.fetchFromPicker(info))
        } catch let pickError as ImagePicker.PickError {
            result = .failure(pickError)
        } catch {
            result = .failure(.other(error))
        }
        // 2.回来成果
        completion?(result)
        // 3.封闭控制器
        dismiss(animated: true)
    }
    // 用户点击了撤销
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        // 1.回来成果:用户点击撤销
        completion?(.failure(.userCancel))
        // 2.封闭控制器
        dismiss(animated: true)
    }
}
  • 经过泛型和重载的特性扩展一下,回来更多类型:
// 相册 -> 图片
let image: UIImage? = try? await ImagePicker.openAlbum()
// 相册 -> 二进制数据(图片、GIF)
let imageData: Data? = try? await ImagePicker.openAlbum()
// 相册 -> 视频路径
let videoURL: URL? = try? await ImagePicker.openAlbum()
// 拍照 -> 图片
let image: UIImage? = try? await ImagePicker.photograph()

PS:当然啦,必定会有拿不到的状况,所有失利的场景我都使用了ImagePicker.PickError抛出,可经过do {} catch {}捕获。

至此,封装的ImagePicker能够很方便地让我获取体系相册的相片和视频。
不过获取相册数据一般都会用第三方库来做,我这个工具类只是更多的用来平常的调试,最首要是熟悉一下async/await的特性。

OK,本文就到此为止,祝大家新年快乐~

Demo地址