[Programming Language]/[SwiftUI]

[SwiftUI] @SceneStorage(화면 저장소) vs @AppStorage(앱 저장소)

Semincolon 2024. 10. 12. 19:01

[머릿말]

앱에서 필요한 데이터를 저장하기 위해 사용하는 두 개념에 대해 접하게 되었다. 데이터를 저장하는 일은 필수적이므로 자주 찾아보게 될 것 같아 글로 남겨놓고자 한다.


@SceneStorage@AppStorage는 모두 프로퍼티 래퍼(Property wrapper)다. 이는 SwiftUI가 데이터를 저장할 목적으로 제공한다. 저장할 데이터에 대한 String 타입의 key 값을 통해 데이터를 구분짓는다.

 

우리가 사용하는 앱은 종료되더라도 데이터는 보존된다. 이는 두 프로퍼티 래퍼를 사용하여 구현해볼 수 있다.

 

⦿ 두 프로퍼티 래퍼를 사용하지 않았을 때는?

두 프로퍼티 래퍼를 사용하지 않은 경우, 앱이 종료되면 데이터는 그 즉시 소멸된다. 간단하게 회원가입 화면을 통해 확인해보자. 아래 코드는 SceneStorage와 AppStorage를 다룰 때도 동일하게 사용될 예정이다.

import SwiftUI

struct ContentView: View {
    
    @State var name: String = ""
    @State var phone_number: String = ""
    
    var body: some View {
        VStack {
            
            Text("회원가입")
                .font(.largeTitle)
                .fontWeight(.black)
                .padding(10)
            
            Form {
                Section(header: Text("이름")) {
                    TextEditor(text: $name)
                }
                
                Section(header: Text("휴대전화")) {
                    HStack {
                        TextEditor(text: $phone_number)
                    }
                }
                    
            }
        }
    }
}

#Preview {
    ContentView()
}

앱을 종료하고 다시 시작하자 입력했던 데이터가 사라진 모습

위 예제를 통해 앱이 종료되고 다시 시작되는 경우 입력했던 데이터가 사라지는 것을 확인할 수 있다.

이를 위해 @SceneStorage@AppStorage를 사용해볼 수 있다.

 

⦿ @SceneStorage

공식 문서에서의 내용은 다음과 같다.

"지속되는 각 장면별 저장소에 읽거나 쓰는 프로퍼티 래퍼"

 

장면별 저장소..? 정확히 감이 잡히지 않을 수 있다. 이는 말 그대로 각 장면별 저장소를 뜻하는데, iPadOS, macOS처럼 다중 창(multi-windowing) 플랫폼에서 동일한 앱을 여러개 실행하는 경우 각각의 앱은 서로 다른 저장소에 데이터를 저장하게 된다.

 

아래 예제는 위에서 본 예제에서 프로퍼티 래퍼를 @Scene에서 @SceneStorage(_:)로 변경한 것이다.

import SwiftUI

struct ContentView: View {
    
    @SceneStorage("name") var name: String = ""
    @SceneStorage("phone_number") var phone_number: String = ""
    
    var body: some View {
        VStack {
            
            Text("회원가입")
                .font(.largeTitle)
                .fontWeight(.black)
                .padding(10)
            
            Form {
                Section(header: Text("이름")) {
                    TextEditor(text: $name)
                }
                
                Section(header: Text("휴대전화")) {
                    HStack {
                        TextEditor(text: $phone_number)
                    }
                }
                    
            }
        }
    }
}

#Preview {
    ContentView()
}

앱을 종료하고 다시 시작해도 입력했던 데이터가 남아있는 모습

이제 앱을 재시작하더라도 입력했던 데이터가 사라지지 않게 되었다.

❖ iPad에서 앱을 여러개 실행하는 경우 ❖

앞서 언급했던 것처럼 멀티 윈도우 환경의 iPad에서 같은 앱을 실행시켜 보았다. 서로 같은 앱, 같은 화면임에도 불구하고 데이터가 공유되지 않는 것을 확인할 수 있다. 곧 다룰 @AppStorage는 같은 앱이면 데이터가 공유된다.

 

⦿ @AppStorage

공식 문서에서의 내용은 다음과 같다.

"UserDefaults의 값을 반영하고 해당 사용자 기본값의 값 변경에 대한 보기를 무효화하는 속성 래퍼 유형"

"값 변경에 대한 보기를 무효화한다"가 무슨 말이지... 나중에 깨닫게 되면 다시 수정해야겠다...

 

AppStorage는 iOS에서 오랫동안 사용된 UserDefaults를 기반으로 만들어졌다. UserDefaultkey-value 쌍으로 앱에 저장되는 소량의 데이터(로그인 정보, 환경 설정 등)를 저장할 때 사용하는 것이다.

 

이는 앞서 본 SceneStorage와는 달리 앱별로 데이터가 저장된다. 즉, 같은 앱이라면 데이터가 공유된다는 것이다. 앱을 종료하고 다시 시작했을 때 데이터가 보존되는 것은 위 예제와 동일하므로 생략하고, iPad에서 2개의 앱을 띄워본 결과를 확인해보겠다.

 

아래 예제는 위 코드에서 @SceneStorage@AppStorage로 바꾼 것이다.

import SwiftUI

struct ContentView: View {
    
    @AppStorage("name") var name: String = ""
    @AppStorage("phone_number") var phone_number: String = ""
    
    var body: some View {
        VStack {
            
            Text("회원가입")
                .font(.largeTitle)
                .fontWeight(.black)
                .padding(10)
            
            Form {
                Section(header: Text("이름")) {
                    TextEditor(text: $name)
                }
                
                Section(header: Text("휴대전화")) {
                    HStack {
                        TextEditor(text: $phone_number)
                    }
                }
                    
            }
        }
    }
}

#Preview {
    ContentView()
}

동일한 앱이므로 데이터가 공유되는 모습

이처럼 AppStorage동일한 앱이라면 데이터가 같은 저장소에 저장되게 된다.


끝!