ios - Today extension: syncing data with container app -
context
i've been playing around today extensions using this example project.
the app quite simple:
- in containing app, have list of todo items, can mark completed
- in today widget, see same list, can switch between completed, , incomplete items using segmented control.
my goal following: whenever there data change, either in container app, or widget, want both reflect changes:
- if mark item completed in container app, pull down notification center, widget should updated
- when same in widget, return app, app's state should updated
the implementation
i understand, container app, , extension run in separate processes, means 2 constraints:
nsuserdefaultsdidchangenotification
useless.- managing model instances in memory useless.
i know, in order access shared container, both targets must opt-in app groups entitlements under same group id.
the data access managed embedded framework, todokit. instead of keeping properties in memory, goes straight nsuserdefaults
appropriate values:
public struct shoppingitemstore: shoppingstoretype { private let defaultitems = [ shoppingitem(name: "coffee"), shoppingitem(name: "banana"), ] private let defaults = nsuserdefaults(suitename: appgroupid) public init() {} public func items() -> [shoppingitem] { if let loaded = loaditems() { return loaded } else { return defaultitems } } public func toggleitem(item: shoppingitem) { let initial = items() let updated = initial.map { original -> shoppingitem in return original == item ? shoppingitem(name: original.name, status: !original.status) : original } saveitems(updated) } private func saveitems(items: [shoppingitem]) { let boxeditems = items.map { item -> [string : bool] in return [item.name : item.status] } defaults?.setvalue(boxeditems, forkey: saveddatakey) defaults?.synchronize() } private func loaditems() -> [shoppingitem]? { if let loaded = defaults?.valueforkey(saveddatakey) as? [[string : bool]] { let unboxed = loaded.map { dict -> shoppingitem in return shoppingitem(name: dict.keys.first!, status: dict.values.first!) } return unboxed } return nil } }
the problem
here's works:
- when modify list in main app, stop simulator, , launch today target xcode, reflects correct state. true vice-versa.
this verifies, app group set correctly.
however, when change in main app, pull down notification center, out of sync. , part, don't understand.
my views data straight shared container. whenever change happens, update data in shared container.
what missing? how can sync these 2 properly? data access class not managint any state, yet don't understand why doesn't behave correctly.
additional info
i know mmwormhole. unfortunately not option me, since need reach proper functionality without including third party solutions.
this terrific article, covers topic, , might possible, need employ
nsfilepresenter
, although seems cumbersome, , don't understand mechanism yet. hope, there easier solution, one.
well, have learned 2 things here:
first of all, always double check entitlements, mine somehow got messed up, , that's why shared container behaved awkwardly.
second: although viewwillappear(_:)
not called, when dismiss notification center, it's still possible trigger update app delegate:
func applicationdidbecomeactive(application: uiapplication) { nsnotificationcenter.defaultcenter().postnotificationname(updatedatanotification, object: nil) }
then in view controller:
override func viewwillappear(animated: bool) { super.viewwillappear(animated) nsnotificationcenter.defaultcenter().addobserverforname(updatedatanotification, object: nil, queue: nsoperationqueue.mainqueue()) { (_) -> void in self.tableview.reloaddata() } } override func viewdiddisappear(animated: bool) { super.viewdiddisappear(animated) nsnotificationcenter.defaultcenter().removeobserver(self) }
updating today widget simple: each time notification center pulled down, viewwillappear(:_)
called, can query new data there.
i'll update example project on github shortly.
Comments
Post a Comment