I set my WidgetView using value of UserDefaults. To share data between widget and core app, I set appGroup already.
let appGroupId = "group.com.myAppGroupId"
Text(UserDefaults(suiteName: appGroupId)!.object(forKey: "githubId") as? String ?? "??")
It shows well in preview, but when I add it to home screen, it cannot read property. And in same condition, when I rebuild app (cmd+r), widget in the home screen read value of UserDefaults well.
I cannot guess the reason.
+++++ Add more code
Actually, I defined my githubId
as UserDefaults like this using property wrapper.
extension UserDefaults {
enum Key: String {
case githubId
}
static let shared: UserDefaults = {
let appGroupId = "group.com.myAppGroupId"
return UserDefaults(suiteName: appGroupId)!
}()
@UserDefault(key: .githubId)
static var githubId: String?
}
And my property wrapper is seems like this.
@propertyWrapper
struct UserDefault<Value> {
let key: String
let defaultValue: Value
var container: UserDefaults = .shared
private let publisher = PassthroughSubject<Value, Never>()
var wrappedValue: Value {
get {
return container.object(forKey: key) as? Value ?? defaultValue
}
set {
if let optional = newValue as? AnyOptional, optional.isNil {
container.removeObject(forKey: key)
} else {
container.set(newValue, forKey: key) //this is where I set my value
}
publisher.send(newValue)
}
}
var projectedValue: AnyPublisher<Value, Never> {
return publisher.eraseToAnyPublisher()
}
}
extension UserDefault where Value: ExpressibleByNilLiteral {
init(key: UserDefaults.Key, _ container: UserDefaults = .shared) {
self.init(key: key.rawValue, defaultValue: nil, container: container)
}
}
In LoginViewModel
, I set githubId value.
class LoginViewModel: ObservableObject {
@Published var githubId = UserDefaults.githubId ?? ""
private var subscriptions = Set<AnyCancellable>()
init() {
$githubId
.sink { githubId in
UserDefaults.githubId = githubId
}
.store(in: &subscriptions)
}
}
Finally I use this value in my app, and widget extension using UserDefaults.githubId
The code I wrote above ( UserDefaults(suiteName: appGroupId)!.object(forKey: "githubId")
) long version of UserDefaults.githubId
.
.sink
won't work in a widget.
Whatever you want to display in a widget has to be done when you setup the timeline. Widgets aren't listening for changes.
You have to reload the widget timelines from the app when there are changes. consider each timeline entry like a screenshot.
WidgetCenter.shared.reloadAllTimelines()
Reloading is metered so make sure you have a check and don't reload excessively.