오랜만에 Optional 문서를 쭉쭉 읽다가 Conforms To 탭을 보았다.
오~ 아~ 옵셔널은 Wrapped
가 이러면 디코더블을 준수하는구나~
음 센더블~...어? View?
스유알못인 나는 View가 프로토콜인지도 몰랐네?
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")
})
}
}
}
버튼을 누르면?
주인장이 별 생각 없이 두 번 눌렀기 때문이다.
주인장이 별 생각 없이 두 번 눌렀기 때문이다.
아니 뭐 그냥 프로젝트 만들 때 SwiftUI
체크만 하면 알아서 struct
로 코드 다 제공해주고 생각보다 쉽잖아?
훗날 나는 이 말을 후회하게 된다.
아니 뭐 그냥 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
이라면 가능하다고 저 메시지가 알려준 것을!
후후후 뭐 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
를 채택해서 화면을 구성할 수는 있다.사람들이 익숙하게 사용하는 데에는 역시 이유가 있다.
이유를 모르고 따라가도 좋지만 이유를 알고보니 구조체로 SwiftUI
에 도전할 마음이 조금은 생긴 것 같다.