contents
Entrypoint
: 응용프로그램이 시작될 때 Program Counter는 Entry Point의 주소로 Branch 한다.
•
Swift의 Entry Point는 main함수
•
Entry Point를 표기하는 방식
1.
Attribute @main
2.
Attribute @NSApplicationMain
3.
Attribute @UIApplicationMain
4.
a main.swift file
5.
a file that contains top-level executable code.
모두 Entry Point(main 함수)를 생성하며 OS는 이를 호출하여 프로그램을 실행한다.
•
main은 Entry Point의 Symbol로 사용이 된다.
@main
스위프트 5.3버전에서 구현된 범용적인 새로운 엔트리 포인트 제공 기능
•
이전에는 @UIApplicationMain Attribute로 엔트리포인트를 지정
•
왜 @main 이 범용적인가?
◦
단일 파일 코드, 프레임워크, 프로젝트, 커스텀 라이브러리 상관없이 동일하게 엔트리포인트를 제공해주기 때문에!
•
'<TYPE>' is annotated with @main and must provide a main static function of type () -> Void or () throws -> Void.
import SwiftUI
@main
struct SwiftUI_TutorialTest {
static func main() {
print("hi")
}
}
Swift
복사
AppDelegate
우리가 만든 프로젝트에서는 main() 를 볼 수 없습니다. UIKit 프레임워크 내부에 숨어있기 때문입니다.
AppDelegate는 프로토콜 확장을 통한 기본 구현으로 static func main() 을 제공합니다.
AppDelegate 클래스는 UIResponder와 UIApplicationDelegate 프로토콜을 채택하고 있습니다.
•
UIApplicationDelegate가 main()의 구현을 제공합니다. 즉, UIApplicationDelegate를 채택한 AppDelegate에 컴파일러에 추가된 unique main entry code인 AppDelegate.main()이 실행됩니다.
App
: 앱의 구조와 동작을 나타내는 타입 or 앱의 content를 나타내는 scene representing 타입.(프로토콜)
@main
struct SwiftUI_TutorialsApp: App {
}
Swift
복사
SwiftUI에서는 App 프로토콜이 main()의 구현을 제공해줍니다.
Scene
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(modelData)
}
}
Swift
복사
•
시스템에서 관리하고 life cycle이 있는 앱 UI 일부
•
시스템은 실행중인 플랫폼에 따라 사용자에게 표시하는 방법을 결정
•
WindowGroup : SwiftUI가 기본적으로 제공하는 primitive scene type 중 하나
◦
동일하게 구조화된 window들의 그룹을 표현하는 Scene
◦
각 Window의 템플릿 역할을 합니다.
◦
WindowGroup은 iPadOS/macOS 와 같이 여러 Window를 지원하는 플랫폼에서 여러 자식을 인스턴스화 할 수 있다.
◦
각 Scene은 UI를 공유하지만, 모두 자신만의 독립적인 State를 가집니다.
여러 Window를 띄운다는게 뭘까!
◦
한 Window에서 상태를 변경해도 다른 Window에 영향을 주지 않는다.
•
Scene의 수명주기는 실행중인 플랫폼에서 담당합니다.
View
•
Scene의 content를 형성. 플랫폼에서 독립적으로 표시가 가능합니다.
•
body라는 프로퍼티가 요구됩니다. 그리고 body는 View 프로토콜이 준수하는 인스턴스를 리턴해야합니다.
View가 Scene를 구성(Scene의 content)하고 Scene이 모여서 App이 된다.
Property Wrappers
: Swift언어로 양방향 Model 혹은 View Model를 구현할 수 있도록 지원해준다.
State property
@State
특정 프로퍼티를 뷰의 상태(State)로 만들어준다. 즉 이 프로퍼티가 변경되면 자동으로 뷰의 데이터도 변경되고, 뷰의 데이터를 바꿔도 이 프로퍼티의 데이터도 자동으로 변경된다.
•
@state 는 private 프로퍼티에만 사용 가능하다.(권장)
◦
뷰 내부에서 이외에는 사용이 불가능하기 때문에 private 으로 선언하는 것
◦
하위 뷰나 다른 뷰에서 참조하기 위해선 @Binding 를 해야한다.
•
state property에 해당되는 변수 값이 변경되면 view를 다시 랜더링한다. → 항상 최신의 값을 가짐
•
상태 프로퍼티에 값을 저장하는 건 ‘단방향 프로세스’를 따른다.
◦
상태가 변경되면 레이아웃에 있는 다른 뷰들도 변경된다.
•
@State 변수는 Heap에 할당된다.
◦
뷰에 포인터가 있고 새로운 View가 만들어지면 포인터를 새로운 뷰로 옮겨서 힙의 같은 메모리를 가르키게 한다. → View의 상태를 저장 변경
•
일반적으로 struct 내의 값은 변경할 수 없다.(immutable)
◦
State Property Wrapper를 사용해서 변경할 수 있게 해준다.
◦
지속적으로 변형 가능한 변수를 만들 수 있다.
◦
String, Int, Bool과 같은 간단한 타입에 사용하기 좋다.
@State private var selection: Tab = .featured
var body: some View {
TabView(selection: $selection) { // 자동으로 변경되는 값을 넣는 부분
CategoryHome()
.tabItem {
Label("Featured", systemImage: "star")
}
.tag(Tab.featured)
LandmarkList()
.tabItem {
Label("List", systemImage: "list.bullet")
}
.tag(Tab.list)
}
}
Swift
복사
@State var dataToShow = \Hike.Observation.elevation
Swift
복사
얘는 뭘까?
@Binding
다른 인스턴스 소유의 @State 프로퍼티를 빌려올 때 사용한다. 상태 프로퍼티가 선언된 뷰와 그 하위 뷰에 대한 현재 값이다.
•
@State 되어 있는 값과 @Binding 값은 연결되기 때문에 어느 한 쪽의 값이 바뀌면 다른 한 쪽도 동일하게 바뀐다.
◦
뷰도 이 데이터의 변경을 알아채고 역시 알아서 업데이트된다.
import SwiftUI
struct FavoriteButton: View {
// changes made inside this view propagate back to the data source
@Binding var isSet: Bool
var body: some View {
Button {
isSet.toggle()
} label: {
Label("Toggle Favorite", systemImage: isSet ? "star.fill" : "star")
.labelStyle(.iconOnly)
.foregroundColor(isSet ? .yellow : .gray)
}
}
}
struct FavoriteButton_Previews: PreviewProvider {
static var previews: some View {
FavoriteButton(isSet: .constant(true))
}
}
Swift
복사
↓ 상위 뷰
HStack {
Text(landmark.name)
.font(.title)
FavoriteButton(isSet: $modelData.landmarks[landmarkIndex].isFavorite)
}
Swift
복사
Observable Property
@ObservedObject
ViewModel를 선언할 때 사용하는 프로퍼티 래퍼로 ObservableObject 프로토콜을 준수하는 타입에 사용 가능하다.
•
@State 의 대표적인 단점은 Value 타입에서만 사용이 가능하다는 점이다.
◦
클래스에서는 사용이 불가능하다.
◦
이 경우에는 observableObject 를 상속받은 클래스의 프로퍼티에 @ObservedObject 를 적용해서 비슷하게 뷰와 프로퍼티를 연결할 수 있게 해준다.
•
다만 클래스의 모든 프로퍼티의 변화를 추적하지 않는다.
◦
추적을 원하는 프로퍼티는 @Published Property Wrapper를 사용해야 한다.
◦
@Published 로 선언된 변수는 다른 뷰에서 사용하게 되면 해당 변수를 사용하는 모든 뷰가 최신 변경된 값을 반영하기 위해 다시 랜더링 된다.
final class ModelData: ObservableObject {
@Published var landmarks: [Landmark] = load("landmarkData.json")
@Published var profile = Profile.default
var hikes: [Hike] = load("hikeData.json")
var features: [Landmark] {
landmarks.filter { $0.isFeatured }
}
var categories: [String : [Landmark]] {
Dictionary(
grouping: landmarks,
by: { $0.category.rawValue }
)
}
}
Swift
복사
struct ContentView: View {
@ObservedObject var data = ModelData()
var body: some View {
VStack {
Text("Hello, \(data.features)!")
.padding()
}
}
}
Swift
복사
@StateObject
기능은 ObservedObject와 동일하지만 단점을 보완해서 iOS14에 추가된 기능
State + ObservedObject
•
ObservedObject와의 차이점
◦
ObservedObject는 View가 새로 그려질 때 새로 생성될 수 있다.(View의 life cycle에 의존)
◦
StateObject는 View가 새로 그려질 때 State처럼 새로 그려지지 않고 참조를 가지고 있어서 새로 생성되지 않는다.(View의 life cycle에 의존하지 않음)
새로 생성된다
문제점 해결
Environment Property
@EnviromentObject
클래스 오브젝트를 추적하기 위한 용도의 Property Wrapper
공유 인스턴스 형태에 적합하게 사용할 수 있다는 점
•
ObservableObject를 상속받은 클래스를 여러 뷰에서 @EnvironmentObject 형식으로 참조 가능
•
이름처럼 환경설정 등 여러 곳에서 공유될 만한 데이터를 관리하는 모델로 사용하기 좋다.
◦
이 객체는 SwiftUI 환경에 저장되며, 뷰에서 뷰로 전달할 필요없이 모든 뷰가 접근할 수 있다.
•
최초 생성 참조가 시작되기 전에 오브젝트를 생성하고 environmentObject() 로 알려주어야 한다.
@main
struct SwiftUI_TutorialsApp: App {
@StateObject private var modelData = ModelData()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(modelData)
}
}
}
Swift
복사
@Environment(\.)
시스템에서 제공하는 Environment를 사용하기 위해서 사용하는 방식.
시스템에서 제공하는 Environment만 사용하는 것이 아니고 직접 앱에 필요한 변수를 추가해서 사용할 수 있다.
struct ProfileHost: View {
// read or write the edit scope
@Environment(\.editMode) var editMode
}
Swift
복사
* 상태 프로퍼티
: 사용자 인터페이스 레이아웃 내의 뷰 상태를 저장하는 데 사용 → 현재 콘텐트 뷰에 관한 것
값이 임시적이기 때문에 해당 뷰가 사라지면 값도 없어진다.
* Observable 객체 프로퍼티
: 사용자 인터페이스 밖에 있으며 앱 내의 SwiftUI뷰 구조체의 하위 뷰에만 필요한 데이터를 사용하기 위해서
ObservableObject 프로토콜을 따라야 하며, 뷰와 바인딩 될 프로퍼티는 @Published 프로퍼티 래퍼를 사용해서 선언되어야 한다. Observable 객체 프로퍼티와 바인딩 하려면 프로퍼티는 @ObservedObject 프로퍼티 래퍼를 사용해야 한다.
* Environment 객체
: 사용자 인터페이스 밖에 있으며 여러 뷰가 접근해야 하는 데이터일 경우
@EnvironmentObject 프로퍼티 래퍼를 사용하여 SwiftUI 파일 내에 선언된다. 뷰 화면이 앱에 추가될 때 Environment 객체 또한 초기화되어야 한다.