diff --git a/Podfile b/App/Podfile similarity index 81% rename from Podfile rename to App/Podfile index 5d0e718..17240d3 100644 --- a/Podfile +++ b/App/Podfile @@ -1,9 +1,8 @@ -platform :ios, '10.0' +platform :ios, '12.0' use_frameworks! target 'SwiftWeather' do pod 'SwiftyJSON' - pod 'FacebookShare' end abstract_target 'Tests' do diff --git a/App/Podfile.lock b/App/Podfile.lock new file mode 100644 index 0000000..2287edc --- /dev/null +++ b/App/Podfile.lock @@ -0,0 +1,24 @@ +PODS: + - Nimble (7.3.1) + - Quick (1.3.2) + - SwiftyJSON (4.2.0) + +DEPENDENCIES: + - Nimble + - Quick + - SwiftyJSON + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - Nimble + - Quick + - SwiftyJSON + +SPEC CHECKSUMS: + Nimble: 04f732da099ea4d153122aec8c2a88fd0c7219ae + Quick: 2623cb30d7a7f41ca62f684f679586558f483d46 + SwiftyJSON: c4bcba26dd9ec7a027fc8eade48e2c911f229e96 + +PODFILE CHECKSUM: c56e55a868d15e020091a8d96e30e28b9f2b5aed + +COCOAPODS: 1.5.3 diff --git a/App/SwiftWeather.xcodeproj/project.pbxproj b/App/SwiftWeather.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f3f7ea6 --- /dev/null +++ b/App/SwiftWeather.xcodeproj/project.pbxproj @@ -0,0 +1,923 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 3BB101FC131B6AF3B272F6C5 /* Pods_SwiftWeather.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9252FD878BBFFABFDB82308F /* Pods_SwiftWeather.framework */; }; + 60243488A06B0574DE0A54BC /* Pods_Tests_SwiftWeatherTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CE5BA965EA8FD85003315EF /* Pods_Tests_SwiftWeatherTests.framework */; }; + AE56AF51217AE0E300ABBA0C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF50217AE0E300ABBA0C /* AppDelegate.swift */; }; + AE56AF53217AE0E400ABBA0C /* WeatherServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF52217AE0E400ABBA0C /* WeatherServiceProtocol.swift */; }; + AE56AF56217AE0E400ABBA0C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AE56AF54217AE0E400ABBA0C /* Main.storyboard */; }; + AE56AF58217AE0E500ABBA0C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AE56AF57217AE0E500ABBA0C /* Assets.xcassets */; }; + AE56AF5B217AE0E500ABBA0C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AE56AF59217AE0E500ABBA0C /* LaunchScreen.storyboard */; }; + AE56AF66217AE0E500ABBA0C /* SwiftWeatherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF65217AE0E500ABBA0C /* SwiftWeatherTests.swift */; }; + AE56AF71217AE0E500ABBA0C /* SwiftWeatherUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF70217AE0E500ABBA0C /* SwiftWeatherUITests.swift */; }; + AE56AF80217D23DB00ABBA0C /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF7F217D23DB00ABBA0C /* LocationService.swift */; }; + AE56AF82217D240F00ABBA0C /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF81217D240F00ABBA0C /* AppError.swift */; }; + AE56AF84217D24AB00ABBA0C /* LiveData.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF83217D24AB00ABBA0C /* LiveData.swift */; }; + AE56AF86217D25CD00ABBA0C /* ForecastDateTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF85217D25CD00ABBA0C /* ForecastDateTime.swift */; }; + AE56AF8E217D294000ABBA0C /* Weather.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF8D217D294000ABBA0C /* Weather.swift */; }; + AE56AF90217D295B00ABBA0C /* TemperatureFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF8F217D295B00ABBA0C /* TemperatureFormatter.swift */; }; + AE56AF92217D2A0A00ABBA0C /* TemperatureConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF91217D2A0A00ABBA0C /* TemperatureConverter.swift */; }; + AE56AF94217D2A3300ABBA0C /* Forecast.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE56AF93217D2A3300ABBA0C /* Forecast.swift */; }; + AE8A58F221841928000ACEBF /* WeatherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE8A58F121841928000ACEBF /* WeatherViewController.swift */; }; + AEB09CD8217E769B00309638 /* WeatherViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB09CD7217E769B00309638 /* WeatherViewModel.swift */; }; + AEB09CDA217E791300309638 /* ForecastViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB09CD9217E791300309638 /* ForecastViewModel.swift */; }; + AEB09CDD217E7A1300309638 /* OpenWeatherMapService.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB09CDC217E7A1300309638 /* OpenWeatherMapService.swift */; }; + AEB09CDF217E7BA800309638 /* WeatherIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB09CDE217E7BA800309638 /* WeatherIcon.swift */; }; + AEB09CE1217EF54400309638 /* ForecastView.xib in Resources */ = {isa = PBXBuildFile; fileRef = AEB09CE0217EF54400309638 /* ForecastView.xib */; }; + AEB09CE3217FCBA600309638 /* ForecastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB09CE2217FCBA600309638 /* ForecastView.swift */; }; + AEB09CE621811D4000309638 /* UIView+loadFromNib.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB09CE521811D4000309638 /* UIView+loadFromNib.swift */; }; + AEB09CEB2181924E00309638 /* weathericons-regular-webfont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AEB09CEA2181924E00309638 /* weathericons-regular-webfont.ttf */; }; + F4D9DB8C4B735544BE23CF3A /* Pods_Tests_SwiftWeatherUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD425E299623844E930423BA /* Pods_Tests_SwiftWeatherUITests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + AE56AF62217AE0E500ABBA0C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = AE56AF45217AE0E300ABBA0C /* Project object */; + proxyType = 1; + remoteGlobalIDString = AE56AF4C217AE0E300ABBA0C; + remoteInfo = SwiftWeather; + }; + AE56AF6D217AE0E500ABBA0C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = AE56AF45217AE0E300ABBA0C /* Project object */; + proxyType = 1; + remoteGlobalIDString = AE56AF4C217AE0E300ABBA0C; + remoteInfo = SwiftWeather; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0EC30DA6FB4175B854B538D0 /* Pods-Tests-SwiftWeatherUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-SwiftWeatherUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests-SwiftWeatherUITests/Pods-Tests-SwiftWeatherUITests.release.xcconfig"; sourceTree = ""; }; + 2CE5BA965EA8FD85003315EF /* Pods_Tests_SwiftWeatherTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tests_SwiftWeatherTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7619A4E169953644AD46894A /* Pods-Tests-SwiftWeatherTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-SwiftWeatherTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests-SwiftWeatherTests/Pods-Tests-SwiftWeatherTests.debug.xcconfig"; sourceTree = ""; }; + 8E22636DE07058E8657E54B5 /* Pods-SwiftWeather.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftWeather.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftWeather/Pods-SwiftWeather.release.xcconfig"; sourceTree = ""; }; + 9252FD878BBFFABFDB82308F /* Pods_SwiftWeather.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftWeather.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9E2C45F574B2F67C35F1A386 /* Pods-SwiftWeather.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftWeather.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftWeather/Pods-SwiftWeather.debug.xcconfig"; sourceTree = ""; }; + AE56AF4D217AE0E300ABBA0C /* SwiftWeather.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftWeather.app; sourceTree = BUILT_PRODUCTS_DIR; }; + AE56AF50217AE0E300ABBA0C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + AE56AF52217AE0E400ABBA0C /* WeatherServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherServiceProtocol.swift; sourceTree = ""; }; + AE56AF55217AE0E400ABBA0C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + AE56AF57217AE0E500ABBA0C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + AE56AF5A217AE0E500ABBA0C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + AE56AF5C217AE0E500ABBA0C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AE56AF61217AE0E500ABBA0C /* SwiftWeatherTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftWeatherTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + AE56AF65217AE0E500ABBA0C /* SwiftWeatherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftWeatherTests.swift; sourceTree = ""; }; + AE56AF67217AE0E500ABBA0C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AE56AF6C217AE0E500ABBA0C /* SwiftWeatherUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftWeatherUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + AE56AF70217AE0E500ABBA0C /* SwiftWeatherUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftWeatherUITests.swift; sourceTree = ""; }; + AE56AF72217AE0E500ABBA0C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AE56AF7F217D23DB00ABBA0C /* LocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = ""; }; + AE56AF81217D240F00ABBA0C /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = ""; }; + AE56AF83217D24AB00ABBA0C /* LiveData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveData.swift; sourceTree = ""; }; + AE56AF85217D25CD00ABBA0C /* ForecastDateTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDateTime.swift; sourceTree = ""; }; + AE56AF8D217D294000ABBA0C /* Weather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weather.swift; sourceTree = ""; }; + AE56AF8F217D295B00ABBA0C /* TemperatureFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureFormatter.swift; sourceTree = ""; }; + AE56AF91217D2A0A00ABBA0C /* TemperatureConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureConverter.swift; sourceTree = ""; }; + AE56AF93217D2A3300ABBA0C /* Forecast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Forecast.swift; sourceTree = ""; }; + AE8A58F121841928000ACEBF /* WeatherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewController.swift; sourceTree = ""; }; + AEB09CD7217E769B00309638 /* WeatherViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewModel.swift; sourceTree = ""; }; + AEB09CD9217E791300309638 /* ForecastViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastViewModel.swift; sourceTree = ""; }; + AEB09CDC217E7A1300309638 /* OpenWeatherMapService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenWeatherMapService.swift; sourceTree = ""; }; + AEB09CDE217E7BA800309638 /* WeatherIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherIcon.swift; sourceTree = ""; }; + AEB09CE0217EF54400309638 /* ForecastView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ForecastView.xib; sourceTree = ""; }; + AEB09CE2217FCBA600309638 /* ForecastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastView.swift; sourceTree = ""; }; + AEB09CE521811D4000309638 /* UIView+loadFromNib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+loadFromNib.swift"; sourceTree = ""; }; + AEB09CEA2181924E00309638 /* weathericons-regular-webfont.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "weathericons-regular-webfont.ttf"; sourceTree = ""; }; + B5783E8E7DCF4B65EE2E87DD /* Pods-Tests-SwiftWeatherUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-SwiftWeatherUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests-SwiftWeatherUITests/Pods-Tests-SwiftWeatherUITests.debug.xcconfig"; sourceTree = ""; }; + BE4E545FA2D472B4933977AB /* Pods-Tests-SwiftWeatherTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-SwiftWeatherTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests-SwiftWeatherTests/Pods-Tests-SwiftWeatherTests.release.xcconfig"; sourceTree = ""; }; + DD425E299623844E930423BA /* Pods_Tests_SwiftWeatherUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tests_SwiftWeatherUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + AE56AF4A217AE0E300ABBA0C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BB101FC131B6AF3B272F6C5 /* Pods_SwiftWeather.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AE56AF5E217AE0E500ABBA0C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 60243488A06B0574DE0A54BC /* Pods_Tests_SwiftWeatherTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AE56AF69217AE0E500ABBA0C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F4D9DB8C4B735544BE23CF3A /* Pods_Tests_SwiftWeatherUITests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 27A98E83829251CFB28F58A5 /* Pods */ = { + isa = PBXGroup; + children = ( + 9E2C45F574B2F67C35F1A386 /* Pods-SwiftWeather.debug.xcconfig */, + 8E22636DE07058E8657E54B5 /* Pods-SwiftWeather.release.xcconfig */, + 7619A4E169953644AD46894A /* Pods-Tests-SwiftWeatherTests.debug.xcconfig */, + BE4E545FA2D472B4933977AB /* Pods-Tests-SwiftWeatherTests.release.xcconfig */, + B5783E8E7DCF4B65EE2E87DD /* Pods-Tests-SwiftWeatherUITests.debug.xcconfig */, + 0EC30DA6FB4175B854B538D0 /* Pods-Tests-SwiftWeatherUITests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 8305301B8FA2229D496A7155 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9252FD878BBFFABFDB82308F /* Pods_SwiftWeather.framework */, + 2CE5BA965EA8FD85003315EF /* Pods_Tests_SwiftWeatherTests.framework */, + DD425E299623844E930423BA /* Pods_Tests_SwiftWeatherUITests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + AE56AF44217AE0E300ABBA0C = { + isa = PBXGroup; + children = ( + AE56AF4F217AE0E300ABBA0C /* SwiftWeather */, + AE56AF64217AE0E500ABBA0C /* SwiftWeatherTests */, + AE56AF6F217AE0E500ABBA0C /* SwiftWeatherUITests */, + AE56AF4E217AE0E300ABBA0C /* Products */, + 27A98E83829251CFB28F58A5 /* Pods */, + 8305301B8FA2229D496A7155 /* Frameworks */, + ); + sourceTree = ""; + }; + AE56AF4E217AE0E300ABBA0C /* Products */ = { + isa = PBXGroup; + children = ( + AE56AF4D217AE0E300ABBA0C /* SwiftWeather.app */, + AE56AF61217AE0E500ABBA0C /* SwiftWeatherTests.xctest */, + AE56AF6C217AE0E500ABBA0C /* SwiftWeatherUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + AE56AF4F217AE0E300ABBA0C /* SwiftWeather */ = { + isa = PBXGroup; + children = ( + AE56AF7E217AED3900ABBA0C /* Common */, + AEB09CE421811D1300309638 /* Utils */, + AE56AF8C217D291500ABBA0C /* Models */, + AEB09CDB217E797500309638 /* Services */, + AE56AF8B217D290C00ABBA0C /* ViewModels */, + AE56AF8A217D290600ABBA0C /* Views */, + AEB09CE92181924E00309638 /* Fonts */, + AE56AF50217AE0E300ABBA0C /* AppDelegate.swift */, + AE56AF57217AE0E500ABBA0C /* Assets.xcassets */, + AE56AF5C217AE0E500ABBA0C /* Info.plist */, + ); + path = SwiftWeather; + sourceTree = ""; + }; + AE56AF64217AE0E500ABBA0C /* SwiftWeatherTests */ = { + isa = PBXGroup; + children = ( + AE56AF65217AE0E500ABBA0C /* SwiftWeatherTests.swift */, + AE56AF67217AE0E500ABBA0C /* Info.plist */, + ); + path = SwiftWeatherTests; + sourceTree = ""; + }; + AE56AF6F217AE0E500ABBA0C /* SwiftWeatherUITests */ = { + isa = PBXGroup; + children = ( + AE56AF70217AE0E500ABBA0C /* SwiftWeatherUITests.swift */, + AE56AF72217AE0E500ABBA0C /* Info.plist */, + ); + path = SwiftWeatherUITests; + sourceTree = ""; + }; + AE56AF7E217AED3900ABBA0C /* Common */ = { + isa = PBXGroup; + children = ( + AE56AF81217D240F00ABBA0C /* AppError.swift */, + AE56AF83217D24AB00ABBA0C /* LiveData.swift */, + AE56AF85217D25CD00ABBA0C /* ForecastDateTime.swift */, + ); + path = Common; + sourceTree = ""; + }; + AE56AF8A217D290600ABBA0C /* Views */ = { + isa = PBXGroup; + children = ( + AE56AF59217AE0E500ABBA0C /* LaunchScreen.storyboard */, + AE56AF54217AE0E400ABBA0C /* Main.storyboard */, + AEB09CE0217EF54400309638 /* ForecastView.xib */, + AEB09CE2217FCBA600309638 /* ForecastView.swift */, + AE8A58F121841928000ACEBF /* WeatherViewController.swift */, + ); + path = Views; + sourceTree = ""; + }; + AE56AF8B217D290C00ABBA0C /* ViewModels */ = { + isa = PBXGroup; + children = ( + AEB09CD7217E769B00309638 /* WeatherViewModel.swift */, + AEB09CD9217E791300309638 /* ForecastViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + AE56AF8C217D291500ABBA0C /* Models */ = { + isa = PBXGroup; + children = ( + AE56AF8D217D294000ABBA0C /* Weather.swift */, + AE56AF8F217D295B00ABBA0C /* TemperatureFormatter.swift */, + AE56AF93217D2A3300ABBA0C /* Forecast.swift */, + AEB09CDE217E7BA800309638 /* WeatherIcon.swift */, + ); + path = Models; + sourceTree = ""; + }; + AEB09CDB217E797500309638 /* Services */ = { + isa = PBXGroup; + children = ( + AE56AF7F217D23DB00ABBA0C /* LocationService.swift */, + AEB09CDC217E7A1300309638 /* OpenWeatherMapService.swift */, + AE56AF52217AE0E400ABBA0C /* WeatherServiceProtocol.swift */, + ); + path = Services; + sourceTree = ""; + }; + AEB09CE421811D1300309638 /* Utils */ = { + isa = PBXGroup; + children = ( + AE56AF91217D2A0A00ABBA0C /* TemperatureConverter.swift */, + AEB09CE521811D4000309638 /* UIView+loadFromNib.swift */, + ); + path = Utils; + sourceTree = ""; + }; + AEB09CE92181924E00309638 /* Fonts */ = { + isa = PBXGroup; + children = ( + AEB09CEA2181924E00309638 /* weathericons-regular-webfont.ttf */, + ); + path = Fonts; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + AE56AF4C217AE0E300ABBA0C /* SwiftWeather */ = { + isa = PBXNativeTarget; + buildConfigurationList = AE56AF75217AE0E500ABBA0C /* Build configuration list for PBXNativeTarget "SwiftWeather" */; + buildPhases = ( + AD00B0AF5E75DF4DD3071E60 /* [CP] Check Pods Manifest.lock */, + AE56AF49217AE0E300ABBA0C /* Sources */, + AE56AF4A217AE0E300ABBA0C /* Frameworks */, + AE56AF4B217AE0E300ABBA0C /* Resources */, + AEA2FDE821865E4B007BCC6B /* Copy token */, + 9B696991AFEB67C33D75DF4C /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftWeather; + productName = SwiftWeather; + productReference = AE56AF4D217AE0E300ABBA0C /* SwiftWeather.app */; + productType = "com.apple.product-type.application"; + }; + AE56AF60217AE0E500ABBA0C /* SwiftWeatherTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = AE56AF78217AE0E500ABBA0C /* Build configuration list for PBXNativeTarget "SwiftWeatherTests" */; + buildPhases = ( + 59779E0A9E27D1D88796E5CC /* [CP] Check Pods Manifest.lock */, + AE56AF5D217AE0E500ABBA0C /* Sources */, + AE56AF5E217AE0E500ABBA0C /* Frameworks */, + AE56AF5F217AE0E500ABBA0C /* Resources */, + F4261CD2EFCB55AFCC6F4D12 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + AE56AF63217AE0E500ABBA0C /* PBXTargetDependency */, + ); + name = SwiftWeatherTests; + productName = SwiftWeatherTests; + productReference = AE56AF61217AE0E500ABBA0C /* SwiftWeatherTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + AE56AF6B217AE0E500ABBA0C /* SwiftWeatherUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = AE56AF7B217AE0E500ABBA0C /* Build configuration list for PBXNativeTarget "SwiftWeatherUITests" */; + buildPhases = ( + BEB787EF504D14F4F4CF475B /* [CP] Check Pods Manifest.lock */, + AE56AF68217AE0E500ABBA0C /* Sources */, + AE56AF69217AE0E500ABBA0C /* Frameworks */, + AE56AF6A217AE0E500ABBA0C /* Resources */, + A5F6C58BA77445FA2AF63665 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + AE56AF6E217AE0E500ABBA0C /* PBXTargetDependency */, + ); + name = SwiftWeatherUITests; + productName = SwiftWeatherUITests; + productReference = AE56AF6C217AE0E500ABBA0C /* SwiftWeatherUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + AE56AF45217AE0E300ABBA0C /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1000; + LastUpgradeCheck = 1000; + ORGANIZATIONNAME = "Jake Lin"; + TargetAttributes = { + AE56AF4C217AE0E300ABBA0C = { + CreatedOnToolsVersion = 10.0; + }; + AE56AF60217AE0E500ABBA0C = { + CreatedOnToolsVersion = 10.0; + TestTargetID = AE56AF4C217AE0E300ABBA0C; + }; + AE56AF6B217AE0E500ABBA0C = { + CreatedOnToolsVersion = 10.0; + TestTargetID = AE56AF4C217AE0E300ABBA0C; + }; + }; + }; + buildConfigurationList = AE56AF48217AE0E300ABBA0C /* Build configuration list for PBXProject "SwiftWeather" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = AE56AF44217AE0E300ABBA0C; + productRefGroup = AE56AF4E217AE0E300ABBA0C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + AE56AF4C217AE0E300ABBA0C /* SwiftWeather */, + AE56AF60217AE0E500ABBA0C /* SwiftWeatherTests */, + AE56AF6B217AE0E500ABBA0C /* SwiftWeatherUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + AE56AF4B217AE0E300ABBA0C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AE56AF5B217AE0E500ABBA0C /* LaunchScreen.storyboard in Resources */, + AE56AF58217AE0E500ABBA0C /* Assets.xcassets in Resources */, + AE56AF56217AE0E400ABBA0C /* Main.storyboard in Resources */, + AEB09CEB2181924E00309638 /* weathericons-regular-webfont.ttf in Resources */, + AEB09CE1217EF54400309638 /* ForecastView.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AE56AF5F217AE0E500ABBA0C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AE56AF6A217AE0E500ABBA0C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 59779E0A9E27D1D88796E5CC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Tests-SwiftWeatherTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9B696991AFEB67C33D75DF4C /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-SwiftWeather/Pods-SwiftWeather-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftWeather/Pods-SwiftWeather-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A5F6C58BA77445FA2AF63665 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Tests-SwiftWeatherUITests/Pods-Tests-SwiftWeatherUITests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", + "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Tests-SwiftWeatherUITests/Pods-Tests-SwiftWeatherUITests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + AD00B0AF5E75DF4DD3071E60 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SwiftWeather-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + AEA2FDE821865E4B007BCC6B /* Copy token */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Copy token"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "$SRCROOT/SwiftWeather/Scripts/copy-token.sh\n"; + }; + BEB787EF504D14F4F4CF475B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Tests-SwiftWeatherUITests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + F4261CD2EFCB55AFCC6F4D12 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Tests-SwiftWeatherTests/Pods-Tests-SwiftWeatherTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", + "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Tests-SwiftWeatherTests/Pods-Tests-SwiftWeatherTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + AE56AF49217AE0E300ABBA0C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AE56AF82217D240F00ABBA0C /* AppError.swift in Sources */, + AE8A58F221841928000ACEBF /* WeatherViewController.swift in Sources */, + AEB09CDF217E7BA800309638 /* WeatherIcon.swift in Sources */, + AE56AF86217D25CD00ABBA0C /* ForecastDateTime.swift in Sources */, + AE56AF90217D295B00ABBA0C /* TemperatureFormatter.swift in Sources */, + AEB09CE621811D4000309638 /* UIView+loadFromNib.swift in Sources */, + AEB09CD8217E769B00309638 /* WeatherViewModel.swift in Sources */, + AE56AF92217D2A0A00ABBA0C /* TemperatureConverter.swift in Sources */, + AE56AF94217D2A3300ABBA0C /* Forecast.swift in Sources */, + AE56AF8E217D294000ABBA0C /* Weather.swift in Sources */, + AEB09CDD217E7A1300309638 /* OpenWeatherMapService.swift in Sources */, + AE56AF84217D24AB00ABBA0C /* LiveData.swift in Sources */, + AE56AF53217AE0E400ABBA0C /* WeatherServiceProtocol.swift in Sources */, + AEB09CE3217FCBA600309638 /* ForecastView.swift in Sources */, + AEB09CDA217E791300309638 /* ForecastViewModel.swift in Sources */, + AE56AF80217D23DB00ABBA0C /* LocationService.swift in Sources */, + AE56AF51217AE0E300ABBA0C /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AE56AF5D217AE0E500ABBA0C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AE56AF66217AE0E500ABBA0C /* SwiftWeatherTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AE56AF68217AE0E500ABBA0C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AE56AF71217AE0E500ABBA0C /* SwiftWeatherUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + AE56AF63217AE0E500ABBA0C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = AE56AF4C217AE0E300ABBA0C /* SwiftWeather */; + targetProxy = AE56AF62217AE0E500ABBA0C /* PBXContainerItemProxy */; + }; + AE56AF6E217AE0E500ABBA0C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = AE56AF4C217AE0E300ABBA0C /* SwiftWeather */; + targetProxy = AE56AF6D217AE0E500ABBA0C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + AE56AF54217AE0E400ABBA0C /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + AE56AF55217AE0E400ABBA0C /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + AE56AF59217AE0E500ABBA0C /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + AE56AF5A217AE0E500ABBA0C /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + AE56AF73217AE0E500ABBA0C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + AE56AF74217AE0E500ABBA0C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + AE56AF76217AE0E500ABBA0C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9E2C45F574B2F67C35F1A386 /* Pods-SwiftWeather.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 6HLFCRTYQU; + INFOPLIST_FILE = SwiftWeather/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = Jake.SwiftWeather; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + AE56AF77217AE0E500ABBA0C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8E22636DE07058E8657E54B5 /* Pods-SwiftWeather.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 6HLFCRTYQU; + INFOPLIST_FILE = SwiftWeather/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = Jake.SwiftWeather; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + AE56AF79217AE0E500ABBA0C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7619A4E169953644AD46894A /* Pods-Tests-SwiftWeatherTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = SwiftWeatherTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = Jake.SwiftWeatherTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftWeather.app/SwiftWeather"; + }; + name = Debug; + }; + AE56AF7A217AE0E500ABBA0C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BE4E545FA2D472B4933977AB /* Pods-Tests-SwiftWeatherTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = SwiftWeatherTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = Jake.SwiftWeatherTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftWeather.app/SwiftWeather"; + }; + name = Release; + }; + AE56AF7C217AE0E500ABBA0C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B5783E8E7DCF4B65EE2E87DD /* Pods-Tests-SwiftWeatherUITests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = SwiftWeatherUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = Jake.SwiftWeatherUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = SwiftWeather; + }; + name = Debug; + }; + AE56AF7D217AE0E500ABBA0C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0EC30DA6FB4175B854B538D0 /* Pods-Tests-SwiftWeatherUITests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = SwiftWeatherUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = Jake.SwiftWeatherUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = SwiftWeather; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + AE56AF48217AE0E300ABBA0C /* Build configuration list for PBXProject "SwiftWeather" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AE56AF73217AE0E500ABBA0C /* Debug */, + AE56AF74217AE0E500ABBA0C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AE56AF75217AE0E500ABBA0C /* Build configuration list for PBXNativeTarget "SwiftWeather" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AE56AF76217AE0E500ABBA0C /* Debug */, + AE56AF77217AE0E500ABBA0C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AE56AF78217AE0E500ABBA0C /* Build configuration list for PBXNativeTarget "SwiftWeatherTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AE56AF79217AE0E500ABBA0C /* Debug */, + AE56AF7A217AE0E500ABBA0C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AE56AF7B217AE0E500ABBA0C /* Build configuration list for PBXNativeTarget "SwiftWeatherUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AE56AF7C217AE0E500ABBA0C /* Debug */, + AE56AF7D217AE0E500ABBA0C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = AE56AF45217AE0E300ABBA0C /* Project object */; +} diff --git a/SwiftWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/App/SwiftWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from SwiftWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to App/SwiftWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/App/SwiftWeather.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/App/SwiftWeather.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/App/SwiftWeather.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftWeather.xcworkspace/contents.xcworkspacedata b/App/SwiftWeather.xcworkspace/contents.xcworkspacedata similarity index 100% rename from SwiftWeather.xcworkspace/contents.xcworkspacedata rename to App/SwiftWeather.xcworkspace/contents.xcworkspacedata diff --git a/App/SwiftWeather.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/App/SwiftWeather.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/App/SwiftWeather.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/App/SwiftWeather/AppDelegate.swift b/App/SwiftWeather/AppDelegate.swift new file mode 100644 index 0000000..3264321 --- /dev/null +++ b/App/SwiftWeather/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// SwiftWeather +// +// Created by Jake Lin on 20/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/SwiftWeather/Assets.xcassets/AppIcon.appiconset/Contents.json b/App/SwiftWeather/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from SwiftWeather/Assets.xcassets/AppIcon.appiconset/Contents.json rename to App/SwiftWeather/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/SwiftWeather/Assets.xcassets/Contents.json b/App/SwiftWeather/Assets.xcassets/Contents.json similarity index 100% rename from SwiftWeather/Assets.xcassets/Contents.json rename to App/SwiftWeather/Assets.xcassets/Contents.json diff --git a/SwiftWeather/Assets.xcassets/background.imageset/Contents.json b/App/SwiftWeather/Assets.xcassets/background.imageset/Contents.json similarity index 100% rename from SwiftWeather/Assets.xcassets/background.imageset/Contents.json rename to App/SwiftWeather/Assets.xcassets/background.imageset/Contents.json diff --git a/SwiftWeather/Assets.xcassets/background.imageset/background.png b/App/SwiftWeather/Assets.xcassets/background.imageset/background.png similarity index 100% rename from SwiftWeather/Assets.xcassets/background.imageset/background.png rename to App/SwiftWeather/Assets.xcassets/background.imageset/background.png diff --git a/App/SwiftWeather/Common/AppError.swift b/App/SwiftWeather/Common/AppError.swift new file mode 100644 index 0000000..b63e88a --- /dev/null +++ b/App/SwiftWeather/Common/AppError.swift @@ -0,0 +1,16 @@ +// +// AppError.swift +// SwiftWeather +// +// Created by Jake Lin on 22/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation + +enum AppError: Error { + case urlError + case networkRequestFailed + case jsonParsingFailed + case unableToFindLocation +} diff --git a/SwiftWeather/ForecastDateTime.swift b/App/SwiftWeather/Common/ForecastDateTime.swift similarity index 78% rename from SwiftWeather/ForecastDateTime.swift rename to App/SwiftWeather/Common/ForecastDateTime.swift index 23e5f1d..f6d83fa 100644 --- a/SwiftWeather/ForecastDateTime.swift +++ b/App/SwiftWeather/Common/ForecastDateTime.swift @@ -1,6 +1,9 @@ // -// Created by Jake Lin on 9/11/15. -// Copyright © 2015 Jake Lin. All rights reserved. +// ForecastDateTime.swift +// SwiftWeather +// +// Created by Jake Lin on 22/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. // import Foundation diff --git a/App/SwiftWeather/Common/LiveData.swift b/App/SwiftWeather/Common/LiveData.swift new file mode 100644 index 0000000..f4c108d --- /dev/null +++ b/App/SwiftWeather/Common/LiveData.swift @@ -0,0 +1,38 @@ +// +// LiveData.swift +// SwiftWeather +// +// Created by Jake Lin on 22/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation + +/// `LiveData` is a data holder class that can be observed. It is a simplified version of Android Architecture component `LiveData` +class LiveData { + typealias Observer = (T) -> Void + var observer: Observer? + private var value: T + + init(_ value: T) { + self.value = value + } + + func observe(_ observer: Observer?) { + self.observer = observer + observer?(value) + } + + func setValue(value: T) { + self.value = value + self.observer?(value) + } + + func postValue(value: T) { + self.value = value + + DispatchQueue.main.async(execute: { [unowned self, value] in + self.observer?(value) + }) + } +} diff --git a/SwiftWeather/fonts/weathericons-regular-webfont.ttf b/App/SwiftWeather/Fonts/weathericons-regular-webfont.ttf similarity index 100% rename from SwiftWeather/fonts/weathericons-regular-webfont.ttf rename to App/SwiftWeather/Fonts/weathericons-regular-webfont.ttf diff --git a/SwiftWeather/Info.plist b/App/SwiftWeather/Info.plist similarity index 63% rename from SwiftWeather/Info.plist rename to App/SwiftWeather/Info.plist index 5b2d434..50b3779 100644 --- a/SwiftWeather/Info.plist +++ b/App/SwiftWeather/Info.plist @@ -2,19 +2,14 @@ - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSLocationWhenInUseUsageDescription - Location is required to retrieve the weather info for your current place. + UIAppFonts weathericons-regular-webfont.ttf CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -26,9 +21,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.0 - CFBundleSignature - ???? + 1.0 CFBundleVersion 1 LSRequiresIPhoneOS @@ -54,25 +47,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - CFBundleURLTypes - - - CFBundleURLSchemes - - fb1725220270856995 - - - - FacebookAppID - 1725220270856995 - FacebookDisplayName - Swift Weather - LSApplicationQueriesSchemes - - fbapi - fb-messenger-share-api - fbauth2 - fbshareextension - diff --git a/App/SwiftWeather/Models/Forecast.swift b/App/SwiftWeather/Models/Forecast.swift new file mode 100644 index 0000000..91903d7 --- /dev/null +++ b/App/SwiftWeather/Models/Forecast.swift @@ -0,0 +1,15 @@ +// +// Forecast.swift +// SwiftWeather +// +// Created by Jake Lin on 22/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation + +struct Forecast { + let time: String + let iconText: String + let temperature: String +} diff --git a/App/SwiftWeather/Models/TemperatureFormatter.swift b/App/SwiftWeather/Models/TemperatureFormatter.swift new file mode 100644 index 0000000..427eca7 --- /dev/null +++ b/App/SwiftWeather/Models/TemperatureFormatter.swift @@ -0,0 +1,22 @@ +// +// Temperature.swift +// SwiftWeather +// +// Created by Jake Lin on 22/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + + +import Foundation + +struct TemperatureFormatter { + static func format(country: String?, openWeatherMapDegrees: Double) -> String { + guard let country = country else { return "" } + + if country == "US" { + return "\(TemperatureConverter.kelvinToFahrenheit(openWeatherMapDegrees))\u{f045}" + } else { + return "\(TemperatureConverter.kelvinToCelsius(openWeatherMapDegrees))\u{f03c}" + } + } +} diff --git a/App/SwiftWeather/Models/Weather.swift b/App/SwiftWeather/Models/Weather.swift new file mode 100644 index 0000000..91dc6c8 --- /dev/null +++ b/App/SwiftWeather/Models/Weather.swift @@ -0,0 +1,16 @@ +// +// File.swift +// SwiftWeather +// +// Created by Jake Lin on 22/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation + +struct Weather { + let location: String + let iconText: String + let temperature: String + let forecasts: [Forecast] +} diff --git a/App/SwiftWeather/Models/WeatherIcon.swift b/App/SwiftWeather/Models/WeatherIcon.swift new file mode 100644 index 0000000..08da862 --- /dev/null +++ b/App/SwiftWeather/Models/WeatherIcon.swift @@ -0,0 +1,294 @@ +// +// WeatherIcon.swift +// SwiftWeather +// +// Created by Jake Lin on 23/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation + +import Foundation + +/* + `WeatherIcon` is used to map Open Weather Map icon string to Weather Icons unicode string. + It is generated by + ``` + var caseString = ''; + var caseAndReturnString = ''; + Array.prototype.forEach.call(document.styleSheets[1].cssRules,function(element){ + if (element.selectorText && element.selectorText.startsWith('.wi-owm')) { + var caseName = element.selectorText.substring(8, + element.selectorText.indexOf('::before')).replace('-', '') + caseString += 'case ' + caseName + '\n'; + caseAndReturnString += 'case .' + caseName + ': return "\\u{' + + element.style['content'].charCodeAt(1).toString(16) + '}"\n' + } + }); + console.log(caseString); + console.log(caseAndReturnString); + ``` + */ +// swiftlint:disable type_body_length +struct WeatherIcon { + let iconText: String + + enum IconType: String, CustomStringConvertible { + case day200 + case day201 + case day202 + case day210 + case day211 + case day212 + case day221 + case day230 + case day231 + case day232 + case day300 + case day301 + case day302 + case day310 + case day311 + case day312 + case day313 + case day314 + case day321 + case day500 + case day501 + case day502 + case day503 + case day504 + case day511 + case day520 + case day521 + case day522 + case day531 + case day600 + case day601 + case day602 + case day611 + case day612 + case day615 + case day616 + case day620 + case day621 + case day622 + case day701 + case day711 + case day721 + case day731 + case day741 + case day761 + case day762 + case day781 + case day800 + case day801 + case day802 + case day803 + case day804 + case day900 + case day902 + case day903 + case day904 + case day906 + case day957 + case night200 + case night201 + case night202 + case night210 + case night211 + case night212 + case night221 + case night230 + case night231 + case night232 + case night300 + case night301 + case night302 + case night310 + case night311 + case night312 + case night313 + case night314 + case night321 + case night500 + case night501 + case night502 + case night503 + case night504 + case night511 + case night520 + case night521 + case night522 + case night531 + case night600 + case night601 + case night602 + case night611 + case night612 + case night615 + case night616 + case night620 + case night621 + case night622 + case night701 + case night711 + case night721 + case night731 + case night741 + case night761 + case night762 + case night781 + case night800 + case night801 + case night802 + case night803 + case night804 + case night900 + case night902 + case night903 + case night904 + case night906 + case night957 + + var description: String { + switch self { + case .day200: return "\u{f010}" + case .day201: return "\u{f010}" + case .day202: return "\u{f010}" + case .day210: return "\u{f005}" + case .day211: return "\u{f005}" + case .day212: return "\u{f005}" + case .day221: return "\u{f005}" + case .day230: return "\u{f010}" + case .day231: return "\u{f010}" + case .day232: return "\u{f010}" + case .day300: return "\u{f00b}" + case .day301: return "\u{f00b}" + case .day302: return "\u{f008}" + case .day310: return "\u{f008}" + case .day311: return "\u{f008}" + case .day312: return "\u{f008}" + case .day313: return "\u{f008}" + case .day314: return "\u{f008}" + case .day321: return "\u{f00b}" + case .day500: return "\u{f00b}" + case .day501: return "\u{f008}" + case .day502: return "\u{f008}" + case .day503: return "\u{f008}" + case .day504: return "\u{f008}" + case .day511: return "\u{f006}" + case .day520: return "\u{f009}" + case .day521: return "\u{f009}" + case .day522: return "\u{f009}" + case .day531: return "\u{f00e}" + case .day600: return "\u{f00a}" + case .day601: return "\u{f0b2}" + case .day602: return "\u{f00a}" + case .day611: return "\u{f006}" + case .day612: return "\u{f006}" + case .day615: return "\u{f006}" + case .day616: return "\u{f006}" + case .day620: return "\u{f006}" + case .day621: return "\u{f00a}" + case .day622: return "\u{f00a}" + case .day701: return "\u{f009}" + case .day711: return "\u{f062}" + case .day721: return "\u{f0b6}" + case .day731: return "\u{f063}" + case .day741: return "\u{f003}" + case .day761: return "\u{f063}" + case .day762: return "\u{f063}" + case .day781: return "\u{f056}" + case .day800: return "\u{f00d}" + case .day801: return "\u{f000}" + case .day802: return "\u{f000}" + case .day803: return "\u{f000}" + case .day804: return "\u{f00c}" + case .day900: return "\u{f056}" + case .day902: return "\u{f073}" + case .day903: return "\u{f076}" + case .day904: return "\u{f072}" + case .day906: return "\u{f004}" + case .day957: return "\u{f050}" + case .night200: return "\u{f02d}" + case .night201: return "\u{f02d}" + case .night202: return "\u{f02d}" + case .night210: return "\u{f025}" + case .night211: return "\u{f025}" + case .night212: return "\u{f025}" + case .night221: return "\u{f025}" + case .night230: return "\u{f02d}" + case .night231: return "\u{f02d}" + case .night232: return "\u{f02d}" + case .night300: return "\u{f02b}" + case .night301: return "\u{f02b}" + case .night302: return "\u{f028}" + case .night310: return "\u{f028}" + case .night311: return "\u{f028}" + case .night312: return "\u{f028}" + case .night313: return "\u{f028}" + case .night314: return "\u{f028}" + case .night321: return "\u{f02b}" + case .night500: return "\u{f02b}" + case .night501: return "\u{f028}" + case .night502: return "\u{f028}" + case .night503: return "\u{f028}" + case .night504: return "\u{f028}" + case .night511: return "\u{f026}" + case .night520: return "\u{f029}" + case .night521: return "\u{f029}" + case .night522: return "\u{f029}" + case .night531: return "\u{f02c}" + case .night600: return "\u{f02a}" + case .night601: return "\u{f0b4}" + case .night602: return "\u{f02a}" + case .night611: return "\u{f026}" + case .night612: return "\u{f026}" + case .night615: return "\u{f026}" + case .night616: return "\u{f026}" + case .night620: return "\u{f026}" + case .night621: return "\u{f02a}" + case .night622: return "\u{f02a}" + case .night701: return "\u{f029}" + case .night711: return "\u{f062}" + case .night721: return "\u{f0b6}" + case .night731: return "\u{f063}" + case .night741: return "\u{f04a}" + case .night761: return "\u{f063}" + case .night762: return "\u{f063}" + case .night781: return "\u{f056}" + case .night800: return "\u{f02e}" + case .night801: return "\u{f022}" + case .night802: return "\u{f022}" + case .night803: return "\u{f022}" + case .night804: return "\u{f086}" + case .night900: return "\u{f056}" + case .night902: return "\u{f073}" + case .night903: return "\u{f076}" + case .night904: return "\u{f072}" + case .night906: return "\u{f024}" + case .night957: return "\u{f050}" + } + } + } + + init(condition: Int, iconString: String) { + var rawValue: String + + // if iconString has 'n', it means night time. + if iconString.range(of: "n") != nil { + rawValue = "night" + String(condition) + } else { + // day time + rawValue = "day" + String(condition) + } + + guard let iconType = IconType(rawValue: rawValue) else { + iconText = "" + return + } + iconText = iconType.description + } +} +// swiftlint:enable type_body_length diff --git a/App/SwiftWeather/Scripts/copy-token.sh b/App/SwiftWeather/Scripts/copy-token.sh new file mode 100755 index 0000000..158dd06 --- /dev/null +++ b/App/SwiftWeather/Scripts/copy-token.sh @@ -0,0 +1,13 @@ +token_file=.access_tokens/openweathermap +token="$(cat $token_file)" +if [ "$token" ]; then +echo $TARGET_BUILD_DIR/$INFOPLIST_PATH +echo $token + plutil -replace OWMAccessToken -string $token $TARGET_BUILD_DIR/$INFOPLIST_PATH +else + echo 'error: Missing OpenWeatherMap access token' + open 'http://openweathermap.org/appid' + echo "error: Get an access token from , then create a new file at $token_file that contains the access token." +exit 1 +fi + diff --git a/App/SwiftWeather/Services/LocationService.swift b/App/SwiftWeather/Services/LocationService.swift new file mode 100644 index 0000000..8123948 --- /dev/null +++ b/App/SwiftWeather/Services/LocationService.swift @@ -0,0 +1,46 @@ +// +// LocationService.swift +// SwiftWeather +// +// Created by Jake Lin on 22/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation +import CoreLocation + +protocol LocationServiceDelegate { + func locationDidUpdate(location: CLLocation) + func locationDidFail(error: AppError) +} + +class LocationService: NSObject { + private let locationManager = CLLocationManager() + var delegate: LocationServiceDelegate? + + override init() { + super.init() + locationManager.delegate = self + locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters + } + + func requestLocation() { + locationManager.requestWhenInUseAuthorization() + locationManager.requestLocation() + } +} + +// MARK: - CLLocationManager Delegate +extension LocationService : CLLocationManagerDelegate { + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let location = locations.first { + print("Current location: \(location)") + delegate?.locationDidUpdate(location: location) + } + } + + func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { + print("Error finding location: \(error.localizedDescription)") + delegate?.locationDidFail(error: .unableToFindLocation) + } +} diff --git a/SwiftWeather/OpenWeatherMapService.swift b/App/SwiftWeather/Services/OpenWeatherMapService.swift similarity index 51% rename from SwiftWeather/OpenWeatherMapService.swift rename to App/SwiftWeather/Services/OpenWeatherMapService.swift index b8de361..72876ab 100644 --- a/SwiftWeather/OpenWeatherMapService.swift +++ b/App/SwiftWeather/Services/OpenWeatherMapService.swift @@ -1,6 +1,9 @@ // -// Created by Jake Lin on 9/2/15. -// Copyright (c) 2015 Jake Lin. All rights reserved. +// OpenWeatherMapService.swift +// SwiftWeather +// +// Created by Jake Lin on 23/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. // import Foundation @@ -8,103 +11,96 @@ import CoreLocation import SwiftyJSON struct OpenWeatherMapService: WeatherServiceProtocol { - fileprivate let urlPath = "http://api.openweathermap.org/data/2.5/forecast" + private let urlPath = "https://api.openweathermap.org/data/2.5/forecast" - fileprivate func getFirstFourForecasts(_ json: JSON) -> [Forecast] { - var forecasts: [Forecast] = [] - - for index in 0...3 { - guard let forecastTempDegrees = json["list"][index]["main"]["temp"].double, - let rawDateTime = json["list"][index]["dt"].double, - let forecastCondition = json["list"][index]["weather"][0]["id"].int, - let forecastIcon = json["list"][index]["weather"][0]["icon"].string else { - break - } - - let country = json["city"]["country"].string - let forecastTemperature = Temperature(country: country!, - openWeatherMapDegrees: forecastTempDegrees) - let forecastTimeString = ForecastDateTime(date: rawDateTime, timeZone: TimeZone.current).shortTime - let weatherIcon = WeatherIcon(condition: forecastCondition, iconString: forecastIcon) - let forcastIconText = weatherIcon.iconText - - let forecast = Forecast(time: forecastTimeString, - iconText: forcastIconText, - temperature: forecastTemperature.degrees) - - forecasts.append(forecast) - } - - return forecasts - } - - func retrieveWeatherInfo(_ location: CLLocation, completionHandler: @escaping WeatherCompletionHandler) { - let sessionConfig = URLSessionConfiguration.default - let session = URLSession(configuration: sessionConfig) - - guard let url = generateRequestURL(location) else { - let error = SWError(errorCode: .urlError) - completionHandler(nil, error) + func requestWeather(location: CLLocation, completionHandler: @escaping WeatherCompletionHandler) { + guard let url = makeRequestURL(location: location) else { + completionHandler(nil, .urlError) return } - let task = session.dataTask(with: url) { (data, response, error) in + let task = URLSession.shared.dataTask(with: url) { (data, response, error) in // Check network error guard error == nil else { - let error = SWError(errorCode: .networkRequestFailed) - completionHandler(nil, error) + completionHandler(nil, .networkRequestFailed) return } // Check JSON serialization error - guard let data = data else { - let error = SWError(errorCode: .jsonSerializationFailed) - completionHandler(nil, error) - return - } - - guard let json = try? JSON(data: data) else { - let error = SWError(errorCode: .jsonParsingFailed) - completionHandler(nil, error) + guard let data = data, let json = try? JSON(data: data) else { + completionHandler(nil, .jsonParsingFailed) return } // Get temperature, location and icon and check parsing error - guard let tempDegrees = json["list"][0]["main"]["temp"].double, + guard let degrees = json["list"][0]["main"]["temp"].double, let country = json["city"]["country"].string, let city = json["city"]["name"].string, let weatherCondition = json["list"][0]["weather"][0]["id"].int, let iconString = json["list"][0]["weather"][0]["icon"].string else { - let error = SWError(errorCode: .jsonParsingFailed) - completionHandler(nil, error) + completionHandler(nil, .jsonParsingFailed) return } - var weatherBuilder = WeatherBuilder() - let temperature = Temperature(country: country, openWeatherMapDegrees:tempDegrees) - weatherBuilder.temperature = temperature.degrees - weatherBuilder.location = city + let temperature = TemperatureFormatter.format(country: country, openWeatherMapDegrees: degrees) let weatherIcon = WeatherIcon(condition: weatherCondition, iconString: iconString) - weatherBuilder.iconText = weatherIcon.iconText + let forecasts = self.getFirstFourForecasts(from: json) + let weather = Weather(location: city, iconText: weatherIcon.iconText, temperature: temperature, forecasts: forecasts) - weatherBuilder.forecasts = self.getFirstFourForecasts(json) + completionHandler(weather, nil) + } + task.resume() + } +} + +private extension OpenWeatherMapService { + func getFirstFourForecasts(from json: JSON) -> [Forecast] { + var forecasts: [Forecast] = [] + + for index in 0...3 { + guard let forecastTempDegrees = json["list"][index]["main"]["temp"].double, + let rawDateTime = json["list"][index]["dt"].double, + let forecastCondition = json["list"][index]["weather"][0]["id"].int, + let forecastIcon = json["list"][index]["weather"][0]["icon"].string else { + break + } - completionHandler(weatherBuilder.build(), nil) + let country = json["city"]["country"].string ?? "US" + let temperature = TemperatureFormatter.format(country: country, + openWeatherMapDegrees: forecastTempDegrees) + let forecastTimeString = ForecastDateTime(date: rawDateTime, timeZone: TimeZone.current).shortTime + let weatherIcon = WeatherIcon(condition: forecastCondition, iconString: forecastIcon) + let forcastIconText = weatherIcon.iconText + + let forecast = Forecast(time: forecastTimeString, + iconText: forcastIconText, + temperature: temperature) + + forecasts.append(forecast) } - task.resume() + return forecasts } - fileprivate func generateRequestURL(_ location: CLLocation) -> URL? { + func makeRequestURL(location: CLLocation) -> URL? { guard var components = URLComponents(string:urlPath) else { return nil } - // get appId from Info.plist - let filePath = Bundle.main.path(forResource: "Info", ofType: "plist")! - let parameters = NSDictionary(contentsOfFile:filePath) - let appId = parameters!["OWMAccessToken"]! as! String + // get API key from Info.plist + guard let filePath = Bundle.main.path(forResource: "Info", ofType: "plist") else { + return nil + } + + let parameters = NSDictionary(contentsOfFile: filePath) + + // This is an Xcode 10 bug: Inconsistent ordering of Run Script phases and ProcessInfoPlistFile, we need to clean and rebuild to get `OWMAccessToken` from the plist. To walkaround it, we put the appId here. + // More details can be found on http://www.openradar.me/44422906 +// guard let appId = parameters?["OWMAccessToken"] as? String else { +// return nil +// } + let appId = parameters?["OWMAccessToken"] as? String ?? "b26c840fef70cdef165c7a67a3d998b6" let latitude = String(location.coordinate.latitude) let longitude = String(location.coordinate.longitude) diff --git a/App/SwiftWeather/Services/WeatherServiceProtocol.swift b/App/SwiftWeather/Services/WeatherServiceProtocol.swift new file mode 100644 index 0000000..0c43652 --- /dev/null +++ b/App/SwiftWeather/Services/WeatherServiceProtocol.swift @@ -0,0 +1,17 @@ +// +// ViewController.swift +// SwiftWeather +// +// Created by Jake Lin on 20/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation +import CoreLocation + +typealias WeatherCompletionHandler = (Weather?, AppError?) -> Void + +protocol WeatherServiceProtocol { + func requestWeather(location: CLLocation, completionHandler: @escaping WeatherCompletionHandler) +} + diff --git a/App/SwiftWeather/Utils/TemperatureConverter.swift b/App/SwiftWeather/Utils/TemperatureConverter.swift new file mode 100644 index 0000000..69a9585 --- /dev/null +++ b/App/SwiftWeather/Utils/TemperatureConverter.swift @@ -0,0 +1,19 @@ +// +// TemperatureConverter.swift +// SwiftWeather +// +// Created by Jake Lin on 22/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation + +struct TemperatureConverter { + static func kelvinToCelsius(_ degrees: Double) -> Double { + return round(degrees - 273.15) + } + + static func kelvinToFahrenheit(_ degrees: Double) -> Double { + return round(degrees * 9 / 5 - 459.67) + } +} diff --git a/App/SwiftWeather/Utils/UIView+loadFromNib.swift b/App/SwiftWeather/Utils/UIView+loadFromNib.swift new file mode 100644 index 0000000..15c4a84 --- /dev/null +++ b/App/SwiftWeather/Utils/UIView+loadFromNib.swift @@ -0,0 +1,21 @@ +// +// UIView+loadFromNib.swift +// SwiftWeather +// +// Created by Jake Lin on 25/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import UIKit + +extension UIView { + func loadFromNib() -> UIView? { + let bundle = Bundle(for: type(of: self)) + let nibName = String(describing: type(of: self)) + let nib = UINib(nibName: nibName, bundle: bundle) + guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return nil } + view.frame = bounds + view.autoresizingMask = [.flexibleWidth, .flexibleHeight] + return view + } +} diff --git a/App/SwiftWeather/ViewModels/ForecastViewModel.swift b/App/SwiftWeather/ViewModels/ForecastViewModel.swift new file mode 100644 index 0000000..08d7639 --- /dev/null +++ b/App/SwiftWeather/ViewModels/ForecastViewModel.swift @@ -0,0 +1,27 @@ +// +// ForecastViewModel.swift +// SwiftWeather +// +// Created by Jake Lin on 23/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation + +struct ForecastViewModel { + let time: LiveData + let iconText: LiveData + let temperature: LiveData + + init() { + time = LiveData("") + iconText = LiveData("") + temperature = LiveData("") + } + + init(forecast: Forecast) { + time = LiveData(forecast.time) + iconText = LiveData(forecast.iconText) + temperature = LiveData(forecast.temperature) + } +} diff --git a/App/SwiftWeather/ViewModels/WeatherViewModel.swift b/App/SwiftWeather/ViewModels/WeatherViewModel.swift new file mode 100644 index 0000000..00d7a59 --- /dev/null +++ b/App/SwiftWeather/ViewModels/WeatherViewModel.swift @@ -0,0 +1,99 @@ +// +// WeatherViewModel.swift +// SwiftWeather +// +// Created by Jake Lin on 23/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import Foundation +import CoreLocation + +private let emptyString = "" + +class WeatherViewModel { + // MARK: - Properties + let message: LiveData + + let location: LiveData + let iconText: LiveData + let temperature: LiveData + let forecasts: LiveData<[ForecastViewModel]> + + // MARK: - Services + private var locationService: LocationService + private var weatherService: WeatherServiceProtocol + + // MARK: - init + init() { + message = LiveData("Loading...") + + location = LiveData(emptyString) + iconText = LiveData(emptyString) + temperature = LiveData(emptyString) + forecasts = LiveData([]) + + // Can put Dependency Injection here + locationService = LocationService() + weatherService = OpenWeatherMapService() + } + + // MARK: - public + func startLocationService() { + locationService.delegate = self + locationService.requestLocation() + } +} + +private extension WeatherViewModel { + func update(weather: Weather) { + message.postValue(value: emptyString) + + location.postValue(value: weather.location) + iconText.postValue(value: weather.iconText) + temperature.postValue(value: weather.temperature) + + let forecastsValue = weather.forecasts.map { ForecastViewModel(forecast: $0) } + forecasts.postValue(value: forecastsValue) + } + + func update(error: AppError) { + switch error { + case .urlError: + message.postValue(value: "The weather service is not working.") + case .networkRequestFailed: + message.postValue(value: "The network appears to be down.") + case .jsonParsingFailed: + message.postValue(value: "We're having trouble parsing weather data.") + case .unableToFindLocation: + message.postValue(value: "We're having trouble getting user location.") + } + + location.postValue(value: emptyString) + iconText.postValue(value: emptyString) + temperature.postValue(value: emptyString) + self.forecasts.postValue(value: []) + } +} + +// MARK: LocationServiceDelegate +extension WeatherViewModel: LocationServiceDelegate { + func locationDidUpdate(location: CLLocation) { + weatherService.requestWeather(location: location) { (weather, error) -> Void in + if let error = error { + print(error) + self.update(error: error) + return + } + + guard let weather = weather else { + return + } + self.update(weather: weather) + } + } + + func locationDidFail(error: AppError) { + self.update(error: error) + } +} diff --git a/SwiftWeather/Base.lproj/LaunchScreen.storyboard b/App/SwiftWeather/Views/Base.lproj/LaunchScreen.storyboard similarity index 52% rename from SwiftWeather/Base.lproj/LaunchScreen.storyboard rename to App/SwiftWeather/Views/Base.lproj/LaunchScreen.storyboard index 5a9c4a6..e89947c 100644 --- a/SwiftWeather/Base.lproj/LaunchScreen.storyboard +++ b/App/SwiftWeather/Views/Base.lproj/LaunchScreen.storyboard @@ -1,22 +1,24 @@ - - + + + + + - + + + + - - - - - + - - + + diff --git a/App/SwiftWeather/Views/Base.lproj/Main.storyboard b/App/SwiftWeather/Views/Base.lproj/Main.storyboard new file mode 100644 index 0000000..ea8dc46 --- /dev/null +++ b/App/SwiftWeather/Views/Base.lproj/Main.storyboard @@ -0,0 +1,133 @@ + + + + + + + + + + + + + WeatherIcons-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/App/SwiftWeather/Views/ForecastView.swift b/App/SwiftWeather/Views/ForecastView.swift new file mode 100644 index 0000000..17dbb93 --- /dev/null +++ b/App/SwiftWeather/Views/ForecastView.swift @@ -0,0 +1,57 @@ +// +// ForecastView.swift +// SwiftWeather +// +// Created by Jake Lin on 24/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import UIKit + +@IBDesignable class ForecastView: UIView { + @IBOutlet private weak var timeLabel: UILabel! + @IBOutlet private weak var iconLabel: UILabel! + @IBOutlet private weak var temperatureLabel: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + loadViewFromNib() + self.timeLabel.text = "" + self.iconLabel.text = "" + self.temperatureLabel.text = "" + } + + override func prepareForInterfaceBuilder() { + super.prepareForInterfaceBuilder() + loadViewFromNib() + } + + private func loadViewFromNib() { + guard let view = loadFromNib() else { return } + addSubview(view) + } + + // MARK: - ViewModel + var viewModel: ForecastViewModel = ForecastViewModel() { + didSet { + viewModel.time.observe { + [unowned self] in + self.timeLabel.text = $0 + } + + viewModel.iconText.observe { + [unowned self] in + self.iconLabel.text = $0 + } + + viewModel.temperature.observe { + [unowned self] in + self.temperatureLabel.text = $0 + } + } + } + + func load(viewModel: ForecastViewModel) { + self.viewModel = viewModel + } +} diff --git a/App/SwiftWeather/Views/ForecastView.xib b/App/SwiftWeather/Views/ForecastView.xib new file mode 100644 index 0000000..fefb865 --- /dev/null +++ b/App/SwiftWeather/Views/ForecastView.xib @@ -0,0 +1,66 @@ + + + + + + + + + + + + + WeatherIcons-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/App/SwiftWeather/Views/WeatherViewController.swift b/App/SwiftWeather/Views/WeatherViewController.swift new file mode 100644 index 0000000..af5da1b --- /dev/null +++ b/App/SwiftWeather/Views/WeatherViewController.swift @@ -0,0 +1,51 @@ +// +// WeatherViewController.swift +// SwiftWeather +// +// Created by Jake Lin on 27/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import UIKit + +class WeatherViewController: UIViewController { + @IBOutlet private weak var locationLabel: UILabel! + @IBOutlet private weak var iconLabel: UILabel! + @IBOutlet private weak var temperatureLabel: UILabel! + @IBOutlet private weak var messageLabel: UILabel! + @IBOutlet private var forecastViews: [ForecastView]! + + private let viewModel = WeatherViewModel() + + override func viewDidLoad() { + super.viewDidLoad() + setupBindings() + + viewModel.startLocationService() + } +} + +private extension WeatherViewController { + func setupBindings() { + viewModel.location.observe { [unowned self] in + self.locationLabel.text = $0 + } + viewModel.iconText.observe { [unowned self] in + self.iconLabel.text = $0 + } + viewModel.temperature.observe { [unowned self] in + self.temperatureLabel.text = $0 + } + viewModel.message.observe { [unowned self] in + self.messageLabel.text = $0 + } + + viewModel.forecasts.observe { [unowned self] in + if $0.count >= 4 { + for (index, forecastView) in self.forecastViews.enumerated() { + forecastView.load(viewModel: $0[index]) + } + } + } + } +} diff --git a/SwiftWeatherTests/Info.plist b/App/SwiftWeatherTests/Info.plist similarity index 89% rename from SwiftWeatherTests/Info.plist rename to App/SwiftWeatherTests/Info.plist index ba72822..6c40a6c 100644 --- a/SwiftWeatherTests/Info.plist +++ b/App/SwiftWeatherTests/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -16,8 +16,6 @@ BNDL CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 diff --git a/App/SwiftWeatherTests/SwiftWeatherTests.swift b/App/SwiftWeatherTests/SwiftWeatherTests.swift new file mode 100644 index 0000000..97862aa --- /dev/null +++ b/App/SwiftWeatherTests/SwiftWeatherTests.swift @@ -0,0 +1,34 @@ +// +// SwiftWeatherTests.swift +// SwiftWeatherTests +// +// Created by Jake Lin on 20/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import XCTest +@testable import SwiftWeather + +class SwiftWeatherTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/SwiftWeatherUITests/Info.plist b/App/SwiftWeatherUITests/Info.plist similarity index 89% rename from SwiftWeatherUITests/Info.plist rename to App/SwiftWeatherUITests/Info.plist index ba72822..6c40a6c 100644 --- a/SwiftWeatherUITests/Info.plist +++ b/App/SwiftWeatherUITests/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -16,8 +16,6 @@ BNDL CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 diff --git a/App/SwiftWeatherUITests/SwiftWeatherUITests.swift b/App/SwiftWeatherUITests/SwiftWeatherUITests.swift new file mode 100644 index 0000000..1a78f63 --- /dev/null +++ b/App/SwiftWeatherUITests/SwiftWeatherUITests.swift @@ -0,0 +1,34 @@ +// +// SwiftWeatherUITests.swift +// SwiftWeatherUITests +// +// Created by Jake Lin on 20/10/18. +// Copyright © 2018 Jake Lin. All rights reserved. +// + +import XCTest + +class SwiftWeatherUITests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + XCUIApplication().launch() + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + +} diff --git a/Podfile.lock b/Podfile.lock deleted file mode 100644 index 299b94e..0000000 --- a/Podfile.lock +++ /dev/null @@ -1,53 +0,0 @@ -PODS: - - Bolts (1.9.0): - - Bolts/AppLinks (= 1.9.0) - - Bolts/Tasks (= 1.9.0) - - Bolts/AppLinks (1.9.0): - - Bolts/Tasks - - Bolts/Tasks (1.9.0) - - FacebookCore (0.3.0): - - Bolts (~> 1.8) - - FBSDKCoreKit (~> 4.27) - - FacebookShare (0.3.0): - - Bolts (~> 1.8) - - FacebookCore (~> 0.3) - - FBSDKCoreKit (~> 4.27) - - FBSDKShareKit (~> 4.27) - - FBSDKCoreKit (4.33.0): - - Bolts (~> 1.7) - - FBSDKShareKit (4.33.0): - - FBSDKCoreKit (~> 4.33.0) - - Nimble (7.0.3) - - Quick (1.2.0) - - SwiftyJSON (4.0.0) - -DEPENDENCIES: - - FacebookShare - - Nimble - - Quick - - SwiftyJSON - -SPEC REPOS: - https://github.com/CocoaPods/Specs.git: - - Bolts - - FacebookCore - - FacebookShare - - FBSDKCoreKit - - FBSDKShareKit - - Nimble - - Quick - - SwiftyJSON - -SPEC CHECKSUMS: - Bolts: ac6567323eac61e203f6a9763667d0f711be34c8 - FacebookCore: 3ffa190a3f1f96cec0e44d3fc221bc322c595ffa - FacebookShare: 0469964297ebd75f052be2c5083389a4208e82b7 - FBSDKCoreKit: 572b047a7e029bc44542bcf8a59414e7ff2b543e - FBSDKShareKit: 1869cb24db2cea90666a50cb9d568deb38e2d16e - Nimble: 7f5a9c447a33002645a071bddafbfb24ea70e0ac - Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08 - SwiftyJSON: 070dabdcb1beb81b247c65ffa3a79dbbfb3b48aa - -PODFILE CHECKSUM: 28f6397e3ce4b1c0258bb51e941d6155f9477e75 - -COCOAPODS: 1.5.0 diff --git a/README.md b/README.md index c47c7de..e2270ac 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ $ git clone https://github.com/JakeLin/SwiftLanguageWeather.git 2) Install pods ```bash -$ cd SwiftLanguageWeather +$ cd SwiftLanguageWeather/App $ pod install ``` diff --git a/SwiftWeather.xcodeproj/project.pbxproj b/SwiftWeather.xcodeproj/project.pbxproj deleted file mode 100644 index 1913c14..0000000 --- a/SwiftWeather.xcodeproj/project.pbxproj +++ /dev/null @@ -1,905 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1E8FF8444E2C29BD30B867EF /* Pods_Tests_SwiftWeatherUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 418594A8DD8093A647A30E12 /* Pods_Tests_SwiftWeatherUITests.framework */; }; - 5120F2EFC3DE077315306794 /* Pods_SwiftWeather.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF971A1BA4A6D64984CB0A75 /* Pods_SwiftWeather.framework */; }; - 655E8966C3E09626A8221261 /* Pods_Tests_SwiftWeatherTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 342A91306F3CF92DD999C97D /* Pods_Tests_SwiftWeatherTests.framework */; }; - AE09C4301B9723DE00C7CCED /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE09C42F1B9723DE00C7CCED /* LocationService.swift */; }; - AE0DC2CD1B8E7B3900E67147 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE0DC2CC1B8E7B3900E67147 /* Observable.swift */; }; - AE26CCAB1B875C2400D518CB /* ForecastView.xib in Resources */ = {isa = PBXBuildFile; fileRef = AE26CCAA1B875C2400D518CB /* ForecastView.xib */; }; - AE2C7D6C1BA028C000A7A714 /* WeatherIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE2C7D6B1BA028C000A7A714 /* WeatherIcon.swift */; }; - AE6C34E91B84742900F726C2 /* weathericons-regular-webfont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AE6C34E81B84742900F726C2 /* weathericons-regular-webfont.ttf */; }; - AEBE643B1B8D1F90004A0814 /* ForecastViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEBE643A1B8D1F90004A0814 /* ForecastViewModel.swift */; }; - AEBE643E1B8D2108004A0814 /* Forecast.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEBE643D1B8D2108004A0814 /* Forecast.swift */; }; - AEBE64431B8D2370004A0814 /* Weather.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEBE64421B8D2370004A0814 /* Weather.swift */; }; - AEBE64451B8D23B8004A0814 /* WeatherViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEBE64441B8D23B8004A0814 /* WeatherViewModel.swift */; }; - AECBA5E61B836BF20004A536 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECBA5E51B836BF20004A536 /* AppDelegate.swift */; }; - AECBA5E81B836BF20004A536 /* WeatherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECBA5E71B836BF20004A536 /* WeatherViewController.swift */; }; - AECBA5EB1B836BF20004A536 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AECBA5E91B836BF20004A536 /* Main.storyboard */; }; - AECBA5ED1B836BF20004A536 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AECBA5EC1B836BF20004A536 /* Assets.xcassets */; }; - AECBA5F01B836BF20004A536 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AECBA5EE1B836BF20004A536 /* LaunchScreen.storyboard */; }; - AECBA6061B836BF20004A536 /* SwiftWeatherUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECBA6051B836BF20004A536 /* SwiftWeatherUITests.swift */; }; - AED4B2C21B876E8B0003D765 /* ForecastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AED4B2C01B876DF50003D765 /* ForecastView.swift */; }; - AEEDF6891B9F09E300C6067B /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEEDF6881B9F09E300C6067B /* Error.swift */; }; - AEEDF68B1B9F99F800C6067B /* WeatherBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEEDF68A1B9F99F800C6067B /* WeatherBuilder.swift */; }; - AEEDF68D1B9F9B2900C6067B /* Temperature.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEEDF68C1B9F9B2900C6067B /* Temperature.swift */; }; - AEF61B281BA23B1200E8F259 /* ForecastDateTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEF61B271BA23B1200E8F259 /* ForecastDateTime.swift */; }; - CAB565DDADB61DF9053BD50F /* WeatherServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB56A3D23F4A93BB599E4BA /* WeatherServiceProtocol.swift */; }; - CAB56F4267B3BC487990A92D /* OpenWeatherMapService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB56D46471CA243B1FD646F /* OpenWeatherMapService.swift */; }; - EADFFD301CAEA22F008357FF /* TemperatureConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADFFD2F1CAEA22F008357FF /* TemperatureConverter.swift */; }; - F3897F7C1C118C4F001609E2 /* ForecastDateTimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3897F7B1C118C4F001609E2 /* ForecastDateTimeSpec.swift */; }; - F3897F7E1C118C6E001609E2 /* WeatherSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3897F7D1C118C6E001609E2 /* WeatherSpec.swift */; }; - F3897F801C118D7D001609E2 /* ForecastSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3897F7F1C118D7D001609E2 /* ForecastSpec.swift */; }; - F3897F821C118F0A001609E2 /* TemperatureSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3897F811C118F0A001609E2 /* TemperatureSpec.swift */; }; - F3897F841C11911B001609E2 /* WeatherIconSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3897F831C11911B001609E2 /* WeatherIconSpec.swift */; }; - F3FADA1B1C1327CB006D8551 /* WeatherBuilderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3FADA1A1C1327CB006D8551 /* WeatherBuilderSpec.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - AECBA5F71B836BF20004A536 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = AECBA5DA1B836BF20004A536 /* Project object */; - proxyType = 1; - remoteGlobalIDString = AECBA5E11B836BF20004A536; - remoteInfo = SwiftWeather; - }; - AECBA6021B836BF20004A536 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = AECBA5DA1B836BF20004A536 /* Project object */; - proxyType = 1; - remoteGlobalIDString = AECBA5E11B836BF20004A536; - remoteInfo = SwiftWeather; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 342A91306F3CF92DD999C97D /* Pods_Tests_SwiftWeatherTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tests_SwiftWeatherTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 418594A8DD8093A647A30E12 /* Pods_Tests_SwiftWeatherUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tests_SwiftWeatherUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 53EEE101C82E858E57980710 /* Pods-Tests-SwiftWeatherUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-SwiftWeatherUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests-SwiftWeatherUITests/Pods-Tests-SwiftWeatherUITests.release.xcconfig"; sourceTree = ""; }; - 900AFFBB41C69FC277CB0A33 /* Pods-SwiftWeather.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftWeather.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftWeather/Pods-SwiftWeather.debug.xcconfig"; sourceTree = ""; }; - 919B9EF9E5898277AD9C772A /* Pods-Tests-SwiftWeatherUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-SwiftWeatherUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests-SwiftWeatherUITests/Pods-Tests-SwiftWeatherUITests.debug.xcconfig"; sourceTree = ""; }; - AE09C42F1B9723DE00C7CCED /* LocationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = ""; }; - AE0DC2CC1B8E7B3900E67147 /* Observable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; - AE26CCAA1B875C2400D518CB /* ForecastView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ForecastView.xib; sourceTree = ""; }; - AE2C7D6B1BA028C000A7A714 /* WeatherIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherIcon.swift; sourceTree = ""; }; - AE6C34E81B84742900F726C2 /* weathericons-regular-webfont.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "weathericons-regular-webfont.ttf"; sourceTree = ""; }; - AEBE643A1B8D1F90004A0814 /* ForecastViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForecastViewModel.swift; sourceTree = ""; }; - AEBE643D1B8D2108004A0814 /* Forecast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Forecast.swift; sourceTree = ""; }; - AEBE64421B8D2370004A0814 /* Weather.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Weather.swift; sourceTree = ""; }; - AEBE64441B8D23B8004A0814 /* WeatherViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = WeatherViewModel.swift; sourceTree = ""; tabWidth = 2; }; - AECBA5E21B836BF20004A536 /* SwiftWeather.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftWeather.app; sourceTree = BUILT_PRODUCTS_DIR; }; - AECBA5E51B836BF20004A536 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - AECBA5E71B836BF20004A536 /* WeatherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewController.swift; sourceTree = ""; }; - AECBA5EA1B836BF20004A536 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - AECBA5EC1B836BF20004A536 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - AECBA5EF1B836BF20004A536 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - AECBA5F11B836BF20004A536 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - AECBA5F61B836BF20004A536 /* SwiftWeatherTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftWeatherTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - AECBA5FC1B836BF20004A536 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - AECBA6011B836BF20004A536 /* SwiftWeatherUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftWeatherUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - AECBA6051B836BF20004A536 /* SwiftWeatherUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftWeatherUITests.swift; sourceTree = ""; }; - AECBA6071B836BF20004A536 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - AED4B2C01B876DF50003D765 /* ForecastView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForecastView.swift; sourceTree = ""; }; - AEEDF6881B9F09E300C6067B /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; - AEEDF68A1B9F99F800C6067B /* WeatherBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherBuilder.swift; sourceTree = ""; }; - AEEDF68C1B9F9B2900C6067B /* Temperature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Temperature.swift; sourceTree = ""; }; - AEF61B271BA23B1200E8F259 /* ForecastDateTime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForecastDateTime.swift; sourceTree = ""; }; - BF971A1BA4A6D64984CB0A75 /* Pods_SwiftWeather.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftWeather.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BFCBC7111E6F5822A73508A5 /* Pods-Tests-SwiftWeatherTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-SwiftWeatherTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests-SwiftWeatherTests/Pods-Tests-SwiftWeatherTests.debug.xcconfig"; sourceTree = ""; }; - CAB56A3D23F4A93BB599E4BA /* WeatherServiceProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherServiceProtocol.swift; sourceTree = ""; }; - CAB56D46471CA243B1FD646F /* OpenWeatherMapService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenWeatherMapService.swift; sourceTree = ""; }; - E835C39D4B929F35B6A57B12 /* Pods-SwiftWeather.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftWeather.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftWeather/Pods-SwiftWeather.release.xcconfig"; sourceTree = ""; }; - EADFFD2F1CAEA22F008357FF /* TemperatureConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemperatureConverter.swift; sourceTree = ""; }; - F3897F7B1C118C4F001609E2 /* ForecastDateTimeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForecastDateTimeSpec.swift; sourceTree = ""; }; - F3897F7D1C118C6E001609E2 /* WeatherSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherSpec.swift; sourceTree = ""; }; - F3897F7F1C118D7D001609E2 /* ForecastSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForecastSpec.swift; sourceTree = ""; }; - F3897F811C118F0A001609E2 /* TemperatureSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemperatureSpec.swift; sourceTree = ""; }; - F3897F831C11911B001609E2 /* WeatherIconSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherIconSpec.swift; sourceTree = ""; }; - F3FADA1A1C1327CB006D8551 /* WeatherBuilderSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeatherBuilderSpec.swift; sourceTree = ""; }; - F4007F80070D3AAE1C564D8D /* Pods-Tests-SwiftWeatherTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests-SwiftWeatherTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests-SwiftWeatherTests/Pods-Tests-SwiftWeatherTests.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - AECBA5DF1B836BF20004A536 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 5120F2EFC3DE077315306794 /* Pods_SwiftWeather.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - AECBA5F31B836BF20004A536 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 655E8966C3E09626A8221261 /* Pods_Tests_SwiftWeatherTests.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - AECBA5FE1B836BF20004A536 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 1E8FF8444E2C29BD30B867EF /* Pods_Tests_SwiftWeatherUITests.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0D4BA09867F4A2B5754C4C42 /* Frameworks */ = { - isa = PBXGroup; - children = ( - BF971A1BA4A6D64984CB0A75 /* Pods_SwiftWeather.framework */, - 342A91306F3CF92DD999C97D /* Pods_Tests_SwiftWeatherTests.framework */, - 418594A8DD8093A647A30E12 /* Pods_Tests_SwiftWeatherUITests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - AE0DC2CB1B8E7ACE00E67147 /* Common */ = { - isa = PBXGroup; - children = ( - AE0DC2CC1B8E7B3900E67147 /* Observable.swift */, - AE09C42F1B9723DE00C7CCED /* LocationService.swift */, - AEEDF6881B9F09E300C6067B /* Error.swift */, - AEF61B271BA23B1200E8F259 /* ForecastDateTime.swift */, - ); - name = Common; - sourceTree = ""; - }; - AE6C34E71B84742900F726C2 /* fonts */ = { - isa = PBXGroup; - children = ( - AE6C34E81B84742900F726C2 /* weathericons-regular-webfont.ttf */, - ); - path = fonts; - sourceTree = ""; - }; - AEBE643C1B8D203C004A0814 /* Forecast */ = { - isa = PBXGroup; - children = ( - AE26CCAA1B875C2400D518CB /* ForecastView.xib */, - AED4B2C01B876DF50003D765 /* ForecastView.swift */, - AEBE643A1B8D1F90004A0814 /* ForecastViewModel.swift */, - AEBE643D1B8D2108004A0814 /* Forecast.swift */, - ); - name = Forecast; - sourceTree = ""; - }; - AEBE643F1B8D22ED004A0814 /* Storyboards */ = { - isa = PBXGroup; - children = ( - AECBA5E91B836BF20004A536 /* Main.storyboard */, - AECBA5EE1B836BF20004A536 /* LaunchScreen.storyboard */, - ); - name = Storyboards; - sourceTree = ""; - }; - AEBE64401B8D2306004A0814 /* Weather */ = { - isa = PBXGroup; - children = ( - AEF61B2B1BA23C2A00E8F259 /* Models */, - AEBE64441B8D23B8004A0814 /* WeatherViewModel.swift */, - AECBA5E71B836BF20004A536 /* WeatherViewController.swift */, - AEF61B291BA23C0500E8F259 /* Services */, - ); - name = Weather; - sourceTree = ""; - }; - AEBE64411B8D231E004A0814 /* Features */ = { - isa = PBXGroup; - children = ( - AEBE64401B8D2306004A0814 /* Weather */, - AEBE643C1B8D203C004A0814 /* Forecast */, - ); - name = Features; - sourceTree = ""; - }; - AECBA5D91B836BF20004A536 = { - isa = PBXGroup; - children = ( - AECBA5E41B836BF20004A536 /* SwiftWeather */, - AECBA5F91B836BF20004A536 /* SwiftWeatherTests */, - AECBA6041B836BF20004A536 /* SwiftWeatherUITests */, - AECBA5E31B836BF20004A536 /* Products */, - E117EDC87D7E5DC450870FDD /* Pods */, - 0D4BA09867F4A2B5754C4C42 /* Frameworks */, - ); - sourceTree = ""; - }; - AECBA5E31B836BF20004A536 /* Products */ = { - isa = PBXGroup; - children = ( - AECBA5E21B836BF20004A536 /* SwiftWeather.app */, - AECBA5F61B836BF20004A536 /* SwiftWeatherTests.xctest */, - AECBA6011B836BF20004A536 /* SwiftWeatherUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - AECBA5E41B836BF20004A536 /* SwiftWeather */ = { - isa = PBXGroup; - children = ( - AE0DC2CB1B8E7ACE00E67147 /* Common */, - AEBE64411B8D231E004A0814 /* Features */, - AEBE643F1B8D22ED004A0814 /* Storyboards */, - AE6C34E71B84742900F726C2 /* fonts */, - AECBA5EC1B836BF20004A536 /* Assets.xcassets */, - AECBA5E51B836BF20004A536 /* AppDelegate.swift */, - AECBA5F11B836BF20004A536 /* Info.plist */, - ); - path = SwiftWeather; - sourceTree = ""; - }; - AECBA5F91B836BF20004A536 /* SwiftWeatherTests */ = { - isa = PBXGroup; - children = ( - F3897F7A1C118C4F001609E2 /* UnitTests */, - AECBA5FC1B836BF20004A536 /* Info.plist */, - ); - path = SwiftWeatherTests; - sourceTree = ""; - }; - AECBA6041B836BF20004A536 /* SwiftWeatherUITests */ = { - isa = PBXGroup; - children = ( - AECBA6051B836BF20004A536 /* SwiftWeatherUITests.swift */, - AECBA6071B836BF20004A536 /* Info.plist */, - ); - path = SwiftWeatherUITests; - sourceTree = ""; - }; - AEF61B291BA23C0500E8F259 /* Services */ = { - isa = PBXGroup; - children = ( - CAB56A3D23F4A93BB599E4BA /* WeatherServiceProtocol.swift */, - CAB56D46471CA243B1FD646F /* OpenWeatherMapService.swift */, - ); - name = Services; - sourceTree = ""; - }; - AEF61B2B1BA23C2A00E8F259 /* Models */ = { - isa = PBXGroup; - children = ( - AEBE64421B8D2370004A0814 /* Weather.swift */, - AEEDF68C1B9F9B2900C6067B /* Temperature.swift */, - EADFFD2F1CAEA22F008357FF /* TemperatureConverter.swift */, - AE2C7D6B1BA028C000A7A714 /* WeatherIcon.swift */, - AEEDF68A1B9F99F800C6067B /* WeatherBuilder.swift */, - ); - name = Models; - sourceTree = ""; - }; - E117EDC87D7E5DC450870FDD /* Pods */ = { - isa = PBXGroup; - children = ( - 900AFFBB41C69FC277CB0A33 /* Pods-SwiftWeather.debug.xcconfig */, - E835C39D4B929F35B6A57B12 /* Pods-SwiftWeather.release.xcconfig */, - BFCBC7111E6F5822A73508A5 /* Pods-Tests-SwiftWeatherTests.debug.xcconfig */, - F4007F80070D3AAE1C564D8D /* Pods-Tests-SwiftWeatherTests.release.xcconfig */, - 919B9EF9E5898277AD9C772A /* Pods-Tests-SwiftWeatherUITests.debug.xcconfig */, - 53EEE101C82E858E57980710 /* Pods-Tests-SwiftWeatherUITests.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - F3897F7A1C118C4F001609E2 /* UnitTests */ = { - isa = PBXGroup; - children = ( - F3897F7B1C118C4F001609E2 /* ForecastDateTimeSpec.swift */, - F3897F7D1C118C6E001609E2 /* WeatherSpec.swift */, - F3897F7F1C118D7D001609E2 /* ForecastSpec.swift */, - F3897F811C118F0A001609E2 /* TemperatureSpec.swift */, - F3897F831C11911B001609E2 /* WeatherIconSpec.swift */, - F3FADA1A1C1327CB006D8551 /* WeatherBuilderSpec.swift */, - ); - path = UnitTests; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - AECBA5E11B836BF20004A536 /* SwiftWeather */ = { - isa = PBXNativeTarget; - buildConfigurationList = AECBA60A1B836BF20004A536 /* Build configuration list for PBXNativeTarget "SwiftWeather" */; - buildPhases = ( - 875372B40EF6130AA88D65AB /* [CP] Check Pods Manifest.lock */, - AECBA5DE1B836BF20004A536 /* Sources */, - AECBA5DF1B836BF20004A536 /* Frameworks */, - AECBA5E01B836BF20004A536 /* Resources */, - ADD0EBFC1C562E52002D8392 /* ShellScript */, - 6691608BB84CB91D5EDADE36 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SwiftWeather; - productName = SwiftWeather; - productReference = AECBA5E21B836BF20004A536 /* SwiftWeather.app */; - productType = "com.apple.product-type.application"; - }; - AECBA5F51B836BF20004A536 /* SwiftWeatherTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = AECBA60D1B836BF20004A536 /* Build configuration list for PBXNativeTarget "SwiftWeatherTests" */; - buildPhases = ( - 2A25B97139655BE3B502C748 /* [CP] Check Pods Manifest.lock */, - AECBA5F21B836BF20004A536 /* Sources */, - AECBA5F31B836BF20004A536 /* Frameworks */, - AECBA5F41B836BF20004A536 /* Resources */, - 3211EE071253F1D6513A17BA /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - AECBA5F81B836BF20004A536 /* PBXTargetDependency */, - ); - name = SwiftWeatherTests; - productName = SwiftWeatherTests; - productReference = AECBA5F61B836BF20004A536 /* SwiftWeatherTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - AECBA6001B836BF20004A536 /* SwiftWeatherUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = AECBA6101B836BF20004A536 /* Build configuration list for PBXNativeTarget "SwiftWeatherUITests" */; - buildPhases = ( - 7E7F83383B6114FE2CC03BD6 /* [CP] Check Pods Manifest.lock */, - AECBA5FD1B836BF20004A536 /* Sources */, - AECBA5FE1B836BF20004A536 /* Frameworks */, - AECBA5FF1B836BF20004A536 /* Resources */, - EC9ED72913B1DE182823A53C /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - AECBA6031B836BF20004A536 /* PBXTargetDependency */, - ); - name = SwiftWeatherUITests; - productName = SwiftWeatherUITests; - productReference = AECBA6011B836BF20004A536 /* SwiftWeatherUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - AECBA5DA1B836BF20004A536 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 0900; - ORGANIZATIONNAME = "Jake Lin"; - TargetAttributes = { - AECBA5E11B836BF20004A536 = { - CreatedOnToolsVersion = 7.0; - DevelopmentTeam = 32GB2HU6K5; - LastSwiftMigration = 0900; - }; - AECBA5F51B836BF20004A536 = { - CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0900; - TestTargetID = AECBA5E11B836BF20004A536; - }; - AECBA6001B836BF20004A536 = { - CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0900; - TestTargetID = AECBA5E11B836BF20004A536; - }; - }; - }; - buildConfigurationList = AECBA5DD1B836BF20004A536 /* Build configuration list for PBXProject "SwiftWeather" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = AECBA5D91B836BF20004A536; - productRefGroup = AECBA5E31B836BF20004A536 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - AECBA5E11B836BF20004A536 /* SwiftWeather */, - AECBA5F51B836BF20004A536 /* SwiftWeatherTests */, - AECBA6001B836BF20004A536 /* SwiftWeatherUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - AECBA5E01B836BF20004A536 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - AECBA5F01B836BF20004A536 /* LaunchScreen.storyboard in Resources */, - AECBA5ED1B836BF20004A536 /* Assets.xcassets in Resources */, - AE6C34E91B84742900F726C2 /* weathericons-regular-webfont.ttf in Resources */, - AECBA5EB1B836BF20004A536 /* Main.storyboard in Resources */, - AE26CCAB1B875C2400D518CB /* ForecastView.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - AECBA5F41B836BF20004A536 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - AECBA5FF1B836BF20004A536 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 2A25B97139655BE3B502C748 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Tests-SwiftWeatherTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 3211EE071253F1D6513A17BA /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Tests-SwiftWeatherTests/Pods-Tests-SwiftWeatherTests-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", - "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Tests-SwiftWeatherTests/Pods-Tests-SwiftWeatherTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 6691608BB84CB91D5EDADE36 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-SwiftWeather/Pods-SwiftWeather-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Bolts/Bolts.framework", - "${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework", - "${BUILT_PRODUCTS_DIR}/FBSDKShareKit/FBSDKShareKit.framework", - "${BUILT_PRODUCTS_DIR}/FacebookCore/FacebookCore.framework", - "${BUILT_PRODUCTS_DIR}/FacebookShare/FacebookShare.framework", - "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Bolts.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKShareKit.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FacebookCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FacebookShare.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftWeather/Pods-SwiftWeather-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 7E7F83383B6114FE2CC03BD6 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Tests-SwiftWeatherUITests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 875372B40EF6130AA88D65AB /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SwiftWeather-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - ADD0EBFC1C562E52002D8392 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "token_file=.access_tokens/openweathermap\ntoken=\"$(cat $token_file)\"\nif [ \"$token\" ]; then\nplutil -replace OWMAccessToken -string $token $TARGET_BUILD_DIR/$INFOPLIST_PATH\nelse\necho 'error: Missing OpenWeatherMap access token'\nopen 'http://openweathermap.org/appid'\necho \"error: Get an access token from , then create a new file at $token_file that contains the access token.\"\nexit 1\nfi"; - }; - EC9ED72913B1DE182823A53C /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Tests-SwiftWeatherUITests/Pods-Tests-SwiftWeatherUITests-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", - "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Tests-SwiftWeatherUITests/Pods-Tests-SwiftWeatherUITests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - AECBA5DE1B836BF20004A536 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - AE0DC2CD1B8E7B3900E67147 /* Observable.swift in Sources */, - AE2C7D6C1BA028C000A7A714 /* WeatherIcon.swift in Sources */, - AECBA5E81B836BF20004A536 /* WeatherViewController.swift in Sources */, - AEBE643B1B8D1F90004A0814 /* ForecastViewModel.swift in Sources */, - AECBA5E61B836BF20004A536 /* AppDelegate.swift in Sources */, - AEF61B281BA23B1200E8F259 /* ForecastDateTime.swift in Sources */, - AEEDF6891B9F09E300C6067B /* Error.swift in Sources */, - AEEDF68B1B9F99F800C6067B /* WeatherBuilder.swift in Sources */, - AEBE64451B8D23B8004A0814 /* WeatherViewModel.swift in Sources */, - AED4B2C21B876E8B0003D765 /* ForecastView.swift in Sources */, - EADFFD301CAEA22F008357FF /* TemperatureConverter.swift in Sources */, - AE09C4301B9723DE00C7CCED /* LocationService.swift in Sources */, - AEBE64431B8D2370004A0814 /* Weather.swift in Sources */, - AEBE643E1B8D2108004A0814 /* Forecast.swift in Sources */, - CAB56F4267B3BC487990A92D /* OpenWeatherMapService.swift in Sources */, - AEEDF68D1B9F9B2900C6067B /* Temperature.swift in Sources */, - CAB565DDADB61DF9053BD50F /* WeatherServiceProtocol.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - AECBA5F21B836BF20004A536 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F3897F7C1C118C4F001609E2 /* ForecastDateTimeSpec.swift in Sources */, - F3897F821C118F0A001609E2 /* TemperatureSpec.swift in Sources */, - F3897F7E1C118C6E001609E2 /* WeatherSpec.swift in Sources */, - F3FADA1B1C1327CB006D8551 /* WeatherBuilderSpec.swift in Sources */, - F3897F841C11911B001609E2 /* WeatherIconSpec.swift in Sources */, - F3897F801C118D7D001609E2 /* ForecastSpec.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - AECBA5FD1B836BF20004A536 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - AECBA6061B836BF20004A536 /* SwiftWeatherUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - AECBA5F81B836BF20004A536 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = AECBA5E11B836BF20004A536 /* SwiftWeather */; - targetProxy = AECBA5F71B836BF20004A536 /* PBXContainerItemProxy */; - }; - AECBA6031B836BF20004A536 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = AECBA5E11B836BF20004A536 /* SwiftWeather */; - targetProxy = AECBA6021B836BF20004A536 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - AECBA5E91B836BF20004A536 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - AECBA5EA1B836BF20004A536 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - AECBA5EE1B836BF20004A536 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - AECBA5EF1B836BF20004A536 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - AECBA6081B836BF20004A536 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - AECBA6091B836BF20004A536 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - AECBA60B1B836BF20004A536 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 900AFFBB41C69FC277CB0A33 /* Pods-SwiftWeather.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = 32GB2HU6K5; - INFOPLIST_FILE = SwiftWeather/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.rushjet.SwiftWeather; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - }; - name = Debug; - }; - AECBA60C1B836BF20004A536 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E835C39D4B929F35B6A57B12 /* Pods-SwiftWeather.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = 32GB2HU6K5; - INFOPLIST_FILE = SwiftWeather/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.rushjet.SwiftWeather; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - }; - name = Release; - }; - AECBA60E1B836BF20004A536 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = BFCBC7111E6F5822A73508A5 /* Pods-Tests-SwiftWeatherTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ENABLE_MODULES = YES; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = SwiftWeatherTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.rushjet.SwiftWeatherTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftWeather.app/SwiftWeather"; - }; - name = Debug; - }; - AECBA60F1B836BF20004A536 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = F4007F80070D3AAE1C564D8D /* Pods-Tests-SwiftWeatherTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ENABLE_MODULES = YES; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = SwiftWeatherTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.rushjet.SwiftWeatherTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftWeather.app/SwiftWeather"; - }; - name = Release; - }; - AECBA6111B836BF20004A536 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 919B9EF9E5898277AD9C772A /* Pods-Tests-SwiftWeatherUITests.debug.xcconfig */; - buildSettings = { - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = SwiftWeatherUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.rushjet.SwiftWeatherUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - TEST_TARGET_NAME = SwiftWeather; - USES_XCTRUNNER = YES; - }; - name = Debug; - }; - AECBA6121B836BF20004A536 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 53EEE101C82E858E57980710 /* Pods-Tests-SwiftWeatherUITests.release.xcconfig */; - buildSettings = { - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = SwiftWeatherUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.rushjet.SwiftWeatherUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - TEST_TARGET_NAME = SwiftWeather; - USES_XCTRUNNER = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - AECBA5DD1B836BF20004A536 /* Build configuration list for PBXProject "SwiftWeather" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - AECBA6081B836BF20004A536 /* Debug */, - AECBA6091B836BF20004A536 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - AECBA60A1B836BF20004A536 /* Build configuration list for PBXNativeTarget "SwiftWeather" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - AECBA60B1B836BF20004A536 /* Debug */, - AECBA60C1B836BF20004A536 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - AECBA60D1B836BF20004A536 /* Build configuration list for PBXNativeTarget "SwiftWeatherTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - AECBA60E1B836BF20004A536 /* Debug */, - AECBA60F1B836BF20004A536 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - AECBA6101B836BF20004A536 /* Build configuration list for PBXNativeTarget "SwiftWeatherUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - AECBA6111B836BF20004A536 /* Debug */, - AECBA6121B836BF20004A536 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = AECBA5DA1B836BF20004A536 /* Project object */; -} diff --git a/SwiftWeather/AppDelegate.swift b/SwiftWeather/AppDelegate.swift deleted file mode 100644 index ef6d9eb..0000000 --- a/SwiftWeather/AppDelegate.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// AppDelegate.swift -// SwiftWeather -// -// Created by Jake Lin on 8/18/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import UIKit - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - func application(_ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for - // certain types of temporary interruptions (such as an incoming phone call or SMS message) or - // when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame - // rates. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store - // enough application state information to restore your application to its current state in case - // it is terminated later. - // If your application supports background execution, this method is called instead of - // applicationWillTerminate: when the user quits. - } - - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo - // many of the changes made on entering the background. - } - - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. - // If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillTerminate(_ application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also - // applicationDidEnterBackground:. - } - -} diff --git a/SwiftWeather/Assets.xcassets/share.imageset/Contents.json b/SwiftWeather/Assets.xcassets/share.imageset/Contents.json deleted file mode 100644 index bfbe1c1..0000000 --- a/SwiftWeather/Assets.xcassets/share.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "share.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/SwiftWeather/Assets.xcassets/share.imageset/share.png b/SwiftWeather/Assets.xcassets/share.imageset/share.png deleted file mode 100644 index 089f04a..0000000 Binary files a/SwiftWeather/Assets.xcassets/share.imageset/share.png and /dev/null differ diff --git a/SwiftWeather/Base.lproj/Main.storyboard b/SwiftWeather/Base.lproj/Main.storyboard deleted file mode 100644 index c3d1b9b..0000000 --- a/SwiftWeather/Base.lproj/Main.storyboard +++ /dev/null @@ -1,271 +0,0 @@ - - - - - - - - - - - - - - - WeatherIcons-Regular - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SwiftWeather/Error.swift b/SwiftWeather/Error.swift deleted file mode 100644 index 93b8272..0000000 --- a/SwiftWeather/Error.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by Jake Lin on 9/8/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -struct SWError { - enum Code: Int { - case urlError = -6000 - case networkRequestFailed = -6001 - case jsonSerializationFailed = -6002 - case jsonParsingFailed = -6003 - case unableToFindLocation = -6004 - } - - let errorCode: Code -} diff --git a/SwiftWeather/Forecast.swift b/SwiftWeather/Forecast.swift deleted file mode 100644 index 9fac80c..0000000 --- a/SwiftWeather/Forecast.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Created by Jake Lin on 8/26/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -struct Forecast { - let time: String - let iconText: String - let temperature: String -} diff --git a/SwiftWeather/ForecastView.swift b/SwiftWeather/ForecastView.swift deleted file mode 100644 index e17b780..0000000 --- a/SwiftWeather/ForecastView.swift +++ /dev/null @@ -1,139 +0,0 @@ -// -// Created by Jake Lin on 8/22/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import UIKit - -@IBDesignable class ForecastView: UIView { - // Our custom view from the XIB file - var view: UIView! - - @IBOutlet weak var timeLabel: UILabel! - @IBOutlet weak var iconLabel: UILabel! - @IBOutlet weak var temperatureLabel: UILabel! - - // MARK: - init - override init(frame: CGRect) { - super.init(frame: frame) - view = loadViewFromNib() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - view = loadViewFromNib() - } - - func loadViewFromNib() -> UIView { - let bundle = Bundle(for: type(of: self)) - let nib = UINib(nibName: nibName(), bundle: bundle) - // swiftlint:disable force_cast - let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView - // swiftlint:enable force_cast - - view.frame = bounds - view.autoresizingMask = [.flexibleWidth, .flexibleHeight] - addSubview(view) - return view - } - - // MARK: - ViewModel - var viewModel: ForecastViewModel? { - didSet { - viewModel?.time.observe { - [unowned self] in - self.timeLabel.text = $0 - } - - viewModel?.iconText.observe { - [unowned self] in - self.iconLabel.text = $0 - } - - viewModel?.temperature.observe { - [unowned self] in - self.temperatureLabel.text = $0 - } - } - } - - func loadViewModel(_ viewModel: ForecastViewModel) { - self.viewModel = viewModel - } - - // MARK: - IBInspectable - @IBInspectable var time: String? { - get { - return timeLabel.text - } - - set { - timeLabel.text = newValue - } - } - - @IBInspectable var icon: String? { - get { - return iconLabel.text - } - - set { - iconLabel.text = newValue - } - } - - @IBInspectable var temperature: String? { - get { - return temperatureLabel.text - } - - set { - temperatureLabel.text = newValue - } - } - - @IBInspectable var timeColor: UIColor { - get { - return timeLabel.textColor - } - - set { - timeLabel.textColor = newValue - } - } - - @IBInspectable var iconColor: UIColor { - get { - return iconLabel.textColor - } - - set { - iconLabel.textColor = newValue - } - } - - @IBInspectable var temperatureColor: UIColor { - get { - return temperatureLabel.textColor - } - - set { - temperatureLabel.textColor = newValue - } - } - - @IBInspectable var bgColor: UIColor { - get { - return view.backgroundColor! - } - - set { - view.backgroundColor = newValue - } - } - - // MARK: - Private - fileprivate func nibName() -> String { - return String(describing: type(of: self)) - } -} diff --git a/SwiftWeather/ForecastView.xib b/SwiftWeather/ForecastView.xib deleted file mode 100644 index 6c7fca9..0000000 --- a/SwiftWeather/ForecastView.xib +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - WeatherIcons-Regular - WeatherIcons-Regular - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SwiftWeather/ForecastViewModel.swift b/SwiftWeather/ForecastViewModel.swift deleted file mode 100644 index df5ed19..0000000 --- a/SwiftWeather/ForecastViewModel.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by Jake Lin on 8/26/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -struct ForecastViewModel { - let time: Observable - let iconText: Observable - let temperature: Observable - - init(_ forecast: Forecast) { - time = Observable(forecast.time) - iconText = Observable(forecast.iconText) - temperature = Observable(forecast.temperature) - } -} diff --git a/SwiftWeather/LocationService.swift b/SwiftWeather/LocationService.swift deleted file mode 100644 index add7ba2..0000000 --- a/SwiftWeather/LocationService.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// Created by Jake Lin on 9/2/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation -import CoreLocation - -protocol LocationServiceDelegate { - func locationDidUpdate(_ service: LocationService, location: CLLocation) - func locationDidFail(withError error: SWError) -} - -class LocationService: NSObject { - var delegate: LocationServiceDelegate? - - fileprivate let locationManager = CLLocationManager() - - override init() { - super.init() - locationManager.delegate = self - locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters - } - - func requestLocation() { - locationManager.requestWhenInUseAuthorization() - locationManager.requestLocation() - } -} - -// MARK: - CLLocationManager Delegate -extension LocationService : CLLocationManagerDelegate { - func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - if let location = locations.first { - print("Current location: \(location)") - delegate?.locationDidUpdate(self, location: location) - } - } - - func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { - let swError = SWError(errorCode: .unableToFindLocation) - delegate?.locationDidFail(withError: swError) - print("Error finding location: \(error.localizedDescription)") - } -} diff --git a/SwiftWeather/Observable.swift b/SwiftWeather/Observable.swift deleted file mode 100644 index 996ef60..0000000 --- a/SwiftWeather/Observable.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Jake Lin on 8/27/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -class Observable { - typealias Observer = (T) -> Void - var observer: Observer? - - func observe(_ observer: Observer?) { - self.observer = observer - observer?(value) - } - - var value: T { - didSet { - observer?(value) - } - } - - init(_ value: T) { - self.value = value - } -} diff --git a/SwiftWeather/Temperature.swift b/SwiftWeather/Temperature.swift deleted file mode 100644 index 743e80d..0000000 --- a/SwiftWeather/Temperature.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by Jake Lin on 9/9/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -struct Temperature { - let degrees: String - - init(country: String, openWeatherMapDegrees: Double) { - if country == "US" { - degrees = String(TemperatureConverter.kelvinToFahrenheit(openWeatherMapDegrees)) + "\u{f045}" - } else { - degrees = String(TemperatureConverter.kelvinToCelsius(openWeatherMapDegrees)) + "\u{f03c}" - } - } -} diff --git a/SwiftWeather/TemperatureConverter.swift b/SwiftWeather/TemperatureConverter.swift deleted file mode 100644 index bb360e7..0000000 --- a/SwiftWeather/TemperatureConverter.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// Created by Tiago Martinho on 4/1/16. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -struct TemperatureConverter { - static func kelvinToCelsius(_ degrees: Double) -> Double { - return round(degrees - 273.15) - } - - static func kelvinToFahrenheit(_ degrees: Double) -> Double { - return round(degrees * 9 / 5 - 459.67) - } -} diff --git a/SwiftWeather/Weather.swift b/SwiftWeather/Weather.swift deleted file mode 100644 index e047d5b..0000000 --- a/SwiftWeather/Weather.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by Jake Lin on 8/26/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -struct Weather { - let location: String - let iconText: String - let temperature: String - - let forecasts: [Forecast] -} diff --git a/SwiftWeather/WeatherBuilder.swift b/SwiftWeather/WeatherBuilder.swift deleted file mode 100644 index 5bdafcf..0000000 --- a/SwiftWeather/WeatherBuilder.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by Jake Lin on 9/9/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -struct WeatherBuilder { - var location: String? - var iconText: String? - var temperature: String? - - var forecasts: [Forecast]? - - func build() -> Weather { - return Weather(location: location!, - iconText: iconText!, - temperature: temperature!, - forecasts: forecasts!) - } -} diff --git a/SwiftWeather/WeatherIcon.swift b/SwiftWeather/WeatherIcon.swift deleted file mode 100644 index 7569227..0000000 --- a/SwiftWeather/WeatherIcon.swift +++ /dev/null @@ -1,289 +0,0 @@ -// -// Created by Jake Lin on 9/9/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation - -/* - `WeatherIcon` is used to map Open Weather Map icon string to Weather Icons unicode string. - It is generated by - ``` - var caseString = ''; - var caseAndReturnString = ''; - Array.prototype.forEach.call(document.styleSheets[1].cssRules,function(element){ - if (element.selectorText && element.selectorText.startsWith('.wi-owm')) { - var caseName = element.selectorText.substring(8, - element.selectorText.indexOf('::before')).replace('-', '') - caseString += 'case ' + caseName + ' = "' + caseName + '"\n'; - caseAndReturnString += 'case .' + caseName + ': return "\\u{' - + element.style['content'].charCodeAt(1).toString(16) + '}"\n' - } - }); - console.log(caseString); - console.log(caseAndReturnString); - ``` -*/ -// swiftlint:disable type_body_length -struct WeatherIcon { - let iconText: String - - enum IconType: String, CustomStringConvertible { - case day200 = "day200" - case day201 = "day201" - case day202 = "day202" - case day210 = "day210" - case day211 = "day211" - case day212 = "day212" - case day221 = "day221" - case day230 = "day230" - case day231 = "day231" - case day232 = "day232" - case day300 = "day300" - case day301 = "day301" - case day302 = "day302" - case day310 = "day310" - case day311 = "day311" - case day312 = "day312" - case day313 = "day313" - case day314 = "day314" - case day321 = "day321" - case day500 = "day500" - case day501 = "day501" - case day502 = "day502" - case day503 = "day503" - case day504 = "day504" - case day511 = "day511" - case day520 = "day520" - case day521 = "day521" - case day522 = "day522" - case day531 = "day531" - case day600 = "day600" - case day601 = "day601" - case day602 = "day602" - case day611 = "day611" - case day612 = "day612" - case day615 = "day615" - case day616 = "day616" - case day620 = "day620" - case day621 = "day621" - case day622 = "day622" - case day701 = "day701" - case day711 = "day711" - case day721 = "day721" - case day731 = "day731" - case day741 = "day741" - case day761 = "day761" - case day762 = "day762" - case day781 = "day781" - case day800 = "day800" - case day801 = "day801" - case day802 = "day802" - case day803 = "day803" - case day804 = "day804" - case day900 = "day900" - case day902 = "day902" - case day903 = "day903" - case day904 = "day904" - case day906 = "day906" - case day957 = "day957" - case night200 = "night200" - case night201 = "night201" - case night202 = "night202" - case night210 = "night210" - case night211 = "night211" - case night212 = "night212" - case night221 = "night221" - case night230 = "night230" - case night231 = "night231" - case night232 = "night232" - case night300 = "night300" - case night301 = "night301" - case night302 = "night302" - case night310 = "night310" - case night311 = "night311" - case night312 = "night312" - case night313 = "night313" - case night314 = "night314" - case night321 = "night321" - case night500 = "night500" - case night501 = "night501" - case night502 = "night502" - case night503 = "night503" - case night504 = "night504" - case night511 = "night511" - case night520 = "night520" - case night521 = "night521" - case night522 = "night522" - case night531 = "night531" - case night600 = "night600" - case night601 = "night601" - case night602 = "night602" - case night611 = "night611" - case night612 = "night612" - case night615 = "night615" - case night616 = "night616" - case night620 = "night620" - case night621 = "night621" - case night622 = "night622" - case night701 = "night701" - case night711 = "night711" - case night721 = "night721" - case night731 = "night731" - case night741 = "night741" - case night761 = "night761" - case night762 = "night762" - case night781 = "night781" - case night800 = "night800" - case night801 = "night801" - case night802 = "night802" - case night803 = "night803" - case night804 = "night804" - case night900 = "night900" - case night902 = "night902" - case night903 = "night903" - case night904 = "night904" - case night906 = "night906" - case night957 = "night957" - - var description: String { - switch self { - case .day200: return "\u{f010}" - case .day201: return "\u{f010}" - case .day202: return "\u{f010}" - case .day210: return "\u{f005}" - case .day211: return "\u{f005}" - case .day212: return "\u{f005}" - case .day221: return "\u{f005}" - case .day230: return "\u{f010}" - case .day231: return "\u{f010}" - case .day232: return "\u{f010}" - case .day300: return "\u{f00b}" - case .day301: return "\u{f00b}" - case .day302: return "\u{f008}" - case .day310: return "\u{f008}" - case .day311: return "\u{f008}" - case .day312: return "\u{f008}" - case .day313: return "\u{f008}" - case .day314: return "\u{f008}" - case .day321: return "\u{f00b}" - case .day500: return "\u{f00b}" - case .day501: return "\u{f008}" - case .day502: return "\u{f008}" - case .day503: return "\u{f008}" - case .day504: return "\u{f008}" - case .day511: return "\u{f006}" - case .day520: return "\u{f009}" - case .day521: return "\u{f009}" - case .day522: return "\u{f009}" - case .day531: return "\u{f00e}" - case .day600: return "\u{f00a}" - case .day601: return "\u{f0b2}" - case .day602: return "\u{f00a}" - case .day611: return "\u{f006}" - case .day612: return "\u{f006}" - case .day615: return "\u{f006}" - case .day616: return "\u{f006}" - case .day620: return "\u{f006}" - case .day621: return "\u{f00a}" - case .day622: return "\u{f00a}" - case .day701: return "\u{f009}" - case .day711: return "\u{f062}" - case .day721: return "\u{f0b6}" - case .day731: return "\u{f063}" - case .day741: return "\u{f003}" - case .day761: return "\u{f063}" - case .day762: return "\u{f063}" - case .day781: return "\u{f056}" - case .day800: return "\u{f00d}" - case .day801: return "\u{f000}" - case .day802: return "\u{f000}" - case .day803: return "\u{f000}" - case .day804: return "\u{f00c}" - case .day900: return "\u{f056}" - case .day902: return "\u{f073}" - case .day903: return "\u{f076}" - case .day904: return "\u{f072}" - case .day906: return "\u{f004}" - case .day957: return "\u{f050}" - case .night200: return "\u{f02d}" - case .night201: return "\u{f02d}" - case .night202: return "\u{f02d}" - case .night210: return "\u{f025}" - case .night211: return "\u{f025}" - case .night212: return "\u{f025}" - case .night221: return "\u{f025}" - case .night230: return "\u{f02d}" - case .night231: return "\u{f02d}" - case .night232: return "\u{f02d}" - case .night300: return "\u{f02b}" - case .night301: return "\u{f02b}" - case .night302: return "\u{f028}" - case .night310: return "\u{f028}" - case .night311: return "\u{f028}" - case .night312: return "\u{f028}" - case .night313: return "\u{f028}" - case .night314: return "\u{f028}" - case .night321: return "\u{f02b}" - case .night500: return "\u{f02b}" - case .night501: return "\u{f028}" - case .night502: return "\u{f028}" - case .night503: return "\u{f028}" - case .night504: return "\u{f028}" - case .night511: return "\u{f026}" - case .night520: return "\u{f029}" - case .night521: return "\u{f029}" - case .night522: return "\u{f029}" - case .night531: return "\u{f02c}" - case .night600: return "\u{f02a}" - case .night601: return "\u{f0b4}" - case .night602: return "\u{f02a}" - case .night611: return "\u{f026}" - case .night612: return "\u{f026}" - case .night615: return "\u{f026}" - case .night616: return "\u{f026}" - case .night620: return "\u{f026}" - case .night621: return "\u{f02a}" - case .night622: return "\u{f02a}" - case .night701: return "\u{f029}" - case .night711: return "\u{f062}" - case .night721: return "\u{f0b6}" - case .night731: return "\u{f063}" - case .night741: return "\u{f04a}" - case .night761: return "\u{f063}" - case .night762: return "\u{f063}" - case .night781: return "\u{f056}" - case .night800: return "\u{f02e}" - case .night801: return "\u{f022}" - case .night802: return "\u{f022}" - case .night803: return "\u{f022}" - case .night804: return "\u{f086}" - case .night900: return "\u{f056}" - case .night902: return "\u{f073}" - case .night903: return "\u{f076}" - case .night904: return "\u{f072}" - case .night906: return "\u{f024}" - case .night957: return "\u{f050}" - } - } - } - - init(condition: Int, iconString: String) { - var rawValue: String - - // if iconString has 'n', it means night time. - if iconString.range(of: "n") != nil { - rawValue = "night" + String(condition) - } else { - // day time - rawValue = "day" + String(condition) - } - - guard let iconType = IconType(rawValue: rawValue) else { - iconText = "" - return - } - iconText = iconType.description - } -} -// swiftlint:enable type_body_length diff --git a/SwiftWeather/WeatherServiceProtocol.swift b/SwiftWeather/WeatherServiceProtocol.swift deleted file mode 100644 index 8a009ed..0000000 --- a/SwiftWeather/WeatherServiceProtocol.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by Jake Lin on 9/2/15. -// Copyright (c) 2015 Jake Lin. All rights reserved. -// - -import Foundation -import CoreLocation - -typealias WeatherCompletionHandler = (Weather?, SWError?) -> Void - -protocol WeatherServiceProtocol { - func retrieveWeatherInfo(_ location: CLLocation, completionHandler: @escaping WeatherCompletionHandler) -} diff --git a/SwiftWeather/WeatherViewController.swift b/SwiftWeather/WeatherViewController.swift deleted file mode 100644 index 6bc79b4..0000000 --- a/SwiftWeather/WeatherViewController.swift +++ /dev/null @@ -1,138 +0,0 @@ -// -// Created by Jake Lin on 8/18/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import UIKit -import FacebookShare -import CoreSpotlight -import MobileCoreServices - -//MARK: - UIViewController Properties -class WeatherViewController: UIViewController { - - //MARK: - IBOutlets - @IBOutlet weak var locationLabel: UILabel! - @IBOutlet weak var iconLabel: UILabel! - @IBOutlet weak var temperatureLabel: UILabel! - @IBOutlet var forecastViews: [ForecastView]! - - let identifier = "WeatherIdentifier" - - //MARK: - Super Methods - override func viewDidLoad() { - super.viewDidLoad() - viewModel = WeatherViewModel() - viewModel?.startLocationService() - setAccessibilityIdentifiers() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - configureLabels() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - configureLabelsWithAnimation() - } - - //MARK: ViewModel - var viewModel: WeatherViewModel? { - didSet { - viewModel?.location.observe { - [unowned self] in - self.locationLabel.text = $0 - - let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String) - attributeSet.title = self.locationLabel.text - - let item = CSSearchableItem(uniqueIdentifier: self.identifier, domainIdentifier: "com.rushjet.SwiftWeather", attributeSet: attributeSet) - CSSearchableIndex.default().indexSearchableItems([item]){error in - if let error = error { - print("Indexing error: \(error.localizedDescription)") - } else { - print("Location item successfully indexed") - } - } - } - - viewModel?.iconText.observe { - [unowned self] in - self.iconLabel.text = $0 - } - - viewModel?.temperature.observe { - [unowned self] in - self.temperatureLabel.text = $0 - } - - viewModel?.forecasts.observe { - [unowned self] (forecastViewModels) in - if forecastViewModels.count >= 4 { - for (index, forecastView) in self.forecastViews.enumerated() { - forecastView.loadViewModel(forecastViewModels[index]) - } - } - } - } - } - - //MARK: Accessibility - func setAccessibilityIdentifiers() { - locationLabel.accessibilityIdentifier = "a11y_current_city" - iconLabel.accessibilityIdentifier = "a11y_wheather_icon" - temperatureLabel.accessibilityIdentifier = "a11y_wheather_temperature" - } - - //MARK: Functions - func configureLabels(){ - locationLabel.center.x -= view.bounds.width - iconLabel.center.x -= view.bounds.width - temperatureLabel.center.x -= view.bounds.width - iconLabel.alpha = 0.0 - locationLabel.alpha = 0.0 - temperatureLabel.alpha = 0.0 - } - - func configureLabelsWithAnimation(){ - UIView.animate(withDuration: 0.5, animations: { - self.locationLabel.center.x += self.view.bounds.width - }) - - UIView.animate(withDuration: 0.5, delay: 0.3, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: [], animations: { - self.iconLabel.center.x += self.view.bounds.width - }, completion: nil) - - UIView.animate(withDuration: 0.5, delay: 0.4, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: [], animations: { - self.temperatureLabel.center.x += self.view.bounds.width - }, completion: nil) - - UIView.animate(withDuration: 0.5, delay: 0.3, options: [], animations: { - self.iconLabel.alpha = 1.0 - }, completion: nil) - - UIView.animate(withDuration: 0.5, delay: 0.4, options: [], animations: { - self.locationLabel.alpha = 1.0 - }, completion: nil) - - UIView.animate(withDuration: 0.5, delay: 0.5, options: [], animations: { - self.temperatureLabel.alpha = 1.0 - }, completion: nil) - } - - //MARK: Actions - @IBAction func shareButtonPressed(_ sender: Any) { - shareOnFacebook() - } - - func shareOnFacebook(){ - let photo = Photo(image: #imageLiteral(resourceName: "background"), userGenerated: false) - let myContent = PhotoShareContent(photos: [photo]) - let shareDialog = ShareDialog(content: myContent) - shareDialog.mode = .native - shareDialog.failsOnInvalidData = true - - try? shareDialog.show() - } -} diff --git a/SwiftWeather/WeatherViewModel.swift b/SwiftWeather/WeatherViewModel.swift deleted file mode 100644 index e826951..0000000 --- a/SwiftWeather/WeatherViewModel.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// Created by Jake Lin on 8/26/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Foundation -import CoreLocation - -class WeatherViewModel { - // MARK: - Constants - fileprivate let emptyString = "" - - // MARK: - Properties - let hasError: Observable - let errorMessage: Observable - - let location: Observable - let iconText: Observable - let temperature: Observable - let forecasts: Observable<[ForecastViewModel]> - - // MARK: - Services - fileprivate var locationService: LocationService - fileprivate var weatherService: WeatherServiceProtocol - - // MARK: - init - init() { - hasError = Observable(false) - errorMessage = Observable(nil) - - location = Observable(emptyString) - iconText = Observable(emptyString) - temperature = Observable(emptyString) - forecasts = Observable([]) - - // Can put Dependency Injection here - locationService = LocationService() - weatherService = OpenWeatherMapService() - } - - // MARK: - public - func startLocationService() { - locationService.delegate = self - locationService.requestLocation() - } - - // MARK: - private - fileprivate func update(_ weather: Weather) { - hasError.value = false - errorMessage.value = nil - - location.value = weather.location - iconText.value = weather.iconText - temperature.value = weather.temperature - - let tempForecasts = weather.forecasts.map { forecast in - return ForecastViewModel(forecast) - } - forecasts.value = tempForecasts - } - - fileprivate func update(_ error: SWError) { - hasError.value = true - - switch error.errorCode { - case .urlError: - errorMessage.value = "The weather service is not working." - case .networkRequestFailed: - errorMessage.value = "The network appears to be down." - case .jsonSerializationFailed: - errorMessage.value = "We're having trouble processing weather data." - case .jsonParsingFailed: - errorMessage.value = "We're having trouble parsing weather data." - case .unableToFindLocation: - errorMessage.value = "We're having trouble getting user location." - } - - location.value = emptyString - iconText.value = emptyString - temperature.value = emptyString - self.forecasts.value = [] - } -} - -// MARK: LocationServiceDelegate -extension WeatherViewModel: LocationServiceDelegate { - func locationDidUpdate(_ service: LocationService, location: CLLocation) { - weatherService.retrieveWeatherInfo(location) { (weather, error) -> Void in - DispatchQueue.main.async(execute: { - if let unwrappedError = error { - print(unwrappedError) - self.update(unwrappedError) - return - } - - guard let unwrappedWeather = weather else { - return - } - self.update(unwrappedWeather) - }) - } - } - - func locationDidFail(withError error: SWError) { - self.update(error) - } -} diff --git a/SwiftWeatherTests/UnitTests/ForecastDateTimeSpec.swift b/SwiftWeatherTests/UnitTests/ForecastDateTimeSpec.swift deleted file mode 100644 index d9c89e7..0000000 --- a/SwiftWeatherTests/UnitTests/ForecastDateTimeSpec.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Created by Tran Xuan Hoang on 12/4/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Quick -import Nimble -@testable import SwiftWeather - -class ForecastDateTimeSpec: QuickSpec { - - private let testTimeZone = TimeZone(abbreviation: "UTC+11:00")! - - override func spec() { - describe("#init") { - it("should init with the rawDate correctly assigned") { - var forecastDateTime = ForecastDateTime(date: 1488096060, timeZone: self.testTimeZone) - expect(forecastDateTime.rawDate).to(beCloseTo(1488096060)) - forecastDateTime = ForecastDateTime(date: 0, timeZone: self.testTimeZone) - expect(forecastDateTime.rawDate).to(beCloseTo(0)) - } - } - - describe("#shortTime") { - it("should return the correct shortTime string with format HH:mm") { - var forecastDateTime = ForecastDateTime(date: 1488096060, timeZone: self.testTimeZone) - expect(forecastDateTime.shortTime).to(equal("7:01 PM")) - forecastDateTime = ForecastDateTime(date: 1488103200, timeZone: self.testTimeZone) - expect(forecastDateTime.shortTime).to(equal("9:00 PM")) - } - } - } -} diff --git a/SwiftWeatherTests/UnitTests/ForecastSpec.swift b/SwiftWeatherTests/UnitTests/ForecastSpec.swift deleted file mode 100644 index 49db22e..0000000 --- a/SwiftWeatherTests/UnitTests/ForecastSpec.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// Created by Tran Xuan Hoang on 12/4/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Quick -import Nimble -@testable import SwiftWeather - -class ForecastSpec: QuickSpec { - - override func spec() { - - describe("#init") { - it("should have time, iconText, temperature") { - let forecast = Forecast(time: "time", iconText: "iconText", temperature: "temperature") - expect(forecast.time).to(equal("time")) - expect(forecast.iconText).to(equal("iconText")) - expect(forecast.temperature).to(equal("temperature")) - } - } - - } - -} diff --git a/SwiftWeatherTests/UnitTests/TemperatureSpec.swift b/SwiftWeatherTests/UnitTests/TemperatureSpec.swift deleted file mode 100644 index ad8c7d6..0000000 --- a/SwiftWeatherTests/UnitTests/TemperatureSpec.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// Created by Tran Xuan Hoang on 12/4/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Quick -import Nimble -@testable import SwiftWeather - -class TemperatureSpec: QuickSpec { - - override func spec() { - - describe("#init(country:openWeatherMapDegrees:)") { - context("country is US") { - it("should convert temperature to Fahrenheit") { - let temperature = Temperature(country: "US", openWeatherMapDegrees: 20) - expect(temperature.degrees).to(equal("-424.0" + "\u{f045}")) - } - } - context("country is not US") { - it("should convert to Celsius") { - let temperature = Temperature(country: "ABC", openWeatherMapDegrees: 20) - expect(temperature.degrees).to(equal("-253.0" + "\u{f03c}")) - } - } - } - - } - -} diff --git a/SwiftWeatherTests/UnitTests/WeatherBuilderSpec.swift b/SwiftWeatherTests/UnitTests/WeatherBuilderSpec.swift deleted file mode 100644 index e5bbdf3..0000000 --- a/SwiftWeatherTests/UnitTests/WeatherBuilderSpec.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Tran Xuan Hoang on 12/5/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Quick -import Nimble -@testable import SwiftWeather - -class WeatherBuilderSpec: QuickSpec { - - override func spec() { - - describe("#build") { - it("should create a Weather model with all the properties correctly") { - let builder = WeatherBuilder(location: "location", iconText: "iconText", - temperature: "temperature", forecasts: []) - let weather = builder.build() - expect(weather.location).to(equal("location")) - expect(weather.iconText).to(equal("iconText")) - expect(weather.temperature).to(equal("temperature")) - expect(weather.forecasts.count).to(equal(0)) - } - } - - } - -} diff --git a/SwiftWeatherTests/UnitTests/WeatherIconSpec.swift b/SwiftWeatherTests/UnitTests/WeatherIconSpec.swift deleted file mode 100644 index 9670f72..0000000 --- a/SwiftWeatherTests/UnitTests/WeatherIconSpec.swift +++ /dev/null @@ -1,172 +0,0 @@ -// -// Created by Tran Xuan Hoang on 12/4/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Quick -import Nimble -@testable import SwiftWeather - -class WeatherIconSpec: QuickSpec { - - let dayDictionary = [ - 200: "\u{f010}", - 201: "\u{f010}", - 202: "\u{f010}", - 210: "\u{f005}", - 211: "\u{f005}", - 212: "\u{f005}", - 221: "\u{f005}", - 230: "\u{f010}", - 231: "\u{f010}", - 232: "\u{f010}", - 300: "\u{f00b}", - 301: "\u{f00b}", - 302: "\u{f008}", - 310: "\u{f008}", - 311: "\u{f008}", - 312: "\u{f008}", - 313: "\u{f008}", - 314: "\u{f008}", - 321: "\u{f00b}", - 500: "\u{f00b}", - 501: "\u{f008}", - 502: "\u{f008}", - 503: "\u{f008}", - 504: "\u{f008}", - 511: "\u{f006}", - 520: "\u{f009}", - 521: "\u{f009}", - 522: "\u{f009}", - 531: "\u{f00e}", - 600: "\u{f00a}", - 601: "\u{f0b2}", - 602: "\u{f00a}", - 611: "\u{f006}", - 612: "\u{f006}", - 615: "\u{f006}", - 616: "\u{f006}", - 620: "\u{f006}", - 621: "\u{f00a}", - 622: "\u{f00a}", - 701: "\u{f009}", - 711: "\u{f062}", - 721: "\u{f0b6}", - 731: "\u{f063}", - 741: "\u{f003}", - 761: "\u{f063}", - 762: "\u{f063}", - 781: "\u{f056}", - 800: "\u{f00d}", - 801: "\u{f000}", - 802: "\u{f000}", - 803: "\u{f000}", - 804: "\u{f00c}", - 900: "\u{f056}", - 902: "\u{f073}", - 903: "\u{f076}", - 904: "\u{f072}", - 906: "\u{f004}", - 957: "\u{f050}" - ] - let nightDictionary = [ - 200: "\u{f02d}", - 201: "\u{f02d}", - 202: "\u{f02d}", - 210: "\u{f025}", - 211: "\u{f025}", - 212: "\u{f025}", - 221: "\u{f025}", - 230: "\u{f02d}", - 231: "\u{f02d}", - 232: "\u{f02d}", - 300: "\u{f02b}", - 301: "\u{f02b}", - 302: "\u{f028}", - 310: "\u{f028}", - 311: "\u{f028}", - 312: "\u{f028}", - 313: "\u{f028}", - 314: "\u{f028}", - 321: "\u{f02b}", - 500: "\u{f02b}", - 501: "\u{f028}", - 502: "\u{f028}", - 503: "\u{f028}", - 504: "\u{f028}", - 511: "\u{f026}", - 520: "\u{f029}", - 521: "\u{f029}", - 522: "\u{f029}", - 531: "\u{f02c}", - 600: "\u{f02a}", - 601: "\u{f0b4}", - 602: "\u{f02a}", - 611: "\u{f026}", - 612: "\u{f026}", - 615: "\u{f026}", - 616: "\u{f026}", - 620: "\u{f026}", - 621: "\u{f02a}", - 622: "\u{f02a}", - 701: "\u{f029}", - 711: "\u{f062}", - 721: "\u{f0b6}", - 731: "\u{f063}", - 741: "\u{f04a}", - 761: "\u{f063}", - 762: "\u{f063}", - 781: "\u{f056}", - 800: "\u{f02e}", - 801: "\u{f022}", - 802: "\u{f022}", - 803: "\u{f022}", - 804: "\u{f086}", - 900: "\u{f056}", - 902: "\u{f073}", - 903: "\u{f076}", - 904: "\u{f072}", - 906: "\u{f024}", - 957: "\u{f050}" - ] - - override func spec() { - - describe("#init(condition:,iconString:)") { - context("day") { - context("invalid condition Int") { - self.expectWeatherIconWithCondition(999, isDay: true, toHaveIconTextEqualToString: "") - } - context("valid condition Int") { - for (condition, description) in self.dayDictionary { - self.expectWeatherIconWithCondition(condition, isDay: true, - toHaveIconTextEqualToString: description) - } - } - } - context("night") { - context("invalid condition Int") { - self.expectWeatherIconWithCondition(999, isDay: false, toHaveIconTextEqualToString: "") - } - context("valid condition Int") { - for (condition, description) in self.nightDictionary { - self.expectWeatherIconWithCondition(condition, isDay: false, - toHaveIconTextEqualToString: description) - } - } - } - } - - } - -} - -extension WeatherIconSpec { - - func expectWeatherIconWithCondition(_ condition: Int, isDay: Bool, - toHaveIconTextEqualToString description: String) { - let weatherIcon = WeatherIcon(condition: condition, iconString: isDay ? "day" : "night") - expect(weatherIcon.iconText).to(equal(description)) - } - -} diff --git a/SwiftWeatherTests/UnitTests/WeatherSpec.swift b/SwiftWeatherTests/UnitTests/WeatherSpec.swift deleted file mode 100644 index 1f370c6..0000000 --- a/SwiftWeatherTests/UnitTests/WeatherSpec.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by Tran Xuan Hoang on 12/4/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import Quick -import Nimble -@testable import SwiftWeather - -class WeatherSpec: QuickSpec { - - override func spec() { - - describe("#init") { - it("should have location, iconText, temperature and forecasts") { - let weather = Weather(location: "location", iconText: "iconText", - temperature: "temperature", forecasts: []) - expect(weather.location).to(equal("location")) - expect(weather.iconText).to(equal("iconText")) - expect(weather.temperature).to(equal("temperature")) - expect(weather.forecasts.count).to(equal(0)) - } - } - - } - -} diff --git a/SwiftWeatherUITests/SwiftWeatherUITests.swift b/SwiftWeatherUITests/SwiftWeatherUITests.swift deleted file mode 100644 index 6d18bed..0000000 --- a/SwiftWeatherUITests/SwiftWeatherUITests.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// Created by Jake Lin on 8/18/15. -// Copyright © 2015 Jake Lin. All rights reserved. -// - -import XCTest -import Quick -import Nimble - -class SwiftWeatherUITests: QuickSpec { - - override func spec() { - let app = XCUIApplication() - - beforeSuite { - self.continueAfterFailure = false - - app.launch() - } - - describe("a wheather viewcontroller") { - context("location service is enabled") { - context("when in portrait") { - beforeEach { - XCUIDevice.shared.orientation = .portrait - } - itBehavesLike("a properly laidout wheather viewcontroller") - } - - context("when in landscape") { - beforeEach { - XCUIDevice.shared.orientation = .landscapeLeft - } - itBehavesLike("a properly laidout wheather viewcontroller") - } - } - } - } -} - -class RegularWheatherViewControllerConfiguration: QuickConfiguration { - override class func configure(_ configuration: Configuration) { - let app = XCUIApplication() - let window = app.windows.element(boundBy: 0) - - sharedExamples("a properly laidout wheather viewcontroller") { (context: SharedExampleContext) in - it("shows city") { - let cityLabel = app.staticTexts["a11y_current_city"] - - expect(cityLabel.exists).to(beTruthy()) - expect(window.frame.contains(cityLabel.frame)).to(beTruthy()) - } - - it("shows wheather icon") { - let wheatherIconLabel = app.staticTexts["a11y_wheather_icon"] - - expect(wheatherIconLabel.exists).to(beTruthy()) - expect(window.frame.contains(wheatherIconLabel.frame)).to(beTruthy()) - } - - it("shows wheather temperature") { - let wheatherTemperatureLabel = app.staticTexts["a11y_wheather_temperature"] - - expect(wheatherTemperatureLabel.exists).to(beTruthy()) - expect(window.frame.contains(wheatherTemperatureLabel.frame)).to(beTruthy()) - } - } - } -} - diff --git a/buddybuild_postclone.sh b/buddybuild_postclone.sh deleted file mode 100644 index 7b33dcd..0000000 --- a/buddybuild_postclone.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -cd /Users/buddybuild/workspace - -mkdir .access_tokens -echo $OPENWEATHERMAP > .access_tokens/openweathermap