SwiftUI

SwiftUI 프로젝트 처음 만들면 생성되는 파일 뜯어보기

j2kb 2023. 4. 15. 19:35
반응형

제목만 보고 뭔가 싶으실 것 같아서 뭘 얘기하고 싶은지 부가적인 설명을 해보겠습니다

 

작성날이 토요일이기에 Saturday라는 이름으로 프로젝트를 한 번 만들었습니다

Saturday라는 폴더아래에 3개

- SaturdayApp.swift

- ContentView.swift

- Assets.xcassets 하나(위에 보이는 Assets이라는 이름되어 있는 거)

 

Preview Content라는 폴더아래에 1개가 있네요. 간단하죠?

- Preview Assets.xcassets

 

호오옥시 몰라 터미널에서 확인해보았는데 정말 4개가 전부입니다(. 한개는 현재 폴더 .. 는 상위 폴더를 의미합니다)

 

 

xcassets??

일단 오늘 다룰 주제는 아니니 간단히 언급만 하자면 xcassets는 XCode에서 사용할 각종 에셋들을 모아놓은 곳입니다. 이미지나 색깔 등등이 있습니다

 

 

App

자 그럼 이제 살펴볼 파일은 2개 남았는데 결론부터 말하면 SaturdayApp.swift에 앱을 켰을 때의 시작화면(?)을 표시하는 부분이 있고 ContentView.swift가 그 시작화면입니다

 

그럼 이제 우리는 SaturdayApp.swift파일만 보면 됩니다

import SwiftUI

@main                            /// 1
struct SaturdayApp: App {        /// 2
    var body: some Scene {       /// 3
        WindowGroup {            /// 4
            ContentView()
        }
    }
}

짠! 되게 간단합니다! 한 줄씩 찬찬히 음미해봅시다ㅎㅎ

 

@main

모든 프로그램은 진입점이 있어야 합니다. 당연히 App도 진입점이 있는데요

시스템에 여기서 시작해라고 알려주는 역할을 이 @main이 합니다.

학부가 컴공이시라면 C++을 대학교에서 배울 때 아래와 같은 코드를 많이 보셨을 거에요.

int main(int argc, char* argv[]) {
    return 0;
}

사실 요런 main() 코드가 UIKit 내부에서 불리고 있습니다.

위 글을 읽어보시면 아시겠지만

main함수 부르지 마 시스템에서 알아서 호출하니까 대신 네 앱의 진입점을 @main로 표시하면 되라고 써있네요!

과거에는 @UIApplicationMain이 이 역할을 했었는데 Xcode 12부터는 @main키워드도 이 역할을 수행하고 있습니다.

지금은 Xcode버전이 14이상이니 UIKit으로 만들던 SwiftUI로 만들던 @main키워드를 자동으로 붙여줍니다.

왜 역할이 같은 키워드를 또 만들었는지에 대한 내용은 살짝 길어질 것 같으니 다음에 열심히 정리해보겠습니다

밑에 green1229님 블로그 링크를 타고 들어가면 설명이 나와있으니 참고하셔도 좋을 것 같아요 :)

 

 

struct 앱의 이름: App

일단 App은 프로토콜입니다

정의를 보면 앱의 구조와 행동을 나타내는 타입이라고 하네요

Command + 클릭을 통해 들어가보면 7만줄 가까이되는 코드가 보이는데요. 그 중 App은 요렇게 생겼습니다.

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public protocol App {
    associatedtype Body : Scene

    @SceneBuilder @MainActor var body: Self.Body { get }

    @MainActor init()
}

associatedtype Body : Scene

Scene프로토콜을 따르는 App 프로토콜에서 사용할 generic 타입을 정의하였네요

 

@SceneBuilder @MainActor var body: Self.Body { get }

단어별로 뜯어보면

@SceneBuilder

Builder는 뭔가를 만드는 객체를 Builder라고 하는데 그럼 Scene을 만드는 것이겠네?!라는 것을 알 수 있구

Scene은 내가 유저들에게 보여주고 싶은 View 계층을 담은 컨테이너입니다.

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public protocol Scene {
    associatedtype Body : Scene
    
    @SceneBuilder @MainActor var body: Self.Body { get }
}

View는 무엇이냐?! 우리가 앱 켰을 때 화면에 보이는 Label이나 Image같은 UI요소 전부를 말합니다

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol View {
    associatedtype Body : View

    @ViewBuilder @MainActor var body: Self.Body { get }
}

 

WWDC에 소개된 그림을 보면 아시겠지만 App은 Scene들로 구성되어 있고 각 Scene은 View의 계층을 담고 있네요

 

 

@MainActor

A singleton actor whose executor is equivalent to the main dispatch queue.

라고 공식문서에 나와있습니다. 우리가 iOS 면접볼 때 필수적으로 공부해야할 그 DispatchQueue.main(sync는 호출하면 안되며.. serial큐이며...)이 맞습니다.

자, MainActor가 무엇이냐? Actor개념을 먼저 살펴보아야합니다.

Swift 5.5.1부터 추가된 Concurrency(동시성)와 관련된 개념인데요.

동시성 프로그래밍에서 발생하는 문제 data race 문제를 다루기 위해 추가되었다고 합니다.

Actor전에는 concurrent 큐에서 작업 하나 실행할 때 flags로 barrier를 준다든가 serial큐에서 sync를 쓰던가 해서 한 변수에 동시 접근하는 것을 막아왔더랬죠. Actor에서는 Isolated access와 nonisolated access를 제공합니다. 기본값은 isolated이구요 이렇게 하면 내부적으로 알아서 data race를 막아준다고 하네요

 

암튼 샛길에서 다시 돌아와서 본론으로 가면 main이라고 써있으니까 왠지 UI관련이 있을것 같죠? 맞습니다.

DispatchQueue.global().async { [weak self] in
    DispatchQueue.main.async {
        self?.testhLabel.text = "UI 업데이트는 언제나 Main thread에서"
    }
}

설명을 위한 예제입니다. 위 코드를 MainActor를 사용하면 아주 예쁘게 바꿀 수 있습니다

DispatchQueue.global().async { [weak self] in
    self?.updateLabel()
}

@MainActor
func updateLabel() {
    recentSearchLabel.text = "요로코롬하면 보라색 버그를 보지 않을 수 있어요"
}

하나 더 주의깊게 볼 것은

@MainActor가 var body라는 프로퍼티 앞에도 붙을 수 있다는 것.

예상이 되시겠지만 이 프로퍼티는 Main Thread에서 접근하세요라는 뜻입니다

 

 

 

var Body: some Scene

방금 위에서 언급한 View들의 계층구조를 담고 있는 그 Scene입니다.

 

 

WindowGroup

// 공식문서에 나온 거
struct WindowGroup<Content> where Content : View

// Xcode에서 Jump to Definition 눌러서 나온 거
public struct WindowGroup<Content> : Scene where Content : View {

 

정의를 보면 WindowGroup은 Scene 프로토콜을 준수했습니다. WWDC에서는 WindowGroup을 User Interface를 관리하는 Scene이라고 말하네요.

iOS보다는 iPadOS, macOS에서 사용되는 기능이구 여러 윈도우를 띄운다던가 여러 윈도우를 하나의 탭으로 묶는다던가 하는 기능들을 제공한다네요.ㅎㅎ

그래서 어차피 iOS는 윈도우 하나만 띄울거니 WindowGroup대신 Window로 할 수 있나 해봤더니 왠걸.. 지원을 안하네요

Window가 뭔지 더 찾아보았지만 딱히 더 쓸 것이 안나와서 여기서 나름대로 정리를 해보자면 WindowGroup은 Window들을 관리하는 기능을 담당하고 각 Window는 View 계층을 담은 유니크한 인스턴스로 사용되는 것 같네요ㅎㅎ

 

 

 

 

 

이번 글을 쓰면서 느낀점은 너무 많은 개념들이 한 곳에 담겨있는 듯한 느낌을 받았습니다.

주제도 잘못 고른 것 같고 정리도 안되고 보기도 어렵고..

나중에 다시 개념 하나씩 하나씩 조급해하지 말고 다시 정리해야겠다는 생각이 드네요.

요즘 딱히 재밌는 것이 없어서 시간도 많은데 잘됐죠 뭐ㅎㅎㅎㅎ

 

아무튼 오늘도 읽어주셔서 감사합니다

 

 

참고 및 출처

https://developer.apple.com/videos/play/wwdc2020/10037/

https://medium.com/@abedalkareemomreyh/what-is-main-in-swift-bc79fbee741c

https://green1229.tistory.com/265

https://developer.apple.com/documentation/swiftui/app/main()

https://babbab2.tistory.com/180

https://ios-development.tistory.com/1063

https://itnext.io/mainactor-in-swift-detailed-walkthrough-94044c83118b

https://itnext.io/swift-actors-e80ff0dc1832

https://medium.com/hcleedev/swift-actor%EB%9E%80-f8f58c68dab9

https://itnext.io/thread-sanitizer-in-ios-8438ee3c8c76

https://developer.apple.com/videos/play/wwdc2022/10061/

 

 

이것들 말고도 많은데.... 도저히 정리가 안됨

반응형