[SwiftUI] View 채택을 꼭 struct로 해야할까?

GUNDY·2023년 11월 1일
1

iOS

목록 보기
1/4
post-thumbnail

오랜만에 Optional 문서를 쭉쭉 읽다가 Conforms To 탭을 보았다.

오~ 아~ 옵셔널은 Wrapped가 이러면 디코더블을 준수하는구나~

음 센더블~...어? View?

SwiftUI의 View가 프로토콜이었어?

스유알못인 나는 View가 프로토콜인지도 몰랐네?

근데 그러면 채택만 시키면 클래스로도 View를 만들 수 있겠네?

그래서 준비했다. View 채택 희망편 - struct

VStack을 활용해 아주 간단히 버튼을 누르면 숫자가 하나씩 올라가는 화면을 만들었다.

import SwiftUI

struct ContentView: View {
    
    @State private var count: Int = 0
    
    var body: some View {
        VStack {
            Text(String(count))
            Button(action: {
                count += 1
            },
                   label: {
                Text("PLUS")
            })
        }
    }
}

버튼을 누르면?

어 왜 2야?

주인장이 별 생각 없이 두 번 눌렀기 때문이다.
주인장이 별 생각 없이 두 번 눌렀기 때문이다.

SwiftUI로 화면 만들기 쉽네!

아니 뭐 그냥 프로젝트 만들 때 SwiftUI 체크만 하면 알아서 struct로 코드 다 제공해주고 생각보다 쉽잖아?

훗날 나는 이 말을 후회하게 된다.

그래서 준비했다. View 채택 절망편 - class

아니 뭐 그냥 View 프로토콜 채택만 하고 body 프로퍼티만 만들어주면 되지 않겠어?

게다가 만들어 놓은 struct에서 단어만 class로 바꿔주면 되지 ㅋㅋ 개꿀~

응 아니야~

...어라라?

Reference to property 'count' in closure requires explicit use of 'self' to make capture semantics explicit

이건 매개변수 action에 전달되는 이스케이핑 클로저 내부에서 호출하는 것이니까 self를 붙여주면 된다 치고

Protocol 'View' requirement '_makeView(view:inputs:)' cannot be satisfied by a non-final class ('ContentView') because it uses 'Self' in a non-parameter, non-result type position

Protocol 'View' requirement '_makeViewList(view:inputs:)' cannot be satisfied by a non-final class ('ContentView') because it uses 'Self' in a non-parameter, non-result type position

너넨 뭐니...?

_makeView(view:inputs:)_makeViewList(view:inputs:)View 프로토콜의 static 메서드였다.

아마도 기본 구현으로 제공되는 메서드라는 생각이 드는데 View 공식 문서 어디에서도 찾을 수 없다.

의도적으로 숨겨놓은 메서드인 것 같은데, 어쨋든 이 메서드들의 매개변수를 보면 view: SwiftUI._GraphValue<Self>Self가 사용됨을 알 수 있다.

혹시 이 악물고 내가 이름까지 다 쳐서 직접 구현하면 어떡할래?

오케이 그래도 문제는 해결되지 않는다.

Covariant 'Self' or 'Self?' can only appear as the type of a property, subscript or method result; did you mean 'ContentView'?

Protocol 'View' requirement '_makeView(view:inputs:)' cannot be satisfied by a non-final class ('ContentView') because it uses 'Self' in a non-parameter, non-result type position

Self 대신 ContentView 써 볼래? 하는 메시지와 이 위치에서 Self 쓸 거면 final 아니면 안 됨~ 하는 메시지가 같이 나온다.

혹시 Self 대신 ContentView를 써 보면?

...

아무튼 그럼 final 붙이면 되는 거 아님?

갓 챠!

는 무슨 터져버리는구만

Thread 1: Fatal error: views must be value types (either a struct or an enum); ContentView is a class.

값 타입이어야 한다는군요

하지만 저는 비로소 알아낸 것이 있습니다.

class는 불가능해도 enum이라면 가능하다고 저 메시지가 알려준 것을!

그래서 준비했다. View 채택 촉망편 - enum

후후후 뭐 class는 역시 무리였지만 enum으로만 글자를 바꿔주면 되지 않겠어?

건디야 또 속냐?

심지어 이제는 프리뷰에서조차...

다시 차분히 문제를 쉬운 것부터 해결해보자.

'ContentView' cannot be constructed because it has no accessible initializers

확실히 나는 기본 이니셜라이저를 따로 구현하지 않았기 때문에 매개변수가 없는 기본 이니셜라이저를 호출할 수가 없지 음음

빈 구현의 이니셜라이저를 하나 추가해야지

바로 멸망이냐?

하긴 내 생각이 짧았지 self를 어떻게 정해주겠어

위와 같은 코드들을 통해 ContentView()를 호출할 수 있도록 마무리해주었다.

Enums must not contain stored properties

음음 열거형엔 저장 프로퍼티를 넣을 수 없지

Non-static property 'count' declared inside an enum cannot have a wrapper

열거형 내부에 선언된 프로퍼티는 static 이어야만 프로퍼티 래퍼를 가질 수 있다? 요건 몰랐네.

타입 프로퍼티로 바꾸면 해결이 되겠군?

그럼 이제 하나 남았나?

Left side of mutating operator isn't mutable: 'self' is immutable

그런데 이 문제는 count 프로퍼티를 static으로 바꾸면서 자동으로 해결이 됐다.

Static member 'count' cannot be used on instance of type 'ContentView'

대신 스태틱 멤버니까 타입 이름 붙여주라고 메시지가 바뀌었다.

자 이제 타입 이름까지 붙여주고 나면

오랜만이야 내 화면아

역시 넌 될 줄 알았어!

이제 기쁜 마음으로 PLUS 버튼을 눌러본다.

어 저 그 너...왜 안 되니?

ContentView.count가 전혀 늘지 않았다.

더 쉽게 마구 눌러서 보려고 print를 찍어보았다.

ContentView.count가 변하지 않는 걸까?

print 다 찍히는 걸 보면 클로저도 잘 호출되는 것 같은데...?

뭐 그런 것 보다

애초에 enum으로 View를 만들 것이라면 case와 관련해서 사용하는 것이 좋을 것 같다.

그렇지만 버튼을 눌렀을 때 case를 바꾸려고 하면 어김없이 다음 메시지가 날 찾아올 것이다.

Cannot use mutating member on immutable value: 'self' is immutable

저장 프로퍼티를 쓸 수 없으니까 어떻게 해야 좋을지 참 고민이 된다.

일단 오늘은 enum으로 View를 만들 수 있다는 것을 안 것에서 그쳐야겠다.

결론

View를 구조체로 하는 데에는

구조체

  • 가장 기본적으로 사용하며 모든 예제가 구조체로 되어있고 저장 프로퍼티까지 쓸 수 있는 갓갓 타입

클래스

  • 참조 타입은 쓸 수도 없다...

열거형

  • 일단 값 타입이라 View를 채택해서 화면을 구성할 수는 있다.
  • 하지만 저장 프로퍼티도 못 쓰고 빈 구현의 이니셜라이저도 못 써서 기본적으로 사용하기엔 꽤나 불편한 타입이라고 할 수 있겠다.

사람들이 익숙하게 사용하는 데에는 역시 이유가 있다.

이유를 모르고 따라가도 좋지만 이유를 알고보니 구조체로 SwiftUI에 도전할 마음이 조금은 생긴 것 같다.

profile
개발자할건디?

0개의 댓글