Commit dc6c3e1a by Dmitriy Stepanets

Added data sync to widget view model

parent ae034e64
No preview for this file type
......@@ -1449,6 +1449,7 @@
CD1B71352660F95000916E71 /* Sources */,
CD1B71362660F95000916E71 /* Frameworks */,
CD1B71372660F95000916E71 /* Resources */,
CD950325269DC532009F7B7D /* Run Script: set build number */,
CE7298CB267A34F3002745D0 /* Embed Frameworks */,
);
buildRules = (
......@@ -1650,6 +1651,24 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
CD950325269DC532009F7B7D /* Run Script: set build number */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Run Script: set build number";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "./scripts/set_build_number.sh\n";
};
CE0456212629A634003D252B /* Run Script: Crashlytics */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
......@@ -2087,7 +2106,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = 1Weather/1Weather.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = SET_BY_BUILD_SCRIPT;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 24W4XMQ38L;
ENABLE_BITCODE = NO;
......@@ -2118,7 +2137,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = 1Weather/1Weather.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = SET_BY_BUILD_SCRIPT;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 24W4XMQ38L;
ENABLE_BITCODE = NO;
......@@ -2148,6 +2167,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = OneWeatherWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = SET_BY_BUILD_SCRIPT;
DEVELOPMENT_TEAM = 24W4XMQ38L;
INFOPLIST_FILE = OneWeatherWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
......@@ -2155,6 +2175,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.onelouder.oneweather.OneWeatherWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
......@@ -2171,6 +2192,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = OneWeatherWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = SET_BY_BUILD_SCRIPT;
DEVELOPMENT_TEAM = 24W4XMQ38L;
INFOPLIST_FILE = OneWeatherWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
......@@ -2178,6 +2200,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.onelouder.oneweather.OneWeatherWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
......@@ -2192,7 +2215,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = OneWeatherNotificationServiceExtension/OneWeatherNotificationServiceExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = SET_BY_BUILD_SCRIPT;
DEVELOPMENT_TEAM = 24W4XMQ38L;
INFOPLIST_FILE = OneWeatherNotificationServiceExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
......@@ -2201,7 +2224,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.0.2;
MARKETING_VERSION = 5.2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.onelouder.oneweather.OneWeatherNotificationServiceExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
......@@ -2216,7 +2239,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = OneWeatherNotificationServiceExtension/OneWeatherNotificationServiceExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = SET_BY_BUILD_SCRIPT;
DEVELOPMENT_TEAM = 24W4XMQ38L;
INFOPLIST_FILE = OneWeatherNotificationServiceExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
......@@ -2225,7 +2248,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.0.2;
MARKETING_VERSION = 5.2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.onelouder.oneweather.OneWeatherNotificationServiceExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
......
......@@ -80,7 +80,7 @@
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
value = "com.onelouder.oneweather.widget.small.temperature"
value = "com.onelouder.oneweather.widget.medium.temperature"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
......@@ -91,7 +91,7 @@
<EnvironmentVariable
key = "_XCWidgetFamily"
value = "medium"
isEnabled = "NO">
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
......
......@@ -31,7 +31,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>9</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-7118865896152167~5937109115</string>
<key>GADNativeAdValidatorEnabled</key>
......
......@@ -165,7 +165,7 @@ private extension WidgetPromotionController {
func prepareScrollView() {
scrollView.delegate = self
view.addSubview(scrollView)s
view.addSubview(scrollView)
scrollView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().inset(kScrollViewTopInset)
......
......@@ -135,7 +135,7 @@ public enum WindDirection: String, Codable, CaseIterable {
case northWest = "NW"
case northNorthWest = "NNW"
public var degrees:CGFloat {
public var degrees: CGFloat {
switch self {
case .north:
return 0
......
......@@ -53,7 +53,6 @@ extension HourlyWeather: UpdatableModelObjectInTime {
result.isDay = incrementalChanges.isDay
// Since this class is meant to contain frequently updated info, the preference is given to incrementalChanges data.
result.temp = incrementalChanges.temp ?? result.temp
result.temp = incrementalChanges.dewPoint ?? result.dewPoint
result.apparentTemp = incrementalChanges.apparentTemp ?? result.apparentTemp
......
......@@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>9</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
......
......@@ -457,7 +457,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
IPHONEOS_DEPLOYMENT_TARGET = 11.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
......@@ -515,7 +515,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
IPHONEOS_DEPLOYMENT_TARGET = 11.4;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
......
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.800",
"blue" : "250",
"green" : "221",
"red" : "216"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.400",
"blue" : "112",
"green" : "68",
"red" : "52"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
"blue" : "246",
"green" : "238",
"red" : "236"
}
},
"idiom" : "universal"
......
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "105",
"green" : "77",
"red" : "68"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -8,11 +8,11 @@
import Foundation
struct HourlyTemps {
let currentTemp: Int
let maxTemp: Int
let lowTemp: Int
let currentTemp: Double
let maxTemp: Double
let lowTemp: Double
init(currentTemp: Int, temps: [Int]) {
init(currentTemp: Double, temps: [Double]) {
self.currentTemp = currentTemp
self.maxTemp = temps.sorted{$0 > $1}.first ?? 0
self.lowTemp = temps.sorted{$0 < $1}.first ?? 0
......
......@@ -57,7 +57,8 @@ public struct LargeTemperatureWidgetView: View {
.padding(.top, 10)
.frame(height: 35)
HourlyTemperatureView(temps: [25, 31, 22, 19])
HourlyTemperatureView(hourlyWeather: widgetViewModel.hourlyWeather)
HStack {
Text("Next few Days")
......
......@@ -14,6 +14,7 @@ public struct MediumTemperatureWidgetView: View {
let widgetViewModel: WidgetViewModel
public init(widgetViewModel: WidgetViewModel?) {
OneWeatherUI.loadFonts
self.widgetViewModel = widgetViewModel ?? WidgetViewModelMock()
}
......@@ -65,7 +66,7 @@ public struct MediumTemperatureWidgetView: View {
}
}
HourlyTemperatureView(temps: [25, 31, 22, 19])
HourlyTemperatureView(hourlyWeather: widgetViewModel.hourlyWeather)
}
.padding([.leading, .trailing], 10)
.background(WidgetColor.Name("PrimaryBackground"))
......@@ -96,7 +97,7 @@ public struct MediumTemperatureWidgetView_Preview: PreviewProvider {
public static var previews: some View {
MediumTemperatureWidgetView(widgetViewModel: WidgetViewModelMock())
.previewDevice("iPhone 11")
.preferredColorScheme(.dark)
.preferredColorScheme(.light)
.frame(width: 338, height: 158)
}
}
......@@ -10,7 +10,7 @@ import SwiftUI
@available(iOS 14, *)
struct HourlyTemperatureView: View {
//Public
let temps: [Int]
let hourlyWeather: [WidgetHourlyWeather]
//Private
@State private var hourlyStackFrame: CGRect = .zero
......@@ -19,10 +19,8 @@ struct HourlyTemperatureView: View {
GeometryReader { geo in
ZStack {
HStack(spacing: 10) {
ForEach(0 ..< temps.count) { index in
HourlyView(hourlyTemps: .init(currentTemp: temps[index],
temps: temps),
viewIndex: index)
ForEach(0 ..< hourlyWeather.count) { index in
HourlyView(hourlyWeather: hourlyWeather[index])
.frame(maxWidth: 70)
}
}
......@@ -30,15 +28,16 @@ struct HourlyTemperatureView: View {
GeometryReader { geoInner in
Color(.clear)
.onAppear {
hourlyStackFrame = geoInner.frame(in: .global)
print("[Widget] \(geoInner.frame(in: .global))")
hourlyStackFrame = hourlyWeather.isEmpty ? .zero : geoInner.frame(in: .global)
}
}
)
if temps.count >= 2 {
TemperatureGraphView(temps: temps,
if hourlyWeather.count >= 2 {
TemperatureGraphView(hourlyWeather: hourlyWeather,
viewModel: .init(graphSize: .init(width: hourlyStackFrame.width,
height: 22),
height: hourlyStackFrame.height - 55),
hourlyItemWidth: 70,
spacingPerItem: 10,
graphInset: .zero))
......
......@@ -11,69 +11,116 @@ import SwiftUI
struct HourlyView: View {
//Private
@Environment(\.colorScheme) private var colorScheme
private let hourlyTemps: HourlyTemps
private let hourlyWeather: WidgetHourlyWeather
//Public
init(hourlyTemps: HourlyTemps, viewIndex: Int) {
self.hourlyTemps = hourlyTemps
public init(hourlyWeather: WidgetHourlyWeather) {
self.hourlyWeather = hourlyWeather
}
var body: some View {
VStack {
Text("\(hourlyTemps.currentTemp)" + "°")
.foregroundColor(WidgetColor.Name("PrimaryTextColor"))
.font(WidgetFont.SFProDisplay.bold(size: 16).font)
Text(hourlyWeather.tempLocalized)
.foregroundColor(WidgetColor.Name("HourlyText"))
.font(hourlyWeather.isSelected ? WidgetFont.SFProDisplay.bold(size: 16).font
: WidgetFont.SFProDisplay.regular(size: 16).font)
.padding(.top, 8)
Spacer()
Text("NOW")
.foregroundColor(WidgetColor.Name("PrimaryTextColor"))
.font(WidgetFont.SFProDisplay.bold(size: 12).font)
Text(hourlyWeather.dateString)
.foregroundColor(WidgetColor.Name("HourlyText"))
.font(hourlyWeather.isSelected ? WidgetFont.SFProDisplay.bold(size: 12).font
: WidgetFont.SFProDisplay.regular(size: 12).font)
.padding(.bottom, 8)
}
.frame(width: 70)
.background(WidgetColor.Name("HourlyContainerBackground"))
.modifier(Background(isSelected: hourlyWeather.isSelected))
.compositingGroup()
.cornerRadius(12)
.primaryShadow(for: colorScheme)
.secondaryShadow(for: colorScheme)
.modifier(Shadow(isSelected: hourlyWeather.isSelected))
.opacity(hourlyWeather.isSelected ? 1 : 0.7)
.modifier(Border(isSelected: hourlyWeather.isSelected))
}
}
@available(iOS 14, *)
private struct Background: ViewModifier {
@Environment(\.colorScheme) private var colorScheme
let isSelected: Bool
func body(content: Content) -> some View {
if isSelected {
content
.background(WidgetColor.Name("HourlyContainerBackground"))
}
else {
let opacity = colorScheme == .dark ? 0.7 : 0.5
content
.background(WidgetColor.Name("HourlyContainerBackground").opacity(opacity))
}
}
}
@available(iOS 14, *)
private struct Border: ViewModifier {
@Environment(\.colorScheme) private var colorScheme
let isSelected: Bool
func body(content: Content) -> some View {
switch colorScheme {
case .light:
if isSelected {
content
}
else {
content
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(WidgetColor.Name("HourlyContainerBorder"),
lineWidth: 1)
)
}
case .dark:
if isSelected {
content
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(WidgetColor.Name("HourlyContainerBorder"),
lineWidth: 1)
)
}
else {
content
}
default:
fatalError("[OneWeatherUI] Unsopported color scheme")
}
}
}
//MARK:- UI helpers
@available(iOS 14, *)
private extension View {
func primaryShadow(for colorScheme: ColorScheme) -> some View {
private struct Shadow: ViewModifier {
@Environment(\.colorScheme) private var colorScheme
let isSelected: Bool
func body(content: Content) -> some View {
if isSelected {
switch colorScheme {
case .light:
return self
content
.shadow(color: WidgetColor.Name("HourlyContainerPrimaryShadow"),
radius: 18, x: 5, y: 5)
case .dark:
return self
content
.shadow(color: WidgetColor.Name("HourlyContainerPrimaryShadow"),
radius: 5, x: 5, y: 5)
default:
fatalError("[OneWeatherUI] Unsopported color scheme")
}
}
func secondaryShadow(for colorScheme: ColorScheme) -> some View {
switch colorScheme {
case .light:
return self
.shadow(color: WidgetColor.Name("HourlyContainerSecondaryShadow"),
radius: 6, x: -4, y: -2)
case .dark:
return self
.shadow(color: WidgetColor.Name("HourlyContainerSecondaryShadow"),
radius: 6, x: -4, y: -2)
default:
fatalError("[OneWeatherUI] Unsopported color scheme")
}
}
else {
content
}
}
}
......@@ -11,7 +11,7 @@ import BezierKit
@available(iOS 14, *)
struct TemperatureGraphView: View {
//Public
let temps: [Int]
let hourlyWeather: [WidgetHourlyWeather]
let graphViewModel: GraphViewModel
//Private
......@@ -19,8 +19,8 @@ struct TemperatureGraphView: View {
private var linePath = UIBezierPath()
private var sections = [CubicCurve]()
init(temps: [Int], viewModel: GraphViewModel) {
self.temps = temps
init(hourlyWeather: [WidgetHourlyWeather], viewModel: GraphViewModel) {
self.hourlyWeather = hourlyWeather
self.graphViewModel = viewModel
self.calulatePoints()
self.prepareSectionsAndLinePath()
......@@ -36,6 +36,7 @@ struct TemperatureGraphView: View {
lineJoin: .round))
.frame(width: graphViewModel.graphSize.width,
height: graphViewModel.graphSize.height)
.shadow(color: WidgetColor.Name("GraphShadow"), radius: 3, x: 0, y: 6)
}
//Tint line
......@@ -53,7 +54,8 @@ struct TemperatureGraphView: View {
Circle()
.overlay(
Circle()
.stroke(WidgetColor.Name("GraphLine"),
.stroke(hourlyWeather[index].isSelected ? WidgetColor.Name("GraphLineTint")
: WidgetColor.Name("GraphLine"),
lineWidth: 2)
)
.frame(width: 5, height: 5)
......@@ -114,12 +116,12 @@ struct TemperatureGraphView: View {
}
private mutating func calulatePoints() {
let maxTemp = temps.sorted{$0 > $1}.first ?? 0
let minTemp = temps.sorted{$0 < $1}.first ?? 0
let maxTemp = hourlyWeather.map{ $0.temp }.sorted{$0 > $1}.first ?? 0
let minTemp = hourlyWeather.map{ $0.temp }.sorted{$0 < $1}.first ?? 0
let diff = maxTemp - minTemp
for index in 0..<temps.count {
let multiply = CGFloat(maxTemp - temps[index]) / CGFloat(diff)
for index in 0..<hourlyWeather.count {
let multiply = CGFloat(maxTemp - hourlyWeather[index].temp) / CGFloat(diff)
let space:CGFloat = index > 0 && index <= 3 ? graphViewModel.spacingPerItem : 0
if index == 0 {
......
......@@ -14,6 +14,7 @@ public struct SmallTemperatureWidgetView: View {
public let widgetViewModel: WidgetViewModel
public init(widgetViewModel: WidgetViewModel?) {
OneWeatherUI.loadFonts
self.widgetViewModel = widgetViewModel ?? WidgetViewModelMock()
}
......
......@@ -8,6 +8,46 @@
import UIKit
@available(iOS 14, *)
public struct WidgetHourlyWeather {
let dateString: String
let temp: Double
let tempLocalized: String
let windSpeed: Double
let windSpeedLocalized: String
let windDirection: String
let windDegrees: CGFloat
let isSelected: Bool
public init(dateString: String, temp: Double, tempLocalized: String, windSpeed: Double, windSpeedLocalized: String, windDirection: String, windDegrees: CGFloat, isSelected: Bool) {
self.dateString = dateString
self.temp = temp
self.tempLocalized = tempLocalized
self.windSpeed = windSpeed
self.windSpeedLocalized = windSpeedLocalized
self.windDirection = windDirection
self.windDegrees = windDegrees
self.isSelected = isSelected
}
}
@available(iOS 14, *)
public struct WidgetDailyWeather {
let date: Date
let weekDay: String
let isToday: Bool
let minTemp: String
let maxTemp: String
public init(date: Date, weekDay: String, isToday: Bool, minTemp: String, maxTemp: String) {
self.date = date
self.weekDay = weekDay
self.isToday = isToday
self.minTemp = minTemp
self.maxTemp = maxTemp
}
}
@available(iOS 14, *)
public protocol WidgetViewModel {
var showLastTimeUpdated: Bool { get }
var lastTimeUpdatedText: String { get }
......@@ -19,4 +59,6 @@ public protocol WidgetViewModel {
var lowTemperature: String { get }
var isDeviceLocation: Bool { get }
var smartText: String { get }
var hourlyWeather: [WidgetHourlyWeather] { get }
var dailyWather: [WidgetDailyWeather] { get }
}
......@@ -20,4 +20,33 @@ struct WidgetViewModelMock: WidgetViewModel {
let lowTemperature = "89°"
let isDeviceLocation = true
let smartText = "Feels like 101° due to high humidity (77%)."
let hourlyWeather: [WidgetHourlyWeather] = {
var array = [WidgetHourlyWeather]()
for index in 0..<4 {
let hourly = WidgetHourlyWeather(dateString: "11 AM",
temp: 17 + 2 * Double(index),
tempLocalized: "\(17 + 2 * index)°",
windSpeed: 15,
windSpeedLocalized: "8m/s",
windDirection: "SSE",
windDegrees: 25,
isSelected: index == 0)
array.append(hourly)
}
return array
}()
let dailyWather: [WidgetDailyWeather] = {
var array = [WidgetDailyWeather]()
for index in 0..<4 {
let daily = WidgetDailyWeather(date: Date(),
weekDay: "Tuesday",
isToday: false,
minTemp: "\(10 + 2 * index)°",
maxTemp: "\(22 + 2 * index)°")
array.append(daily)
}
return array
}()
}
......@@ -16,6 +16,8 @@ import CoreDataStorage
class WeatherProvider: TimelineProvider {
typealias Entry = WeatherEntry
var storage: Storage = CoreDataStorage()
var weatherSource: WeatherSource = WdtWeatherSource()
func placeholder(in context: Context) -> WeatherEntry {
return WeatherEntry()
......@@ -25,8 +27,6 @@ class WeatherProvider: TimelineProvider {
let entry = WeatherEntry()
completion(entry)
}
var storage: Storage = CoreDataStorage()
var weatherSource: WeatherSource = WdtWeatherSource()
func isFreshEnough(_ location: Location) -> Bool {
guard let lastTimeUpdated = location.lastWeatherUpdateDate else {
......
......@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>9</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
......
......@@ -15,16 +15,16 @@ struct WidgetPlaceholderView: View {
var body: some View {
switch family {
case .systemSmall:
SmallTemperatureWidgetView(widgetViewModel: ForecastWidgetViewModel(location: WeatherEntry.defaultLocation))
SmallTemperatureWidgetView(widgetViewModel: nil)
.redacted(reason: .placeholder)
case .systemMedium:
MediumTemperatureWidgetView(widgetViewModel: ForecastWidgetViewModel(location: WeatherEntry.defaultLocation))
MediumTemperatureWidgetView(widgetViewModel: nil)
.redacted(reason: .placeholder)
case .systemLarge:
LargeTemperatureWidgetView(widgetViewModel: ForecastWidgetViewModel(location: WeatherEntry.defaultLocation))
LargeTemperatureWidgetView(widgetViewModel: nil)
.redacted(reason: .placeholder)
default:
SmallTemperatureWidgetView(widgetViewModel: ForecastWidgetViewModel(location: WeatherEntry.defaultLocation))
SmallTemperatureWidgetView(widgetViewModel: nil)
.redacted(reason: .placeholder)
}
}
......
......@@ -9,6 +9,18 @@ import SwiftUI
import OneWeatherCore
import OneWeatherUI
private struct Formatter {
private static let fmt: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "h a"
return formatter
}()
static func dateString(date: Date) -> String {
return fmt.string(from: date)
}
}
struct ForecastWidgetViewModel: WidgetViewModel {
let showLastTimeUpdated: Bool
let lastTimeUpdatedText: String
......@@ -19,9 +31,47 @@ struct ForecastWidgetViewModel: WidgetViewModel {
let highTemperature: String
let lowTemperature: String
let isDeviceLocation: Bool
var smartText: String
let smartText: String
let hourlyWeather: [WidgetHourlyWeather]
let dailyWather: [WidgetDailyWeather]
init(location: Location) {
func convertToWidgetHourly(modelHourly: [HourlyWeather]) -> [WidgetHourlyWeather] {
var array = [WidgetHourlyWeather]()
for index in 0..<min(modelHourly.count, 4) {
let hourly = modelHourly[index]
let isSelected = Calendar.isNow(fromDate: hourly.date, timeZone: hourly.timeZone)
let dateString = isSelected ? "day.now".localized().uppercased() : Formatter.dateString(date: hourly.date)
let widgetHourly = WidgetHourlyWeather(dateString: dateString,
temp: hourly.temp?.settingsConvertedValue ?? 0,
tempLocalized: hourly.temp?.settingsConverted.shortString ?? "",
windSpeed: hourly.windSpeed?.settingsConvertedValue ?? 0,
windSpeedLocalized: hourly.windSpeed?.settingsConverted.shortString ?? "",
windDirection: hourly.windDirection?.fullLocalized ?? "",
windDegrees: hourly.windDirection?.degrees ?? 0,
isSelected: isSelected)
array.append(widgetHourly)
}
return array
}
func convertToWidgetDaily(modelDaily: [DailyWeather]) -> [WidgetDailyWeather] {
var array = [WidgetDailyWeather]()
for index in 0..<min(modelDaily.count, 4) {
let daily = modelDaily[index]
let dailyHourly = WidgetDailyWeather(date: daily.date,
weekDay: daily.weekDay.rawValue,
isToday: daily.isToday,
minTemp: daily.minTemp?.settingsConverted.shortString ?? "",
maxTemp: daily.maxTemp?.settingsConverted.shortString ?? "")
array.append(dailyHourly)
}
return array
}
self.cityName = location.cityName ?? "--"
self.temperature = "\(location.today?.temp?.shortString ?? "--")"
self.weatherType = location.today?.type.localized(isDay: location.today?.isDay ?? true) ?? "--"
......@@ -50,5 +100,8 @@ struct ForecastWidgetViewModel: WidgetViewModel {
}
let smartTextProvider = SmartTextProvider()
self.smartText = smartTextProvider.smartText(for: location)
self.hourlyWeather = convertToWidgetHourly(modelHourly: location.hourly)
self.dailyWather = convertToWidgetDaily(modelDaily: location.daily)
}
}
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