Commit 4de104bf by Demid Merzlyakov

IOS-72: sync data between the app and the widget.

parent 57ba63d8
......@@ -491,6 +491,7 @@
CE14445D2638B6A8008E2162 /* 1Weather.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = 1Weather.entitlements; sourceTree = "<group>"; };
CE14445E2638B6CF008E2162 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
CE30E37E2668FBE3006DF5CD /* OneWeatherAnalytics.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OneWeatherAnalytics.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CE35F60E2681FA27008D507E /* OneWeatherWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OneWeatherWidgetExtension.entitlements; sourceTree = "<group>"; };
CE376C97261EE484000B1159 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
CE578FE225FB415F00E8B85D /* CityCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CityCell.swift; sourceTree = "<group>"; };
CE578FE325FB415F00E8B85D /* LocationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationViewController.swift; sourceTree = "<group>"; };
......@@ -597,6 +598,7 @@
CD1237B6255D5C5900C98139 = {
isa = PBXGroup;
children = (
CE35F60E2681FA27008D507E /* OneWeatherWidgetExtension.entitlements */,
CD1237C1255D5C5900C98139 /* 1Weather */,
CE849DB62638C33600DEFFBD /* OneWeatherNotificationServiceExtension */,
CD1B713E2660F95000916E71 /* OneWeatherWidget */,
......@@ -2040,6 +2042,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = OneWeatherWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 24W4XMQ38L;
INFOPLIST_FILE = OneWeatherWidget/Info.plist;
......@@ -2061,6 +2064,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = OneWeatherWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 24W4XMQ38L;
INFOPLIST_FILE = OneWeatherWidget/Info.plist;
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CE849DB42638C33600DEFFBD"
BuildableName = "OneWeatherNotificationServiceExtension.appex"
BlueprintName = "OneWeatherNotificationServiceExtension"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD1237BE255D5C5900C98139"
BuildableName = "1Weather.app"
BlueprintName = "1Weather"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD1237BE255D5C5900C98139"
BuildableName = "1Weather.app"
BlueprintName = "1Weather"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD1237BE255D5C5900C98139"
BuildableName = "1Weather.app"
BlueprintName = "1Weather"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD1B71382660F95000916E71"
BuildableName = "OneWeatherWidgetExtension.appex"
BlueprintName = "OneWeatherWidgetExtension"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD1237BE255D5C5900C98139"
BuildableName = "1Weather.app"
BlueprintName = "1Weather"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.springboard">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD1B71382660F95000916E71"
BuildableName = "OneWeatherWidgetExtension.appex"
BlueprintName = "OneWeatherWidgetExtension"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD1237BE255D5C5900C98139"
BuildableName = "1Weather.app"
BlueprintName = "1Weather"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
value = "com.outlouder.oneweather.widget"
isEnabled = "NO">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetDefaultView"
value = "timeline"
isEnabled = "NO">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetFamily"
value = "small"
isEnabled = "NO">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD1237BE255D5C5900C98139"
BuildableName = "1Weather.app"
BlueprintName = "1Weather"
ReferencedContainer = "container:1Weather.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
......@@ -11,20 +11,59 @@ import OneWeatherCore
import OneWeatherAnalytics
public class CoreDataStorage: Storage {
private let modelName = "1WModel"
private var lastSavedAppData: AppData? = nil
private let log = Logger(componentName: "CoreDataStorage 💾")
private lazy var managedContext: NSManagedObjectContext? = {
persistentContainer.newBackgroundContext()
}()
/// The Persistent Store used to be in the app directory, but was then moved to an app group container to be shared with widgets.
/// For all users who still have it in the old location, we need to move it to the new one.
private func movePersistentStoreIfNeeded() {
let legacyStoreUrl = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("\(modelName).sqlite")
guard let storeUrl = self.storeUrl else {
return
}
let fileManager = FileManager.default
guard fileManager.fileExists(atPath: legacyStoreUrl.path) && !fileManager.fileExists(atPath: storeUrl.path) else {
return
}
do {
log.info("Moving the persistent store to the app group location.")
try fileManager.moveItem(at: legacyStoreUrl, to: storeUrl)
}
catch {
log.error("Error moving the model from the old location to the new one: \(error)")
}
}
private var storeUrl: URL? {
let appGroupIdentifier = "group.com.onelouder.oneweather"
let fileManager = FileManager.default
guard let containerUrl = fileManager.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) else {
log.error("Failed to get container URL for app group \(appGroupIdentifier)!")
assertionFailure("Failed to get container URL for app group \(appGroupIdentifier)!")
return nil
}
return containerUrl.appendingPathComponent("\(modelName).sqlite")
}
private lazy var persistentContainer: NSPersistentContainer = {
guard
let modelURL = Bundle(for: CoreDataStorage.self).url(forResource: "1WModel", withExtension: "momd"),
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL),
let storeUrl = self.storeUrl
else {
return NSPersistentContainer(name: "1WModel")
}
movePersistentStoreIfNeeded()
let container = NSPersistentContainer(name: "1WModel", managedObjectModel: managedObjectModel)
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeUrl)]
NSPersistentContainer.defaultDirectoryURL()
container.loadPersistentStores { [weak self] (description, error) in
if let error = error {
self?.log.error("Error loading persistent stores: \(error)")
......
......@@ -13,6 +13,7 @@ struct OneWeatherWidget: Widget {
private let kind = "com.onelouder.oneweather.widget"
var body: some WidgetConfiguration {
// We currently display selectedLocation, so it's not really configurable, but we'll probably need to switch to an IntentConfiguration at some point.
StaticConfiguration(kind: kind, provider: WeatherProvider()) { weatherEntry in
SmallWidgetView(widgetViewModel: .init(location: weatherEntry.location))
}
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.onelouder.oneweather</string>
</array>
</dict>
</plist>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment