- Published on
๐ Swift - Property Wrapper
- Authors
- Name
- ์ด์ฐฝ์ค
์ค๋์ UserDefaults๋ฅผ ์ด๋ป๊ฒ ํ๋ฉด ์กฐ๊ธ์ด๋ผ๋ ๋ ํธํ๊ฒ ์ฌ์ฉํ ์ ์์๊น ๊ณ ๋ฏผํ๋ค๊ฐ ์์ฃผ ์ข์ ์น๊ตฌ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค.
๋ฐ๋ก Property Wrapper๋ผ๋ ์น๊ตฌ์ธ๋ฐ์.
์ด ์น๊ตฌ๋ SwiftUI์ Combine์ ๊ธฐ๋ฅ์ UIKit์์๋ ์ ์ฉํ ์ ์๋๋ก Swift 5.1๋ฒ์ ์ ๋ฑ์ฅํ์ต๋๋ค.
๋ง์ ๊ฒ์๋ฌผ์์ SwiftUI์ @State
๋ฅผ ์์๋ก ๋ค๋ฉฐ ์ค๋ช
ํ๊ณ ์๋๋ฐ, ์ ๋ ๊ฒฝํ์ด ์์ด์.. ๋์ด๊ฐ๊ตฌ์โฆ
์ด๋ฆ์ ๋ณด๋ฉด ํ๋กํผํฐ๋ฅผ ๊ฐ์ธ์ ๋ญ๊ฐ๋ฅผ ํ๋ ์น๊ตฌ์ธ ๊ฒ ๊ฐ์ฃ ?
๋ฐ๋ก ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ฉ๋
์ผ๋จ ์ด๋ค ๊ฐ๋ ์ผ๋ก ์ฌ์ฉ๋๋ ๊ฑด์ง๋ถํฐ ์์๋ด ์๋ค.
Swift์๋ lazy
ํ๋กํผํฐ ๋ผ๋ ๊ฒ์ด ์์ต๋๋ค.
ํด๋น ํ๋กํผํฐ๊ฐ ์ฌ์ฉ๋ ๋ ๋ก๋๋ฅผ ์์ํ๋๋ก ํด์ฃผ๋ ํค์๋์ด์ฃ .
์ด lazy
ํค์๋๋ ๋ชจ๋ ํค์๋๋ค์ ๋ถ์ผ ์ ์๊ณ ๋์ผํ ๋์์ ํฉ๋๋ค.
๋ง์ฝ ์ด ํค์๋๊ฐ ์๋ค๋ฉด ํด๋น ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ํด์ ์ด๋ค ๊ณผ์ ์ ๋ฐ๊ฒ ๋ ๊น์?
func getMyClass() -> MyClass {
if self.myVar == nil {
self.myVar = MyClass()
}
return self.myVar
}
์์ ๊ฐ์ด ํด๋น ํ๋กํผํฐ๊ฐ ์ด๊ธฐํ๊ฐ ๋๋์ง ํ์ธ์ ํ ํ์ ๊ฐ์ ๋ถ๋ฌ์ค๋ ํจ์๋ฅผ ๋ง๋ค์ด ๋ชจ๋ ํ๋กํผํฐ์ ์ง์ ๊ตฌํํ๋ฉฐ ์ ์ฉ์์ผ์ฃผ์ด์ผ ํ ๊ฑฐ์์.
๋คํํ๋ ์ ๋์์ ์ฐ๋ฆฌ๋
lazy var myClass = MyClass()
์ด๋ ๊ฒ ๊ฐํธํ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
Property Wrapper๋ lazy
์ ๊ฐ์ด ๋์ผํ ๋์์ ํ๋ ํ๋กํผํฐ๋ค์ ๋ํด์ ๊ณตํต์ ์ผ๋ก ๋ฏธ๋ฆฌ ๊ตฌํ๋ ๋์๋ค์ ๋ถ์ฌํ ์ ์๋ ๊ธฐ๋ฅ์
๋๋ค.
์ ์
ํ๋กํผํฐ๋ฅผ ๊ฐ์ธ๋ ๊ธฐ๋ฅ์ด๊ธฐ ๋๋ฌธ์ ์ฐ์ ํ๋กํผํฐ๋ฅผ ๊ฐ์ง ์ ์๋ class, struct, enum ์์ @propertyWrapper
๋ฅผ ๋ถ์ฌ ์ ์ํฉ๋๋ค.
์์๋ก UserDefaults๋ก ์๋์ผ๋ก ๋๊ธฐํ ์์ ์ ํ๋ ํ๋กํผํฐ๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
@propertyWrapper
struct UserDeafult<T> {
var key: String
var defaultValue: T
}
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ๊น์ง๋ง ํ๋ฉด ํด๋น ํ๋กํผํฐ์ ๋ณ๊ฒฝ์ด ์์ ๋๋ง๋ค ์ด๋ค ์์ ์ด ์คํ๋๋์ง์ ๋ํ ๋ช ์๊ฐ ์์ฃ .
๊ทธ๋์ ์๋ฌ๊ฐ ๋ฐ์ํ๊ณ , ๊ทธ ์๋ฌ๋ฅผ ์ดํด๋ณด๋ฉด wrappedValue
๊ฐ ์ ์๋์ง ์์๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
wrappedValue
๋ฅผ ์ ์ํด์ฃผ๊ณ , get-set
์ ์ฌ์ฉํ์ฌ ํด๋น ํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋ฐ์ํ๋ ๋ก์ง์ ๋ฃ์ด์ค๋๋ค.
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
์ด์ ํฉ์ณ๋ณผ๊น์?
@propertyWrapper
struct UserDefault<T> {
var key: String
var defaultValue: T
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.get(newValue, forKey: key)
}
}
}
์ฌ์ฉ
์ ๊ทธ๋ผ ์ ์ฉ์ ์ด๋ป๊ฒ ํ ๊น์?
lazy
์ ๋ง์ฐฌ๊ฐ์ง๋ก ์์ ๋ช
์ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
struct AppData {
@UserDefault(key: "isLoggedIn", defaultValue: false)
static var isLoggedIn: Bool
@UserDefault(key: "username", defaultValue: "")
static var username: String
}
AppData.isLoggedIn = true
print(AppData.isLoggedIn) // true
print(AppData.username) // ""
์กฐ๊ธ ๋ ๋์๊ฐ์ ์ปค์คํ ํ์ ์๋ ์ ์ฉ๋ ์ ์๋๋ก ๋ฐ๊ฟ๋ด ์๋ค.
@propertyWrapper
struct UserDefault<T: Codable> {
// key์ default ๊ฐ์ ๋ฐ๋ ์ผ์ด ์์ผ๋ let์ผ๋ก ๋ณ๊ฒฝ
private let key: String
private let defaultValue: T
init(key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
guard let data = Userdefaults.standard.object(forKey: key) as? Data else {
return defaultValue
}
let value = try? JSONDecoder().decode(T.self, from: data)
return value ?? defaultValue
}
set {
let data = try? JSONEncoder().encode(newValue)
UserDefaults.standard.set(data, forKey: key)
}
}
}
์ด๋ ๊ฒ ์๋์ผ๋ก UserDefaults
๋ก ๊ฐ์ ๋๊ธฐํ์์ผ์ฃผ๋ Property Wrapper๋ฅผ ๋ง๋ค๊ณ ์ ์ฉํด๋ณด์์ต๋๋ค.
์ฐธ๊ณ ๊ฒ์๋ฌผ
# Create the Perfect UserDefaults Wrapper Using Property Wrapper