Skip to content

Commit

Permalink
110
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis committed Jul 27, 2020
1 parent a55ee66 commit a4f38a3
Show file tree
Hide file tree
Showing 15 changed files with 946 additions and 0 deletions.

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import Combine
import SwiftUI

class AppViewModel: ObservableObject {
@Published var isConnected = true
@Published var weatherResults: [WeatherResponse.ConsolidatedWeather] = []

var weatherRequestCancellable: AnyCancellable?

init(
isConnected: Bool = true,
weatherClient: WeatherClientProtocol = WeatherClient()
) {
self.isConnected = isConnected

self.weatherRequestCancellable = weatherClient
.weather()
.sink(
receiveCompletion: { _ in },
receiveValue: { [weak self] response in
self?.weatherResults = response.consolidatedWeather
})
}
}

struct ContentView: View {
@ObservedObject var viewModel: AppViewModel

var body: some View {
NavigationView {
ZStack(alignment: .bottom) {
ZStack(alignment: .bottomTrailing) {
List {
ForEach(self.viewModel.weatherResults, id: \.id) { weather in
VStack(alignment: .leading) {
Text(dayOfWeekFormatter.string(from: weather.applicableDate).capitalized)
.font(.title)

Text("Current temp: \(weather.theTemp, specifier: "%.1f")°C")
Text("Max temp: \(weather.maxTemp, specifier: "%.1f")°C")
Text("Min temp: \(weather.minTemp, specifier: "%.1f")°C")
}
}
}

Button(
action: { }
) {
Image(systemName: "location.fill")
.foregroundColor(.white)
.frame(width: 60, height: 60)
}
.background(Color.black)
.clipShape(Circle())
.padding()
}

if !self.viewModel.isConnected {
HStack {
Image(systemName: "exclamationmark.octagon.fill")

Text("Not connected to internet")
}
.foregroundColor(.white)
.padding()
.background(Color.red)
}
}
.navigationBarTitle("Weather")
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(
viewModel: AppViewModel(
// weatherClient: MockWeatherClient()
weatherClient: MockWeatherClient(
_weather: {
Just(WeatherResponse(consolidatedWeather: []))
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
},
_searchLocations: { _ in
Just([])
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
}
)
)
)
}
}


let dayOfWeekFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "EEEE"
return formatter
}()


import CoreLocation
import Foundation

struct Location {}

protocol WeatherClientProtocol {
func weather() -> AnyPublisher<WeatherResponse, Error>
func searchLocations(coordinate: CLLocationCoordinate2D) -> AnyPublisher<[Location], Error>
}

struct WeatherClient: WeatherClientProtocol {
func weather() -> AnyPublisher<WeatherResponse, Error> {
URLSession.shared.dataTaskPublisher(for: URL(string: "https://www.metaweather.com/api/location/2459115")!)
.map { data, _ in data }
.decode(type: WeatherResponse.self, decoder: weatherJsonDecoder)
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}

//struct MockWeatherClient: WeatherClientProtocol {
// func weather() -> AnyPublisher<WeatherResponse, Error> {
// Just(
// WeatherResponse(
// consolidatedWeather: [
// .init(applicableDate: Date(), id: 1, maxTemp: 30, minTemp: 10, theTemp: 20),
// .init(applicableDate: Date().addingTimeInterval(86400), id: 2, maxTemp: -10, minTemp: -30, theTemp: -20),
// ]
// )
// )
// .setFailureType(to: Error.self)
// .delay(for: 2, scheduler: DispatchQueue.main)
//// Fail(error: NSError(domain: "", code: 1, userInfo: nil))
// .eraseToAnyPublisher()
// }
//}

struct MockWeatherClient: WeatherClientProtocol {
var _weather: () -> AnyPublisher<WeatherResponse, Error>
var _searchLocations: (CLLocationCoordinate2D) -> AnyPublisher<[Location], Error>

func weather() -> AnyPublisher<WeatherResponse, Error> {
self._weather()
}

func searchLocations(coordinate: CLLocationCoordinate2D) -> AnyPublisher<[Location], Error> {
self._searchLocations(coordinate)
}
}

struct WeatherResponse: Decodable, Equatable {
var consolidatedWeather: [ConsolidatedWeather]

struct ConsolidatedWeather: Decodable, Equatable {
var applicableDate: Date
var id: Int
var maxTemp: Double
var minTemp: Double
var theTemp: Double
}
}

private let weatherJsonDecoder: JSONDecoder = {
let jsonDecoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
jsonDecoder.dateDecodingStrategy = .formatted(formatter)
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
return jsonDecoder
}()
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading

0 comments on commit a4f38a3

Please sign in to comment.