๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŽ iOS

[SwiftUI] Property Wrappers

by hyebin (Helia) 2024. 4. 4.
๋ฐ˜์‘ํ˜•

Property Wrappers

property wrapper๋Š” ํ”„๋กœํผํ‹ฐ์— custom ํ•œ ๋™์ž‘์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ๋ž˜ํ•‘ ํ•˜์—ฌ ์ถ”๊ฐ€ ๋กœ์ง์ด๋‚˜ ๋™์ž‘์„ ์บก์Аํ™” ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ, ๊ฐ€๋…์„ฑ ๋ฐ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@State

  • view์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • view ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ž๋™์œผ๋กœ view๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ€๋ณ€ ๊ฐ’์œผ๋กœ ์ทจ๊ธ‰๋˜๋ฉฐ, ๋ทฐ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ํ•ด๋‹น ๊ฐ’์ด ์ดˆ๊ธฐํ™”๋˜๊ณ , ์‚ฌ์šฉ์ž ์ƒํ˜ธ ์ž‘์šฉ ๋˜๋Š” ์™ธ๋ถ€ ์ด๋ฒคํŠธ์— ๋”ฐ๋ผ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ทฐ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— private์œผ๋กœ ์„ ์–ธํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. 
import SwiftUI

struct ContentView: View {
    @State private var count = 0
    
    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Increment") {
                count += 1
            }
        }
    }
}

@Binding

  • ๋ฐ์ดํ„ฐ์˜ ์–‘๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์ƒ์œ„ @State ๋ณ€์ˆ˜๋ฅผ ์ „๋‹ฌ๋ฐ›์•„ ํ•˜์œ„ ๋ทฐ์—์„œ ์บ์น˜ํ•ด ๋ณ€ํ™” ๊ฐ์ง€ ๋ฐ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
  • ํ•˜๋‚˜์˜ ์†Œ์Šค์—์„œ ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฅธ ๊ณณ์— ์ „ํŒŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Binding์€ ๋‹ค๋ฅธ ๋ทฐ๊ฐ€ ์†Œ์œ ํ•œ ์†์„ฑ์„ ์—ฐ๊ฒฐํ•˜๊ธฐ์— ์†Œ์œ ๊ถŒ ๋ฐ ์ €์žฅ ๊ณต๊ฐ„์ด ์—†์Šต๋‹ˆ๋‹ค.

๋ถ€๋ชจ๋ทฐ

import SwiftUI

struct ParentView: View {
    @State private var value = 0
    
    var body: some View {
        ChildView(value: $value)
    }
}

์ž์‹๋ทฐ

import SwiftUI

struct ChildView: View {
    @Binding var value: Int
    
    var body: some View {
        Button("Increment") {
            value += 1 // ๋ถ€๋ชจ ๋ทฐ์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝ
        }
    }
}

@Published

  • ObservableObject๋กœ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค ๋‚ด์—์„œ ํ”„๋กœํผํ‹ฐ ์„ ์–ธ ์‹œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Published๋กœ ์„ ์–ธ๋œ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ทฐ์—์„œ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • Published๋กœ ์„ ์–ธ๋œ ํ”„๋กœํผํ‹ฐ๋Š” ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋Š” ๋ชจ๋“  ๋ทฐ์— ์•Œ๋ ค์ฃผ๋ฉฐ, ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ๋ทฐ๋“ค์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

@ObservedObject

  • ๋ทฐ์—์„œ ObservableObject ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ์–ธ ์‹œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ObservableObject๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋ทฐ์— ๋ฐ”์ธ๋”ฉํ•˜์—ฌ ํ•ด๋‹น ๊ฐ์ฒด์˜ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ , ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
import SwiftUI

class Model: ObservableObject {
    @Published var data: String = ""
    
     func updateData(newData: String) {
        data = newData
     }
}

struct ContentView: View {
    @ObservedObject var model = Model()
    
    var body: some View {
        VStack {
            Text("Data: \(model.data)")
            Button("Update Data") {
                model.updateData(newData: "New Data")
            }
        }
    }
}

@StateObject

  • ๋ทฐ์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” Property Wrappers์ž…๋‹ˆ๋‹ค.
  • ๋ทฐ์—์„œ ObservableObject ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ์–ธ ์‹œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋ทฐ๋งˆ๋‹ค ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ, ๋ทฐ๊ฐ€ ์‚ฌ๋ผ์ง€๊ธฐ ์ „๊นŒ์ง€ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ObservedObject์˜ ๋ทฐ ๋ Œ๋”๋ง ์‹œ ์ธ์Šคํ„ด์Šค ์ดˆ๊ธฐํ™” ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
  • SwiftUI๊ฐ€ ๋ทฐ์˜ ์ƒํƒœ๋ฅผ ์ถ”์ ํ•˜๊ณ  ์ ์ ˆํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋งค๋ฒˆ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๋Š” ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹Œ ์ตœ์ดˆ ์ƒ์„ฑ ์„ ์–ธ ์‹œ์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.
import SwiftUI

class ViewModel: ObservableObject {
    @Published var count: Int = 0
    
    func increment() {
        count += 1
    }
}

struct ContentView: View {
    @StateObject var viewModel = ViewModel()
    
    var body: some View {
        VStack {
            Text("Count: \(viewModel.count)")
            Button("Increment") {
                viewModel.increment()
            }
        }
    }
}

@Environment

  • SwiftUI ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•˜์—ฌ ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉํ•˜๋ ค๋Š” ๊ณต์œ  ๋ฐ์ดํ„ฐ์˜ ์ด๋ฆ„์„ keyPath๋กœ ์ „๋‹ฌํ•˜์—ฌ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์‹œ์Šคํ…œ ๊ณต์œ  ๋ฐ์ดํ„ฐ๋Š” ๊ฐ€๋ณ€ ํ•˜๊ธฐ์— var๋กœ ์„ ์–ธ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ทฐ๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์‹œ์ ์— ๊ฐ’์ด ์ž๋™์œผ๋กœ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค.
import SwiftUI

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme
    
    var body: some View {
        Text("Current color scheme: \(colorScheme)")
    }
}

@EnvironmentObject

  • SwiftUI ํ™˜๊ฒฝ์—์„œ ์ฃผ์ž…๋œ ๊ฐ์ฒด๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์•ฑ์˜ ์—ฌ๋Ÿฌ ๋ทฐ์—์„œ ๋™์ผํ•œ ๊ฐ์ฒด์— ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ๋ทฐ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ํ†ตํ•ด ์ „ํŒŒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋ฐ์ดํ„ฐ๋ฅผ ์ „์—ญ์ ์œผ๋กœ ๊ณต์œ ํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • @EnvironmentObject๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ์•ฑ์˜ ์ƒ์œ„ ๋ทฐ ๊ณ„์ธต์—์„œ environmentObject(_:) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™˜๊ฒฝ์— ์ฃผ์ž…๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
import SwiftUI

class UserData: ObservableObject {
    @Published var username = "Guest"
}

struct ContentView: View {
    @EnvironmentObject var userData: UserData
    
    var body: some View {
        VStack {
            Text("Welcome, \(userData.username)!")
            EditUsernameView()
        }
    }
}

struct EditUsernameView: View {
    @EnvironmentObject var userData: UserData
    
    var body: some View {
        TextField("Username", text: $userData.username)
    }
}

struct MainView: View {
    var body: some View {
        ContentView().environmentObject(UserData())
    }
}

 

๋ฐ˜์‘ํ˜•

'๐ŸŽ iOS' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[SwiftUI] Text  (0) 2024.04.18
[SwiftUI] Stack (VStack/HStack/ZStack)  (0) 2024.04.08
[๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ] ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ž€?  (0) 2023.09.18
[SwiftUI] SwiftUI๋ž€?  (0) 2023.03.15
Pinch Gesture  (0) 2023.01.13