[Swift 톺아보기] SwiftUI - 2

kio·2022년 7월 19일
1

Swift

목록 보기
3/11
struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

이 기본코드를 분석해보자

SwiftUI로 프로젝트를 만들고 처음 보는 이 화면 이 ContentView가 무엇을 뜻하는 지 보자.

우선 첫번째

"엥? 화면에 보여지는 것이 Viewcontroller가 아닌 View다? 심지어 struct자나?"**

그렇다 처음부터 신기했던건 이 struct이다.

우선 기존의 UIKit을 보자

class ViewController: UIViewController {
	...
}

class인 UIKit과 달리 SwiftUI는 struct이다.

알다시피 struct는 값타입 이기때문에 멀티쓰레딩환경에서나 강한순환참조 문제를 맞이하지 않는다는 장점이 있고, 또 메모리를 가볍게 사용한다.
참조타입이 class는 컴파일단계에서 언제 메모리에 할당되는지 해제되는지 알 수가 없고 그 과정에서 참조계산을 해야하는 하고, process에서 heap은 공유되는 영역이기 때문에 thread safety하지 않다.( 아무래도 os관련된 블로그를 다시해야할거 같다. )

그렇기 때문에 struct로 하는 것은 매우 좋은 방법이다.

하지만 그에 따르는 단점도 있기 마련이다.

struct 단점

  1. 상속을 받을 수 없다. 애초에 상속이라는게 어떤한 값을 참조받는 것인데 그렇게 되면 stack에 올릴 수 없다. ( struct안에 참조타입이 있으면 stack에 저장되긴 하지만 참조를 계산해야하는 문제가 발생한다. )
    그 뒤에 나오지만 SwiftUI에서 이 문제를 View protocol로 해결했다.

  2. Objective-c에서는 사용이 안된다.
    이 문제는 그냥 문제로 남겨두었다. 그래서 ios 13 부터 지원한다.

두번째,

"엥? struct인데 어떻게 상속을 받고 있지???"

좀 멍청하긴하지만 난 처음에 contetnview가 uiview를 상속받고 있는줄 알고, 뒤집어 졌다. 사실 uiview 즉 uikit의 class가 아닌 SwiftUI에 있는 프로토콜이었다.

그럼 거의 오늘 젤 중요한 View protocol에 대해서 알아보자

View Protocol

이건 애플 공식문서에서 나타낸는 View protocol의 정의이다.

앱 사용자 인터페이스의 일부를 나타내고 보기를 구성하는 데 사용하는 modifiers 제공하는 유형입니다.

요것이 protocol의 요구사항이다 body라는 것과 modifier라는 것이 있다.

associatedtype은 추후에 알아보도록 하고, modifier란 것에 대해 알아보자


자 modifier의 document이다 view를 받아 새로운 view를 반환한다.

이것을 보는 순간 전에 블로그에 올린 functor와 monad가 떠올랐다.

틈새 복습

모양이 상당히 유사하다.
functor보단 monad의 모양과 유사하다.
monad가 non-blocking pipeline을 위해 입력과 출력값을 통일 시키는 것처럼
ModifiedContent라는것을 뱉는다.
이렇게 되면 modifier를 배출했을 때 오류가 생겨도 문제가 생기지 않고 똑같이 다음 단계로 처리할수 있을 것이다.
공부하면서 일련적으로 처리하는 ( 함수형 프로그래밍에서 연속된 함수를 처리 ) 어떠한 행동에이 이처럼 입력과 출력을 똑같이 맞추고, 에러를 컨트롤 할 수 있는 형태로 하는것이 정석인것 같다.
그렇다고 swiftui 에서는 error를 묶어서 처리하는 것 같진 않다

modifier는 수정자라는 의미로 View에 소속되어 있는 친구이다.
View를 수정하는 것들을 modifier의 메소드라고 한다.

struct ContentView: View {
    var body: some View {
        Text("Hello, world")
            .font(.largeTitle)
    }
}

이 코드를 보자. font는 modifier 메소드이다.
그럼 그럼 Text는 View라는 소리이다 Text를 봐보면

이렇게 View 프로토콜을 지키고 있다. Body는 never이다 즉 원시뷰라는 소리이다.
never이란

즉 그냥 값 없이 끝난 것이다.
이것이 무슨 소리이냐
사실 View 하나의 body가 핵심이다.
하지만 이 body가 여러개의 View를 호출해서 하나의 view로 만든다.
이 글에 따르면 마치 재귀 함수같이 body가 또다른 View를 부르고 또 부르고 이처럼 연속적이다 그럼 끝이 필요하다.
여기서 저 body가 never인 View들이 원시 view인 것이다.
그렇기 떄문에 이것은 다른 View가 아닌 자기 자신이 마지막인 것이다.

즉 modifier 메소드는 body를 self의 형태의 modifier를 합친 것으로 수정하는 일의 연속인 것이다

세번째,

"오케이 body는 알겠어 View의 본체다. 이거지? 그럼 some View는 뭐야 body는 왜 some View가 되어있는거지?"

위에 View의 implementing을 보시면 아시겠지만, Bodysms Associatedtype 즉 특정되지 않은 타입이라는 것입니다.
허나 제약은 있죠 view여야 합니다.

그럼 우리는 body라는 연산 프로퍼티의 리턴값의 정확한 타입은 모릅니다.
body안에 Text가 있을지, button이 있을지 아무도 모릅니다.
그러면 body는 무슨 타입이여야할까요?

func isSame<T:Equatable>(_ a: T, _ b: T) -> Bool

이렇게 generic의 타입이 어떤 프로토콜을 준수해야한다면 위와같이 작성할수 있습니다. 이 함수를 사용하는 곳에선 이걸 지켜야한다 라는거죠
그럼 넣는건 상관없는데 반환 값이 내부에서 바뀐다면? 하지만 특정 프로토콜을 지킨다면?

그럴때 some을 사용하면 됩니다.
외부에서 지켜야 하는 규칙이 generic이라면 some은 내부에서 지켜야하는 것입니다.
즉 그냥 타입을 입력하는 곳에 "a 프로토콜을 지키는 어떤 것도 된다 라고 하려면 some a 하면 된다. ( 항상 되는 것은 당연히 아니죱)"

네번째,

Text("Hello, world")

위에서 줄기차게 설명해서 이제는 쉬울것이다.
결국 Text는 View를 채택한 struct이고, 원시뷰이기 때문에 바로 그림이 그려집니다. "Hello, world"는 그저 init에서 stringprotocol을 지키는 변수를 받으면 그 컨텐츠로 지정해주는 것 뿐입니다.

last modifier를 이해하기 위한 살짝의 응용

struct ContentView: View {
    var body: some View {
        Text("Hello, world")
            .modifier(changeText())
    }
}
struct changeText: ViewModifier {
  func body(content: Content) -> some View {
    content
      .font(.largeTitle)
      .foregroundColor(.yellow)
      .border(.black, width: 10) 
  }
}

custom을 위한 viewmodifier라는 것을 자체적으로 만들어 놨고, 이는 modifier 취급을 하면 된다 ( 쉽게 생각해서 )
역시 ViewModifier이기 때문에 View의 특성을 가지고 있는데 이때 body는 함수의 형태이다.
content라는 body에 이것저것 꾸며서 이를 보내는 것이다.

위에서 주구장창 얘기를 해서 쉽게 머리에 들어온것같다.

회고

  • never를 이해했다.
  • 원시 뷰에 대해 이해했다.
  • 기본적인 SwiftUI의 구조를 파악했다

의문점

  • UIKit에서는 ViewController를 중심으로 responder chain patter으로 event에 대응 하는 곳을 찾았다면 viewcontroller가 없는 swiftui는 어떻게 event를 대응하나 궁금했다.

  • View프로토콜안에 error를 전달받을 수 있는지? 이렇게 new View를 전달하는 과정으로 이루어진 SwiftUI에서 중간에 ui에 error 발생하면 무시를 하는지 아님 어떻게 처리하는 지 의문점이 들었다.

    자 오늘도 피드백이나 충고 겸허히 받아드리니까 많이들 해주세요 제발~~~

0개의 댓글