[swift] RxSwift를 사용한 사진 처리 방법

이번 포스트에서는 RxSwift를 사용하여 사진 처리를 하는 방법에 대해 알아보겠습니다.

사진 가져오기

먼저, RxSwift에서 사진을 가져오는 방법을 알아보겠습니다. 사진을 가져오기 위해서는 UIKit의 UIImagePickerController를 사용할 수 있습니다. UIImagePickerController는 카메라 또는 앨범에서 사진을 선택할 수 있는 기능을 제공합니다.

import RxSwift
import RxCocoa
import UIKit

func imagePickerViewController() -> Observable<UIImage> {
    return Observable.create { observer in
        let imagePicker = UIImagePickerController()
        imagePicker.sourceType = .photoLibrary
        imagePicker.allowsEditing = false
        
        let disposable = Disposables.create {
            imagePicker.dismiss(animated: true, completion: nil)
        }
        
        imagePicker.rx.didFinishPickingMediaWithInfo
            .subscribe(onNext: { info in
                if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
                    observer.onNext(image)
                }
                observer.onCompleted()
            })
            .disposed(by: imagePicker.rx.disposeBag)
        
        UIApplication.shared.keyWindow?.rootViewController?.present(imagePicker, animated: true, completion: nil)
        
        return disposable
    }
}

위의 코드에서 imagePickerViewController 함수는 UIImagePickerController를 생성하고, 이미지를 선택하면 해당 이미지를 onNext 이벤트로 방출하고, 선택을 완료하면 onComplete 이벤트를 방출하는 Observable을 생성합니다. 이 함수를 호출하면 사용자가 사진을 선택할 수 있는 화면이 표시됩니다.

사진 필터링

이제 사진을 가져왔다면, RxSwift를 사용하여 사진에 필터를 적용해보겠습니다. 필터링에는 Core Image 프레임워크를 사용할 수 있습니다.

import CoreImage

func applyFilter(to image: UIImage) -> Observable<UIImage> {
    return Observable.create { observer in
        DispatchQueue.global(qos: .background).async {
            let inputImage = CIImage(image: image)
            
            if let filter = CIFilter(name: "CIPhotoEffectMono") {
                filter.setValue(inputImage, forKey: kCIInputImageKey)
                
                if let outputImage = filter.outputImage,
                   let cgImage = CIContext().createCGImage(outputImage, from: outputImage.extent) {
                    let processedImage = UIImage(cgImage: cgImage, scale: image.scale, orientation: image.imageOrientation)
                    
                    observer.onNext(processedImage)
                }
            }
            
            observer.onCompleted()
        }
        
        return Disposables.create()
    }
}

위의 코드에서 applyFilter 함수는 UIImage에 필터를 적용하여 새로운 UIImage를 생성하고, 이를 onNext 이벤트로 방출합니다. 필터링은 백그라운드 스레드에서 수행되므로 메인 스레드에서 UI 업데이트를 수행해야 합니다.

사진 처리 흐름

마지막으로, 사진을 가져와서 필터를 적용하는 전체적인 흐름을 살펴보겠습니다.

imagePickerViewController()
    .observeOn(ConcurrentDispatchQueueScheduler(qos: .background))
    .flatMap(applyFilter)
    .observeOn(MainScheduler.instance)
    .subscribe(onNext: { filteredImage in
        // 처리된 사진을 보여줌
        imageView.image = filteredImage
    })
    .disposed(by: disposeBag)

위의 코드에서는 우선 imagePickerViewController 함수를 호출하여 사용자로부터 사진을 가져옵니다. 가져온 사진은 백그라운드 스레드에서 필터를 적용한 후, 메인 스레드에서 UI에 반영됩니다. subscribe를 통해 최종적으로 처리된 사진을 받고, imageView에 표시합니다.

이처럼 RxSwift를 사용하면 비동기적인 작업의 흐름을 간결하게 표현할 수 있으며, UI 업데이트와 백그라운드 작업의 조합도 쉽게 처리할 수 있습니다.

참고 자료