├── .gitignore ├── Icon ├── LICENSE ├── README.md ├── Screenshot.png ├── Screenshot1.png ├── Screenshot2.png ├── Shared ├── Data │ ├── NetworkWeather.swift │ ├── ViewModel+Network.swift │ ├── WeatherModels.swift │ └── WeatherViewModels.swift ├── Info.plist └── Shared.h ├── WeatherWidget ├── Assets.xcassets │ ├── 01-s.imageset │ │ ├── 01-s.png │ │ └── Contents.json │ ├── 02-s.imageset │ │ ├── 02-s.png │ │ └── Contents.json │ ├── 03-s.imageset │ │ ├── 03-s.png │ │ └── Contents.json │ ├── 04-s.imageset │ │ ├── 04-s.png │ │ └── Contents.json │ ├── 05-s.imageset │ │ ├── 05-s.png │ │ └── Contents.json │ ├── 06-s.imageset │ │ ├── 06-s.png │ │ └── Contents.json │ ├── 07-s.imageset │ │ ├── 07-s.png │ │ └── Contents.json │ ├── 08-s.imageset │ │ ├── 08-s.png │ │ └── Contents.json │ ├── 11-s.imageset │ │ ├── 11-s.png │ │ └── Contents.json │ ├── 12-s.imageset │ │ ├── 12-s.png │ │ └── Contents.json │ ├── 13-s.imageset │ │ ├── 13-s.png │ │ └── Contents.json │ ├── 14-s.imageset │ │ ├── 14-s.png │ │ └── Contents.json │ ├── 15-s.imageset │ │ ├── 15-s.png │ │ └── Contents.json │ ├── 16-s.imageset │ │ ├── 16-s.png │ │ └── Contents.json │ ├── 17-s.imageset │ │ ├── 17-s.png │ │ └── Contents.json │ ├── 18-s.imageset │ │ ├── 18-s.png │ │ └── Contents.json │ ├── 19-s.imageset │ │ ├── 19-s.png │ │ └── Contents.json │ ├── 20-s.imageset │ │ ├── 20-s.png │ │ └── Contents.json │ ├── 21-s.imageset │ │ ├── 21-s.png │ │ └── Contents.json │ ├── 22-s.imageset │ │ ├── 22-s.png │ │ └── Contents.json │ ├── 23-s.imageset │ │ ├── 23-s.png │ │ └── Contents.json │ ├── 24-s.imageset │ │ ├── 24-s.png │ │ └── Contents.json │ ├── 25-s.imageset │ │ ├── 25-s.png │ │ └── Contents.json │ ├── 26-s.imageset │ │ ├── 26-s.png │ │ └── Contents.json │ ├── 29-s.imageset │ │ ├── 29-s.png │ │ └── Contents.json │ ├── 30-s.imageset │ │ ├── 30-s.png │ │ └── Contents.json │ ├── 31-s.imageset │ │ ├── 31-s.png │ │ └── Contents.json │ ├── 32-s.imageset │ │ ├── 32-s.png │ │ └── Contents.json │ ├── 33-s.imageset │ │ ├── 33-s.png │ │ └── Contents.json │ ├── 34-s.imageset │ │ ├── 34-s.png │ │ └── Contents.json │ ├── 35-s.imageset │ │ ├── 35-s.png │ │ └── Contents.json │ ├── 36-s.imageset │ │ ├── 36-s.png │ │ └── Contents.json │ ├── 37-s.imageset │ │ ├── 37-s.png │ │ └── Contents.json │ ├── 38-s.imageset │ │ ├── 38-s.png │ │ └── Contents.json │ ├── 39-s.imageset │ │ ├── 39-s.png │ │ └── Contents.json │ ├── 40-s.imageset │ │ ├── 40-s.png │ │ └── Contents.json │ ├── 41-s.imageset │ │ ├── 41-s.png │ │ └── Contents.json │ ├── 42-s.imageset │ │ ├── 42-s.png │ │ └── Contents.json │ ├── 43-s.imageset │ │ ├── 43-s.png │ │ └── Contents.json │ ├── 44-s.imageset │ │ ├── 44-s.png │ │ └── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── BlueSky.imageset │ │ ├── BlueSky.jpg │ │ └── Contents.json │ ├── Clouds.imageset │ │ ├── Clouds.jpg │ │ └── Contents.json │ ├── Contents.json │ ├── HighTemp.imageset │ │ ├── Contents.json │ │ └── HighTemp.png │ ├── LowTemp.imageset │ │ ├── Contents.json │ │ └── LowTemp.png │ └── WidgetBackground.colorset │ │ └── Contents.json ├── Info.plist ├── WeatherWidget.swift ├── WidgetBundle.swift ├── WidgetLargeView.swift ├── WidgetSmallForLargeView.swift └── WidgetSmallView.swift ├── WeatherwithWidget.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── WeatherWidgetExtension.xcscheme │ └── WeatherwithWidget.xcscheme └── WeatherwithWidget ├── AppViewModel.swift ├── Assets.xcassets ├── 01-s.imageset │ ├── 01-s.png │ └── Contents.json ├── 02-s.imageset │ ├── 02-s.png │ └── Contents.json ├── 03-s.imageset │ ├── 03-s.png │ └── Contents.json ├── 04-s.imageset │ ├── 04-s.png │ └── Contents.json ├── 05-s.imageset │ ├── 05-s.png │ └── Contents.json ├── 06-s.imageset │ ├── 06-s.png │ └── Contents.json ├── 07-s.imageset │ ├── 07-s.png │ └── Contents.json ├── 08-s.imageset │ ├── 08-s.png │ └── Contents.json ├── 11-s.imageset │ ├── 11-s.png │ └── Contents.json ├── 12-s.imageset │ ├── 12-s.png │ └── Contents.json ├── 13-s.imageset │ ├── 13-s.png │ └── Contents.json ├── 14-s.imageset │ ├── 14-s.png │ └── Contents.json ├── 15-s.imageset │ ├── 15-s.png │ └── Contents.json ├── 16-s.imageset │ ├── 16-s.png │ └── Contents.json ├── 17-s.imageset │ ├── 17-s.png │ └── Contents.json ├── 18-s.imageset │ ├── 18-s.png │ └── Contents.json ├── 19-s.imageset │ ├── 19-s.png │ └── Contents.json ├── 20-s.imageset │ ├── 20-s.png │ └── Contents.json ├── 21-s.imageset │ ├── 21-s.png │ └── Contents.json ├── 22-s.imageset │ ├── 22-s.png │ └── Contents.json ├── 23-s.imageset │ ├── 23-s.png │ └── Contents.json ├── 24-s.imageset │ ├── 24-s.png │ └── Contents.json ├── 25-s.imageset │ ├── 25-s.png │ └── Contents.json ├── 26-s.imageset │ ├── 26-s.png │ └── Contents.json ├── 29-s.imageset │ ├── 29-s.png │ └── Contents.json ├── 30-s.imageset │ ├── 30-s.png │ └── Contents.json ├── 31-s.imageset │ ├── 31-s.png │ └── Contents.json ├── 32-s.imageset │ ├── 32-s.png │ └── Contents.json ├── 33-s.imageset │ ├── 33-s.png │ └── Contents.json ├── 34-s.imageset │ ├── 34-s.png │ └── Contents.json ├── 35-s.imageset │ ├── 35-s.png │ └── Contents.json ├── 36-s.imageset │ ├── 36-s.png │ └── Contents.json ├── 37-s.imageset │ ├── 37-s.png │ └── Contents.json ├── 38-s.imageset │ ├── 38-s.png │ └── Contents.json ├── 39-s.imageset │ ├── 39-s.png │ └── Contents.json ├── 40-s.imageset │ ├── 40-s.png │ └── Contents.json ├── 41-s.imageset │ ├── 41-s.png │ └── Contents.json ├── 42-s.imageset │ ├── 42-s.png │ └── Contents.json ├── 43-s.imageset │ ├── 43-s.png │ └── Contents.json ├── 44-s.imageset │ ├── 44-s.png │ └── Contents.json ├── AccentColor.colorset │ └── Contents.json ├── AppIcon-1.appiconset │ ├── Contents.json │ ├── H-120-Swift.png │ ├── H-152-Swift-1.png │ ├── H-167-Swift.png │ ├── H-180-Swift.png │ └── H-76-Swift-1.png ├── AppIcon.appiconset │ └── Contents.json ├── BlueSky.imageset │ ├── BlueSky.jpg │ └── Contents.json ├── Contents.json ├── HighTemp.imageset │ ├── Contents.json │ └── HighTemp.png ├── LowTemp.imageset │ ├── Contents.json │ └── LowTemp.png ├── MountainView.imageset │ ├── Contents.json │ └── MountainView@2x.jpg ├── blade_big.imageset │ ├── Contents.json │ └── blade_big@2x.png ├── blade_small.imageset │ ├── Contents.json │ └── blade_small@2x.png ├── stand_l.imageset │ ├── Contents.json │ └── stand_l.pdf └── stand_s.imageset │ ├── Contents.json │ └── stand_s.pdf ├── Info.plist ├── Preview Content └── Preview Assets.xcassets │ └── Contents.json ├── Views ├── ContentView.swift ├── CurrentWeatherView.swift ├── DetailsPanelView.swift ├── ForecastPanelView.swift ├── ScrollViewWithOffset.swift └── WindAndPressurePanelView.swift └── WeatherwithWidgetApp.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | 3 | ## Build generated 4 | build/ 5 | DerivedData 6 | 7 | ## Various settings 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | 18 | ## Other 19 | *.xccheckout 20 | *.moved-aside 21 | *.xcuserstate 22 | *.xcscmblueprint 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | -------------------------------------------------------------------------------- /Icon : -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/Icon -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Weather with Widgets 2 | 3 | WeatherWithWidgets is an iOS 14 weather app written with SwiftUI. It uses the AccuWeather API (free limited use keys) and has a UI similar to the Yahoo! weather app. It also has a large and small widget. These can be run / tested in Xcode separately from the app. I haven't installed iOS 14 on my iPhone yet, so it has only been tested on simulators. They work fine standalone, and launch the app when pressed. 4 | 5 | 6 | < 7 | 8 | 9 | 10 | 11 | 12 | ## License 13 | 14 | WeatherWithWidgets is licensed under the Unlicense. See the LICENSE file for more information, but basically this is sample code and you can do whatever you want with it. 15 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/Screenshot.png -------------------------------------------------------------------------------- /Screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/Screenshot1.png -------------------------------------------------------------------------------- /Screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/Screenshot2.png -------------------------------------------------------------------------------- /Shared/Data/NetworkWeather.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkWeather.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/17/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | import os.log 12 | 13 | 14 | enum NetworkError: Error, LocalizedError { 15 | case invalidHTTPResponse, invalidServerResponse, jsonParsingError, apiError(reason: String) 16 | 17 | var errorDescription: String? { 18 | switch self { 19 | case .invalidHTTPResponse: 20 | return "Invalid HTTP response" 21 | case .invalidServerResponse: 22 | return "Invalid service sesponse" 23 | case .jsonParsingError: 24 | return "Error parsing JSON" 25 | case .apiError(let reason): 26 | return reason 27 | } 28 | } 29 | } 30 | 31 | 32 | enum DataFetcher { 33 | 34 | static func fetch(url: URL?, myType: T.Type) -> AnyPublisher { 35 | guard let url = url else { 36 | fatalError("Did you enter your AccuWeather API Key?") 37 | } 38 | 39 | return URLSession.shared.dataTaskPublisher(for: url) 40 | .tryMap { data, response -> T in 41 | guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { 42 | throw NetworkError.invalidHTTPResponse 43 | } 44 | //print(String(bytes: data, encoding: String.Encoding.utf8)) 45 | let value = try JSONDecoder().decode(T.self, from: data) 46 | return value 47 | } 48 | .mapError { error in 49 | if let _ = error as? DecodingError { 50 | return NetworkError.jsonParsingError 51 | } else if let error = error as? NetworkError { 52 | return error 53 | } 54 | 55 | // handle service errors (e.g. not being able to connect) 56 | return NetworkError.invalidServerResponse 57 | } 58 | .eraseToAnyPublisher() 59 | } 60 | 61 | } 62 | 63 | 64 | extension OSLog { 65 | private static var subsystem = Bundle.main.bundleIdentifier! 66 | 67 | /// Logs the view cycles like viewDidLoad. 68 | static let networkLogger = OSLog(subsystem: subsystem, category: "log_networkcall") 69 | } 70 | 71 | -------------------------------------------------------------------------------- /Shared/Data/ViewModel+Network.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModel+Network.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 6/1/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | import os.log 12 | 13 | 14 | /* 15 | You need a AccuWeather API key which you can get a free low-use key here: https://developer.accuweather.com/getting-started (start with Register button at the top of the screen) 16 | 17 | You can find location codes a number of ways here: https://developer.accuweather.com/accuweather-locations-api/apis 18 | */ 19 | 20 | 21 | let locationCode = "337169" // Mountain View, CA 22 | let accuWeatherapikey = "" // 23 | 24 | extension URL { 25 | 26 | static var weather: URL? { 27 | URL(string: "https://dataservice.accuweather.com/currentconditions/v1/\(locationCode)?apikey=\(accuWeatherapikey)&details=true") 28 | } 29 | 30 | static var forecastWeather: URL? { 31 | URL(string: "https://dataservice.accuweather.com/forecasts/v1/daily/5day/\(locationCode)?apikey=\(accuWeatherapikey)&details=true") 32 | } 33 | 34 | static var hourlyForecastWeather: URL? { 35 | URL(string: "https://dataservice.accuweather.com/forecasts/v1/hourly/12hour/\(locationCode)?apikey=\(accuWeatherapikey)&details=false") 36 | } 37 | 38 | } 39 | 40 | extension ViewModel { 41 | 42 | public func getWeather(completion: ((ViewModel) -> Void)? = nil) { 43 | 44 | let currentWeatherPublisher = DataFetcher.fetch(url: URL.weather, myType: [CurrentData].self) 45 | let forecastWeatherPublisher = DataFetcher.fetch(url: URL.forecastWeather, myType: ForecastData.self) 46 | 47 | // wait until both fetches finish before updating the data and forcing the UI to refresh 48 | let _ = Publishers.Zip(currentWeatherPublisher, forecastWeatherPublisher) 49 | .receive(on: DispatchQueue.main) 50 | .sink(receiveCompletion: { completion in 51 | switch completion { 52 | case .finished: 53 | break 54 | case .failure(let theError): 55 | self.handleDownloadError(error: theError) 56 | } 57 | }, receiveValue: { (current, forecast) in 58 | DispatchQueue.main.async { 59 | if current.count > 0 { 60 | self.current = current.first! 61 | } 62 | self.forecast = forecast 63 | completion?(self) 64 | } 65 | }) 66 | .store(in: &disposables) 67 | } 68 | 69 | // in real life you might want a bit more than this... 70 | func handleDownloadError(error: NetworkError) { 71 | switch error { 72 | case NetworkError.invalidHTTPResponse: 73 | //print(error.errorDescription) 74 | break 75 | case NetworkError.invalidServerResponse: 76 | //print(error.errorDescription) 77 | break 78 | case NetworkError.jsonParsingError: 79 | //print(error.errorDescription) 80 | break 81 | default: 82 | //print(error.errorDescription) 83 | break 84 | } 85 | } 86 | 87 | // weather for small widget - not used. getWeather is quick enough. no need to complicate things. 88 | func getCurrentWeather() { 89 | let currentWeatherPublisher = DataFetcher.fetch(url: URL.weather, myType: [CurrentData].self) 90 | 91 | let _ = currentWeatherPublisher 92 | .receive(on: DispatchQueue.main) 93 | .sink(receiveCompletion: { completion in 94 | switch completion { 95 | case .finished: 96 | break 97 | case .failure(let theError): 98 | self.handleDownloadError(error: theError) 99 | } 100 | }, receiveValue: { current in 101 | if current.count > 0 { 102 | self.current = current.first! 103 | } 104 | 105 | }) 106 | .store(in: &disposables) 107 | } 108 | 109 | // medium widget - possibly. show hourly forecast for day along with current weather? but don't think that will fit. 110 | func getHourlyForecastWeather() { 111 | let currentWeatherPublisher = DataFetcher.fetch(url: URL.weather, myType: [CurrentData].self) 112 | let hourlyForecastWeatherPublisher = DataFetcher.fetch(url: URL.hourlyForecastWeather, myType: [HourlyData].self) 113 | 114 | // wait until both fetches finish before updating the data and forcing the UI to refresh 115 | let _ = Publishers.Zip(currentWeatherPublisher, hourlyForecastWeatherPublisher) 116 | .receive(on: DispatchQueue.main) 117 | .sink(receiveCompletion: { completion in 118 | switch completion { 119 | case .finished: 120 | break 121 | case .failure(let theError): 122 | self.handleDownloadError(error: theError) 123 | } 124 | }, receiveValue: { (current, hourlyForecast) in 125 | if current.count > 0 { 126 | self.current = current.first! 127 | } 128 | self.hourlyForecast.hourlyForecasts = hourlyForecast 129 | }) 130 | .store(in: &disposables) 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /Shared/Data/WeatherModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeatherData.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/17/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct AccuValue : Decodable { 12 | let Value: Double 13 | let Unit: String 14 | } 15 | 16 | struct ImperialInfo : Decodable { 17 | let Imperial: AccuValue // also has Metric 18 | } 19 | 20 | struct DirectionDetail : Decodable { 21 | let Degrees: Int 22 | let Localized: String 23 | } 24 | 25 | struct WindSpeed : Decodable { 26 | let Imperial: AccuValue 27 | } 28 | 29 | struct WindInfo : Decodable { 30 | let Direction: DirectionDetail 31 | let Speed: ImperialInfo? 32 | } 33 | 34 | public struct CurrentData : Decodable { 35 | let LocalObservationDateTime: String 36 | let EpochTime: Int 37 | let WeatherText: String 38 | let WeatherIcon: Int 39 | let PrecipitationType: String? // check for "Rain" if not nil 40 | let IsDayTime: Bool 41 | let Temperature: ImperialInfo 42 | let RealFeelTemperature: ImperialInfo // "Patented AccuWeather RealFeel Temperature" 43 | let RelativeHumidity: Int? 44 | let Wind: WindInfo 45 | let UVIndex: Int? 46 | let Visibility: ImperialInfo 47 | let Pressure: ImperialInfo 48 | let ApparentTemperature: ImperialInfo 49 | let WindChillTemperature: ImperialInfo 50 | } 51 | 52 | 53 | struct ForecastTemperatureInfo : Decodable { 54 | let Minimum: AccuValue 55 | let Maximum: AccuValue 56 | } 57 | 58 | struct ConditionsInfo : Decodable { 59 | let Icon: Int 60 | let IconPhrase: String 61 | } 62 | 63 | struct DailyData : Decodable { 64 | let Date: String 65 | let EpochDate: Int 66 | let Temperature: ForecastTemperatureInfo 67 | let Day: ConditionsInfo 68 | } 69 | 70 | public struct ForecastData : Decodable { 71 | let DailyForecasts : [DailyData] 72 | } 73 | 74 | struct HourlyData : Decodable { 75 | let DateTime: String 76 | let EpochDateTime: Int 77 | let WeatherIcon: Int 78 | let IconPhrase: String 79 | let IsDaylight: Bool 80 | let Temperature: AccuValue 81 | } 82 | 83 | struct HourlyForecastData : Decodable { 84 | var hourlyForecasts: [HourlyData] 85 | } 86 | 87 | -------------------------------------------------------------------------------- /Shared/Data/WeatherViewModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeatherDataModel.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/19/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import Combine 11 | 12 | let degreesChar = "\u{00B0}" //"°" 13 | 14 | 15 | final public class ViewModel: ObservableObject { 16 | 17 | @Published var current: CurrentData 18 | public var forecast: ForecastData 19 | @Published var hourlyForecast: HourlyForecastData 20 | 21 | var disposables = Set() 22 | 23 | 24 | public init() { 25 | // dummy data because screen will appear before we have real data. usually very brief. 26 | // much better solution is to take the time to create views that can handle case with no data. the forecast panel does a crude version of this. 27 | self.current = CurrentData(LocalObservationDateTime: "", EpochTime: 23423434, WeatherText: "Partly Cloudy", WeatherIcon: 7, PrecipitationType: nil, IsDayTime: true, Temperature: ImperialInfo(Imperial: AccuValue(Value: 55, Unit: "F")), RealFeelTemperature: ImperialInfo(Imperial: AccuValue(Value: 60, Unit: "F")), RelativeHumidity: 22, Wind: WindInfo(Direction: DirectionDetail(Degrees: 268, Localized: "NW"), Speed: ImperialInfo(Imperial: AccuValue(Value: 6, Unit: "mph"))), UVIndex: 4, Visibility: ImperialInfo(Imperial: AccuValue(Value: 10, Unit: "mi")), Pressure: ImperialInfo(Imperial: AccuValue(Value: 29.81, Unit: "inHg")), ApparentTemperature: ImperialInfo(Imperial: AccuValue(Value: 64.0, Unit: "F")), WindChillTemperature: ImperialInfo(Imperial: AccuValue(Value: 55.5, Unit: "F"))) 28 | 29 | self.forecast = ForecastData(DailyForecasts: [DailyData(Date: "", EpochDate: 789798, Temperature: ForecastTemperatureInfo(Minimum: AccuValue(Value: 50, Unit: "F"), Maximum: AccuValue(Value: 88, Unit: "F")), Day: ConditionsInfo(Icon: 6, IconPhrase: "")), DailyData(Date: "", EpochDate: 3423423, Temperature: ForecastTemperatureInfo(Minimum: AccuValue(Value: 51, Unit: "F"), Maximum: AccuValue(Value: 87, Unit: "F")), Day: ConditionsInfo(Icon: 6, IconPhrase: "")), DailyData(Date: "", EpochDate: 23423, Temperature: ForecastTemperatureInfo(Minimum: AccuValue(Value: 52, Unit: "F"), Maximum: AccuValue(Value: 86, Unit: "F")), Day: ConditionsInfo(Icon: 6, IconPhrase: "")), DailyData(Date: "", EpochDate: 234342, Temperature: ForecastTemperatureInfo(Minimum: AccuValue(Value: 53, Unit: "F"), Maximum: AccuValue(Value: 85, Unit: "F")), Day: ConditionsInfo(Icon: 6, IconPhrase: "")), DailyData(Date: "", EpochDate: 23332284, Temperature: ForecastTemperatureInfo(Minimum: AccuValue(Value: 54, Unit: "F"), Maximum: AccuValue(Value: 84, Unit: "F")), Day: ConditionsInfo(Icon: 6, IconPhrase: ""))]) 30 | 31 | self.hourlyForecast = HourlyForecastData(hourlyForecasts: [HourlyData(DateTime: "", EpochDateTime: 789798, WeatherIcon: 6, IconPhrase: "", IsDaylight: true, Temperature: AccuValue(Value: 50, Unit: "F"))]) 32 | 33 | } 34 | 35 | //MARK: - Current Conditions view 36 | public var temperature: String { 37 | return doubleToRoundedString(dbl: self.current.Temperature.Imperial.Value) + degreesChar 38 | } 39 | 40 | public var currentConditions: String { 41 | return self.current.WeatherText 42 | } 43 | 44 | public var highTemp: String { 45 | return doubleToRoundedString(dbl: self.forecast.DailyForecasts[0].Temperature.Maximum.Value) + degreesChar 46 | } 47 | 48 | public var lowTemp: String { 49 | return doubleToRoundedString(dbl: self.forecast.DailyForecasts[0].Temperature.Minimum.Value) + degreesChar 50 | } 51 | 52 | public var weatherIcon: UIImage? { 53 | return getWeatherIcon(icon: self.current.WeatherIcon) 54 | } 55 | 56 | func getWeatherIcon(icon: Int) -> UIImage { 57 | var iconString = "" 58 | if icon < 10 { 59 | iconString += "0\(icon)" 60 | } else { 61 | iconString += "\(icon)" 62 | } 63 | iconString += "-s" 64 | 65 | return UIImage(named: iconString)! 66 | } 67 | 68 | public var weatherIconName: String { 69 | return getWeatherIconName(icon: self.current.WeatherIcon) 70 | } 71 | 72 | func getWeatherIconName(icon: Int) -> String { 73 | var iconString = "" 74 | if icon < 10 { 75 | iconString += "0\(icon)" 76 | } else { 77 | iconString += "\(icon)" 78 | } 79 | iconString += "-s" 80 | 81 | return iconString 82 | } 83 | 84 | 85 | public var isRaining: Bool { 86 | self.current.PrecipitationType != nil && self.current.PrecipitationType == "Rain" 87 | } 88 | 89 | //MARK: - Details panel 90 | public var feelsLike: String { 91 | return doubleToRoundedString(dbl: self.current.ApparentTemperature.Imperial.Value) + degreesChar 92 | } 93 | 94 | public var uvIndexColor: Color { 95 | return self.current.UVIndex != nil ? UVIndex(value: self.current.UVIndex!).color : .blue 96 | } 97 | 98 | public var humidity: String { 99 | return self.current.RelativeHumidity != nil ? "\(self.current.RelativeHumidity!)" + "%" : "n/a" 100 | } 101 | 102 | public var visibility: String { 103 | return doubleToRoundedString(dbl: self.current.Visibility.Imperial.Value) + " mi" 104 | } 105 | 106 | //MARK: - Wind and Pressure panel 107 | var windSpeedString: String { 108 | if (self.current.Wind.Speed?.Imperial.Value == nil || (self.current.Wind.Speed?.Imperial.Value)! < 0.5) { 109 | return "0" 110 | } 111 | return doubleToRoundedString(dbl: self.current.Wind.Speed?.Imperial.Value) 112 | } 113 | 114 | var windSpeed: Double? { 115 | return self.current.Wind.Speed?.Imperial.Value 116 | } 117 | 118 | var bladeDuration: Double? { 119 | var speed = 100.0 // this is actually rotation duration - less is faster (well, if > 0) 120 | guard let windSpeed = self.windSpeed else { 121 | return nil 122 | } 123 | 124 | if windSpeed > 11 { 125 | speed = 4 126 | } else if windSpeed > 7 { 127 | speed = 8 128 | } else if windSpeed > 4 { 129 | speed = 12 130 | } else if windSpeed > 1.2 { 131 | speed = 16 132 | } 133 | 134 | return speed 135 | } 136 | 137 | var windDirection: String { 138 | return self.current.Wind.Direction.Localized 139 | } 140 | 141 | var windSpeedInfo: String { 142 | let info = self.windSpeedString + " mph " + self.windDirection 143 | return info 144 | } 145 | 146 | var pressure: String { 147 | return pressureInInchesString(inches: self.current.Pressure.Imperial.Value) 148 | } 149 | 150 | //MARK: - Forecast panel 151 | public func forecastInfo() -> [ForecastInfo] { 152 | var forecast = [ForecastInfo]() 153 | 154 | guard self.forecast.DailyForecasts.count > 1 else { 155 | return forecast 156 | } 157 | 158 | for index in 0...4 { 159 | let dailyInfo = self.forecast.DailyForecasts[index] 160 | 161 | let dowString = dowFrom(date: dailyInfo.EpochDate) 162 | let icon = getWeatherIconName(icon: dailyInfo.Day.Icon) 163 | let min = doubleToRoundedString(dbl: dailyInfo.Temperature.Minimum.Value) + degreesChar 164 | let max = doubleToRoundedString(dbl: dailyInfo.Temperature.Maximum.Value) + degreesChar 165 | forecast.append(ForecastInfo(dow: dowString, icon: icon, min: min, max: max)) 166 | } 167 | 168 | return forecast 169 | } 170 | } 171 | 172 | public struct ForecastInfo { 173 | public let dow: String 174 | public let icon: String 175 | public let min: String 176 | public let max: String 177 | } 178 | 179 | 180 | //MARK: utilities for formatting data 181 | 182 | fileprivate func millibarsToInchesString(millibars: Int?) -> String { 183 | guard let millibars = millibars else { 184 | return "0.00 in." 185 | } 186 | 187 | let inches = Double(millibars) * 0.0295301 188 | 189 | return String(format: "%.1f", inches) + " in." 190 | } 191 | 192 | fileprivate func pressureInInchesString(inches: Double) -> String { 193 | 194 | return String(format: "%.1f", inches) + " in." 195 | } 196 | 197 | fileprivate func metersToMilesString(meters: Int?) -> String { 198 | guard let meters = meters else { 199 | return "n/a" 200 | } 201 | 202 | let miles = Double(meters) / 1609.34 203 | return "\(Int(miles.rounded()))" + " mi" 204 | } 205 | 206 | fileprivate enum UVIndex { 207 | case high 208 | case medium 209 | case mediumHigh 210 | case low 211 | 212 | // US EPA index 213 | init(value: Int) { 214 | switch value { 215 | case 0...2: 216 | self = .low 217 | case 3...5: 218 | self = .medium 219 | case 6...7: 220 | self = .mediumHigh 221 | default: 222 | self = .high 223 | } 224 | } 225 | 226 | var color: Color { 227 | switch self { 228 | case .high: 229 | return .red 230 | case .mediumHigh: 231 | return .orange 232 | case .medium: 233 | return .yellow 234 | case .low: 235 | return .green 236 | } 237 | } 238 | } 239 | 240 | fileprivate func getWindDirection(degrees: Double?) -> String { 241 | var dir = "N" 242 | 243 | guard let degrees = degrees else { 244 | return dir 245 | } 246 | 247 | if (degrees > 340) { 248 | dir = "N" 249 | } else if (degrees > 290) { 250 | dir = "NW" 251 | } else if (degrees > 250) { 252 | dir = "W" 253 | } else if (degrees > 200) { 254 | dir = "SW" 255 | } else if (degrees > 160) { 256 | dir = "S" 257 | } else if (degrees > 110) { 258 | dir = "SE" 259 | } else if (degrees > 70) { 260 | dir = "E" 261 | } else if (degrees > 20) { 262 | dir = "NE" 263 | } 264 | 265 | return dir 266 | } 267 | 268 | fileprivate func doubleToRoundedString(dbl: Double?) -> String { 269 | guard let dbl = dbl else { 270 | return "" 271 | } 272 | 273 | guard dbl != 0.0 else { 274 | return "0" 275 | } 276 | 277 | return "\(Int(dbl.rounded()))" 278 | } 279 | 280 | fileprivate extension String { 281 | func capitalizingFirstLetter() -> String { 282 | return prefix(1).capitalized + dropFirst() 283 | } 284 | 285 | mutating func capitalizeFirstLetter() { 286 | self = self.capitalizingFirstLetter() 287 | } 288 | } 289 | 290 | fileprivate func dateToString(date: Int) -> String { 291 | let date = Date(timeIntervalSince1970: TimeInterval(date)) 292 | let formatter = DateFormatter() 293 | 294 | formatter.dateFormat = "MMM dd, YYYY" 295 | formatter.timeZone = TimeZone(abbreviation: "UTC") 296 | formatter.locale = NSLocale.current 297 | 298 | let updatedDateStr = formatter.string(from: date) 299 | return updatedDateStr 300 | } 301 | 302 | fileprivate func dowFrom(date: Int) -> String { 303 | let date = Date(timeIntervalSince1970: TimeInterval(date)) 304 | let formatter = DateFormatter() 305 | 306 | formatter.dateFormat = "EEEE" 307 | formatter.timeZone = TimeZone(abbreviation: "UTC") 308 | formatter.locale = NSLocale.current 309 | 310 | let dow = formatter.string(from: date) 311 | return dow 312 | } 313 | -------------------------------------------------------------------------------- /Shared/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Shared/Shared.h: -------------------------------------------------------------------------------- 1 | // 2 | // Shared.h 3 | // Shared 4 | // 5 | // Created by Gary on 6/28/20. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for Shared. 11 | FOUNDATION_EXPORT double SharedVersionNumber; 12 | 13 | //! Project version string for Shared. 14 | FOUNDATION_EXPORT const unsigned char SharedVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/01-s.imageset/01-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/01-s.imageset/01-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/01-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "01-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/02-s.imageset/02-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/02-s.imageset/02-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/02-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "02-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/03-s.imageset/03-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/03-s.imageset/03-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/03-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "03-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/04-s.imageset/04-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/04-s.imageset/04-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/04-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "04-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/05-s.imageset/05-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/05-s.imageset/05-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/05-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "05-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/06-s.imageset/06-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/06-s.imageset/06-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/06-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "06-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/07-s.imageset/07-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/07-s.imageset/07-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/07-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "07-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/08-s.imageset/08-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/08-s.imageset/08-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/08-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "08-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/11-s.imageset/11-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/11-s.imageset/11-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/11-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "11-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/12-s.imageset/12-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/12-s.imageset/12-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/12-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "12-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/13-s.imageset/13-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/13-s.imageset/13-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/13-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "13-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/14-s.imageset/14-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/14-s.imageset/14-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/14-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "14-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/15-s.imageset/15-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/15-s.imageset/15-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/15-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "15-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/16-s.imageset/16-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/16-s.imageset/16-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/16-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/17-s.imageset/17-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/17-s.imageset/17-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/17-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "17-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/18-s.imageset/18-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/18-s.imageset/18-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/18-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "18-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/19-s.imageset/19-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/19-s.imageset/19-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/19-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "19-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/20-s.imageset/20-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/20-s.imageset/20-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/20-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "20-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/21-s.imageset/21-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/21-s.imageset/21-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/21-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "21-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/22-s.imageset/22-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/22-s.imageset/22-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/22-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "22-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/23-s.imageset/23-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/23-s.imageset/23-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/23-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "23-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/24-s.imageset/24-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/24-s.imageset/24-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/24-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "24-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/25-s.imageset/25-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/25-s.imageset/25-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/25-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "25-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/26-s.imageset/26-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/26-s.imageset/26-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/26-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "26-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/29-s.imageset/29-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/29-s.imageset/29-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/29-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "29-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/30-s.imageset/30-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/30-s.imageset/30-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/30-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "30-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/31-s.imageset/31-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/31-s.imageset/31-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/31-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "31-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/32-s.imageset/32-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/32-s.imageset/32-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/32-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "32-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/33-s.imageset/33-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/33-s.imageset/33-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/33-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "33-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/34-s.imageset/34-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/34-s.imageset/34-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/34-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "34-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/35-s.imageset/35-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/35-s.imageset/35-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/35-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "35-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/36-s.imageset/36-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/36-s.imageset/36-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/36-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "36-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/37-s.imageset/37-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/37-s.imageset/37-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/37-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "37-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/38-s.imageset/38-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/38-s.imageset/38-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/38-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "38-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/39-s.imageset/39-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/39-s.imageset/39-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/39-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "39-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/40-s.imageset/40-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/40-s.imageset/40-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/40-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "40-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/41-s.imageset/41-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/41-s.imageset/41-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/41-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "41-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/42-s.imageset/42-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/42-s.imageset/42-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/42-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "42-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/43-s.imageset/43-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/43-s.imageset/43-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/43-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "43-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/44-s.imageset/44-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/44-s.imageset/44-s.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/44-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "44-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/BlueSky.imageset/BlueSky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/BlueSky.imageset/BlueSky.jpg -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/BlueSky.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BlueSky.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/Clouds.imageset/Clouds.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/Clouds.imageset/Clouds.jpg -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/Clouds.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Clouds.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/HighTemp.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "HighTemp.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/HighTemp.imageset/HighTemp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/HighTemp.imageset/HighTemp.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/LowTemp.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LowTemp.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/LowTemp.imageset/LowTemp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherWidget/Assets.xcassets/LowTemp.imageset/LowTemp.png -------------------------------------------------------------------------------- /WeatherWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /WeatherWidget/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | WeatherWidget 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionPointIdentifier 26 | com.apple.widgetkit-extension 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /WeatherWidget/WeatherWidget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeatherWidget.swift 3 | // WeatherWidget 4 | // 5 | // Created by Gary on 6/28/20. 6 | // 7 | 8 | import WidgetKit 9 | import SwiftUI 10 | import os.log 11 | 12 | var lastUpdateTime = Date() 13 | 14 | struct WeatherEntry: TimelineEntry { 15 | let date: Date 16 | let viewModel: ViewModel! 17 | } 18 | 19 | struct CurrentWidgetProvider: TimelineProvider { 20 | typealias Entry = WeatherEntry 21 | 22 | 23 | let viewModel = ViewModel() 24 | 25 | // short-lived widget, such as widget selection menu 26 | // use placeholder if necessary 27 | func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> ()) { 28 | let entry = WeatherEntry(date: Date(), viewModel: ViewModel()) 29 | completion(entry) 30 | } 31 | 32 | // normal usage 33 | func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { 34 | 35 | // we're getting called twice. don't want to make duplicate calls and use up free api calls 36 | let differenceInSeconds = Int(Date().timeIntervalSince(lastUpdateTime)) 37 | if differenceInSeconds < 5 { 38 | viewModel.getWeather { viewModel in 39 | // testing to see how often this gets hit. remove before release. 40 | os_log("widget getting weather", log: OSLog.networkLogger, type: .info) 41 | 42 | let entry = WeatherEntry(date: Date(), viewModel: viewModel) 43 | // make sure that we get refreshed 44 | // to be really usefull to the user it would be better to do this more like 45 | // every 15 minutes. But, that would be more api calls per day than we get 46 | let refreshDate = Calendar.current.date(byAdding: .minute, value: 60, to: Date()) 47 | let timeline = Timeline(entries: [entry], policy: .after(refreshDate!)) 48 | completion(timeline) 49 | } 50 | } 51 | lastUpdateTime = Date() 52 | 53 | } 54 | 55 | func placeholder(in context: Context) -> WeatherEntry { 56 | return WeatherEntry(date: Date(), viewModel: viewModel) 57 | } 58 | } 59 | 60 | 61 | struct PlaceholderView : View { 62 | var body: some View { 63 | Image("Clouds") 64 | } 65 | } 66 | 67 | fileprivate func showLog() -> Bool 68 | { 69 | os_log("widget displaying view", log: OSLog.networkLogger, type: .info) 70 | return true 71 | } 72 | 73 | struct WeatherWidgetEntryView : View { 74 | let viewModel: ViewModel 75 | let height: CGFloat 76 | let notUsed = showLog() 77 | 78 | @Environment(\.widgetFamily) var family 79 | 80 | @ViewBuilder 81 | var body: some View { 82 | ZStack(alignment: .top) { 83 | Image("BlueSky") 84 | .resizable() 85 | .aspectRatio(contentMode: .fill) 86 | .clipped() 87 | switch family { 88 | case .systemSmall: 89 | WidgetSmallView(viewModel: viewModel, height: height) 90 | case .systemMedium: 91 | VStack { 92 | Text("Medium Widget View") 93 | Spacer() 94 | Text("Medium Widget View Part 2") 95 | } 96 | case .systemLarge: 97 | WidgetLargeView(viewModel: viewModel, height: height) 98 | 99 | @unknown default: 100 | fatalError() 101 | } 102 | } 103 | } 104 | } 105 | 106 | struct CurrentWeatherWidget: Widget { 107 | public var body: some WidgetConfiguration { 108 | StaticConfiguration(kind: "com.gary.weatherwidget", 109 | provider: CurrentWidgetProvider()) { entry in 110 | GeometryReader { metrics in 111 | WeatherWidgetEntryView(viewModel: entry.viewModel, height: metrics.size.height) 112 | } 113 | } 114 | .configurationDisplayName("Weather") 115 | .description("Weather info for Mountain View, CA, USA") 116 | // .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) 117 | .supportedFamilies([.systemSmall, .systemLarge]) // couldn't find a medium design I liked. maybe later 118 | } 119 | } 120 | 121 | -------------------------------------------------------------------------------- /WeatherWidget/WidgetBundle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetBundle.swift 3 | // Shared 4 | // 5 | // Created by Gary on 6/28/20. 6 | // 7 | 8 | import SwiftUI 9 | import WidgetKit 10 | 11 | 12 | @main 13 | struct TubeWidgets: WidgetBundle { 14 | @WidgetBundleBuilder 15 | var body: some Widget { 16 | CurrentWeatherWidget() 17 | } 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /WeatherWidget/WidgetLargeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetLargeView.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/17/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | let panelViewFontName = "Helvetica Neue" 12 | let panelViewFontSize: CGFloat = 18 13 | let panelBackgroundColor = Color(UIColor(hex: 0x000049, alpha: 0.1)) 14 | 15 | 16 | struct WidgetLargeView: View { 17 | @ObservedObject var viewModel: ViewModel 18 | let forecast: [ForecastInfo] 19 | let height: CGFloat 20 | 21 | init(viewModel: ViewModel, height: CGFloat) { 22 | self.viewModel = viewModel 23 | self.forecast = viewModel.forecastInfo() 24 | self.height = height 25 | } 26 | 27 | var body: some View { 28 | VStack(spacing: 4) { 29 | WidgetSmallForLargeView(viewModel: viewModel, height: height * 0.3) 30 | .frame(height: height * 0.3) 31 | //.padding(.bottom, 4) 32 | Group { 33 | if (self.forecast.count > 0) { 34 | VStack { 35 | PanelHeaderView(title: "Forecast") 36 | .background(panelBackgroundColor) 37 | .foregroundColor(.white) 38 | VStack(alignment: .leading, spacing: 0) { 39 | Group { 40 | ForEach((0...4), id: \.self) { 41 | WidgetForecastDayView(dailyWeather: self.forecast[$0]) 42 | } 43 | } 44 | .foregroundColor(.white) 45 | .background(panelBackgroundColor) 46 | } 47 | } 48 | .padding(.bottom, 20) 49 | Spacer() 50 | } else { 51 | Spacer() 52 | VStack { 53 | Text("No data") 54 | } 55 | .padding(.bottom, 10) 56 | .background(panelBackgroundColor) 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | 64 | struct WidgetForecastDayView: View { 65 | var dailyWeather: ForecastInfo 66 | 67 | var body: some View { 68 | HStack { 69 | Text(dailyWeather.dow) 70 | .scaledPanelFont() 71 | .frame(width: 110, alignment: .leading) 72 | Spacer() 73 | Image(dailyWeather.icon) 74 | .resizable() 75 | .clipped() 76 | .frame(width: 50) 77 | .aspectRatio(1.6666666, contentMode: .fit) 78 | Spacer() 79 | Text(dailyWeather.max) 80 | .scaledPanelFont() 81 | Text(dailyWeather.min) 82 | .scaledPanelFont() 83 | }.padding(.leading, 20) 84 | .padding(.trailing, 20) 85 | } 86 | } 87 | 88 | struct PanelHeaderView: View { 89 | let title: String 90 | 91 | var body: some View { 92 | HStack { 93 | Text(title) 94 | .scaledPanelFont(size: 24) 95 | .padding(.top, 6) 96 | .padding(.leading, 10) 97 | Spacer() 98 | 99 | }.padding(.bottom, 2) 100 | } 101 | } 102 | 103 | extension View { 104 | 105 | func scaledPanelFont(size: CGFloat = panelViewFontSize) -> some View { 106 | return self.modifier(ScaledFont(name: panelViewFontName, size: size)) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /WeatherWidget/WidgetSmallForLargeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurrentWeatherView.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/17/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | fileprivate let currentWeatherViewFontSize: CGFloat = 16 12 | 13 | 14 | public struct WidgetSmallForLargeView: View { 15 | let viewModel: ViewModel 16 | let height: CGFloat 17 | 18 | 19 | public init (viewModel: ViewModel, height: CGFloat) { 20 | self.viewModel = viewModel 21 | self.height = height 22 | } 23 | 24 | public var body: some View { 25 | HStack { 26 | VStack(alignment: .leading) { 27 | Group { 28 | HStack { 29 | Image(uiImage: viewModel.weatherIcon!) 30 | .resizable() 31 | .frame(width: 37, height: 22, alignment: .leading) 32 | .offset(x: -10) 33 | .padding(.top, 5) 34 | Text(viewModel.currentConditions) 35 | .scaledCurrentWeatherPanelFont() 36 | .padding(.top, 5) 37 | .padding(.leading, -8) 38 | Spacer() 39 | } 40 | HStack { 41 | Image("HighTemp") 42 | .resizable() 43 | .frame(width: 15, height: 19) 44 | .aspectRatio(contentMode: .fit) 45 | .padding(.trailing, 3) 46 | Text(viewModel.highTemp) 47 | .scaledCurrentWeatherPanelFont() 48 | .padding(.trailing, 8) 49 | Image("LowTemp") 50 | .resizable() 51 | .frame(width: 15, height: 19) 52 | .aspectRatio(contentMode: .fit) 53 | .padding(.trailing, 3) 54 | Text(viewModel.lowTemp) 55 | .scaledCurrentWeatherPanelFont() 56 | .padding(.trailing, 8) 57 | } 58 | }.foregroundColor(.white) 59 | } .padding(.leading, 20) 60 | 61 | HStack(alignment: .top) { 62 | Text(viewModel.temperature) 63 | .scaledFont(name: "HelveticaNeue-UltraLight", size: height > 140 ? 48 : 32) 64 | .frame(maxWidth: .infinity, alignment: height > 140 ? .center : .leading) 65 | .foregroundColor(.white) 66 | }.padding(.leading, 40) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /WeatherWidget/WidgetSmallView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurrentWeatherView.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/17/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | fileprivate let currentWeatherViewFontSize: CGFloat = 16 12 | 13 | 14 | public struct WidgetSmallView: View { 15 | let viewModel: ViewModel 16 | let height: CGFloat 17 | 18 | 19 | public init (viewModel: ViewModel, height: CGFloat) { 20 | self.viewModel = viewModel 21 | self.height = height 22 | } 23 | 24 | public var body: some View { 25 | VStack(alignment: .leading) { 26 | Group { 27 | HStack { 28 | Image(uiImage: viewModel.weatherIcon!) 29 | .resizable() 30 | .frame(width: 37, height: 22, alignment: .leading) 31 | .offset(x: -10) 32 | .padding(.top, 5) 33 | Text(viewModel.currentConditions) 34 | .scaledCurrentWeatherPanelFont() 35 | .padding(.top, 5) 36 | .padding(.leading, -8) 37 | Spacer() 38 | } 39 | HStack { 40 | Image("HighTemp") 41 | .resizable() 42 | .frame(width: 15, height: 19) 43 | .aspectRatio(contentMode: .fit) 44 | .padding(.trailing, 3) 45 | Text(viewModel.highTemp) 46 | .scaledCurrentWeatherPanelFont() 47 | .padding(.trailing, 8) 48 | Image("LowTemp") 49 | .resizable() 50 | .frame(width: 15, height: 19) 51 | .aspectRatio(contentMode: .fit) 52 | .padding(.trailing, 3) 53 | Text(viewModel.lowTemp) 54 | .scaledCurrentWeatherPanelFont() 55 | .padding(.trailing, 8) 56 | } 57 | HStack { 58 | Text(viewModel.temperature) 59 | .scaledFont(name: "HelveticaNeue-UltraLight", size: height > 140 ? 48 : 32) 60 | .frame(maxWidth: .infinity, alignment: height > 140 ? .center : .leading) 61 | } 62 | .offset(y: height - (height > 140 ? 150 : 110)) 63 | }.foregroundColor(.white) 64 | } .padding(.leading, 20) 65 | } 66 | } 67 | 68 | struct ScaledFont: ViewModifier { 69 | @Environment(\.sizeCategory) var sizeCategory 70 | var name: String 71 | var size: CGFloat 72 | 73 | func body(content: Content) -> some View { 74 | let scaledSize = UIFontMetrics.default.scaledValue(for: size) 75 | return content.font(.custom(name, size: scaledSize)) 76 | } 77 | } 78 | 79 | extension View { 80 | func scaledFont(name: String, size: CGFloat) -> some View { 81 | return self.modifier(ScaledFont(name: name, size: size)) 82 | } 83 | 84 | func scaledCurrentWeatherPanelFont() -> some View { 85 | return self.modifier(ScaledFont(name: panelViewFontName, size: currentWeatherViewFontSize)) 86 | } 87 | } 88 | 89 | extension UIColor { 90 | convenience init(hex: Int, alpha: CGFloat = 1.0) { 91 | self.init( 92 | red: CGFloat((hex & 0xFF0000) >> 16) / 255.0, 93 | green: CGFloat((hex & 0x00FF00) >> 8) / 255.0, 94 | blue: CGFloat(hex & 0x0000FF) / 255.0, 95 | alpha: alpha ) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /WeatherwithWidget.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3E3659EA24B4D1A50015C0A6 /* CurrentWeatherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8244224A91AE400503A23 /* CurrentWeatherView.swift */; }; 11 | 3E3659EB24B4D1A50015C0A6 /* DetailsPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EF3F17F24A939DF007E0C31 /* DetailsPanelView.swift */; }; 12 | 3E3659EC24B4D1A50015C0A6 /* ForecastPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8244424A91F4700503A23 /* ForecastPanelView.swift */; }; 13 | 3E3659ED24B4D1D50015C0A6 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8241824A8EEC300503A23 /* ContentView.swift */; }; 14 | 3E70FA60253B6CC700B24876 /* WidgetSmallForLargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E70FA58253B6B5900B24876 /* WidgetSmallForLargeView.swift */; }; 15 | 3E96B50324A9859E00209C18 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E96B50224A9859E00209C18 /* WidgetKit.framework */; }; 16 | 3E96B50524A9859E00209C18 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E96B50424A9859E00209C18 /* SwiftUI.framework */; }; 17 | 3E96B50824A9859E00209C18 /* WeatherWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E96B50724A9859E00209C18 /* WeatherWidget.swift */; }; 18 | 3E96B50A24A9859E00209C18 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3E96B50924A9859E00209C18 /* Assets.xcassets */; }; 19 | 3E96B50E24A9859E00209C18 /* WeatherWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 3E96B50024A9859E00209C18 /* WeatherWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 20 | 3E96B51524A985FF00209C18 /* WidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E96B51324A985EB00209C18 /* WidgetBundle.swift */; }; 21 | 3E96B51A24AB7DAD00209C18 /* WidgetSmallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E96B51824AB7CCD00209C18 /* WidgetSmallView.swift */; }; 22 | 3E96B51E24AB84DA00209C18 /* WidgetLargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E96B51B24AB82DA00209C18 /* WidgetLargeView.swift */; }; 23 | 3EA9DFC624C36A8000693B45 /* ScrollViewWithOffset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA9DFC524C36A8000693B45 /* ScrollViewWithOffset.swift */; }; 24 | 3ED8241724A8EEC300503A23 /* WeatherwithWidgetApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8241624A8EEC300503A23 /* WeatherwithWidgetApp.swift */; }; 25 | 3ED8241B24A8EEC500503A23 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3ED8241A24A8EEC500503A23 /* Assets.xcassets */; }; 26 | 3ED8241E24A8EEC500503A23 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3ED8241D24A8EEC500503A23 /* Preview Assets.xcassets */; }; 27 | 3ED8244724A9246F00503A23 /* AppViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8244624A9246F00503A23 /* AppViewModel.swift */; }; 28 | 3ED8C91E24C36EF900F5B5BF /* NetworkWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8243824A9192C00503A23 /* NetworkWeather.swift */; }; 29 | 3ED8C91F24C36EFA00F5B5BF /* NetworkWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8243824A9192C00503A23 /* NetworkWeather.swift */; }; 30 | 3ED8C92024C36EFD00F5B5BF /* ViewModel+Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8243924A9192C00503A23 /* ViewModel+Network.swift */; }; 31 | 3ED8C92124C36EFE00F5B5BF /* ViewModel+Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8243924A9192C00503A23 /* ViewModel+Network.swift */; }; 32 | 3ED8C92224C36F0200F5B5BF /* WeatherModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8243724A9192C00503A23 /* WeatherModels.swift */; }; 33 | 3ED8C92324C36F0300F5B5BF /* WeatherModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8243724A9192C00503A23 /* WeatherModels.swift */; }; 34 | 3ED8C92424C36F0700F5B5BF /* WeatherViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8243A24A9192C00503A23 /* WeatherViewModels.swift */; }; 35 | 3ED8C92524C36F0800F5B5BF /* WeatherViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8243A24A9192C00503A23 /* WeatherViewModels.swift */; }; 36 | 3ED8C92724C3704400F5B5BF /* WindAndPressurePanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8C92624C3704400F5B5BF /* WindAndPressurePanelView.swift */; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXContainerItemProxy section */ 40 | 3E96B50C24A9859E00209C18 /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = 3ED8240B24A8EEC300503A23 /* Project object */; 43 | proxyType = 1; 44 | remoteGlobalIDString = 3E96B4FF24A9859E00209C18; 45 | remoteInfo = WeatherWidgetExtension; 46 | }; 47 | /* End PBXContainerItemProxy section */ 48 | 49 | /* Begin PBXCopyFilesBuildPhase section */ 50 | 3E96B50F24A9859E00209C18 /* Embed App Extensions */ = { 51 | isa = PBXCopyFilesBuildPhase; 52 | buildActionMask = 2147483647; 53 | dstPath = ""; 54 | dstSubfolderSpec = 13; 55 | files = ( 56 | 3E96B50E24A9859E00209C18 /* WeatherWidgetExtension.appex in Embed App Extensions */, 57 | ); 58 | name = "Embed App Extensions"; 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXCopyFilesBuildPhase section */ 62 | 63 | /* Begin PBXFileReference section */ 64 | 3E70FA58253B6B5900B24876 /* WidgetSmallForLargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetSmallForLargeView.swift; sourceTree = ""; }; 65 | 3E96B50024A9859E00209C18 /* WeatherWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WeatherWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 3E96B50224A9859E00209C18 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; 67 | 3E96B50424A9859E00209C18 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; 68 | 3E96B50724A9859E00209C18 /* WeatherWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherWidget.swift; sourceTree = ""; }; 69 | 3E96B50924A9859E00209C18 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 70 | 3E96B50B24A9859E00209C18 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 71 | 3E96B51324A985EB00209C18 /* WidgetBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetBundle.swift; sourceTree = ""; }; 72 | 3E96B51824AB7CCD00209C18 /* WidgetSmallView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetSmallView.swift; sourceTree = ""; }; 73 | 3E96B51B24AB82DA00209C18 /* WidgetLargeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetLargeView.swift; sourceTree = ""; }; 74 | 3EA9DFC524C36A8000693B45 /* ScrollViewWithOffset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewWithOffset.swift; sourceTree = ""; }; 75 | 3ED8241324A8EEC300503A23 /* WeatherwithWidget.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeatherwithWidget.app; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | 3ED8241624A8EEC300503A23 /* WeatherwithWidgetApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherwithWidgetApp.swift; sourceTree = ""; }; 77 | 3ED8241824A8EEC300503A23 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 78 | 3ED8241A24A8EEC500503A23 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 79 | 3ED8241D24A8EEC500503A23 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 80 | 3ED8241F24A8EEC500503A23 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | 3ED8242C24A8EF8300503A23 /* Shared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Shared.h; sourceTree = ""; }; 82 | 3ED8242D24A8EF8300503A23 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 83 | 3ED8243724A9192C00503A23 /* WeatherModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherModels.swift; sourceTree = ""; }; 84 | 3ED8243824A9192C00503A23 /* NetworkWeather.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkWeather.swift; sourceTree = ""; }; 85 | 3ED8243924A9192C00503A23 /* ViewModel+Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ViewModel+Network.swift"; sourceTree = ""; }; 86 | 3ED8243A24A9192C00503A23 /* WeatherViewModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherViewModels.swift; sourceTree = ""; }; 87 | 3ED8244224A91AE400503A23 /* CurrentWeatherView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrentWeatherView.swift; sourceTree = ""; }; 88 | 3ED8244424A91F4700503A23 /* ForecastPanelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForecastPanelView.swift; sourceTree = ""; }; 89 | 3ED8244624A9246F00503A23 /* AppViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewModel.swift; sourceTree = ""; }; 90 | 3ED8C92624C3704400F5B5BF /* WindAndPressurePanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindAndPressurePanelView.swift; sourceTree = ""; }; 91 | 3EF3F17F24A939DF007E0C31 /* DetailsPanelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailsPanelView.swift; sourceTree = ""; }; 92 | /* End PBXFileReference section */ 93 | 94 | /* Begin PBXFrameworksBuildPhase section */ 95 | 3E96B4FD24A9859E00209C18 /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | 3E96B50524A9859E00209C18 /* SwiftUI.framework in Frameworks */, 100 | 3E96B50324A9859E00209C18 /* WidgetKit.framework in Frameworks */, 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | 3ED8241024A8EEC300503A23 /* Frameworks */ = { 105 | isa = PBXFrameworksBuildPhase; 106 | buildActionMask = 2147483647; 107 | files = ( 108 | ); 109 | runOnlyForDeploymentPostprocessing = 0; 110 | }; 111 | /* End PBXFrameworksBuildPhase section */ 112 | 113 | /* Begin PBXGroup section */ 114 | 3E96B50124A9859E00209C18 /* Frameworks */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 3E96B50224A9859E00209C18 /* WidgetKit.framework */, 118 | 3E96B50424A9859E00209C18 /* SwiftUI.framework */, 119 | ); 120 | name = Frameworks; 121 | sourceTree = ""; 122 | }; 123 | 3E96B50624A9859E00209C18 /* WeatherWidget */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 3E96B51824AB7CCD00209C18 /* WidgetSmallView.swift */, 127 | 3E70FA58253B6B5900B24876 /* WidgetSmallForLargeView.swift */, 128 | 3E96B51B24AB82DA00209C18 /* WidgetLargeView.swift */, 129 | 3E96B51324A985EB00209C18 /* WidgetBundle.swift */, 130 | 3E96B50724A9859E00209C18 /* WeatherWidget.swift */, 131 | 3E96B50924A9859E00209C18 /* Assets.xcassets */, 132 | 3E96B50B24A9859E00209C18 /* Info.plist */, 133 | ); 134 | path = WeatherWidget; 135 | sourceTree = ""; 136 | }; 137 | 3ED8240A24A8EEC300503A23 = { 138 | isa = PBXGroup; 139 | children = ( 140 | 3ED8241524A8EEC300503A23 /* WeatherwithWidget */, 141 | 3ED8242B24A8EF8300503A23 /* Shared */, 142 | 3E96B50624A9859E00209C18 /* WeatherWidget */, 143 | 3E96B50124A9859E00209C18 /* Frameworks */, 144 | 3ED8241424A8EEC300503A23 /* Products */, 145 | ); 146 | sourceTree = ""; 147 | }; 148 | 3ED8241424A8EEC300503A23 /* Products */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 3ED8241324A8EEC300503A23 /* WeatherwithWidget.app */, 152 | 3E96B50024A9859E00209C18 /* WeatherWidgetExtension.appex */, 153 | ); 154 | name = Products; 155 | sourceTree = ""; 156 | }; 157 | 3ED8241524A8EEC300503A23 /* WeatherwithWidget */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 3ED8241624A8EEC300503A23 /* WeatherwithWidgetApp.swift */, 161 | 3ED8244124A91ABD00503A23 /* Views */, 162 | 3ED8241A24A8EEC500503A23 /* Assets.xcassets */, 163 | 3ED8241F24A8EEC500503A23 /* Info.plist */, 164 | 3ED8241C24A8EEC500503A23 /* Preview Content */, 165 | 3ED8244624A9246F00503A23 /* AppViewModel.swift */, 166 | ); 167 | path = WeatherwithWidget; 168 | sourceTree = ""; 169 | }; 170 | 3ED8241C24A8EEC500503A23 /* Preview Content */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 3ED8241D24A8EEC500503A23 /* Preview Assets.xcassets */, 174 | ); 175 | path = "Preview Content"; 176 | sourceTree = ""; 177 | }; 178 | 3ED8242B24A8EF8300503A23 /* Shared */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 3ED8243F24A9193F00503A23 /* Data */, 182 | 3ED8242C24A8EF8300503A23 /* Shared.h */, 183 | 3ED8242D24A8EF8300503A23 /* Info.plist */, 184 | ); 185 | path = Shared; 186 | sourceTree = ""; 187 | }; 188 | 3ED8243F24A9193F00503A23 /* Data */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 3ED8243824A9192C00503A23 /* NetworkWeather.swift */, 192 | 3ED8243924A9192C00503A23 /* ViewModel+Network.swift */, 193 | 3ED8243724A9192C00503A23 /* WeatherModels.swift */, 194 | 3ED8243A24A9192C00503A23 /* WeatherViewModels.swift */, 195 | ); 196 | path = Data; 197 | sourceTree = ""; 198 | }; 199 | 3ED8244124A91ABD00503A23 /* Views */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 3ED8241824A8EEC300503A23 /* ContentView.swift */, 203 | 3ED8244224A91AE400503A23 /* CurrentWeatherView.swift */, 204 | 3EF3F17F24A939DF007E0C31 /* DetailsPanelView.swift */, 205 | 3ED8244424A91F4700503A23 /* ForecastPanelView.swift */, 206 | 3EA9DFC524C36A8000693B45 /* ScrollViewWithOffset.swift */, 207 | 3ED8C92624C3704400F5B5BF /* WindAndPressurePanelView.swift */, 208 | ); 209 | path = Views; 210 | sourceTree = ""; 211 | }; 212 | /* End PBXGroup section */ 213 | 214 | /* Begin PBXNativeTarget section */ 215 | 3E96B4FF24A9859E00209C18 /* WeatherWidgetExtension */ = { 216 | isa = PBXNativeTarget; 217 | buildConfigurationList = 3E96B51224A9859E00209C18 /* Build configuration list for PBXNativeTarget "WeatherWidgetExtension" */; 218 | buildPhases = ( 219 | 3E96B4FC24A9859E00209C18 /* Sources */, 220 | 3E96B4FD24A9859E00209C18 /* Frameworks */, 221 | 3E96B4FE24A9859E00209C18 /* Resources */, 222 | ); 223 | buildRules = ( 224 | ); 225 | dependencies = ( 226 | ); 227 | name = WeatherWidgetExtension; 228 | productName = WeatherWidgetExtension; 229 | productReference = 3E96B50024A9859E00209C18 /* WeatherWidgetExtension.appex */; 230 | productType = "com.apple.product-type.app-extension"; 231 | }; 232 | 3ED8241224A8EEC300503A23 /* WeatherwithWidget */ = { 233 | isa = PBXNativeTarget; 234 | buildConfigurationList = 3ED8242224A8EEC500503A23 /* Build configuration list for PBXNativeTarget "WeatherwithWidget" */; 235 | buildPhases = ( 236 | 3ED8240F24A8EEC300503A23 /* Sources */, 237 | 3ED8241024A8EEC300503A23 /* Frameworks */, 238 | 3ED8241124A8EEC300503A23 /* Resources */, 239 | 3E96B50F24A9859E00209C18 /* Embed App Extensions */, 240 | ); 241 | buildRules = ( 242 | ); 243 | dependencies = ( 244 | 3E96B50D24A9859E00209C18 /* PBXTargetDependency */, 245 | ); 246 | name = WeatherwithWidget; 247 | productName = WeatherwithWidget; 248 | productReference = 3ED8241324A8EEC300503A23 /* WeatherwithWidget.app */; 249 | productType = "com.apple.product-type.application"; 250 | }; 251 | /* End PBXNativeTarget section */ 252 | 253 | /* Begin PBXProject section */ 254 | 3ED8240B24A8EEC300503A23 /* Project object */ = { 255 | isa = PBXProject; 256 | attributes = { 257 | LastSwiftUpdateCheck = 1200; 258 | LastUpgradeCheck = 1200; 259 | TargetAttributes = { 260 | 3E96B4FF24A9859E00209C18 = { 261 | CreatedOnToolsVersion = 12.0; 262 | }; 263 | 3ED8241224A8EEC300503A23 = { 264 | CreatedOnToolsVersion = 12.0; 265 | }; 266 | }; 267 | }; 268 | buildConfigurationList = 3ED8240E24A8EEC300503A23 /* Build configuration list for PBXProject "WeatherwithWidget" */; 269 | compatibilityVersion = "Xcode 9.3"; 270 | developmentRegion = en; 271 | hasScannedForEncodings = 0; 272 | knownRegions = ( 273 | en, 274 | Base, 275 | ); 276 | mainGroup = 3ED8240A24A8EEC300503A23; 277 | productRefGroup = 3ED8241424A8EEC300503A23 /* Products */; 278 | projectDirPath = ""; 279 | projectRoot = ""; 280 | targets = ( 281 | 3ED8241224A8EEC300503A23 /* WeatherwithWidget */, 282 | 3E96B4FF24A9859E00209C18 /* WeatherWidgetExtension */, 283 | ); 284 | }; 285 | /* End PBXProject section */ 286 | 287 | /* Begin PBXResourcesBuildPhase section */ 288 | 3E96B4FE24A9859E00209C18 /* Resources */ = { 289 | isa = PBXResourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 3E96B50A24A9859E00209C18 /* Assets.xcassets in Resources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | 3ED8241124A8EEC300503A23 /* Resources */ = { 297 | isa = PBXResourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | 3ED8241E24A8EEC500503A23 /* Preview Assets.xcassets in Resources */, 301 | 3ED8241B24A8EEC500503A23 /* Assets.xcassets in Resources */, 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | /* End PBXResourcesBuildPhase section */ 306 | 307 | /* Begin PBXSourcesBuildPhase section */ 308 | 3E96B4FC24A9859E00209C18 /* Sources */ = { 309 | isa = PBXSourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | 3E96B50824A9859E00209C18 /* WeatherWidget.swift in Sources */, 313 | 3E70FA60253B6CC700B24876 /* WidgetSmallForLargeView.swift in Sources */, 314 | 3ED8C92524C36F0800F5B5BF /* WeatherViewModels.swift in Sources */, 315 | 3ED8C92324C36F0300F5B5BF /* WeatherModels.swift in Sources */, 316 | 3ED8C91F24C36EFA00F5B5BF /* NetworkWeather.swift in Sources */, 317 | 3E96B51A24AB7DAD00209C18 /* WidgetSmallView.swift in Sources */, 318 | 3E96B51E24AB84DA00209C18 /* WidgetLargeView.swift in Sources */, 319 | 3E96B51524A985FF00209C18 /* WidgetBundle.swift in Sources */, 320 | 3ED8C92124C36EFE00F5B5BF /* ViewModel+Network.swift in Sources */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | 3ED8240F24A8EEC300503A23 /* Sources */ = { 325 | isa = PBXSourcesBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | 3E3659EC24B4D1A50015C0A6 /* ForecastPanelView.swift in Sources */, 329 | 3ED8C91E24C36EF900F5B5BF /* NetworkWeather.swift in Sources */, 330 | 3E3659EA24B4D1A50015C0A6 /* CurrentWeatherView.swift in Sources */, 331 | 3E3659ED24B4D1D50015C0A6 /* ContentView.swift in Sources */, 332 | 3ED8C92224C36F0200F5B5BF /* WeatherModels.swift in Sources */, 333 | 3ED8244724A9246F00503A23 /* AppViewModel.swift in Sources */, 334 | 3ED8C92724C3704400F5B5BF /* WindAndPressurePanelView.swift in Sources */, 335 | 3ED8C92024C36EFD00F5B5BF /* ViewModel+Network.swift in Sources */, 336 | 3EA9DFC624C36A8000693B45 /* ScrollViewWithOffset.swift in Sources */, 337 | 3E3659EB24B4D1A50015C0A6 /* DetailsPanelView.swift in Sources */, 338 | 3ED8C92424C36F0700F5B5BF /* WeatherViewModels.swift in Sources */, 339 | 3ED8241724A8EEC300503A23 /* WeatherwithWidgetApp.swift in Sources */, 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | }; 343 | /* End PBXSourcesBuildPhase section */ 344 | 345 | /* Begin PBXTargetDependency section */ 346 | 3E96B50D24A9859E00209C18 /* PBXTargetDependency */ = { 347 | isa = PBXTargetDependency; 348 | target = 3E96B4FF24A9859E00209C18 /* WeatherWidgetExtension */; 349 | targetProxy = 3E96B50C24A9859E00209C18 /* PBXContainerItemProxy */; 350 | }; 351 | /* End PBXTargetDependency section */ 352 | 353 | /* Begin XCBuildConfiguration section */ 354 | 3E96B51024A9859E00209C18 /* Debug */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 358 | ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; 359 | CODE_SIGN_STYLE = Automatic; 360 | DEVELOPMENT_TEAM = 5EP5YM7PC7; 361 | INFOPLIST_FILE = WeatherWidget/Info.plist; 362 | LD_RUNPATH_SEARCH_PATHS = ( 363 | "$(inherited)", 364 | "@executable_path/Frameworks", 365 | "@executable_path/../../Frameworks", 366 | ); 367 | PRODUCT_BUNDLE_IDENTIFIER = com.gary.WeatherwithWidget.WeatherWidget; 368 | PRODUCT_NAME = "$(TARGET_NAME)"; 369 | SKIP_INSTALL = YES; 370 | SWIFT_VERSION = 5.0; 371 | TARGETED_DEVICE_FAMILY = "1,2"; 372 | }; 373 | name = Debug; 374 | }; 375 | 3E96B51124A9859E00209C18 /* Release */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 379 | ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; 380 | CODE_SIGN_STYLE = Automatic; 381 | DEVELOPMENT_TEAM = 5EP5YM7PC7; 382 | INFOPLIST_FILE = WeatherWidget/Info.plist; 383 | LD_RUNPATH_SEARCH_PATHS = ( 384 | "$(inherited)", 385 | "@executable_path/Frameworks", 386 | "@executable_path/../../Frameworks", 387 | ); 388 | PRODUCT_BUNDLE_IDENTIFIER = com.gary.WeatherwithWidget.WeatherWidget; 389 | PRODUCT_NAME = "$(TARGET_NAME)"; 390 | SKIP_INSTALL = YES; 391 | SWIFT_VERSION = 5.0; 392 | TARGETED_DEVICE_FAMILY = "1,2"; 393 | }; 394 | name = Release; 395 | }; 396 | 3ED8242024A8EEC500503A23 /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ALWAYS_SEARCH_USER_PATHS = NO; 400 | CLANG_ANALYZER_NONNULL = YES; 401 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 402 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 403 | CLANG_CXX_LIBRARY = "libc++"; 404 | CLANG_ENABLE_MODULES = YES; 405 | CLANG_ENABLE_OBJC_ARC = YES; 406 | CLANG_ENABLE_OBJC_WEAK = YES; 407 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 408 | CLANG_WARN_BOOL_CONVERSION = YES; 409 | CLANG_WARN_COMMA = YES; 410 | CLANG_WARN_CONSTANT_CONVERSION = YES; 411 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 412 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 413 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 414 | CLANG_WARN_EMPTY_BODY = YES; 415 | CLANG_WARN_ENUM_CONVERSION = YES; 416 | CLANG_WARN_INFINITE_RECURSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 419 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 420 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 421 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 422 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 423 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 424 | CLANG_WARN_STRICT_PROTOTYPES = YES; 425 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 426 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 427 | CLANG_WARN_UNREACHABLE_CODE = YES; 428 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 429 | COPY_PHASE_STRIP = NO; 430 | DEBUG_INFORMATION_FORMAT = dwarf; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | ENABLE_TESTABILITY = YES; 433 | GCC_C_LANGUAGE_STANDARD = gnu11; 434 | GCC_DYNAMIC_NO_PIC = NO; 435 | GCC_NO_COMMON_BLOCKS = YES; 436 | GCC_OPTIMIZATION_LEVEL = 0; 437 | GCC_PREPROCESSOR_DEFINITIONS = ( 438 | "DEBUG=1", 439 | "$(inherited)", 440 | ); 441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 443 | GCC_WARN_UNDECLARED_SELECTOR = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 445 | GCC_WARN_UNUSED_FUNCTION = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 448 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 449 | MTL_FAST_MATH = YES; 450 | ONLY_ACTIVE_ARCH = YES; 451 | SDKROOT = iphoneos; 452 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 453 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 454 | }; 455 | name = Debug; 456 | }; 457 | 3ED8242124A8EEC500503A23 /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ALWAYS_SEARCH_USER_PATHS = NO; 461 | CLANG_ANALYZER_NONNULL = YES; 462 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 463 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 464 | CLANG_CXX_LIBRARY = "libc++"; 465 | CLANG_ENABLE_MODULES = YES; 466 | CLANG_ENABLE_OBJC_ARC = YES; 467 | CLANG_ENABLE_OBJC_WEAK = YES; 468 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 469 | CLANG_WARN_BOOL_CONVERSION = YES; 470 | CLANG_WARN_COMMA = YES; 471 | CLANG_WARN_CONSTANT_CONVERSION = YES; 472 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 473 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 474 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 475 | CLANG_WARN_EMPTY_BODY = YES; 476 | CLANG_WARN_ENUM_CONVERSION = YES; 477 | CLANG_WARN_INFINITE_RECURSION = YES; 478 | CLANG_WARN_INT_CONVERSION = YES; 479 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 480 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 481 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 482 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 483 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 484 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 485 | CLANG_WARN_STRICT_PROTOTYPES = YES; 486 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 487 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 488 | CLANG_WARN_UNREACHABLE_CODE = YES; 489 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 490 | COPY_PHASE_STRIP = NO; 491 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 492 | ENABLE_NS_ASSERTIONS = NO; 493 | ENABLE_STRICT_OBJC_MSGSEND = YES; 494 | GCC_C_LANGUAGE_STANDARD = gnu11; 495 | GCC_NO_COMMON_BLOCKS = YES; 496 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 497 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 498 | GCC_WARN_UNDECLARED_SELECTOR = YES; 499 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 500 | GCC_WARN_UNUSED_FUNCTION = YES; 501 | GCC_WARN_UNUSED_VARIABLE = YES; 502 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 503 | MTL_ENABLE_DEBUG_INFO = NO; 504 | MTL_FAST_MATH = YES; 505 | SDKROOT = iphoneos; 506 | SWIFT_COMPILATION_MODE = wholemodule; 507 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 508 | VALIDATE_PRODUCT = YES; 509 | }; 510 | name = Release; 511 | }; 512 | 3ED8242324A8EEC500503A23 /* Debug */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 516 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 517 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 518 | CODE_SIGN_STYLE = Automatic; 519 | DEVELOPMENT_ASSET_PATHS = "\"WeatherwithWidget/Preview Content\""; 520 | DEVELOPMENT_TEAM = ""; 521 | ENABLE_PREVIEWS = YES; 522 | INFOPLIST_FILE = WeatherwithWidget/Info.plist; 523 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 524 | LD_RUNPATH_SEARCH_PATHS = ( 525 | "$(inherited)", 526 | "@executable_path/Frameworks", 527 | ); 528 | PRODUCT_BUNDLE_IDENTIFIER = com.gary.WeatherwithWidget; 529 | PRODUCT_NAME = "$(TARGET_NAME)"; 530 | SWIFT_VERSION = 5.0; 531 | TARGETED_DEVICE_FAMILY = "1,2"; 532 | }; 533 | name = Debug; 534 | }; 535 | 3ED8242424A8EEC500503A23 /* Release */ = { 536 | isa = XCBuildConfiguration; 537 | buildSettings = { 538 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 539 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 540 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 541 | CODE_SIGN_STYLE = Automatic; 542 | DEVELOPMENT_ASSET_PATHS = "\"WeatherwithWidget/Preview Content\""; 543 | DEVELOPMENT_TEAM = ""; 544 | ENABLE_PREVIEWS = YES; 545 | INFOPLIST_FILE = WeatherwithWidget/Info.plist; 546 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 547 | LD_RUNPATH_SEARCH_PATHS = ( 548 | "$(inherited)", 549 | "@executable_path/Frameworks", 550 | ); 551 | PRODUCT_BUNDLE_IDENTIFIER = com.gary.WeatherwithWidget; 552 | PRODUCT_NAME = "$(TARGET_NAME)"; 553 | SWIFT_VERSION = 5.0; 554 | TARGETED_DEVICE_FAMILY = "1,2"; 555 | }; 556 | name = Release; 557 | }; 558 | /* End XCBuildConfiguration section */ 559 | 560 | /* Begin XCConfigurationList section */ 561 | 3E96B51224A9859E00209C18 /* Build configuration list for PBXNativeTarget "WeatherWidgetExtension" */ = { 562 | isa = XCConfigurationList; 563 | buildConfigurations = ( 564 | 3E96B51024A9859E00209C18 /* Debug */, 565 | 3E96B51124A9859E00209C18 /* Release */, 566 | ); 567 | defaultConfigurationIsVisible = 0; 568 | defaultConfigurationName = Release; 569 | }; 570 | 3ED8240E24A8EEC300503A23 /* Build configuration list for PBXProject "WeatherwithWidget" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 3ED8242024A8EEC500503A23 /* Debug */, 574 | 3ED8242124A8EEC500503A23 /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | 3ED8242224A8EEC500503A23 /* Build configuration list for PBXNativeTarget "WeatherwithWidget" */ = { 580 | isa = XCConfigurationList; 581 | buildConfigurations = ( 582 | 3ED8242324A8EEC500503A23 /* Debug */, 583 | 3ED8242424A8EEC500503A23 /* Release */, 584 | ); 585 | defaultConfigurationIsVisible = 0; 586 | defaultConfigurationName = Release; 587 | }; 588 | /* End XCConfigurationList section */ 589 | }; 590 | rootObject = 3ED8240B24A8EEC300503A23 /* Project object */; 591 | } 592 | -------------------------------------------------------------------------------- /WeatherwithWidget.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WeatherwithWidget.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WeatherwithWidget.xcodeproj/xcshareddata/xcschemes/WeatherWidgetExtension.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 60 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 85 | 86 | 90 | 91 | 95 | 96 | 97 | 98 | 105 | 107 | 113 | 114 | 115 | 116 | 118 | 119 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /WeatherwithWidget.xcodeproj/xcshareddata/xcschemes/WeatherwithWidget.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /WeatherwithWidget/AppViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppViewModel.swift 3 | // WeatherwithWidget 4 | // 5 | // Created by Gary on 6/28/20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | 11 | //final class AppViewModel: ObservableObject { 12 | // 13 | // @Published var current: CurrentData 14 | // @Published var forecast: ForecastData 15 | // @Published var environment = false 16 | // 17 | //} 18 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/01-s.imageset/01-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/01-s.imageset/01-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/01-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "01-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/02-s.imageset/02-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/02-s.imageset/02-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/02-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "02-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/03-s.imageset/03-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/03-s.imageset/03-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/03-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "03-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/04-s.imageset/04-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/04-s.imageset/04-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/04-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "04-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/05-s.imageset/05-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/05-s.imageset/05-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/05-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "05-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/06-s.imageset/06-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/06-s.imageset/06-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/06-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "06-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/07-s.imageset/07-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/07-s.imageset/07-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/07-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "07-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/08-s.imageset/08-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/08-s.imageset/08-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/08-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "08-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/11-s.imageset/11-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/11-s.imageset/11-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/11-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "11-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/12-s.imageset/12-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/12-s.imageset/12-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/12-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "12-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/13-s.imageset/13-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/13-s.imageset/13-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/13-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "13-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/14-s.imageset/14-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/14-s.imageset/14-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/14-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "14-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/15-s.imageset/15-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/15-s.imageset/15-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/15-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "15-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/16-s.imageset/16-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/16-s.imageset/16-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/16-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/17-s.imageset/17-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/17-s.imageset/17-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/17-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "17-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/18-s.imageset/18-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/18-s.imageset/18-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/18-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "18-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/19-s.imageset/19-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/19-s.imageset/19-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/19-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "19-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/20-s.imageset/20-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/20-s.imageset/20-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/20-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "20-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/21-s.imageset/21-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/21-s.imageset/21-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/21-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "21-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/22-s.imageset/22-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/22-s.imageset/22-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/22-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "22-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/23-s.imageset/23-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/23-s.imageset/23-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/23-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "23-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/24-s.imageset/24-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/24-s.imageset/24-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/24-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "24-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/25-s.imageset/25-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/25-s.imageset/25-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/25-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "25-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/26-s.imageset/26-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/26-s.imageset/26-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/26-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "26-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/29-s.imageset/29-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/29-s.imageset/29-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/29-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "29-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/30-s.imageset/30-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/30-s.imageset/30-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/30-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "30-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/31-s.imageset/31-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/31-s.imageset/31-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/31-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "31-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/32-s.imageset/32-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/32-s.imageset/32-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/32-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "32-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/33-s.imageset/33-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/33-s.imageset/33-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/33-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "33-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/34-s.imageset/34-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/34-s.imageset/34-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/34-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "34-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/35-s.imageset/35-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/35-s.imageset/35-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/35-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "35-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/36-s.imageset/36-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/36-s.imageset/36-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/36-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "36-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/37-s.imageset/37-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/37-s.imageset/37-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/37-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "37-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/38-s.imageset/38-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/38-s.imageset/38-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/38-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "38-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/39-s.imageset/39-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/39-s.imageset/39-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/39-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "39-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/40-s.imageset/40-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/40-s.imageset/40-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/40-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "40-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/41-s.imageset/41-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/41-s.imageset/41-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/41-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "41-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/42-s.imageset/42-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/42-s.imageset/42-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/42-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "42-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/43-s.imageset/43-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/43-s.imageset/43-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/43-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "43-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/44-s.imageset/44-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/44-s.imageset/44-s.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/44-s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "44-s.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "H-120-Swift.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "60x60" 38 | }, 39 | { 40 | "filename" : "H-180-Swift.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "scale" : "1x", 48 | "size" : "20x20" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "scale" : "2x", 53 | "size" : "20x20" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "scale" : "1x", 58 | "size" : "29x29" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "scale" : "2x", 63 | "size" : "29x29" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "scale" : "1x", 68 | "size" : "40x40" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "40x40" 74 | }, 75 | { 76 | "filename" : "H-76-Swift-1.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "76x76" 80 | }, 81 | { 82 | "filename" : "H-152-Swift-1.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "76x76" 86 | }, 87 | { 88 | "filename" : "H-167-Swift.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "83.5x83.5" 92 | }, 93 | { 94 | "idiom" : "ios-marketing", 95 | "scale" : "1x", 96 | "size" : "1024x1024" 97 | } 98 | ], 99 | "info" : { 100 | "author" : "xcode", 101 | "version" : 1 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-120-Swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-120-Swift.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-152-Swift-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-152-Swift-1.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-167-Swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-167-Swift.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-180-Swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-180-Swift.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-76-Swift-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/AppIcon-1.appiconset/H-76-Swift-1.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/BlueSky.imageset/BlueSky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/BlueSky.imageset/BlueSky.jpg -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/BlueSky.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BlueSky.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/HighTemp.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "HighTemp.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/HighTemp.imageset/HighTemp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/HighTemp.imageset/HighTemp.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/LowTemp.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LowTemp.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/LowTemp.imageset/LowTemp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/LowTemp.imageset/LowTemp.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/MountainView.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "MountainView@2x.jpg", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/MountainView.imageset/MountainView@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/MountainView.imageset/MountainView@2x.jpg -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/blade_big.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "blade_big@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/blade_big.imageset/blade_big@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/blade_big.imageset/blade_big@2x.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/blade_small.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "blade_small@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/blade_small.imageset/blade_small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/blade_small.imageset/blade_small@2x.png -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/stand_l.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "stand_l.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/stand_l.imageset/stand_l.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/stand_l.imageset/stand_l.pdf -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/stand_s.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "stand_s.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeatherwithWidget/Assets.xcassets/stand_s.imageset/stand_s.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcanfly/WeatherWithWidgets/56e868fa9167916af0ceeebd188fcb2e95ed3a5a/WeatherwithWidget/Assets.xcassets/stand_s.imageset/stand_s.pdf -------------------------------------------------------------------------------- /WeatherwithWidget/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | 28 | UIApplicationSupportsIndirectInputEvents 29 | 30 | UILaunchScreen 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /WeatherwithWidget/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WeatherwithWidget/Views/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // WeatherwithWidget 4 | // 5 | // Created by Gary on 6/28/20. 6 | // 7 | 8 | import SwiftUI 9 | import WidgetKit 10 | import os.log 11 | 12 | 13 | struct ContentView: View { 14 | @ObservedObject var viewModel: ViewModel 15 | @State private var scrollViewOffset: CGPoint = .zero 16 | @Environment(\.scenePhase) var scenePhase 17 | 18 | let panelHeight: CGFloat = 120 19 | let cityName = "Mountain View" 20 | 21 | 22 | func calcBlurRadius(height: CGFloat) -> CGFloat { 23 | // blur the background image more as view is scrolled down 24 | let blur = CGFloat((-self.scrollViewOffset.y / (height * 0.7)) * 15.0) 25 | return blur 26 | } 27 | 28 | var body: some View { 29 | GeometryReader { geometry in 30 | ZStack(alignment: .top) { 31 | Image("BlueSky") 32 | .resizable() 33 | .aspectRatio(contentMode: .fill) 34 | // need frame to constrain geometry bounds on some devices 35 | .frame(width: geometry.size.width, height: geometry.size.height) 36 | .blur(radius: self.calcBlurRadius(height: geometry.size.height)) 37 | HStack(alignment: .center) { 38 | Text(self.cityName) 39 | .font(.title) 40 | .foregroundColor(.white) 41 | }.offset(y: 40) 42 | OffsetScrollView(.vertical, showsIndicators: false, offset: self.$scrollViewOffset) { 43 | VStack(alignment: .center, spacing: 90) { 44 | CurrentWeatherView(viewModel: self.viewModel) 45 | .frame(width: geometry.size.width, height: panelHeight) 46 | DetailsPanelView(viewModel: self.viewModel) 47 | .frame(width: geometry.size.width, height: panelHeight) 48 | .padding(.bottom, 60) 49 | ForecastPanelView(forecast:self.viewModel.forecastInfo()) 50 | .frame(width: geometry.size.width, height: panelHeight) 51 | .padding(.bottom, 60) 52 | WindAndPressurePanelView(viewModel: self.viewModel) 53 | .frame(width: geometry.size.width, height: panelHeight) 54 | Spacer() 55 | }.padding(.top, geometry.size.height - geometry.safeAreaInsets.bottom - 230) 56 | } 57 | .frame(width: geometry.size.width) 58 | .offset(y: geometry.safeAreaInsets.top + 54) 59 | } 60 | } 61 | .ignoresSafeArea() 62 | .onAppear { 63 | // testing to see how often this gets hit. remove before release. 64 | os_log("app onAppear getting weather", log: OSLog.networkLogger, type: .info) 65 | self.viewModel.getWeather() 66 | } 67 | .onChange(of: scenePhase) { phase in 68 | if phase == .active { 69 | // testing to see how often this gets hit. remove before release. 70 | os_log("app becoming active getting weather", log: OSLog.networkLogger, type: .info) 71 | self.viewModel.getWeather() 72 | } else if phase == .inactive { 73 | os_log("telling widget to refresh", log: OSLog.networkLogger, type: .info) 74 | WidgetCenter.shared.reloadAllTimelines() 75 | } 76 | } 77 | } 78 | } 79 | 80 | 81 | struct ContentView_Previews: PreviewProvider { 82 | static var previews: some View { 83 | ContentView(viewModel: ViewModel()) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /WeatherwithWidget/Views/CurrentWeatherView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CurrentWeatherView.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/17/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | 12 | fileprivate let currentWeatherViewFontSize: CGFloat = 24 13 | 14 | 15 | public struct CurrentWeatherView: View { 16 | @ObservedObject var viewModel: ViewModel 17 | 18 | 19 | public var body: some View { 20 | VStack(alignment: .leading) { 21 | Group { 22 | HStack { 23 | Image(viewModel.weatherIconName) 24 | .frame(width: 75, height: 45, alignment: .leading) 25 | Text(viewModel.currentConditions) 26 | .scaledCurrentWeatherPanelFont() 27 | .padding(.top, 5) 28 | } 29 | HStack { 30 | Image("HighTemp") 31 | .resizable() 32 | .frame(width: 13, height: 17) 33 | .aspectRatio(contentMode: .fit) 34 | .padding(.trailing, 8) 35 | Text(viewModel.highTemp) 36 | .scaledCurrentWeatherPanelFont() 37 | .padding(.trailing, currentWeatherViewFontSize) 38 | Image("LowTemp") 39 | .resizable() 40 | .frame(width: 13, height: 17) 41 | .aspectRatio(contentMode: .fit) 42 | .padding(.trailing, 8) 43 | Text(viewModel.lowTemp) 44 | .scaledCurrentWeatherPanelFont() 45 | .padding(.trailing, currentWeatherViewFontSize) 46 | Spacer() 47 | } 48 | HStack { 49 | Text(viewModel.temperature) 50 | .scaledFont(name: "HelveticaNeue-UltraLight", size: 72) 51 | } 52 | }.foregroundColor(.white) 53 | } .padding(.leading, 20) 54 | } 55 | } 56 | 57 | struct ScaledFont: ViewModifier { 58 | @Environment(\.sizeCategory) var sizeCategory 59 | var name: String 60 | var size: CGFloat 61 | 62 | func body(content: Content) -> some View { 63 | let scaledSize = UIFontMetrics.default.scaledValue(for: size) 64 | return content.font(.custom(name, size: scaledSize)) 65 | } 66 | } 67 | 68 | extension View { 69 | func scaledFont(name: String, size: CGFloat) -> some View { 70 | return self.modifier(ScaledFont(name: name, size: size)) 71 | } 72 | 73 | func scaledCurrentWeatherPanelFont() -> some View { 74 | return self.modifier(ScaledFont(name: panelViewFontName, size: currentWeatherViewFontSize)) 75 | } 76 | } 77 | 78 | extension UIColor { 79 | convenience init(hex: Int, alpha: CGFloat = 1.0) { 80 | self.init( 81 | red: CGFloat((hex & 0xFF0000) >> 16) / 255.0, 82 | green: CGFloat((hex & 0x00FF00) >> 8) / 255.0, 83 | blue: CGFloat(hex & 0x0000FF) / 255.0, 84 | alpha: alpha ) 85 | } 86 | } 87 | 88 | 89 | struct CurrentWeatherView_Previews: PreviewProvider { 90 | static var previews: some View { 91 | CurrentWeatherView(viewModel: ViewModel()) 92 | .background(Color.blue) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /WeatherwithWidget/Views/DetailsPanelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsView.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/17/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | 12 | struct DetailsPanelView: View { 13 | @ObservedObject var viewModel: ViewModel 14 | let screenWidth = UIScreen.main.bounds.width 15 | 16 | 17 | var body: some View { 18 | VStack(alignment: .leading) { 19 | VStack { 20 | PanelHeaderView(title: "Details") 21 | .padding(.leading, screenWidth > 500 ? 0 : 14) 22 | HStack { 23 | Image(viewModel.weatherIconName) 24 | .font(.system(size: 60.0)) 25 | .padding(.leading, 70) 26 | .padding(.trailing, 80) 27 | .padding(.bottom, 20) 28 | Spacer() 29 | Divider() 30 | VStack(alignment: .trailing, spacing: 4) { 31 | HStack { 32 | Text("Feels like") 33 | .scaledPanelFont() 34 | .alignmentGuide(HorizontalAlignment.center, computeValue: { _ in -190 } ) 35 | Spacer() 36 | Text(viewModel.feelsLike) 37 | .scaledPanelFont() 38 | } 39 | Divider() 40 | HStack { 41 | Text("Humidity") 42 | .scaledPanelFont() 43 | Spacer() 44 | Text(viewModel.humidity) 45 | .scaledPanelFont() 46 | } 47 | Divider() 48 | HStack { 49 | Text("Visibility") 50 | .scaledPanelFont() 51 | Spacer() 52 | Text(viewModel.visibility) 53 | .scaledPanelFont() 54 | } 55 | Divider() 56 | HStack { 57 | Text("UV Index") 58 | .scaledPanelFont() 59 | Spacer() 60 | RoundedRectangle(cornerRadius: 8, style: .circular) 61 | .frame(width: 30, height: 10) 62 | .foregroundColor(viewModel.uvIndexColor) 63 | }.padding(.bottom, 0) 64 | }.padding(.trailing, screenWidth > 500 ? 20 : 36) 65 | .frame(width: 180) 66 | } 67 | Spacer() 68 | }.foregroundColor(.white) 69 | }.background(panelBackgroundColor) 70 | } 71 | } 72 | 73 | 74 | struct DetailsView_Previews: PreviewProvider { 75 | static var previews: some View { 76 | DetailsPanelView(viewModel: ViewModel()) 77 | .background(Color.blue) 78 | .frame(width: 400, height: 120) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /WeatherwithWidget/Views/ForecastPanelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ForecastPanelView.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/17/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | 12 | let panelViewFontName = "Helvetica Neue" 13 | let panelViewFontSize: CGFloat = 16 14 | let panelBackgroundColor = Color(UIColor(hex: 0x000049, alpha: 0.1)) 15 | 16 | struct ForecastPanelView: View { 17 | let forecast: [ForecastInfo] 18 | 19 | var body: some View { 20 | Group { 21 | if (forecast.count > 0) { 22 | VStack(alignment: .leading, spacing: 6) { 23 | Group { 24 | PanelHeaderView(title: "Forecast") 25 | .padding(.bottom, 10) 26 | ForEach((0...4), id: \.self) { 27 | ForecastDayView(dailyWeather: self.forecast[$0]) 28 | } 29 | } 30 | .foregroundColor(.white) 31 | } 32 | .padding(.bottom, 10) 33 | .background(panelBackgroundColor) 34 | } else { 35 | VStack { 36 | Text("No data") 37 | } 38 | .padding(.bottom, 10) 39 | .background(panelBackgroundColor) 40 | 41 | } 42 | } 43 | } 44 | } 45 | 46 | struct ForecastDayView: View { 47 | var dailyWeather: ForecastInfo 48 | 49 | var body: some View { 50 | HStack { 51 | Text(dailyWeather.dow) 52 | .scaledPanelFont() 53 | .frame(width: 110, alignment: .leading) 54 | Spacer() 55 | Image(dailyWeather.icon) 56 | Spacer() 57 | Text(dailyWeather.max) 58 | .scaledPanelFont() 59 | Text(dailyWeather.min) 60 | .scaledPanelFont() 61 | }.padding(.leading, 20) 62 | .padding(.trailing, 20) 63 | } 64 | } 65 | 66 | struct PanelHeaderView: View { 67 | let title: String 68 | 69 | var body: some View { 70 | HStack { 71 | Text(title) 72 | .scaledPanelFont(size: 26) 73 | .padding(.top, 20) 74 | .padding(.leading, 10) 75 | Spacer() 76 | 77 | }.padding(.bottom, 2) 78 | } 79 | } 80 | 81 | extension View { 82 | 83 | func scaledPanelFont(size: CGFloat = panelViewFontSize) -> some View { 84 | return self.modifier(ScaledFont(name: panelViewFontName, size: size)) 85 | } 86 | } 87 | 88 | //struct ForecastPanelView_Previews: PreviewProvider { 89 | // static var previews: some View { 90 | // ZStack { 91 | // Color.blue 92 | // ForecastPanelView(dailyWeather: []) 93 | // } 94 | // } 95 | //} 96 | -------------------------------------------------------------------------------- /WeatherwithWidget/Views/ScrollViewWithOffset.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollViewWithOffset.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/21/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | // from Zac White: https://zacwhite.com/2019/scrollview-content-offsets-swiftui/ 10 | 11 | 12 | 13 | import SwiftUI 14 | 15 | public struct OffsetScrollView: View where Content : View { 16 | 17 | /// The content of the scroll view. 18 | public var content: Content 19 | 20 | /// The scrollable axes. 21 | /// 22 | /// The default is `.vertical`. 23 | public var axes: Axis.Set 24 | 25 | /// If true, the scroll view may indicate the scrollable component of 26 | /// the content offset, in a way suitable for the platform. 27 | /// 28 | /// The default is `true`. 29 | public var showsIndicators: Bool 30 | 31 | /// The initial offset in the global frame, used for calculating the relative offset 32 | @State private var initialOffset: CGPoint? = nil 33 | 34 | /// The offset of the scrollview updated as the scroll view scrolls 35 | @Binding public var offset: CGPoint 36 | 37 | public init(_ axes: Axis.Set = .vertical, showsIndicators: Bool = true, offset: Binding = .constant(.zero), @ViewBuilder content: () -> Content) { 38 | self.axes = axes 39 | self.showsIndicators = false //always want this 40 | self._offset = offset 41 | self.content = content() 42 | } 43 | 44 | /// Declares the content and behavior of this view. 45 | public var body: some View { 46 | ScrollView(axes, showsIndicators: showsIndicators) { 47 | VStack(alignment: .leading, spacing: 0) { 48 | GeometryReader { geometry in 49 | Run { 50 | let globalOrigin = geometry.frame(in: .global).origin 51 | self.initialOffset = self.initialOffset ?? globalOrigin 52 | let initialOffset = (self.initialOffset ?? .zero) 53 | let offset = CGPoint(x: globalOrigin.x - initialOffset.x, y: globalOrigin.y - initialOffset.y) 54 | self.offset = offset 55 | } 56 | }.frame(width: 0, height: 0) 57 | 58 | content 59 | } 60 | } 61 | } 62 | } 63 | 64 | 65 | struct Run: View { 66 | let block: () -> Void 67 | 68 | var body: some View { 69 | DispatchQueue.main.async(execute: block) 70 | return AnyView(EmptyView()) 71 | } 72 | } 73 | 74 | 75 | -------------------------------------------------------------------------------- /WeatherwithWidget/Views/WindAndPressurePanelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WindAndPressureView.swift 3 | // WeatherSwiftUI 4 | // 5 | // Created by Gary on 2/18/20. 6 | // Copyright © 2020 Gary Hanson. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | 12 | struct WindAndPressurePanelView: View { 13 | @ObservedObject var viewModel: ViewModel 14 | 15 | var bladeAnimation: Animation? { 16 | // duration / repeatForever is seriously broken. this animation will never change even though it gets called with new model values 17 | // so, this should work because it gets called when model changes - but it doesn't work 18 | // and setting the duration to a default number instead of returning nil animation was tried first. didn't work either. 19 | // also tried making this a function call instead of a var. 20 | // I'm not the first to find this bug. 21 | // use a default speed to have blade rotate. looks more interesting than not rotating. 22 | guard let duration = viewModel.bladeDuration else { 23 | return nil 24 | } 25 | 26 | return Animation.linear(duration: duration).repeatForever(autoreverses: false) 27 | } 28 | 29 | // Another problem that I believe is related to the above is that when the device is rotated the blades just kind of start floating around 30 | // they act as if their yoffset is fairly random. I removed the yoffset modifier and tried to position differenty and this didn't make any difference 31 | // Everything else about layout on device rotation seems to be fine. Don't allow rotation until this gets fixed. 32 | 33 | 34 | @State private var rotateLargeFan = true 35 | @State private var rotateSmallFan = true 36 | 37 | var body: some View { 38 | VStack(alignment: .leading) { 39 | Group { 40 | PanelHeaderView(title: "Wind and Pressure") 41 | Spacer() 42 | HStack(alignment: .bottom) { 43 | ZStack { 44 | // Large fan 45 | Image("stand_l") 46 | Image("blade_big") 47 | .rotationEffect(.degrees(rotateLargeFan ? 360*4 : 0)) 48 | .offset(y: -35) 49 | .animation(self.bladeAnimation) 50 | .onAppear() { 51 | self.rotateLargeFan.toggle() 52 | } 53 | } 54 | ZStack { 55 | // Small fan 56 | Image("stand_s") 57 | Image("blade_small") 58 | .rotationEffect(.degrees(rotateSmallFan ? 360*4 : 0)) 59 | .offset(y: -25) 60 | .animation(self.bladeAnimation) 61 | //.animation(self.bladeAnimation, value: viewModel.bladeDuration != nil) // using the value param doesn't work at all 62 | .onAppear() { 63 | self.rotateSmallFan.toggle() 64 | } 65 | } 66 | .offset(x: -20) 67 | VStack(alignment: .leading) { 68 | Text("Wind") 69 | .scaledPanelFont() 70 | Text(viewModel.windSpeedInfo) 71 | .scaledPanelFont() 72 | }.offset(y: -60) 73 | VStack(alignment: .center) { 74 | Text("Barometer") 75 | .scaledPanelFont() 76 | Text(viewModel.pressure) 77 | .scaledPanelFont() 78 | }.frame(width: 100) 79 | .padding(.leading, 30) 80 | }.padding(.top, 20) 81 | } 82 | }.foregroundColor(.white) 83 | .background(panelBackgroundColor) 84 | } 85 | } 86 | 87 | 88 | struct WindAndPressureView_Previews: PreviewProvider { 89 | static var previews: some View { 90 | ZStack { 91 | Color.blue 92 | WindAndPressurePanelView(viewModel: ViewModel()) 93 | } 94 | .frame(width: 400, height: 120) 95 | } 96 | } 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /WeatherwithWidget/WeatherwithWidgetApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeatherwithWidgetApp.swift 3 | // WeatherwithWidget 4 | // 5 | // Created by Gary on 6/28/20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct WeatherwithWidgetApp: App { 12 | @ObservedObject var viewModel: ViewModel = ViewModel() 13 | 14 | var body: some Scene { 15 | WindowGroup { 16 | ContentView(viewModel: viewModel) 17 | } 18 | } 19 | } 20 | --------------------------------------------------------------------------------