Commit ae034e64 by Dmitriy Stepanets

Merge branch 'feature/IOS-101-widget-medium-temperature' into…

Merge branch 'feature/IOS-101-widget-medium-temperature' into feature/IOS-123-widget-promotion-sheet
parents 84bc8c41 0ebf55bb
...@@ -211,7 +211,6 @@ ...@@ -211,7 +211,6 @@
CE5F0CBA268A02C100B99572 /* MediumTemperatureWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5F0CB9268A02C100B99572 /* MediumTemperatureWidget.swift */; }; CE5F0CBA268A02C100B99572 /* MediumTemperatureWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5F0CB9268A02C100B99572 /* MediumTemperatureWidget.swift */; };
CE5F0CBC268A031800B99572 /* OneWeatherWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5F0CBB268A031800B99572 /* OneWeatherWidgetsBundle.swift */; }; CE5F0CBC268A031800B99572 /* OneWeatherWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5F0CBB268A031800B99572 /* OneWeatherWidgetsBundle.swift */; };
CE6BE4942634170800626822 /* USStateCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6BE4932634170800626822 /* USStateCode.swift */; }; CE6BE4942634170800626822 /* USStateCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6BE4932634170800626822 /* USStateCode.swift */; };
CE6F5F0C263C8B3D00973137 /* SmartTextProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6F5F0B263C8B3C00973137 /* SmartTextProvider.swift */; };
CE7298C9267A34F3002745D0 /* BlendFIPSSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEEF40FF265E47FF00425D8F /* BlendFIPSSource.framework */; }; CE7298C9267A34F3002745D0 /* BlendFIPSSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEEF40FF265E47FF00425D8F /* BlendFIPSSource.framework */; };
CE7298CA267A34F3002745D0 /* BlendFIPSSource.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEEF40FF265E47FF00425D8F /* BlendFIPSSource.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CE7298CA267A34F3002745D0 /* BlendFIPSSource.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEEF40FF265E47FF00425D8F /* BlendFIPSSource.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CE7298CC267A34F5002745D0 /* BlendHealthSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD3883C12657B6A10070FD6F /* BlendHealthSource.framework */; }; CE7298CC267A34F5002745D0 /* BlendHealthSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD3883C12657B6A10070FD6F /* BlendHealthSource.framework */; };
...@@ -519,7 +518,6 @@ ...@@ -519,7 +518,6 @@
CE5F0CB9268A02C100B99572 /* MediumTemperatureWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediumTemperatureWidget.swift; sourceTree = "<group>"; }; CE5F0CB9268A02C100B99572 /* MediumTemperatureWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediumTemperatureWidget.swift; sourceTree = "<group>"; };
CE5F0CBB268A031800B99572 /* OneWeatherWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneWeatherWidgetsBundle.swift; sourceTree = "<group>"; }; CE5F0CBB268A031800B99572 /* OneWeatherWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneWeatherWidgetsBundle.swift; sourceTree = "<group>"; };
CE6BE4932634170800626822 /* USStateCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = USStateCode.swift; sourceTree = "<group>"; }; CE6BE4932634170800626822 /* USStateCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = USStateCode.swift; sourceTree = "<group>"; };
CE6F5F0B263C8B3C00973137 /* SmartTextProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartTextProvider.swift; sourceTree = "<group>"; };
CE81A421266E289E00800EFF /* NativeAdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeAdView.swift; sourceTree = "<group>"; }; CE81A421266E289E00800EFF /* NativeAdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeAdView.swift; sourceTree = "<group>"; };
CE849DB52638C33600DEFFBD /* OneWeatherNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OneWeatherNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; CE849DB52638C33600DEFFBD /* OneWeatherNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OneWeatherNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
CE849DB72638C33600DEFFBD /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; }; CE849DB72638C33600DEFFBD /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
...@@ -1348,7 +1346,6 @@ ...@@ -1348,7 +1346,6 @@
CE13B76126246743007CBD4D /* CCPA */, CE13B76126246743007CBD4D /* CCPA */,
CE6BE4932634170800626822 /* USStateCode.swift */, CE6BE4932634170800626822 /* USStateCode.swift */,
CE895F0E26393FD800214175 /* WeatherImageProvider.swift */, CE895F0E26393FD800214175 /* WeatherImageProvider.swift */,
CE6F5F0B263C8B3C00973137 /* SmartTextProvider.swift */,
); );
path = Model; path = Model;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1896,7 +1893,6 @@ ...@@ -1896,7 +1893,6 @@
CD17C5F625D15B4400EE884E /* TodayViewController.swift in Sources */, CD17C5F625D15B4400EE884E /* TodayViewController.swift in Sources */,
CD86245E25E646350097F3FB /* SunUvView.swift in Sources */, CD86245E25E646350097F3FB /* SunUvView.swift in Sources */,
CDF8F12D26208E7B00DB384A /* MapCurrentTimeView.swift in Sources */, CDF8F12D26208E7B00DB384A /* MapCurrentTimeView.swift in Sources */,
CE6F5F0C263C8B3D00973137 /* SmartTextProvider.swift in Sources */,
CEC7D8EE2639FE2700B8836D /* OLInAppStoreManager.swift in Sources */, CEC7D8EE2639FE2700B8836D /* OLInAppStoreManager.swift in Sources */,
CD82300725D6A73F00A05501 /* TodayConditionButton.swift in Sources */, CD82300725D6A73F00A05501 /* TodayConditionButton.swift in Sources */,
CDC6126A25E90C8800188DA7 /* GraphLineSettings.swift in Sources */, CDC6126A25E90C8800188DA7 /* GraphLineSettings.swift in Sources */,
......
...@@ -80,8 +80,8 @@ ...@@ -80,8 +80,8 @@
<EnvironmentVariables> <EnvironmentVariables>
<EnvironmentVariable <EnvironmentVariable
key = "_XCWidgetKind" key = "_XCWidgetKind"
value = "com.outlouder.oneweather.widget" value = "com.onelouder.oneweather.widget.small.temperature"
isEnabled = "NO"> isEnabled = "YES">
</EnvironmentVariable> </EnvironmentVariable>
<EnvironmentVariable <EnvironmentVariable
key = "_XCWidgetDefaultView" key = "_XCWidgetDefaultView"
......
...@@ -9,6 +9,7 @@ import Foundation ...@@ -9,6 +9,7 @@ import Foundation
import CoreData import CoreData
import OneWeatherCore import OneWeatherCore
import OneWeatherAnalytics import OneWeatherAnalytics
import WidgetKit
public class CoreDataStorage: Storage { public class CoreDataStorage: Storage {
private let modelName = "1WModel" private let modelName = "1WModel"
...@@ -92,6 +93,11 @@ public class CoreDataStorage: Storage { ...@@ -92,6 +93,11 @@ public class CoreDataStorage: Storage {
context.insert(coreAppData) context.insert(coreAppData)
try self.save(context: context) try self.save(context: context)
self.lastSavedAppData = appData self.lastSavedAppData = appData
// This shouldn't be here in theory, but it's the simplest way to work around the DelayedSaveStorage.
// TODO: find a better place for it.
if #available(iOS 14, *) {
WidgetCenter.shared.reloadAllTimelines()
}
} }
self.log.info("Save: success") self.log.info("Save: success")
} }
......
...@@ -75,6 +75,7 @@ public class DelayedSaveStorage: Storage { ...@@ -75,6 +75,7 @@ public class DelayedSaveStorage: Storage {
self?.latestKnownAppData = nil self?.latestKnownAppData = nil
} }
} }
self?.saveQueue.addOperation(saveOperation)
} }
saveDelayQueue.addOperation(saveWithDelayOperation) saveDelayQueue.addOperation(saveWithDelayOperation)
} }
...@@ -106,5 +107,6 @@ public class DelayedSaveStorage: Storage { ...@@ -106,5 +107,6 @@ public class DelayedSaveStorage: Storage {
self?.latestKnownAppDataSynchronizationQueue.waitUntilAllOperationsAreFinished() self?.latestKnownAppDataSynchronizationQueue.waitUntilAllOperationsAreFinished()
} }
self.saveQueue.waitUntilAllOperationsAreFinished() self.saveQueue.waitUntilAllOperationsAreFinished()
self.latestKnownAppDataSynchronizationQueue.waitUntilAllOperationsAreFinished()
} }
} }
...@@ -82,6 +82,14 @@ ...@@ -82,6 +82,14 @@
CDFE458D26566BD50021A29F /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE458C26566BD50021A29F /* Storage.swift */; }; CDFE458D26566BD50021A29F /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE458C26566BD50021A29F /* Storage.swift */; };
CDFE459426566D7B0021A29F /* HealthSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE459326566D7B0021A29F /* HealthSource.swift */; }; CDFE459426566D7B0021A29F /* HealthSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE459326566D7B0021A29F /* HealthSource.swift */; };
CDFE459626566D860021A29F /* FIPSSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE459526566D860021A29F /* FIPSSource.swift */; }; CDFE459626566D860021A29F /* FIPSSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE459526566D860021A29F /* FIPSSource.swift */; };
CEFE851826948C15003C67D3 /* SmartTextProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE851726948C15003C67D3 /* SmartTextProvider.swift */; };
CEFE851C2694986D003C67D3 /* SmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE851B2694986D003C67D3 /* SmartText.swift */; };
CEFE851E2694C477003C67D3 /* SmartTextMacro.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE851D2694C477003C67D3 /* SmartTextMacro.swift */; };
CEFE85202694C4BC003C67D3 /* DefaultSmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE851F2694C4BC003C67D3 /* DefaultSmartText.swift */; };
CEFE85222694C57A003C67D3 /* OngoingPrecipitationSmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE85212694C57A003C67D3 /* OngoingPrecipitationSmartText.swift */; };
CEFE85242694C5D1003C67D3 /* ApproachingPrecipitationSmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE85232694C5D1003C67D3 /* ApproachingPrecipitationSmartText.swift */; };
CEFE85262694C5E4003C67D3 /* WindSmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE85252694C5E4003C67D3 /* WindSmartText.swift */; };
CEFE85282694C5F7003C67D3 /* HumiditySmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE85272694C5F7003C67D3 /* HumiditySmartText.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
...@@ -180,6 +188,14 @@ ...@@ -180,6 +188,14 @@
CDFE458C26566BD50021A29F /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; }; CDFE458C26566BD50021A29F /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
CDFE459326566D7B0021A29F /* HealthSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthSource.swift; sourceTree = "<group>"; }; CDFE459326566D7B0021A29F /* HealthSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthSource.swift; sourceTree = "<group>"; };
CDFE459526566D860021A29F /* FIPSSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FIPSSource.swift; sourceTree = "<group>"; }; CDFE459526566D860021A29F /* FIPSSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FIPSSource.swift; sourceTree = "<group>"; };
CEFE851726948C15003C67D3 /* SmartTextProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartTextProvider.swift; sourceTree = "<group>"; };
CEFE851B2694986D003C67D3 /* SmartText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartText.swift; sourceTree = "<group>"; };
CEFE851D2694C477003C67D3 /* SmartTextMacro.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartTextMacro.swift; sourceTree = "<group>"; };
CEFE851F2694C4BC003C67D3 /* DefaultSmartText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultSmartText.swift; sourceTree = "<group>"; };
CEFE85212694C57A003C67D3 /* OngoingPrecipitationSmartText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OngoingPrecipitationSmartText.swift; sourceTree = "<group>"; };
CEFE85232694C5D1003C67D3 /* ApproachingPrecipitationSmartText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApproachingPrecipitationSmartText.swift; sourceTree = "<group>"; };
CEFE85252694C5E4003C67D3 /* WindSmartText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindSmartText.swift; sourceTree = "<group>"; };
CEFE85272694C5F7003C67D3 /* HumiditySmartText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HumiditySmartText.swift; sourceTree = "<group>"; };
D9993F9D0D3A6FC16441D26F /* Pods_OneWeatherCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OneWeatherCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D9993F9D0D3A6FC16441D26F /* Pods_OneWeatherCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OneWeatherCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -414,6 +430,7 @@ ...@@ -414,6 +430,7 @@
CDD2F8ED2665100F00B48322 /* Model */ = { CDD2F8ED2665100F00B48322 /* Model */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CEFE851926949844003C67D3 /* SmartTexts */,
CDD2F8EE2665102B00B48322 /* LocationManager.swift */, CDD2F8EE2665102B00B48322 /* LocationManager.swift */,
CDD2F8F02665112800B48322 /* DeviceLocationMonitor.swift */, CDD2F8F02665112800B48322 /* DeviceLocationMonitor.swift */,
); );
...@@ -451,6 +468,29 @@ ...@@ -451,6 +468,29 @@
path = Sources; path = Sources;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CEFE851926949844003C67D3 /* SmartTexts */ = {
isa = PBXGroup;
children = (
CEFE851726948C15003C67D3 /* SmartTextProvider.swift */,
CEFE851B2694986D003C67D3 /* SmartText.swift */,
CEFE851D2694C477003C67D3 /* SmartTextMacro.swift */,
CEFE851A26949855003C67D3 /* SmartTexts */,
);
path = SmartTexts;
sourceTree = "<group>";
};
CEFE851A26949855003C67D3 /* SmartTexts */ = {
isa = PBXGroup;
children = (
CEFE851F2694C4BC003C67D3 /* DefaultSmartText.swift */,
CEFE85212694C57A003C67D3 /* OngoingPrecipitationSmartText.swift */,
CEFE85232694C5D1003C67D3 /* ApproachingPrecipitationSmartText.swift */,
CEFE85252694C5E4003C67D3 /* WindSmartText.swift */,
CEFE85272694C5F7003C67D3 /* HumiditySmartText.swift */,
);
path = SmartTexts;
sourceTree = "<group>";
};
F74D258AC48F534A8A9B9EDB /* Frameworks */ = { F74D258AC48F534A8A9B9EDB /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -600,8 +640,10 @@ ...@@ -600,8 +640,10 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
CD3883972657AFE00070FD6F /* Settings.swift in Sources */, CD3883972657AFE00070FD6F /* Settings.swift in Sources */,
CEFE85242694C5D1003C67D3 /* ApproachingPrecipitationSmartText.swift in Sources */,
CD2D55DA26553751007B70F4 /* NWSAlert.swift in Sources */, CD2D55DA26553751007B70F4 /* NWSAlert.swift in Sources */,
CD11AFE526651BCD00EC4BA0 /* LegacySettings.swift in Sources */, CD11AFE526651BCD00EC4BA0 /* LegacySettings.swift in Sources */,
CEFE85262694C5E4003C67D3 /* WindSmartText.swift in Sources */,
CD11AFED26651E0A00EC4BA0 /* WdtWeatherSourceError.swift in Sources */, CD11AFED26651E0A00EC4BA0 /* WdtWeatherSourceError.swift in Sources */,
CD2D55D526553384007B70F4 /* UserDefaultsOptionalValue.swift in Sources */, CD2D55D526553384007B70F4 /* UserDefaultsOptionalValue.swift in Sources */,
CD11AFE126651A4700EC4BA0 /* NWSAlertInfoParser.swift in Sources */, CD11AFE126651A4700EC4BA0 /* NWSAlertInfoParser.swift in Sources */,
...@@ -613,10 +655,12 @@ ...@@ -613,10 +655,12 @@
CD550FBC265531A100257FB5 /* WeatherLayerType.swift in Sources */, CD550FBC265531A100257FB5 /* WeatherLayerType.swift in Sources */,
CD550FBD265531A100257FB5 /* SevereLayerType.swift in Sources */, CD550FBD265531A100257FB5 /* SevereLayerType.swift in Sources */,
CD91685826552FD000EC04EF /* DefaultSettingsFactory.swift in Sources */, CD91685826552FD000EC04EF /* DefaultSettingsFactory.swift in Sources */,
CEFE851C2694986D003C67D3 /* SmartText.swift in Sources */,
CDFE459626566D860021A29F /* FIPSSource.swift in Sources */, CDFE459626566D860021A29F /* FIPSSource.swift in Sources */,
CD91685926552FD000EC04EF /* DefaultSettings.swift in Sources */, CD91685926552FD000EC04EF /* DefaultSettings.swift in Sources */,
CD91685A26552FD000EC04EF /* DefaultSettingsImperial.swift in Sources */, CD91685A26552FD000EC04EF /* DefaultSettingsImperial.swift in Sources */,
CD91685B26552FD000EC04EF /* DefaultSettingsMetric.swift in Sources */, CD91685B26552FD000EC04EF /* DefaultSettingsMetric.swift in Sources */,
CEFE85282694C5F7003C67D3 /* HumiditySmartText.swift in Sources */,
CD615FB82655295C00B717DB /* UIColor+Hex.swift in Sources */, CD615FB82655295C00B717DB /* UIColor+Hex.swift in Sources */,
CD2D55E0265537DC007B70F4 /* NWSAlertInfoBlock.swift in Sources */, CD2D55E0265537DC007B70F4 /* NWSAlertInfoBlock.swift in Sources */,
CD91685726552FAE00EC04EF /* MulticastDelegate.swift in Sources */, CD91685726552FAE00EC04EF /* MulticastDelegate.swift in Sources */,
...@@ -638,22 +682,26 @@ ...@@ -638,22 +682,26 @@
CD615FC42655295C00B717DB /* UnitPressure+Atmosphere.swift in Sources */, CD615FC42655295C00B717DB /* UnitPressure+Atmosphere.swift in Sources */,
CD615FC52655295C00B717DB /* CLAuthorizationStatus+Localized.swift in Sources */, CD615FC52655295C00B717DB /* CLAuthorizationStatus+Localized.swift in Sources */,
CD615FC62655295C00B717DB /* UIApplication+Settings.swift in Sources */, CD615FC62655295C00B717DB /* UIApplication+Settings.swift in Sources */,
CEFE85202694C4BC003C67D3 /* DefaultSmartText.swift in Sources */,
CD8E48A526651414008E7F8D /* NWSCurrentEventsReponse.swift in Sources */, CD8E48A526651414008E7F8D /* NWSCurrentEventsReponse.swift in Sources */,
CD2D55D8265533F4007B70F4 /* UserDefaultsWrapper.swift in Sources */, CD2D55D8265533F4007B70F4 /* UserDefaultsWrapper.swift in Sources */,
CD615FC72655295C00B717DB /* UITabBarController+Hide.swift in Sources */, CD615FC72655295C00B717DB /* UITabBarController+Hide.swift in Sources */,
CDD2F8F62665117400B48322 /* NWSAlertsManager.swift in Sources */, CDD2F8F62665117400B48322 /* NWSAlertsManager.swift in Sources */,
CD11AFE726651BF900EC4BA0 /* LegacyWdtLocation.swift in Sources */, CD11AFE726651BF900EC4BA0 /* LegacyWdtLocation.swift in Sources */,
CD615FC82655295C00B717DB /* CACornerMask+All.swift in Sources */, CD615FC82655295C00B717DB /* CACornerMask+All.swift in Sources */,
CEFE851E2694C477003C67D3 /* SmartTextMacro.swift in Sources */,
CD11AFE326651B6300EC4BA0 /* LegacyMigrationManager.swift in Sources */, CD11AFE326651B6300EC4BA0 /* LegacyMigrationManager.swift in Sources */,
CD2D55DD2655377F007B70F4 /* NWSAlertExtendedInfo.swift in Sources */, CD2D55DD2655377F007B70F4 /* NWSAlertExtendedInfo.swift in Sources */,
CD615FC92655295C00B717DB /* UIDevice+Convenience.swift in Sources */, CD615FC92655295C00B717DB /* UIDevice+Convenience.swift in Sources */,
CD615F95265526E700B717DB /* UpdatableModelObject.swift in Sources */, CD615F95265526E700B717DB /* UpdatableModelObject.swift in Sources */,
CD615FA4265528F000B717DB /* HelperTypes.swift in Sources */, CD615FA4265528F000B717DB /* HelperTypes.swift in Sources */,
CD3883EC2657B83D0070FD6F /* FIPSResponse.swift in Sources */, CD3883EC2657B83D0070FD6F /* FIPSResponse.swift in Sources */,
CEFE85222694C57A003C67D3 /* OngoingPrecipitationSmartText.swift in Sources */,
CD615F96265526E700B717DB /* UpdatableModelObjectInTime.swift in Sources */, CD615F96265526E700B717DB /* UpdatableModelObjectInTime.swift in Sources */,
CD615F97265526E700B717DB /* PartialLocation.swift in Sources */, CD615F97265526E700B717DB /* PartialLocation.swift in Sources */,
CD615F98265526E700B717DB /* GeoNamesPlace.swift in Sources */, CD615F98265526E700B717DB /* GeoNamesPlace.swift in Sources */,
CD615F99265526E700B717DB /* Location.swift in Sources */, CD615F99265526E700B717DB /* Location.swift in Sources */,
CEFE851826948C15003C67D3 /* SmartTextProvider.swift in Sources */,
CD11AFE926651C9200EC4BA0 /* StuffThatIsPresentInTheMainProject.swift in Sources */, CD11AFE926651C9200EC4BA0 /* StuffThatIsPresentInTheMainProject.swift in Sources */,
CDFE459426566D7B0021A29F /* HealthSource.swift in Sources */, CDFE459426566D7B0021A29F /* HealthSource.swift in Sources */,
CD615F9A265526E700B717DB /* CurrentWeather.swift in Sources */, CD615F9A265526E700B717DB /* CurrentWeather.swift in Sources */,
......
...@@ -62,6 +62,11 @@ public enum WeatherType: String, CaseIterable { ...@@ -62,6 +62,11 @@ public enum WeatherType: String, CaseIterable {
} }
return stringId return stringId
} }
public var isPrecipitation: Bool {
let precipitationTypes = Set<WeatherType>([.snowy, .thunderstorm, .heavySnow, .lightSnow, .freezingRain, .lightHail, .lightDrizzle, .heavyRain])
return precipitationTypes.contains(self)
}
} }
public enum WeatherConditionType: CaseIterable { public enum WeatherConditionType: CaseIterable {
......
...@@ -428,8 +428,9 @@ public class LocationManager { ...@@ -428,8 +428,9 @@ public class LocationManager {
if let updatedLocation = updatedLocation { if let updatedLocation = updatedLocation {
self.log.info("Update weather finished for \(location)") self.log.info("Update weather finished for \(location)")
self.makeChanges(to: location, in: "weather") { (oldLocation) -> Location in self.makeChanges(to: location, in: "weather") { (oldLocation) -> Location in
completion?(updatedLocation) let merged = oldLocation.mergedWith(incrementalChanges: updatedLocation)
return oldLocation.mergedWith(incrementalChanges: updatedLocation) completion?(merged)
return merged
} }
} }
else { else {
......
//
// SmartText.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 06.07.2021.
//
import Foundation
/// A single smart text case. E.g. "feels like X degrees", "strong wind, stay at home", etc.
/// The text itself is stored in localized strings, and `templateKey` is used to fetch the actual text. But then we need to insert values into localized text, like temperature, humidity, etc. And it needs to be localized as well (e.g. temperature in F, or in C depending on the user settings.).
/// For that purpose, `SmartTextMacro` surve. See the description for `SmartTextMacro`for more details, but in general smart text macro can check wether a particular piece of information is available in this smart text, and is capable of extracting it from the weather data and formatting it for being printed.
/// So, the overall logic is as follows:
/// ```
/// 1. Take smart text.
/// 2. Check if it's applicable.
/// 3. If it is, extract macro values from a location object.
/// 4. Take the templateKey and get it's localized value.
/// 5. Substitute macros in the localized template with actual localized values.
/// ```
public protocol SmartText {
var templateKey: String { get }
var requiredMacros: [SmartTextMacro] { get }
/// Wether this smart text can be applied to this location. E.g. if this smart text requires temperature forecast for the next 5 days, and this location object only has 3 days forecast, it is not applicable.
/// - Parameter location: A location object, for which we'd like to generate a smart text.
func applicable(to location: Location) -> Bool
/// A function for building
/// - Parameter location: <#location description#>
func buildText(for location: Location) -> String?
}
public extension SmartText {
func buildText(for location: Location) -> String? {
var result: String? = self.templateKey.localized()
for macro in requiredMacros {
if let macroValue = macro.string(from: location) {
result = result?.replacingOccurrences(of: macro.templateKey, with: macroValue)
}
else {
result = nil
break
}
}
return result
}
}
//
// SmartTextProvider.swift
// 1Weather
//
// Created by Demid Merzlyakov on 01.05.2021.
//
import Foundation
public class SmartTextProvider {
private var prioritizedSmartTexts: [SmartText] = [
OngoingPrecipitationSmartText(),
ApproachingPrecipitationSmartText(),
WindSmartText(minAllowedWindSpeed: WindSpeed(value: 75, unit: .kilometersPerHour)),
HumiditySmartText(),
WindSmartText(minAllowedWindSpeed: WindSpeed(value: 50, unit: .kilometersPerHour)),
DefaultSmartText()]
public init() {
}
public func smartText(for location: Location) -> String {
var result = ""
for candidate in prioritizedSmartTexts {
if candidate.applicable(to: location) {
if let candidateText = candidate.buildText(for: location) {
result = candidateText
break
}
}
}
return result
}
}
//
// ApproachingPrecipitationSmartText.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 06.07.2021.
//
import Foundation
public struct ApproachingPrecipitationSmartText: SmartText {
public let templateKey: String = "today.smart.approachingPrecipitation"
public let requiredMacros: [SmartTextMacro] = [SmartTextMacros.ExpectedPrecipitationHour(), SmartTextMacros.ExpectedPrecipitationProbability(), SmartTextMacros.ExpectedPrecipitationWeatherType()]
public func applicable(to location: Location) -> Bool {
guard let today = location.today else {
return false
}
guard !today.type.isPrecipitation else {
return false
}
return SmartTextMacros.expectedPrecipitation(for: location) != nil
}
}
//
// DefaultSmartText.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 06.07.2021.
//
import Foundation
/// A smart text that is always applicable. It can be used to provide default text.
public struct DefaultSmartText: SmartText {
public let templateKey = "today.smart.default"
public let requiredMacros: [SmartTextMacro] = [SmartTextMacros.FeelsLikeTemp()]
public func applicable(to location: Location) -> Bool {
true
}
}
//
// HumiditySmartText.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 06.07.2021.
//
import Foundation
public struct HumiditySmartText: SmartText {
public let templateKey: String = "today.smart.humidity"
public let requiredMacros: [SmartTextMacro] = [SmartTextMacros.FeelsLikeTemp(), SmartTextMacros.Humidity(), SmartTextMacros.HumidityType()]
public func applicable(to location: Location) -> Bool {
guard let humidity = location.today?.humidity else {
return false
}
return humidity > 50 || humidity < 30
}
}
//
// OngoingPrecipitationSmartText.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 06.07.2021.
//
import Foundation
public struct OngoingPrecipitationSmartText: SmartText {
public let templateKey: String = "today.smart.ongoingPrecipitation"
public let requiredMacros: [SmartTextMacro] = [SmartTextMacros.WeatherType(), SmartTextMacros.WeatherTypeChangeHour()]
public func applicable(to location: Location) -> Bool {
guard let today = location.today else {
return false
}
return today.type.isPrecipitation
}
}
//
// WindSmartText.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 06.07.2021.
//
import Foundation
public struct WindSmartText: SmartText {
public let templateKey: String = "today.smart.wind"
public let requiredMacros: [SmartTextMacro] = [SmartTextMacros.WindType(), SmartTextMacros.WindSpeed(), SmartTextMacros.WindDirection()]
let minAllowedWindSpeed: WindSpeed
public func applicable(to location: Location) -> Bool {
guard let today = location.today else {
return false
}
guard let windSpeed = today.windSpeed, let _ = today.windDirection else {
return false
}
return windSpeed >= minAllowedWindSpeed
}
}
...@@ -55,7 +55,7 @@ public struct MediumTemperatureWidgetView: View { ...@@ -55,7 +55,7 @@ public struct MediumTemperatureWidgetView: View {
.padding(.top, 12) .padding(.top, 12)
HStack(alignment: .top) { HStack(alignment: .top) {
Text("SmartText") Text(widgetViewModel.smartText)
.font(WidgetFont.SFProDisplay.regular(size: 12).font) .font(WidgetFont.SFProDisplay.regular(size: 12).font)
Spacer() Spacer()
......
...@@ -18,4 +18,5 @@ public protocol WidgetViewModel { ...@@ -18,4 +18,5 @@ public protocol WidgetViewModel {
var highTemperature: String { get } var highTemperature: String { get }
var lowTemperature: String { get } var lowTemperature: String { get }
var isDeviceLocation: Bool { get } var isDeviceLocation: Bool { get }
var smartText: String { get }
} }
...@@ -10,8 +10,8 @@ import UIKit ...@@ -10,8 +10,8 @@ import UIKit
@available(iOS 14, *) @available(iOS 14, *)
struct WidgetViewModelMock: WidgetViewModel { struct WidgetViewModelMock: WidgetViewModel {
private class OneWeatherUIClass {} private class OneWeatherUIClass {}
let showLastTimeUpdated = false let showLastTimeUpdated = true
let lastTimeUpdatedText = "Last update text" let lastTimeUpdatedText = "Last updated 2h ago."
let cityName = "New York" let cityName = "New York"
let temperature = "96°" let temperature = "96°"
let weatherType = "Partly Cloudy" let weatherType = "Partly Cloudy"
...@@ -19,4 +19,5 @@ struct WidgetViewModelMock: WidgetViewModel { ...@@ -19,4 +19,5 @@ struct WidgetViewModelMock: WidgetViewModel {
let highTemperature = "97°" let highTemperature = "97°"
let lowTemperature = "89°" let lowTemperature = "89°"
let isDeviceLocation = true let isDeviceLocation = true
let smartText = "Feels like 101° due to high humidity (77%)."
} }
...@@ -17,16 +17,6 @@ import CoreDataStorage ...@@ -17,16 +17,6 @@ import CoreDataStorage
class WeatherProvider: TimelineProvider { class WeatherProvider: TimelineProvider {
typealias Entry = WeatherEntry typealias Entry = WeatherEntry
private lazy var manager: LocationManager = {
LocationManager.shared = LocationManager(weatherUpdateSource: WdtWeatherSource(),
healthSource: BlendHealthSource(),
nwsAlertsManager: NWSAlertsManager(),
fipsSource: BlendFIPSSource(),
pushNotificationsManager: nil,
storage: DelayedSaveStorage(storage: CoreDataStorage(), delay: 2))
return LocationManager.shared
}()
func placeholder(in context: Context) -> WeatherEntry { func placeholder(in context: Context) -> WeatherEntry {
return WeatherEntry() return WeatherEntry()
} }
...@@ -35,13 +25,45 @@ class WeatherProvider: TimelineProvider { ...@@ -35,13 +25,45 @@ class WeatherProvider: TimelineProvider {
let entry = WeatherEntry() let entry = WeatherEntry()
completion(entry) completion(entry)
} }
var storage: Storage = CoreDataStorage()
var weatherSource: WeatherSource = WdtWeatherSource()
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) { func isFreshEnough(_ location: Location) -> Bool {
guard let currentLocation = manager.selectedLocation else { return } guard let lastTimeUpdated = location.lastWeatherUpdateDate else {
return false
manager.updateWeather(for: currentLocation, updateType: .preferIncremental) { updatedLocation in }
guard let location = updatedLocation else { return } return Date().timeIntervalSince(lastTimeUpdated) < self.weatherSource.weatherUpdateInterval
}
func getUpToDateLocation(_ completion: @escaping (Location?) -> () ) {
storage.load { [weak self] (locations, selectedIndex, error) in
guard let self = self else {
completion(nil)
return
}
guard let locations = locations, let selectedIndex = selectedIndex, selectedIndex < locations.count else {
completion(nil)
return
}
let selectedLocation = locations[selectedIndex]
guard !self.isFreshEnough(selectedLocation) else {
completion(selectedLocation)
return
}
self.weatherSource.updateWeather(for: selectedLocation, type: .preferIncremental) { updatedLocation, error in
guard let updatedLocation = updatedLocation else {
completion(selectedLocation)
return
}
completion(updatedLocation)
}
}
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
getUpToDateLocation { location in
let nextRefresh = Calendar.current.date(byAdding: .minute, value: 30, to: Date())! let nextRefresh = Calendar.current.date(byAdding: .minute, value: 30, to: Date())!
let entry = WeatherEntry(location: location, date: nextRefresh) let entry = WeatherEntry(location: location, date: nextRefresh)
let timeline = Timeline(entries: [entry], policy: .atEnd) let timeline = Timeline(entries: [entry], policy: .atEnd)
......
...@@ -19,6 +19,7 @@ struct ForecastWidgetViewModel: WidgetViewModel { ...@@ -19,6 +19,7 @@ struct ForecastWidgetViewModel: WidgetViewModel {
let highTemperature: String let highTemperature: String
let lowTemperature: String let lowTemperature: String
let isDeviceLocation: Bool let isDeviceLocation: Bool
var smartText: String
init(location: Location) { init(location: Location) {
self.cityName = location.cityName ?? "--" self.cityName = location.cityName ?? "--"
...@@ -47,5 +48,7 @@ struct ForecastWidgetViewModel: WidgetViewModel { ...@@ -47,5 +48,7 @@ struct ForecastWidgetViewModel: WidgetViewModel {
self.showLastTimeUpdated = false self.showLastTimeUpdated = false
lastTimeUpdatedText = "" lastTimeUpdatedText = ""
} }
let smartTextProvider = SmartTextProvider()
self.smartText = smartTextProvider.smartText(for: location)
} }
} }
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