◎위챗 : speedseoul
https://www.sangkon.com/ios-newbie-01/
iPhone 3GS, iPhone 4/4S 시절에 잠시 했던 경험은 전혀(!) 도움이 되지 않았습니다. 옛 경험은 잊고 새로운 환경에 적응할 수 있는 자신만의 방법이 필요합니다.
나의 사수가 iOS 개발에 특화된 개발자라 학습 시간이 대폭 줄었습니다. 아마 시간적으로 따지자면 20~30시간 정도는 줄일 수 있다고 판단됩니다. Issue 3개를 5일간 처리했는데, 이 중에서 2개는 전혀 모르는 상태로 시작했다는 것을 감안하면 매우 훌륭한 출발입니다.
시간만 허락한다면 짝코딩을 통해서 좀 더 개발 시간을 단축할 수 있을 것 같습니다. 큰 틀에 대해서 같이 논의하고 세부적인 사항에 대한 내용을 코드 작성 후 리뷰를 하는 방식은 초급자가 해당 코드에 대한 이해도를 높이고, 코드 설계에 대한 눈높이를 맞출 수 있기 때문입니다(제 개인적인 견해입니다.)
하지만 우리 사수가 combine
을 도입... 난 어쩔...
// 대략적인 흐름
// 1. 권한 인증에 필요한 상태 정의
enum SessionSetupResult {
case success
case notAuthorized
case configurationFailed
}
// 1. 초기 상태
var isVideoPermission: SessionSetupResult = .success
var isAudioPermission: SessionSetupResult = .success
// 2. 권한 검사
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
break
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video, completionHandler: { granted in
if !granted {
isVideoPermission = .notAuthorized
}
})
default:
isVideoPermission = .notAuthorized
}
switch AVCaptureDevice.authorizationStatus(for: .audio) {
case .authorized:
break
case .notDetermined:
AVCaptureDevice.requestAccess(for: .audio, completionHandler: { granted in
if !granted {
isAudioPermission = .notAuthorized
}
})
default:
isAudioPermission = .notAuthorized
}
// 3. 권한 검증
if ((isVideoPermission != .success) || (isAudioPermission != .success )) {
print("Audio and Video not permission")
}
UIViewController에 카메라 Preview 화면을 붙여야 했으며, 이를 위해서 AVFoundation을 사용하였습니다(물론 나의 사수께서 MetalKit으로 나중에 변경). 이걸 하려니 UIViewController의 Life Cycle과 App의 상태를 알아야 했기 때문에 관련 내용을 정리하였습니다.
화면을 구성하는 요소인 UIView와 UIController에 대한 이해도가 너무 낮아서 기본적으로 주어진 것들을 제대로 사용하지 못하고 코드를 잔망스럽게 만들었습니다. 아래 코드에서 확인 할 수 있듯이 iOS에서 제공하는 기본적인 구성요소에 대한 내용을 충실하게 알아둘 필요가 있습니다.
Life Cycle을 명확한게 이해하지 못하면, 필요한 코드를 적절한 곳에 사용하지 못합니다. 모바일 앱의 경우 대부분의 제어권이 외부에 있기 때문에 Life Cycle에 대해서 심도 깊은 학습이 필요합니다. 한번만 잘 해두면 두 번 괴로울 일이 없으니 잘 알아두도록 합시다.
// 나의 사수(jaemyeong.dev@gmail.com) 코드
class PreviewView: UIView {
//
override class var layerClass: AnyClass {
return AVCaptureVideoPreviewLayer.self
}
var previewLayer:AVCaptureVideoPreviewLayer {
return self.layer as! AVCaptureVideoPreviewLayer
}
}
// 이것은 나의 잔망스러운 코드
// 잘 만들어진 코드 같지만 사수꼐서 자바처럼 만들지 말라하셨음
class LiveFilterViewController: UIViewController {
private var window: UIWindow?
private var mainView: UIImageView!
private let captureSession = AVCaptureSession()
private let previewLayer = AVCaptureVideoPreviewLayer()
var position:AVCaptureDevice.Position = .back
var perferredDeviceType:AVCaptureDevice.DeviceType = .builtInWideAngleCamera
var cameraDeviceInput:AVCaptureDeviceInput?
var audioDeviceInput:AVCaptureDeviceInput?
var videoOutput:AVCaptureVideoDataOutput?
var audioOutput:AVCaptureAudioDataOutput?
override func viewDidLoad() {
super.viewDidLoad()
self.window = UIWindow(frame: UIScreen.main.bounds)
mainView = UIImageView(frame: (self.window?.frame)!)
self.view.addSubview(mainView)
setPreViewLayer()
getAuthrized(for: [.video, .audio])
}
func setPreViewLayer() {
previewLayer.session = self.captureSession
previewLayer.videoGravity = .resizeAspectFill
previewLayer.frame = view.bounds
mainView.layer.addSublayer(previewLayer)
}
...
App은 (언제나 그러하듯이) 5가지 상태(state) 중 하나로 iOS 시스템(system)에서 동작합니다.
저는 iOS 꼬꼬마라서 다른 상태는 관심이 별로 없고, Active
상태에 지대한 관심이 있습니다. 기본적으로 App이 화면에 출력되는 과정은 아래와 같습니다.
UIApplication
객체(@UIApplicationMain
) 를 생성한다.UIApplication
객체는 event loop(https://developer.apple.com/library/archive/documentation/General/Conceptual/Devpedia-CocoaApp/MainEventLoop.html)를 관리하며, AppDelegate
에 이벤트를 전달합니다.info.plist
를 참고하여 App에 필요한 정보를 가져온다.AppDelegate
객체 생성 후 UIApplication
객체와 연결한다.AppDelegate
는 UIApplication
객체가 이벤트 및 관련 코드에 관한 것을 위임한 클래스입니다. App당 하나의 위임 객체를 갖도록 보장되며, UIApplicationDelegate
프로토콜(protocol)을 사용합니다.UIApplication
객체는 application(_:willFinishLaunchingWithOptions:)
와 application(_:didFinishLaunchingWithOptions:)
을 차례대로 호출한다.@IBOutlet
, @IBAction
등)과 연결한다.applicationWillTerminate(_:)
호출하고, App을 종료한다.App의 상태가 변하면 AppDelegate에 구현된 UIApplicationDelegate
메서드가 호출됩니다. App의 실행 상태에 따라 필요한 메서드를 실행할 수 있습니다.
application(_:willFinishLaunchingWithOptions:)
application(_:didFinishLaunchingWithOptions:)
applicationDidBecomeActive(_:)
applicationWillResignActive(_:)
applicationDidEnterBackground(_:)
applicationWillEnterForeground(_:)
applicationWillTerminate(_:)