본문 바로가기

iOS

[WWDC 21] Demystify SwiftUI

ref: https://developer.apple.com/videos/play/wwdc2021/10022/

 

Demystify SwiftUI - WWDC21 - Videos - Apple Developer

Peek behind the curtain into the core tenets of SwiftUI philosophy: Identity, Lifetime, and Dependencies. Find out about common patterns,...

developer.apple.com

 

안녕하세요 :-) 정말 오랜만에 글을 써요

SwiftUI 재밌습니다. 종종 스유 글 쓰겠습니다!

 

오늘 소개해드릴 글은, SwiftUI를 조금만 알고 접해도 될 거에요.

소개에 앞서서, 두 방법으로 아무 뷰를 생성해 볼게요.

 

(1) SwiftUI

struct BottomSheet<Content: View>: View {
  ~
  var body: some View {
  ~
  }
}

 

(2) UIKit

final class BottomSheet: UIView, UIComponentable {
	~
}

 

이제 두개가 만들어져서 사용만 하면 될 거에요. 사용 전에, 눈에 띄는 가장 큰 차이는

SwiftUI (이하 스유)는 값 타입인 struct 

UIKit은 참조타입인 class 타입인 점만 알아도, 오늘 포스팅에는 문제가 없을것 같아요.

 

해당 세션은 크게 세 주제에 대해 설명해 주십니다. 

(1) Identity, (2) Lifetime (3) Dependencies 

 

위 내용중 중요한 내용만 간추려 다뤄보도록 하겠습니다!

 

(1) Identity

그런데 왜 위 주제를 UIKit에서는 해주지 않고, SwiftUI에서는 해 주어야 할까요?
근본적인 원인은 view 객체를 구조체 (struct) 가 담당하기 때문입니다. 
사람에게 지문이 있듯, 메모리 주소로 비교할 수 있었던 class와는 달리 (pointer identity)
구조체는 값 타입이므로 이전처럼 뷰를 비교할 수 없습니다.

 

이것까지만 들으면 어? 스유..? 하며 의문이 드실수 있는데, SwiftUI의 뷰는 UIKit의 뷰에 비해서

1. 훨신 값이 쌉니다 (UIView NSView 등의 상속을 받지 않는 등의 이유). 이 세션에는 없지만, 그리는 비용도 작습니다.
2. 덕분에 효율적인 메모리 사용
3. 그렇다면 어떻게 구별하나요? 이제 소개합니다. SwiftUI 고유의 방법으로 뷰를 식별합니다.

 

- 1 Explicit identity

이름에서도 알 수 있듯, 뷰 객체에 identity를 지정해 줄 수 있습니다. 

물론 선택입니다. A부터 Z 객체가 있을때, 모든 객체에 identity를 지정해 줄 필요는 없습니다.

상황 설명을 해보면, ScrollViewReader에 책을 포함하고 있고, 

어느 특정 액션을 하면 원하는 스크롤 위치로 이동시키고 싶은것이에요.
SwiftUI 코드를 위에서 아래로 읽어볼게요.

 

1) Pretzel 그림이 있고 설명이 있는 header에 id 값을 부여합니다.

2) 그 뒤, 책을 읽다가, 
3) 어느 임의의 버튼을 누르면! * headerId 를 찾아

 ScrollViewReader의 proxy를 통해 스크롤 시켜주는 것이에요.

 

해당 코드에서 ScrollView는 id value를 가질 필요가 없겠죠?

이것이 Explicit Identity입니다. 원하는 뷰 객체에 아이덴티티를 부여해서 만들어 보아요

물론 identity는 해셔블 이쿼터블을 만족하는 엘리먼트여야 한다는 점 참고!

 

-2 Structure Identity

되게 귀여운 강아지에요, 세션 진행하시는 분 강아지인지는 모르겠네요 ㅎㅎ

 

둘은 같은 강아지인데, 어느때는 착하고, 어느때는 말썽을 피울수도 있겠지요?

그렇다고 해서 둘은 다른 강아지가 아닙니다.

해당 뷰도 마찬가지입니다. SwiftUI로 뷰를 하나 그렸어요. 해당 그림에서 뷰 구조체는 몇개여야 할까요?

 

정답은 하나여야 합니다. 하나로 만들어야 함을 권장하고 있습니다.

이 뷰가 하나라고 인식이 되면, SwiftUI는 fluent한 animation을 자체적으로 제공해 준답니다. 

 

예를 들어서 이런 코드를 짜 볼게요.

이렇게 body를 리턴하는 클로저가 있습니다. body는 @viewBulider라는 propertyWrapper가 사실 감싸고 있는거에요.

(몰라도됩니다) (근데 알면좋음) (보니까 있다가 설명하네요)

 

그럼 이 코드를 컴파일러가 볼때는 어떻게 보게 되냐면

이렇게 보게 되는거에요!

 

우측을 잘 봐 보시면, true Statement일때와, false Statement일때 각각 를 리턴해 주고 있지요,

따라서, 이 구문은 2개의 뷰를 리턴하고 있는거에요. 만약 조건이 true / false / true / false 반복하게 된다면,

해당 뷰는 뷰를 지우고 새로운 뷰를 만들기를 4번 반복하게 될거랍니다.

 

여기서는, true 일때는 무조건 AdoptionDirectory, false 일때는 무조건 DogList를 리턴하게 되지요?

이렇게 구조로도 identity를 가질 수 있다는 것입니다 :-)

 

부연 설명을 해볼게요.

이 코드는 어떨까요? PawView 라는 같은 뷰를 같이 리턴하니까, 하나의 뷰만 리턴하는 것일까요?

그렇지 않습니다. 각각 if 조건문의 값을 identity로 가지고 있는거에요. 이건 그래서 뷰가 2개!

 

이건 권장하지 않아요. 그럼 어떻게 개선하냐면?

위와 같은 방식으로 선언했을때는 

if나 switch같은 구조에 뷰를 리턴하고 있지 않지요? 그럼 얘는 하나의 뷰를 리턴하게 됩니다.

 

조금 이해가 가면서도 아리송하시지요?

요건 아까 제가 언급해드린, @viewBuilder라는 프로퍼티를 설명들으면 이해가 될 거에요.

 

그럼 위의 설명을 조금 이해하셨다면, 이 조건문은 어떠세요?

상당히 많은 가지로 뷰를 리턴하고 있어요. 

게다가, AnyView로 리턴하고 있어요. Swift로 생각하면, AnyObject 또는 Any로 온 값을 구별할 수 있나요?

정보가 없다면 알 수 없겠죠, 따라서 해당 코드는 틀린 코드가 됩니다. 비권장!

해당 코드를 개선해 볼 예정입니다.

 

아! 그렇다고 해서, if나 switch statement를 쓰지 말라고 하는것은 아닙니다. 필요할 때는 써야 하지요?

 

자, @ViewBuilder가 무엇인지 설명할 때가 되었어요. 모든 body 변수에는,

뷰빌더 propertyWrapper가 암시적으로 내포되어 있고 보이지는 않습니다.

이는 암묵적으로 하나의 제네릭 뷰를 리턴한다는 뜻을 알리는 것입니다.

따라서 해당 코드는, 컴파일러가 예측할 수 있습니다 (아 얘는 하나의 제네릭 뷰를 리턴하겠구나).

 

그러면?

위 코드에서 AnyView를 먼저 지우고, 구문을 정리해 볼게요.

리턴이 없다며 에러가 납니다. 컴파일러가 뷰를 리턴해주는 것이라고 감지하지 못했나봐요.

 

그럼 위에서 배운대로 
해당 함수에, 이 함수는 하나의 제네릭 뷰를 리턴한다는 뜻의 Viewbuilder 어노테이션을 명시적으로 선언해 주게 되면,

에러가 사라지게 됩니다. 아직 조금 아쉽지요?

switch - case로 정리하면 백점.

컴파일러는 이런 구조로, structure-identity를 구분하게 됩니다. 

 

이런 식으로,
SwiftUI의 모든 뷰는 identity를 가지게 됩니다. 명시적으로 캐치가 필요한 경우는 .id 를 통해서 값을 줄 수 있겠고,

일반적으로는 structure-identity를 가지게 된다는 것입니다!

 

이어서, lifetime, Dependencies에 대해서도 글을 써 보겠습니다!