---
# System prepended metadata

title: iOS App Dev Tutorial SwiftUI

---

# iOS App Dev Tutorial SwiftUI

> Apple의 공식 iOS App Dev Tutorial 중 SwiftUI 파트를 따라하며 학습합니다.

<br>

🗓️ 학습 기간: `23.04.13` ~

<br>

# Record blu's study

[Keyword](#-keyword)

[Study](#-study)

[Trouble Shooting](#-trouble-shooting)

[Check List](#-check-list)

[Reference Link](#-reference-link)

[A-ha!](#-a-ha)

## 🔑 Keyword
`SwiftUI` , `Stack`, `Text`, `SF Symbols`, `Alignment`, `Supplement accessibility data`, `Label`, `List`, `NavigationStack`, `Property wrappers`, `Scene`, `View life cycle events`, `AVPlayer`

## 📚 Study

<details>
    <summary><b>SwiftUI</b></summary>
<div>
    
- **SwiftUI overview**    
SwiftUI is a **declarative framework** for building apps for any Apple platform. SwiftUI provides a common API that you can use to define an app’s user interface and platform-specific behavior.    
Create apps more quickly and with fewer errors with these key SwiftUI features:    
    - **Declarative syntax**—Define which views appear onscreen - using simple Swift structures.    
    - **A compositional API**—Quickly create and iterate your user interface using built-in views and modifiers. Compose more complex views by combining simpler views.    
    - **A powerful layout system**—Easily arrange views onscreen relative to their parent views and to each other. When existing views and controls don’t suit your needs, you can draw your own.    
    - **Views that reflect app data**—Define a view’s data dependencies, and SwiftUI automatically updates the view when data changes, avoiding errors from invalid view states.    
    - **Automatic accessibility support**—SwiftUI adds basic accessibility that you’ll learn to enhance with minimal effort.    
- **나만의 언어로 정리하는 Swift는**
    - multiplatform app의 레이아웃과 액션을 구성하는 데 사용
    - declarative framework(선언형 프레임워크)
    - UIKit 는 **Event driven** 이라면 SwiftUI는 **Data driven**
    - View를 Customize 하기 위해 **수정자**로 알려진 메서드를 호출, 수정자는 새 View를 반환함, 수정자는 단일 보기에서 여러 개 사용할 수 있으며, 수정자를 연결하기 위해서는 수직으로 쌓으면 됨
- SwiftUI view file의 구조
    - View 프로토콜을 준수하며 View를 반환하는 body 속성의 단일 요구사항
        - View의 콘텐츠, 레이아웃, 동작을 설명
    - 캔버스에 표시할 해당 Viewdml preview를 정의
    
</div>
</details>

<details>
    <summary><b>Text</b></summary>
<div>
    
- `.font()` modifier(수정자)로 텍스트 크기를 조절할 수 있음
</div>
</details>

<details>
    <summary><b>SF Symbols</b></summary>
<div>
    
- System이 SF Symbols의 이미지는 font처럼 취급하여 사용자의 디바이스 설정에 따라 동적으로 확장됨
</div>
</details>

<details>
    <summary><b>Alignment</b></summary>
<div>
    
- VStack의 alignment는 HorizontalAlignment 타입
- VStack 정렬의 기본 동작은 **center**이지만 재정의하여 정렬 가능
- 일부 system은 left, right를 사용하지만 SwiftUI는 **leading, trailing 정렬을 사용하여 앱의 현지화를 용이하게 함**
- 직접 지정해주는 방법도 있지만 VStack 선택 후 Attributes inspector에서 Alignment 옵션을 사용하여 정렬을 설정할 수도 있음
- **Alignment**
        HorizontalAlignment와 VerticalAlignment를 포괄하는 상위 개념!
        ZStack에 view를 배치하거나 `overlay(alignment:content:)` 또는 `background(alignment:content:)를 사용하여 다른 view 앞이나 뒤에 view를 배치하는 경우와 같이 특정 레이아웃 컨테이너 및 수정자의 동작을 지시하는 정렬 가이드
        ![Alignment](https://docs-assets.developer.apple.com/published/09693fd98ab76356519a900fd33d9e7f/Alignment-1-iOS@2x.png)
- **HorizontalAlignment**
        VStack에서 뷰를 수직으로 배치할 때와 같이 수평으로 배치하는 방법에 대한 가이드
        ![HorizontalAlignment](https://docs-assets.developer.apple.com/published/cb8ad6030a1ebcfee545d02f406500ee/HorizontalAlignment-1-iOS@2x.png)
- **VerticalAlignment**
        HStack에 뷰를 나란히 배치하거나 GridRow를 사용하여 Grid에 뷰 행을 생성할 때와 같이 뷰를 수직으로 배칠할 때의 정렬 가이드
        ![VerticalAlignment](https://docs-assets.developer.apple.com/published/a63aa800a94319cd283176a8b21bb7af/VerticalAlignment-1-iOS@2x.png)
</div>
</details>

<details>
    <summary><b>Supplement accessibility data (접근성 보완)</b></summary>
<div>
    
- SwiftUI에는 접근성 기능이 내장되어 있음
- 약간의 추가 작업으로 접근성 지원을 받을 수 있음
- 예) Text view의 문자열 콘텐츠에 자동으로 접근 가능, 하지만 사용자의 접근성 환경을 개선하기 위해 추론된 데이터를 보완해야 할 수 있음
- `.accessibilityAddTraits()`를 사용하면 특성을 추가해서 읽어주는 것, 예시대로 `.isHeader`를 넣으면 사용한 요소와 함께 "heading"이라고 읽어주어 view 의 information architecture를 전달하는 데 도움이 됨
</div>
</details>

<details>
    <summary><b>Label</b></summary>
<div>
    
- 제목이 있는 아이콘으로 구성된 표준 레이블
- 가장 일반적인 것은 아이콘 + 레이블 조합
- `.labelStyle()` 수정자를 통해 타이틀만 보이게, 아이콘만 보이게, 둘 다 보이게 설정할 수 있음
- 기본 스타일을 수정하여 커스터마이징된 레이블 스타일을 만들 수도 있음
- 완전히 새로운 스타일을 만들고 싶은 경우 LabelStyle 프로토콜을 채택 후 LabelStyleConfiguration을 구현하면 됨
- 레이블 그룹에 공통 레이블 스타일을 적용하려면 포함된 뷰 계층 구조에 스타일을 적용하면 됨
    - TrailingIconLabelStyle.swift 파일 참고!
- icon 부분에 SF Symbol과 같은 이미지 대신 programmatically하게 view를 사용하여 레이블을 만드는 것도 가능함    
예) `Circle()` 
</div>
</details>

<details>
    <summary><b>List</b></summary>
<div>

- List를 사용하여 단순히 생성하는 경우 아래와 같은 오류가 발생함
    > Initializer 'init(_:rowContent:)' requires that 'DailyScrum' conform to 'Identifiable'
    > ➜ DailyScrum이 Identifiable을 준수해야 함!!
- 왜 내부에 들어갈 데이터 타입은 Identifiable을 준수해야 할까?
    - List의 정의부는 다음과 같음
        `@MainActor struct List<SelectionValue, Content> where SelectionValue : Hashable, Content : View`
        데이터 타입에 해당하는 SelectionValue가 Hashable을 채택하고 있기 때문!
    - 공식 튜토리얼상 컬렉션의 개별 item을 식별하는 방법이 필요하기 때문! 튜토리얼에는 모든 테스트 데이터가 다른 이름을 가지고 있으므로 해당 속성을 키 값의 경로로 사용하도록 `id: \.title`으로 설정해줌
- 튜토리얼상 테스트 데이터는 다른 이름을 가지고 있었지만 사용자가 동일한 이름으로 새 데이터를 생성하는 경우 문제가 발생함!
    - 사용자가 생성한 콘텐츠로 작업하기 위해 SelectionValue가 ID를 전달하는 identifiable 프로토콜을 준수할 수 있음
    - identifiable은 채택하면 엔티티에 대한 안정적인 식별자를 제공하기 위한 id 프로퍼티를 요구함
    - UUID()를 데이터 모델의 초기화자에 사용하면 매개변수의 기본값을 정의하기 위해 초기화자나 함수를 호출할 때 해당 매개변수를 생략할 수 있음
    
</div>
</details>
    
<details>
    <summary><b>Property wrappers in SwiftUI</b></summary>
<div>
    
- 일반적인 프로퍼티 초기화 패턴을 캡슐화하여 속성에 동작을 효율적으로 추가하는 데 도움을 줌
- SwiftUI는 `@State` 및 `@Binding` 프로퍼티 래퍼를 사용하여 뷰가 쉽게 액세스할 수 있는 정보 소스를 유지하는 데 도움을 줌
- **값 타입**의 경우 `@State` 및 `@Binding`을 사용
- **참조 타입**의 경우 `@ObservedObject`, `@StateObject`, `@EnvironmentObject`를 사용
    - 참조 타입의 경우 프로퍼티 래퍼를 사용하기 위해서 관찰 가능하게(observable) 만들어야 함
    - `ObservableObject` 프로토콜을 채택하여 클래스를 관찰 가능하게 만들고 각 속성 선언 시 `@Published` 키워드를 추가함

**값 타입 property wrapper**
- `@State`
    - 프로퍼티를 `@State로 선언하면 view 내에서 신뢰할 수 있는 데이터 원본이 생성됨
    - memberwise initializer에서 초기값을 설정하는 것을 방지하기 위해 private로 선언해야 함
    - Class와 같은 참조 타입을 저장해야 하는 경우 `StateObject`를 사용
    - 시스템은 `@State` 속성 값에 따라 달라지는 view의 모든 요소를 식별함
    - 사용자의 상호작용은 `@State` 프로퍼티를 변경할 수 있음
    - 시스템은 해당 프로퍼티애 의존하는 view를 업데이트하여 새로운 버전의 UI를 렌더링함
        - 영구적인 상태보다 **일시적인** 상태를 관리하는 데 도움이 되므로 state property를 private로 선언하는 것을 권장 (예: 버튼의 강조 표시 상태, 필터 설정, 현재 선택된 목록 등)
    - State의 기본값에 접근하기 위해서는 WrappedValue 속성을 사용해야 하지만 shortcut으로 swift를 사용하면 State 인스턴스를 직접 참조하여 래핑된 값에 접근할 수 있음
    - view structure에 로컬인 가변 소스를 정의하기 위한 구문
    - 그렇다면, 다른 뷰(하위 뷰)에서 동일한 데이터 소스를 사용하려면?! ➜ Binding을 전달하면 됨!
        - 속성 이름 앞에 달러 기호($)를 붙이면 해당 상태의 projectedValue에 접근하여 State Value에 대한 바인딩을 가져올 수 있음
- `@Binding`
    - `@Binding`으로 래핑하는 프로퍼티는 `@State` 프로퍼티와 같은 기존 소스와 읽기 및 쓰기 접근권한을 공유함
    - 데이터를 직접 저장하지 않는 대신 기존 정보 소스와 해당 데이터를 표시하고 업데이트하는 view 사이에 양방향 연결을 생성
    - 이 연결을 통해 데이터와 연결된 여러 보기가 동기화됨
    - 시스템은 `@State`의 데이터와 `@Binding`이 포함된 view 간에 종속성을 설정함
    - 상위/하위 view는 원본으로 정의한 프로퍼티를 읽거나 수정할 수 있음
    - binding을 사용하여 신뢰할 수 있는 단일 데이터 소스를 전파하는 패턴은 view 계층 구조에 효과적임!
    ➜ 데이터 소스의 변화를 관찰하는 코드를 별도로 작성하지 않아도 되기 때문!

**참조 타입 property wrapper**
- `@StateObject`
    - App, Scene, View 내부에서 관찰 가능한(observable) 개체를 만듦
    - 시스템은 개체를 초기화하고 개체를 전달하는 다른 view에서 사용할 수 있도록 개체를 유지
- `@ObservedObject`
    - 상위 소스에서 개체를 수신했음을 알려주는 프로퍼티 래퍼
    - 상위 구조에서 개체를 생성하고 소유하고 있으므로 하위 뷰는 `ObservedObject`에 대한 초기값이 필요하지 않음
- `@EnvironmentObject`
    - 위 2개의 프로퍼티 래퍼는 상하위 view가 명확한 경우 사용하지만, `EnvironmentObject`는 복잡한 view 계층구조에서 관찰 가능한 개체를 공유함
    - 이니셜라이저를 통해 개체를 전달하는 대신 `environmentObject(_:)` 수정자를 통해 개체를 environment에 배치
    - 배치된 개체는 중간 view에 대한 참조가 없더라도 사용 가능
    - 중간 view에서 불필요한 종속성을 생성하지 않도록 도와줌
</div>
</details>
    
<details>
<summary><b>Scene</b></summary>
<div>
    
**Scene architecture**    
- Scene
    - 시스템이 관리하는 수명주기가 있는 앱 User interface의 일부
    - 앱이 제공하는 view 계층 구조의 컨테이너
    - iOS, WatchOS에서는 하나의 Scene만 display할 수 있지만, macOS나 iPadOS의 경우 여러 Scene을 사용할 수 있음
- 앱을 만들기 위해서는 `App` 프로토콜을 준수하는 구조를 정의해야 함, 정의부에 `@main` 속성을 사용하여 앱의 유일한 진입점임을 시스템에 알림
- App structure의 본문에 `Scene` 프로토콜을 준수하는 하나 이상의 Scene을 추가
- SwiftUI는 `WindowGroup`과 같은 구체적인 Scene을 제공

**Scene phases and transitions**
- App 실행 중에 Scene은 세 단계로 전환될 수 있음
![Scene Phases](https://docs-assets.developer.apple.com/published/d98f744283ca9cfb101e137d778d4611/SUI067_010-040@2x.png)
- `active`: Scene이 foreground에 있고, 사용자와 상호작용 할 수 있는 단계
- `inactive`: Scene을 볼 수 있지만 system이 상호작용을 비활성화한 단계
- `background`: 앱이 실행 중이지만 사용자 인터페이스에 표시되지 않는 단계, 앱 종료 전 단계
- `scenePhase` environment 값을 사용하여 Scene의 현재 상태를 읽을 수 있음
- `onChange(of:perform:)` 수정자를 사용하면 Scene이 `inactive`한 단계가 됐을 때 앱 데이터를 저장하는 작업이 가능
</div>
</details>

<details>
    <summary><b>View life cycle events</b></summary>
<div>
    
- view의 생명주기 이벤트에 대해 응답하는 세 가지 수정자
- `onAppear(perform:)`: view가 화면에 그려질 때마다 응답
- `onDisappear(perform:)`: view가 화면에서 사라질 때 응답
- `task(priority:_:)`: view가 화면에 나타나기 전 비동기 작업을 수행
</div>
</details>

## 🏀 Trouble Shooting

## ✅ Check List
- [ ] iOS App Dev Tutorials (SwiftUI)
    - [x] SwiftUI essentials
    - [x] Views
    - [x] Navigation and modal presentation
    - [x] Passing Data
    - [ ] State management
    - [ ] Persistence and concurrency
    - [ ] Drawing
    - [ ] Recording audio

- [x] 선언형 프레임워크란?
    - 주로 UI 개발에서 사용되어 선언형 UI라고도 함
    - 원하는 결과를 명시(선언)하고 프레임워크가 자동으로 이를 구현하는 방식의 프로그래밍 패러다임을 말함
    - 코드의 구체적인 로직을 작성하지 않고도 원하는 결과를 달성할 수 있음
    - *어떤 방법*으로 해야 하는지를 나타내기 보다 *무엇*을 나타낼지를 묘사하는 것
    - **왜 SwiftUI가 선언형 프레임워크인가?**
        - UI를 구현하기 위해 코드를 작성하는 대신 UI의 레이아웃, 스타일, 상호작용을 명시(선언)하고 SwiftUI가 이를 처리하기 때문
        - 선언적인 문법
            개발자가 어떻게 UI를 만들지 명시하면 SwiftUI가 코드를 추상화하고 UI를 생성
        - 상태 관리
            앱의 상태를 관리하기 위해 데이터 바인딩을 사용, 상태 변경이 감지되면 자동으로 UI를 업데이트하고 변경사항을 적용함
    - 이처럼 SwiftUI는 선언적 문법과 상태 관리를 통해 UI를 구현하는 간결하고 직관적인 선언형 프레임워크
            
- [x] UIKit과 SwiftUI의 차이점
    - **UIKit**
        - Event Driven
            예를 들어 버튼이 눌렸을 때 버튼이 눌렸을 때 호출할 함수를 호출하고, 호출된 함수 내부에서 배경 색을 바꿈
            이처럼 View는 자신이 어떻게 변화할지를 예상할 수 없음
        - Objcect-C 기반으로 만들어진 프레임워크
    - **SwiftUI** (WWDC19에서 발표)
        - Data Driven
        예를 들면 버튼이 눌렸을 때 버튼이 눌렸는지 확인하는 Bool 타입의 값을 변경, 상태에 맞게 이미 선언한 대로 View를 다시 그림
        - Swift를 기반으로 만들어진 것
    
- [x] Preview struct 내에서 static으로 프로퍼티를 선언해주는 이유?(Getting started with Scrumdinger)
    ![](https://hackmd.io/_uploads/r1JAicQ4h.png)
    - preview는 시간이나 사용자 입력에 따라 변경되는 것이 아니라 정적인 뷰를 보여주는 것이 목적이기 때문에 사용되는 데이터도 정적인 데이터가 되어야 함
    - static 프로퍼티는 인스턴스화 없이도 클래스나 구조체에서 직접 접근할 수 있는 프로퍼티이므로 preview에서도 쉽게 사용할 수 있기 때문
    - preview에서는 데이터를 쉽게 수정하거나 변경할 수 있어야 하기 때문
    - static 프로퍼티를 사용하면 view에서 사용되는 데이터를 따로 분리할 수 있기 때문, view와 데이터의 분리를 통해 더욱 모듈화된(기능 단위로 나누어진) 코드를 작성할 수 있음
    
- [ ] where절 사용에 대한 공부(Using stacks to arrange views)
- [ ] Hashable protocol
- [ ] Property Wrappers

## 🔗 Reference Link
- 튜토리얼 시 숙지가 필요한 Swift 공식문서
    - [Swift doc - The Basics](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/)
    - [Swift doc - Closures](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/)
- [Apple Developer - Text](https://developer.apple.com/documentation/swiftui/text/)
- [Apple Developer - HorizontalAlignment](https://developer.apple.com/documentation/swiftui/horizontalalignment)
- [Apple Developer - Label](https://developer.apple.com/documentation/swiftui/label)
- [Apple Developer - List](https://developer.apple.com/documentation/swiftui/list/)
- [Apple Developer - Property Wrappers](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/#Property-Wrappers)
- [Apple Developer - Managing model data in your app](https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app)
- [WWDC21 - Demystify SwiftUI](https://developer.apple.com/videos/play/wwdc2021/10022/)
- [Swift doc - Control Flow(where)](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/controlflow#Where)
    
## 💡 A-ha!
- **Creating a card view 파트의 Check Your Understanding**
    > Which framework should you import to define the structure?
    ```swift
    struct Person {
       var name: String
       var age: Int
       var favoriteColor: Color
    }
    ```
    Foundation을 선택했는데 Color는 SwiftUI에 속해있는 것이다...! UIKit는 UIColor였음!
- **모르고 사용하던 VStack의 alignment가 HorizontalAlignment속성이라는 것...!**
    >An Alignment contains a HorizontalAlignment guide and a VerticalAlignment guide.
    
    위의 공식문서 내용에 의하면 Alignment에는 HorizontalAlignment, VerticalAlignment guide가 포함되어 있다고 한다!
    ➜ 즉, Alignment가 HorizontalAlignment와 VerticalAlignment를 포괄하는 상위 개념!
- **Creating a navigation hierarchy 파트의 Check Your Understanding**
    > Which code snippet displays the navigation title in the NavigationStack?
    ```swift
    struct ListView: View {
        var body: some View {
            NavigationStack {
                List {
                    Section(header: Text("Cats")) {
                        NavigationLink(destination: CatsView()) {
                            Text("Duncan")
                        }
                    }
                }
                .navigationTitle("Adoptable Animals")
            }
        }
    }
    ```
    title이나 bar item과 같은 navigation modifier(수정자)를 child view에 추가하면 시스템에서 modifier를 parent `NavigationStack`에 전파함!
    ➜ NavigationLink 자체에는 navigation title을 추가하지 않음!
- **Passing data with bindings 파트의 Check your Understanding**
    > What is the type of $book.identifier?
     ```swift
     struct Book {
     var title: String
     var identifier: Int
     }
    
     @State private var book = Book(title: "The Adventures of Smudge", identifier: 19237)
     ```
    정답: `Binding<Int>`
    Binding 된 타입의 내부의 속성 타입은 해당 타입만을 기술하는 것이 아닌 `Binding<Type>`임!
    위의 경우 `Int`가 아닌 `Binding<Int>`임!
    
<details>
    <summary><b>깜빡일지</b></summary>
<div>

`23.04.21`, `23.05.02`

</div>
</details>

