먹고 기도하고 코딩하라

[Swift] if #unavailable 본문

앱/Swift

[Swift] if #unavailable

사과먹는사람 2023. 3. 10. 21:32
728x90
728x90

 

Swift 5.6 버전이 나온지 어느덧 1년이 지나 다소 뒷북이긴 한데 의외로 많이 알려지지 않은 것 같다. 

사실 if #unavailable에 대한 설명 자체는 여기에 잘 정리되어 있는데, 간단히 살펴보고 어떤 경우에 쓰면 좋은지를 함께 소개하고자 한다.

 

 

기존 if #available의 쓰임새

타겟 기기의 iOS 버전에 따라 분기를 태워야 하는 경우가 있다.

최근에 카플레이를 해서 이걸로 예시를 들자면, Carplay 프레임워크는 iOS 14 이상에서만 사용할 수 있다. 그리고 이 프레임워크를 사용해야 카플레이를 지원하기 위한 앱을 프로그래밍할 수 있다.

그러니까 다음과 같은 상황이다.

  • 주 기능은 원래 제공하고 있었다.
  • iOS 14 이상에서는 카플레이도 추가로 제공하겠다.

즉, 플랫폼별로 제시하는 OS 버전 최하 조건을 만족했을 때 뭔가를 추가로 하겠다는 게 available의 일반적인 사용법이다.

 

“경고” 메시지를 담은 alert를 띄우는 상황을 생각해보자. 폰에는 기본적으로 alert를 띄워주고, iOS 14 이상, 카플레이 스크린의 씬이 살아있을 경우에만 카플레이 화면에 추가로 alert를 띄워주려 한다.

presentAlert는 기본적으로 한다.

그리고 타겟 기기가 iOS 14.0 이상 && CPSceneDelegate의 isSceneAlive가 true일 때만 showAlert를 추가로 실행한다.

func showAlert() {
  let message: String = "경고"
	presentAlert(message: message)
	if #available(iOS 14.0, *) {
		if CPSceneDelegate.isSceneAlive { delegate in 
			delegate.showAlert(message: message)
		}
	}
}

이처럼 #available 조건은 어떤 코드가 특정 버전 이상에서 추가로 가능할 때 처리해주기 위해 쓰기 좋다.

 

 

if #unavailable이 필요한 이유

이번에는 앱의 메인 윈도우를 만드는 상황을 생각해보자.

메인 UIWindow를 만드는 방법은 iOS 13 전과 그 이후가 다르다. AppDelegat만 있을 때는 didFinishLaunching에서 윈도우를 셋업하지만, SceneDelegate을 지원하는 앱은 AppDelegate가 앱의 메인 UIScene에 연결되고 나면 윈도우를 셋업하는데, 이 타이밍에서 didFinishLaunching은 이미 return하고 끝나 있다.

메인 윈도우 셋업하는 타이밍 자체가 다르기 때문에 iOS 13 미만 유저와 그 이상 유저에 모두 대응하려면 didFinishLaunching에서 다음과 같이 13 미만에서만 윈도우를 써줘야 할 것이다.

if #available(iOS 13.0, *) {

} else {
	// iOS 13 미만에서는 appDelegate의 window를 사용한다.
	window = UIWindow(frame: UIScreen.main.bounds)
	window?.rootViewController = UINavigationController(rootViewController: MainViewController())
	window?.makeKeyAndVisible()
}

의도대로 동작은 하지만 if문이 텅 빈 채로 남아 있다.

available 컨디션 자체가 제시하는 OS “이상”일 때만 추가로 쓰는 것이지, “미만”일 때 조치를 취하려고 쓰는 게 아니라서 이렇게 쓰이는 것이다.

 

그렇다면 guard문을 쳐서 else문에 넣으면 어떨까?

guard #available(iOS 13, *) else {
	// iOS 미만에서는 appDelegate의 window를 사용한다.
	window = UIWindow(frame: UIScreen.main.bounds)
	window?.rootViewController = UINavigationController(rootViewController: MainViewController())
	window?.makeKeyAndVisible()
	return
}

이건 guard문의 원래 취지로 권장되는 코드 스타일에 어긋난다.

guard의 본질은 early exit이다. 현재 블록의 다른 expression들을 실행하기에 앞서, 이 조건이 안 맞는다면 블록을 탈출하는 용도로 쓰는 것이지 else문에 하고 싶은 걸 넣는 방식으로 쓰는 게 아니다.

 

 

if #unavailable의 사용법

이럴 때 #unavailable을 써주면 좋다. #available의 반대격 된다.

아래 코드는 iOS 13 미만에서만 실행된다. 의미가 명확하며, if문 안쪽을 비운 채로 그냥 내버려두지 않을 수 있다.

// iOS 13 미만에서는 appDelegate의 window를 사용한다.
if #unavailable(iOS 13.0) {
	window = UIWindow(frame: UIScreen.main.bounds)
	window?.rootViewController = UINavigationController(rootViewController: MainViewController())
	window?.makeKeyAndVisible()
}

#available의 경우, if문 안쪽으로 들어가는 블록이 availability가 더 높은데, #unavailable의 경우 else문으로 들어가는 블록이 availability가 더 높다.

 

 

 

References

 

728x90
반응형
Comments