├── .gitignore
├── Design
├── Template-AppIcons-iOS-assets
│ ├── Icon-App-20x20@1x.png
│ ├── Icon-App-29x29@1x.png
│ ├── Icon-App-29x29@2x.png
│ ├── Icon-App-29x29@3x.png
│ ├── Icon-App-40x40@1x.png
│ ├── Icon-App-40x40@2x.png
│ ├── Icon-App-40x40@3x.png
│ ├── Icon-App-512x512@1x.png
│ ├── Icon-App-512x512@2x.png
│ ├── Icon-App-512x512@3x.png
│ ├── Icon-App-60x60@1x.png
│ ├── Icon-App-60x60@2x.png
│ ├── Icon-App-60x60@3x.png
│ ├── Icon-App-76x76@1x.png
│ ├── Icon-App-76x76@2x.png
│ ├── Icon-App-76x76@3x.png
│ └── Icon-App-83.5x83.5@2x.png
├── Template-AppIcons-iOS.psd
├── arrow-assets
│ ├── Arrow.png
│ ├── scaled-at-25
│ │ ├── Arrow.png
│ │ ├── Arrow@2x.png
│ │ └── Arrow@3x.png
│ ├── scaled-at-50
│ │ └── Arrow@2x.png
│ └── scaled-at-75
│ │ └── Arrow@3x.png
├── arrow.psd
├── wx-icons-highres-assets
│ ├── Cloudy.png
│ ├── Haze.png
│ ├── Mist.png
│ ├── Moon.png
│ ├── Mostly-sunny-day.png
│ ├── Mostly-sunny-night.png
│ ├── Partly-sunny-day.png
│ ├── Partly-sunny-night.png
│ ├── Snow.png
│ ├── Sun.png
│ ├── Thunder-cloud.png
│ ├── Thunder.png
│ ├── Wind.png
│ ├── scaled-at-25
│ │ ├── Cloudy.png
│ │ ├── Haze.png
│ │ ├── Mist.png
│ │ ├── Moon.png
│ │ ├── Mostly-sunny-day.png
│ │ ├── Mostly-sunny-night.png
│ │ ├── Partly-sunny-day.png
│ │ ├── Partly-sunny-night.png
│ │ ├── Snow.png
│ │ ├── Sun.png
│ │ ├── Thunder-cloud.png
│ │ ├── Thunder.png
│ │ └── Wind.png
│ ├── scaled-at-50
│ │ ├── Cloudy@2x.png
│ │ ├── Haze@2x.png
│ │ ├── Mist@2x.png
│ │ ├── Moon@2x.png
│ │ ├── Mostly-sunny-day@2x.png
│ │ ├── Mostly-sunny-night@2x.png
│ │ ├── Partly-sunny-day@2x.png
│ │ ├── Partly-sunny-night@2x.png
│ │ ├── Snow@2x.png
│ │ ├── Sun@2x.png
│ │ ├── Thunder-cloud@2x.png
│ │ ├── Thunder@2x.png
│ │ └── Wind@2x.png
│ └── scaled-at-75
│ │ ├── Cloudy@3x.png
│ │ ├── Haze@3x.png
│ │ ├── Mist@3x.png
│ │ ├── Moon@3x.png
│ │ ├── Mostly-sunny-day@3x.png
│ │ ├── Mostly-sunny-night@3x.png
│ │ ├── Partly-sunny-day@3x.png
│ │ ├── Partly-sunny-night@3x.png
│ │ ├── Snow@3x.png
│ │ ├── Sun@3x.png
│ │ ├── Thunder-cloud@3x.png
│ │ ├── Thunder@3x.png
│ │ └── Wind@3x.png
├── wx-icons-highres.psd
├── x-assets
│ ├── Delete.png
│ ├── scaled-at-25
│ │ └── Delete.png
│ ├── scaled-at-50
│ │ └── Delete@2x.png
│ └── scaled-at-75
│ │ └── Delete@3x.png
└── x.psd
├── LatLon.request
├── Playground.playground
├── Contents.swift
└── contents.xcplayground
├── Podfile
├── Podfile.lock
├── Pods
├── IGListKit
│ ├── LICENSE.md
│ ├── README.md
│ └── Source
│ │ ├── IGListAdapter.h
│ │ ├── IGListAdapter.m
│ │ ├── IGListAdapterDataSource.h
│ │ ├── IGListAdapterDelegate.h
│ │ ├── IGListAdapterUpdater.h
│ │ ├── IGListAdapterUpdater.m
│ │ ├── IGListAdapterUpdaterDelegate.h
│ │ ├── IGListAssert.h
│ │ ├── IGListBatchUpdateData.h
│ │ ├── IGListBatchUpdateData.mm
│ │ ├── IGListCollectionContext.h
│ │ ├── IGListCollectionView.h
│ │ ├── IGListCollectionView.m
│ │ ├── IGListDiff.h
│ │ ├── IGListDiff.mm
│ │ ├── IGListDiffable.h
│ │ ├── IGListDisplayDelegate.h
│ │ ├── IGListExperiments.h
│ │ ├── IGListGridCollectionViewLayout.h
│ │ ├── IGListGridCollectionViewLayout.m
│ │ ├── IGListIndexPathResult.h
│ │ ├── IGListIndexPathResult.m
│ │ ├── IGListIndexSetResult.h
│ │ ├── IGListIndexSetResult.m
│ │ ├── IGListKit.h
│ │ ├── IGListMacros.h
│ │ ├── IGListMoveIndex.h
│ │ ├── IGListMoveIndex.m
│ │ ├── IGListMoveIndexPath.h
│ │ ├── IGListMoveIndexPath.m
│ │ ├── IGListReloadDataUpdater.h
│ │ ├── IGListReloadDataUpdater.m
│ │ ├── IGListScrollDelegate.h
│ │ ├── IGListSectionController.h
│ │ ├── IGListSectionController.m
│ │ ├── IGListSectionType.h
│ │ ├── IGListSingleSectionController.h
│ │ ├── IGListSingleSectionController.m
│ │ ├── IGListStackedSectionController.h
│ │ ├── IGListStackedSectionController.m
│ │ ├── IGListSupplementaryViewSource.h
│ │ ├── IGListUpdatingDelegate.h
│ │ ├── IGListWorkingRangeDelegate.h
│ │ ├── Internal
│ │ ├── IGListAdapterInternal.h
│ │ ├── IGListAdapterProxy.h
│ │ ├── IGListAdapterProxy.m
│ │ ├── IGListAdapterUpdaterInternal.h
│ │ ├── IGListDisplayHandler.h
│ │ ├── IGListDisplayHandler.m
│ │ ├── IGListIndexPathResultInternal.h
│ │ ├── IGListIndexSetResultInternal.h
│ │ ├── IGListMoveIndexInternal.h
│ │ ├── IGListMoveIndexPathInternal.h
│ │ ├── IGListSectionControllerInternal.h
│ │ ├── IGListSectionMap.h
│ │ ├── IGListSectionMap.m
│ │ ├── IGListStackedSectionControllerInternal.h
│ │ ├── IGListWorkingRangeHandler.h
│ │ ├── IGListWorkingRangeHandler.mm
│ │ ├── UICollectionView+IGListBatchUpdateData.h
│ │ └── UICollectionView+IGListBatchUpdateData.m
│ │ ├── NSNumber+IGListDiffable.h
│ │ ├── NSNumber+IGListDiffable.m
│ │ ├── NSString+IGListDiffable.h
│ │ └── NSString+IGListDiffable.m
├── Local Podspecs
│ └── IGListKit.podspec.json
├── Manifest.lock
├── Pods.xcodeproj
│ └── project.pbxproj
└── Target Support Files
│ ├── IGListKit
│ ├── IGListKit-dummy.m
│ ├── IGListKit-prefix.pch
│ ├── IGListKit-umbrella.h
│ ├── IGListKit.modulemap
│ ├── IGListKit.xcconfig
│ └── Info.plist
│ ├── Pods-SimpleWeather
│ ├── Info.plist
│ ├── Pods-SimpleWeather-acknowledgements.markdown
│ ├── Pods-SimpleWeather-acknowledgements.plist
│ ├── Pods-SimpleWeather-dummy.m
│ ├── Pods-SimpleWeather-frameworks.sh
│ ├── Pods-SimpleWeather-resources.sh
│ ├── Pods-SimpleWeather-umbrella.h
│ ├── Pods-SimpleWeather.debug.xcconfig
│ ├── Pods-SimpleWeather.modulemap
│ └── Pods-SimpleWeather.release.xcconfig
│ └── Pods-SimpleWeatherTests
│ ├── Info.plist
│ ├── Pods-SimpleWeatherTests-acknowledgements.markdown
│ ├── Pods-SimpleWeatherTests-acknowledgements.plist
│ ├── Pods-SimpleWeatherTests-dummy.m
│ ├── Pods-SimpleWeatherTests-frameworks.sh
│ ├── Pods-SimpleWeatherTests-resources.sh
│ ├── Pods-SimpleWeatherTests-umbrella.h
│ ├── Pods-SimpleWeatherTests.debug.xcconfig
│ ├── Pods-SimpleWeatherTests.modulemap
│ └── Pods-SimpleWeatherTests.release.xcconfig
├── README.md
├── SimpleWeather.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── SimpleWeather.xcworkspace
└── contents.xcworkspacedata
├── SimpleWeather
├── .DS_Store
├── AppDelegate.swift
├── Classes
│ ├── .DS_Store
│ ├── Models
│ │ ├── Alert+JSONConvertible.swift
│ │ ├── Alert.swift
│ │ ├── Astronomy+JSONConvertible.swift
│ │ ├── Astronomy.swift
│ │ ├── Condition+Icon.swift
│ │ ├── Condition.swift
│ │ ├── ConditionsSection+Forecast.swift
│ │ ├── ConditionsSection+IGListDiffable.swift
│ │ ├── ConditionsSection+ViewModels.swift
│ │ ├── ConditionsSection.swift
│ │ ├── DailyForecastSection+Forecast.swift
│ │ ├── DailyForecastSection+IGListDiffable.swift
│ │ ├── DailyForecastSection.swift
│ │ ├── EmbeddedSection+Forecast.swift
│ │ ├── EmbeddedSection+IGListDiffable.swift
│ │ ├── EmbeddedSection.swift
│ │ ├── Forecast+JSONConvertible.swift
│ │ ├── Forecast.swift
│ │ ├── ForecastDay+Equatable.swift
│ │ ├── ForecastDay+JSONConvertible.swift
│ │ ├── ForecastDay.swift
│ │ ├── ForecastHourly+JSONConvertible.swift
│ │ ├── ForecastHourly.swift
│ │ ├── JSONConvertible.swift
│ │ ├── Location+JSONConvertible.swift
│ │ ├── Location.swift
│ │ ├── MKLocalSearchCompletion+IGListDiffable.swift
│ │ ├── Observation+JSONConvertible.swift
│ │ ├── Observation.swift
│ │ ├── RadarSection+IGListDiffable.swift
│ │ ├── RadarSection.swift
│ │ ├── SavedLocation+Equatable.swift
│ │ ├── SavedLocation+IGListDiffable.swift
│ │ ├── SavedLocation+NSEncoding.swift
│ │ ├── SavedLocation.swift
│ │ └── Wind.swift
│ ├── SectionControllers
│ │ ├── ConditionsSectionController.swift
│ │ ├── DailyForecastSectionController.swift
│ │ ├── EmbeddedAdapterSectionController.swift
│ │ ├── ErrorSectionController.swift
│ │ ├── HourlyForecastSectionController.swift
│ │ ├── RadarSectionController.swift
│ │ ├── SavedLocationSectionController.swift
│ │ └── SearchResultSectionController.swift
│ ├── Systems
│ │ ├── API.swift
│ │ ├── CurrentLocationRequester.swift
│ │ ├── Date+Daytime.swift
│ │ ├── KeypathParsing.swift
│ │ ├── MKCoordinateRegion+Bounds.swift
│ │ ├── MKCoordinateRegion+Convenience.swift
│ │ ├── SavedLocationStore.swift
│ │ ├── URL+DiskCacheKey.swift
│ │ ├── URLBuilder+Wunderground.swift
│ │ ├── URLBuilder.swift
│ │ ├── URLSession+Convenience.swift
│ │ ├── URLSessionDataTaskResponse.swift
│ │ └── ViewPulser.swift
│ ├── ViewControllers
│ │ ├── AlertsViewController.swift
│ │ ├── SavedLocationsViewController.swift
│ │ ├── SearchViewController.swift
│ │ └── WeatherViewController.swift
│ ├── ViewModels
│ │ ├── ConditionsCellViewModel.swift
│ │ ├── ConditionsDetailCellViewModel.swift
│ │ ├── ErrorViewModel+IGListDiffable.swift
│ │ ├── ErrorViewModel.swift
│ │ ├── ForecastDayCellViewModel+Equatable.swift
│ │ ├── ForecastDayCellViewModel.swift
│ │ ├── ForecastHourCellViewModel+IGListDiffable.swift
│ │ ├── ForecastHourCellViewModel.swift
│ │ ├── ForecastHourlyDataSource.swift
│ │ ├── RadarOverlay.swift
│ │ ├── SavedLocationCellViewModel.swift
│ │ ├── WeatherNavigationViewModel.swift
│ │ └── WeatherStateMachine.swift
│ └── Views
│ │ ├── BorderedButton.swift
│ │ ├── ConditionsCell.swift
│ │ ├── ConditionsDetailCell.swift
│ │ ├── EmbeddedCollectionViewCell.swift
│ │ ├── ForecastDayCell.swift
│ │ ├── ForecastHourCell.swift
│ │ ├── HighLowAttributedString.swift
│ │ ├── LoadingView.swift
│ │ ├── MapCell.swift
│ │ ├── RadarOverlayView.swift
│ │ ├── RetryCell.swift
│ │ ├── RoundedCollectionViewCell.swift
│ │ ├── SavedLocationCell.swift
│ │ ├── SavedLocationContentView.swift
│ │ ├── SavedLocationDeleteView.swift
│ │ └── SearchResultCell.swift
├── Resources
│ ├── .DS_Store
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x-1.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x-1.png
│ │ │ ├── Icon-App-40x40@1x-2.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x-1.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@1x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ ├── Arrow.imageset
│ │ │ ├── Arrow.png
│ │ │ ├── Arrow@2x.png
│ │ │ ├── Arrow@3x.png
│ │ │ └── Contents.json
│ │ ├── Cloudy.imageset
│ │ │ ├── Cloudy.png
│ │ │ ├── Cloudy@2x.png
│ │ │ ├── Cloudy@3x.png
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── Delete.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Delete.png
│ │ │ ├── Delete@2x.png
│ │ │ └── Delete@3x.png
│ │ ├── Disclosure.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Disclosure.png
│ │ │ ├── Disclosure@2x.png
│ │ │ └── Disclosure@3x.png
│ │ ├── Haze.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Haze.png
│ │ │ ├── Haze@2x.png
│ │ │ └── Haze@3x.png
│ │ ├── Mist.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Mist.png
│ │ │ ├── Mist@2x.png
│ │ │ └── Mist@3x.png
│ │ ├── Moon.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Moon.png
│ │ │ ├── Moon@2x.png
│ │ │ └── Moon@3x.png
│ │ ├── Mostly-sunny-day.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Mostly-sunny-day.png
│ │ │ ├── Mostly-sunny-day@2x.png
│ │ │ └── Mostly-sunny-day@3x.png
│ │ ├── Mostly-sunny-night.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Mostly-sunny-night.png
│ │ │ ├── Mostly-sunny-night@2x.png
│ │ │ └── Mostly-sunny-night@3x.png
│ │ ├── Partly-sunny-day.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Partly-sunny-day.png
│ │ │ ├── Partly-sunny-day@2x.png
│ │ │ └── Partly-sunny-day@3x.png
│ │ ├── Snow.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Snow.png
│ │ │ ├── Snow@2x.png
│ │ │ └── Snow@3x.png
│ │ ├── Sun.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Sun.png
│ │ │ ├── Sun@2x.png
│ │ │ └── Sun@3x.png
│ │ ├── Thunder-cloud.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Thunder-cloud.png
│ │ │ ├── Thunder-cloud@2x.png
│ │ │ └── Thunder-cloud@3x.png
│ │ └── Wind.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Wind.png
│ │ │ ├── Wind@2x.png
│ │ │ └── Wind@3x.png
│ ├── Info.plist
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
└── SimpleWeather-Briding-Header.h
├── SimpleWeatherTests
├── APISessionTests.swift
├── ConditionsSectionTests.swift
├── DateTests.swift
├── Info.plist
├── KeypathParsingTests.swift
├── ModelTests.swift
├── WeatherNavigationViewModelTests.swift
└── sample_response.json
├── SimpleWeatherUITests
├── Info.plist
└── SimpleWeatherUITests.swift
├── fastlane
├── Appfile
├── Fastfile
├── README.md
├── Snapfile
├── SnapshotHelper.swift
└── SnapshotHelper2-3.swift
└── report.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/Preview.html
64 | fastlane/screenshots
65 | fastlane/test_output
66 |
67 | .DS_Store
68 |
69 |
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-512x512@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-512x512@1x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-512x512@2x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-512x512@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-512x512@3x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-60x60@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-60x60@1x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-76x76@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-76x76@3x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS-assets/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS-assets/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/Design/Template-AppIcons-iOS.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/Template-AppIcons-iOS.psd
--------------------------------------------------------------------------------
/Design/arrow-assets/Arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/arrow-assets/Arrow.png
--------------------------------------------------------------------------------
/Design/arrow-assets/scaled-at-25/Arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/arrow-assets/scaled-at-25/Arrow.png
--------------------------------------------------------------------------------
/Design/arrow-assets/scaled-at-25/Arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/arrow-assets/scaled-at-25/Arrow@2x.png
--------------------------------------------------------------------------------
/Design/arrow-assets/scaled-at-25/Arrow@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/arrow-assets/scaled-at-25/Arrow@3x.png
--------------------------------------------------------------------------------
/Design/arrow-assets/scaled-at-50/Arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/arrow-assets/scaled-at-50/Arrow@2x.png
--------------------------------------------------------------------------------
/Design/arrow-assets/scaled-at-75/Arrow@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/arrow-assets/scaled-at-75/Arrow@3x.png
--------------------------------------------------------------------------------
/Design/arrow.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/arrow.psd
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Cloudy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Cloudy.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Haze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Haze.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Mist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Mist.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Moon.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Mostly-sunny-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Mostly-sunny-day.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Mostly-sunny-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Mostly-sunny-night.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Partly-sunny-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Partly-sunny-day.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Partly-sunny-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Partly-sunny-night.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Snow.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Sun.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Thunder-cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Thunder-cloud.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Thunder.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/Wind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/Wind.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Cloudy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Cloudy.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Haze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Haze.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Mist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Mist.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Moon.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Mostly-sunny-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Mostly-sunny-day.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Mostly-sunny-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Mostly-sunny-night.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Partly-sunny-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Partly-sunny-day.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Partly-sunny-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Partly-sunny-night.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Snow.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Sun.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Thunder-cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Thunder-cloud.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Thunder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Thunder.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-25/Wind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-25/Wind.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Cloudy@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Cloudy@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Haze@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Haze@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Mist@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Mist@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Moon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Moon@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Mostly-sunny-day@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Mostly-sunny-day@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Mostly-sunny-night@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Mostly-sunny-night@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Partly-sunny-day@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Partly-sunny-day@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Partly-sunny-night@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Partly-sunny-night@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Snow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Snow@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Sun@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Sun@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Thunder-cloud@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Thunder-cloud@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Thunder@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Thunder@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-50/Wind@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-50/Wind@2x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Cloudy@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Cloudy@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Haze@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Haze@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Mist@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Mist@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Moon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Moon@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Mostly-sunny-day@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Mostly-sunny-day@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Mostly-sunny-night@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Mostly-sunny-night@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Partly-sunny-day@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Partly-sunny-day@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Partly-sunny-night@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Partly-sunny-night@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Snow@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Snow@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Sun@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Sun@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Thunder-cloud@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Thunder-cloud@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Thunder@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Thunder@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres-assets/scaled-at-75/Wind@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres-assets/scaled-at-75/Wind@3x.png
--------------------------------------------------------------------------------
/Design/wx-icons-highres.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/wx-icons-highres.psd
--------------------------------------------------------------------------------
/Design/x-assets/Delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/x-assets/Delete.png
--------------------------------------------------------------------------------
/Design/x-assets/scaled-at-25/Delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/x-assets/scaled-at-25/Delete.png
--------------------------------------------------------------------------------
/Design/x-assets/scaled-at-50/Delete@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/x-assets/scaled-at-50/Delete@2x.png
--------------------------------------------------------------------------------
/Design/x-assets/scaled-at-75/Delete@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/x-assets/scaled-at-75/Delete@3x.png
--------------------------------------------------------------------------------
/Design/x.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/Design/x.psd
--------------------------------------------------------------------------------
/LatLon.request:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | baseURL
6 | http://api.wunderground.com/api/36c847c1ef056205/forecast/geolookup/conditions/forecast10day/alerts/hourly/q/40.71,-74.json
7 | followRedirect
8 |
9 | handleJSONPCallbacks
10 |
11 | headers
12 |
13 | httpMethod
14 | GET
15 | jsonpScript
16 |
17 | paramBodyUIChoice
18 | 0
19 | parameters
20 |
21 | parametersType
22 | 0
23 | presentBeforeChallenge
24 |
25 | stringEncoding
26 | 4
27 | usingHTTPBody
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Playground.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 |
3 | target 'SimpleWeather' do
4 | use_frameworks!
5 | pod 'IGListKit'
6 | end
7 |
8 | target 'SimpleWeatherTests' do
9 | use_frameworks!
10 | pod 'IGListKit'
11 | end
12 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - IGListKit (2.0.0)
3 |
4 | DEPENDENCIES:
5 | - IGListKit
6 |
7 | SPEC CHECKSUMS:
8 | IGListKit: ee255e4e61865bc3e01ca96b690c5435c2887849
9 |
10 | PODFILE CHECKSUM: f19cdf1c0457c987c46c6bbd1fd4c966fe79fd7b
11 |
12 | COCOAPODS: 1.1.1
13 |
--------------------------------------------------------------------------------
/Pods/IGListKit/LICENSE.md:
--------------------------------------------------------------------------------
1 | BSD License
2 |
3 | For `IGListKit` software
4 |
5 | Copyright (c) 2016, Facebook, Inc. All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification,
8 | are permitted provided that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this
11 | list of conditions and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 |
17 | * Neither the name Facebook nor the names of its contributors may be used to
18 | endorse or promote products derived from this software without specific
19 | prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListAdapterDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @class IGListAdapter;
13 |
14 | /**
15 | Conform to `IGListAdapterDelegate` to receive display events for objects in a list.
16 | */
17 | @protocol IGListAdapterDelegate
18 |
19 | /**
20 | Notifies the delegate that a list object is about to be displayed.
21 |
22 | @param listAdapter The list adapter sending this information.
23 | @param object The object that will display.
24 | @param index The index of the object in the list.
25 | */
26 | - (void)listAdapter:(IGListAdapter *)listAdapter willDisplayObject:(id)object atIndex:(NSInteger)index;
27 |
28 | /**
29 | Notifies the delegate that a list object is no longer being displayed.
30 |
31 | @param listAdapter The list adapter sending this information.
32 | @param object The object that ended display.
33 | @param index The index of the object in the list.
34 | */
35 | - (void)listAdapter:(IGListAdapter *)listAdapter didEndDisplayingObject:(id)object atIndex:(NSInteger)index;
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListAdapterUpdater.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 | #import
14 | #import
15 | #import
16 |
17 | NS_ASSUME_NONNULL_BEGIN
18 |
19 | /**
20 | An `IGListAdapterUpdater` is a concrete type that conforms to `IGListUpdatingDelegate`.
21 | It is an out-of-box upater for `IGListAdapter` objects to use.
22 |
23 | @note This updater performs re-entrant, coalesced updating for a list. It also uses a least-minimal diff
24 | for calculating UI updates when `IGListAdapter` calls
25 | `-performUpdateWithCollectionView:fromObjects:toObjects:completion:`.
26 | */
27 | IGLK_SUBCLASSING_RESTRICTED
28 | @interface IGListAdapterUpdater : NSObject
29 |
30 | /**
31 | The delegate that receives events with data on the performance of a transition.
32 | */
33 | @property (nonatomic, weak) id delegate;
34 |
35 | /**
36 | A flag indicating if a move should be treated as a "delete, then insert" operation.
37 | */
38 | @property (nonatomic, assign) BOOL movesAsDeletesInserts;
39 |
40 | /**
41 | A bitmask of experiments to conduct on the updater.
42 | */
43 | @property (nonatomic, assign) IGListExperiment experiments;
44 |
45 | @end
46 |
47 | NS_ASSUME_NONNULL_END
48 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListAssert.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #ifndef IGAssert
11 | #define IGAssert( condition, ... ) NSCAssert( (condition) , ##__VA_ARGS__)
12 | #endif // IGAssert
13 |
14 | #ifndef IGParameterAssert
15 | #define IGParameterAssert( condition ) IGAssert( (condition) , @"Invalid parameter not satisfying: %@", @#condition)
16 | #endif // IGParameterAssert
17 |
18 | #ifndef IGAssertMainThread
19 | #define IGAssertMainThread() IGAssert( ([NSThread isMainThread] == YES), @"Must be on the main thread")
20 | #endif // IGAssertMainThread
21 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListCollectionView.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "IGListCollectionView.h"
11 |
12 | @implementation IGListCollectionView
13 |
14 | - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout {
15 | if (self = [super initWithFrame:frame collectionViewLayout:layout]) {
16 |
17 | UIColor *backgroundAppearanceColor = (UIColor *) [[[self class] appearance] backgroundColor];
18 | if (!backgroundAppearanceColor) {
19 | self.backgroundColor = [UIColor whiteColor];
20 | }
21 |
22 | self.alwaysBounceVertical = YES;
23 | }
24 | return self;
25 | }
26 |
27 | - (void)layoutSubviews {
28 | /**
29 | UICollectionView will sometimes lay its cells out with an animation. This is especially noticeable on older devices
30 | while scrolling quickly. The simplest fix is to just disable animations for -layoutSubviews, which is where cells
31 | and other views inside the UICollectionView are laid out.
32 | */
33 | [UIView performWithoutAnimation:^{
34 | [super layoutSubviews];
35 | }];
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListDiffable.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | /**
13 | The `IGListDiffable` protocol provides methods needed to compare the identity and equality of two objects.
14 | */
15 | @protocol IGListDiffable
16 |
17 | /**
18 | Returns a key that uniquely identifies the object.
19 |
20 | @return A key that can be used to uniquely identify the object.
21 |
22 | @note Two objects may share the same identifier, but are not equal. A common pattern is to use the `NSObject`
23 | category for automatic conformance. However this means that objects will be identified on their
24 | pointer value so finding updates becomes impossible.
25 |
26 | @warning This value should never be mutated.
27 | */
28 | - (nonnull id)diffIdentifier;
29 |
30 | /**
31 | Returns whether the receiver and a given object are equal.
32 |
33 | @param object The object to be compared to the receiver.
34 |
35 | @return `YES` if the receiver and object are equal, otherwise `NO`.
36 | */
37 | - (BOOL)isEqualToDiffableObject:(nullable id)object;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListGridCollectionViewLayout.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | /**
16 | `IGListGridCollectionViewLayout` provides a vertically-scrolling, section-based grid layout for `UICollectionView`.
17 | Items in the layout are displayed consecutively in a grid with exactly 1 item per section.
18 | If items are square, the appearance would be similar to the iOS Photos app.
19 | However, the size of the items for each section can vary.
20 | */
21 | IGLK_SUBCLASSING_RESTRICTED
22 | @interface IGListGridCollectionViewLayout : UICollectionViewLayout
23 |
24 | /**
25 | The minimum spacing to use between lines of items in the grid. The default value is `0.0`.
26 | */
27 | @property (nonatomic, assign) IBInspectable CGFloat minimumLineSpacing;
28 |
29 | /**
30 | The minimum spacing to use between items in the same row. The default value is `0.0`.
31 | */
32 | @property (nonatomic, assign) IBInspectable CGFloat minimumInteritemSpacing;
33 |
34 | /**
35 | The default size to use for cells. The default value is `(0.0, 0.0)`.
36 | If this size is non-zero, the layout will use this item size for all items.
37 | When the size is zero (the default), then the layout will query the collection view's delegate for the size.
38 | */
39 | @property (nonatomic, assign) IBInspectable CGSize itemSize;
40 |
41 | @end
42 |
43 | NS_ASSUME_NONNULL_END
44 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListKit.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | /**
13 | * Project version number for IGListKit.
14 | */
15 | FOUNDATION_EXPORT double IGListKitVersionNumber;
16 |
17 | /**
18 | * Project version string for IGListKit.
19 | */
20 | FOUNDATION_EXPORT const unsigned char IGListKitVersionString[];
21 |
22 | #import
23 | #import
24 | #import
25 | #import
26 | #import
27 | #import
28 | #import
29 | #import
30 | #import
31 | #import
32 | #import
33 | #import
34 | #import
35 | #import
36 | #import
37 | #import
38 | #import
39 | #import
40 | #import
41 | #import
42 | #import
43 | #import
44 | #import
45 | #import
46 | #import
47 | #import
48 | #import
49 | #import
50 | #import
51 | #import
52 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListMacros.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #ifndef IGLK_SUBCLASSING_RESTRICTED
11 | #if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
12 | #define IGLK_SUBCLASSING_RESTRICTED __attribute__((objc_subclassing_restricted))
13 | #else
14 | #define IGLK_SUBCLASSING_RESTRICTED
15 | #endif // #if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
16 | #endif // #ifndef IGLK_SUBCLASSING_RESTRICTED
17 |
18 | #ifndef IGLK_UNAVAILABLE
19 | #define IGLK_UNAVAILABLE(message) __attribute__((unavailable(message)))
20 | #endif // #ifndef IGLK_UNAVAILABLE
21 |
22 | #if IGLK_LOGGING_ENABLED
23 | #define IGLKLog( s, ... ) do { NSLog( @"IGListKit: %@", [NSString stringWithFormat: (s), ##__VA_ARGS__] ); } while(0)
24 | #else
25 | #define IGLKLog( s, ... )
26 | #endif
27 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListMoveIndex.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | /**
15 | An object representing a move between indexes.
16 | */
17 | @interface IGListMoveIndex : NSObject
18 |
19 | /**
20 | An index in the old collection.
21 | */
22 | @property (nonatomic, assign, readonly) NSInteger from;
23 |
24 | /**
25 | An index in the new collection.
26 | */
27 | @property (nonatomic, assign, readonly) NSInteger to;
28 |
29 | /**
30 | :nodoc:
31 | */
32 | - (instancetype)init NS_UNAVAILABLE;
33 |
34 | /**
35 | :nodoc:
36 | */
37 | + (instancetype)new NS_UNAVAILABLE;
38 |
39 | @end
40 |
41 | NS_ASSUME_NONNULL_END
42 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListMoveIndex.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "IGListMoveIndex.h"
11 | #import "IGListMoveIndexInternal.h"
12 |
13 | @implementation IGListMoveIndex
14 |
15 | - (instancetype)initWithFrom:(NSInteger)from to:(NSInteger)to {
16 | if (self = [super init]) {
17 | _from = from;
18 | _to = to;
19 | }
20 | return self;
21 | }
22 |
23 | - (NSUInteger)hash {
24 | return _from ^ _to;
25 | }
26 |
27 | - (BOOL)isEqual:(id)object {
28 | if (object == self) {
29 | return YES;
30 | }
31 | if ([object isKindOfClass:[IGListMoveIndex class]]) {
32 | const NSInteger f1 = self.from, f2 = [object from];
33 | const NSInteger t1 = self.to, t2 = [object to];
34 | return f1 == f2 && t1 == t2;
35 | }
36 | return NO;
37 | }
38 |
39 | - (NSComparisonResult)compare:(id)object {
40 | const NSInteger right = [object from];
41 | const NSInteger left = [self from];
42 | if (left == right) {
43 | return NSOrderedSame;
44 | } else if (left < right) {
45 | return NSOrderedAscending;
46 | } else {
47 | return NSOrderedDescending;
48 | }
49 | }
50 |
51 | - (NSString *)description {
52 | return [NSString stringWithFormat:@"<%@ %p; from: %zi; to: %zi;>", NSStringFromClass(self.class), self, self.from, self.to];
53 | }
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListMoveIndexPath.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | /**
15 | An object representing a move between indexes.
16 | */
17 | @interface IGListMoveIndexPath : NSObject
18 |
19 | /**
20 | An index path in the old collection.
21 | */
22 | @property (nonatomic, strong, readonly) NSIndexPath *from;
23 |
24 | /**
25 | An index path in the new collection.
26 | */
27 | @property (nonatomic, strong, readonly) NSIndexPath *to;
28 |
29 | /**
30 | :nodoc:
31 | */
32 | - (instancetype)init NS_UNAVAILABLE;
33 |
34 | /**
35 | :nodoc:
36 | */
37 | + (instancetype)new NS_UNAVAILABLE;
38 |
39 | @end
40 |
41 | NS_ASSUME_NONNULL_END
42 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListMoveIndexPath.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "IGListMoveIndexPath.h"
11 | #import "IGListMoveIndexPathInternal.h"
12 |
13 | #import
14 |
15 | @implementation IGListMoveIndexPath
16 |
17 | - (instancetype)initWithFrom:(NSIndexPath *)from to:(NSIndexPath *)to {
18 | NSParameterAssert(from != nil);
19 | NSParameterAssert(to != nil);
20 | if (self = [super init]) {
21 | _from = from;
22 | _to = to;
23 | }
24 | return self;
25 | }
26 |
27 | - (NSUInteger)hash {
28 | return [_from hash] ^ [_to hash];
29 | }
30 |
31 | - (BOOL)isEqual:(id)object {
32 | if (object == self) {
33 | return YES;
34 | }
35 | if ([object isKindOfClass:[IGListMoveIndexPath class]]) {
36 | NSIndexPath *f1 = self.from, *f2 = [object from];
37 | NSIndexPath *t1 = self.to, *t2 = [object to];
38 | return [f1 isEqual:f2] && [t1 isEqual:t2];
39 | }
40 | return NO;
41 | }
42 |
43 | - (NSComparisonResult)compare:(id)object {
44 | return [[self from] compare:[object from]];
45 | }
46 |
47 | - (NSString *)description {
48 | return [NSString stringWithFormat:@"<%@ %p; from: %@; to: %@;>", NSStringFromClass(self.class), self, self.from, self.to];
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListReloadDataUpdater.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 | #import
14 |
15 | /**
16 | An `IGListReloadDataUpdater` is a concrete type that conforms to `IGListUpdatingDelegate`.
17 | It is an out-of-box upater for `IGListAdapter` objects to use.
18 |
19 | @note This updater performs simple, synchronous updates using `-[UICollectionView reloadData]`.
20 | */
21 | IGLK_SUBCLASSING_RESTRICTED
22 | @interface IGListReloadDataUpdater : NSObject
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListScrollDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @class IGListAdapter;
13 | @class IGListSectionController;
14 |
15 | @protocol IGListSectionType;
16 |
17 | /**
18 | Implement this protocol to receive display events for an section controller when it is on screen.
19 | */
20 | @protocol IGListScrollDelegate
21 |
22 | /**
23 | Tells the delegate that the section controller was scrolled on screen.
24 |
25 | @param listAdapter The list adapter whose collection view was scrolled.
26 | @param sectionController The visible section controller that was scrolled.
27 | */
28 | - (void)listAdapter:(IGListAdapter *)listAdapter didScrollSectionController:(IGListSectionController *)sectionController;
29 |
30 | /**
31 | Tells the delegate that the section controller will be dragged on screen.
32 |
33 | @param listAdapter The list adapter whose collection view will drag.
34 | @param sectionController The visible section controller that will drag.
35 | */
36 | - (void)listAdapter:(IGListAdapter *)listAdapter willBeginDraggingSectionController:(IGListSectionController *)sectionController;
37 |
38 | /**
39 | Tells the delegate that the section controller did end dragging on screen.
40 |
41 | @param listAdapter The list adapter whose collection view ended dragging.
42 | @param sectionController The visible section controller that ended dragging.
43 | */
44 | - (void)listAdapter:(IGListAdapter *)listAdapter didEndDraggingSectionController:(IGListSectionController *)sectionController willDecelerate:(BOOL)decelerate;
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListStackedSectionController.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import
12 | #import
13 |
14 | /**
15 | An instace of `IGListStackedSectionController` is a clustered section controller,
16 | composed of many child section controllers. It constructs and routes item-level
17 | indexes to the appropriate child section controller with a local index. This lets you build section controllers made up
18 | of individual units that can be shared and reused with other section controllers.
19 |
20 | For example, you can create a "Comments" section controller that displays lists of text that is used alongside photo,
21 | video, or slideshow section controllers. You then have four small and manageable section controllers instead of one
22 | huge class.
23 | */
24 | IGLK_SUBCLASSING_RESTRICTED
25 | @interface IGListStackedSectionController : IGListSectionController
26 |
27 | /**
28 | Creates a new stacked section controller.
29 |
30 | @param sectionControllers An array of section controllers that make up the stack.
31 |
32 | @note The order of the section controllers dictates the order in which they appear.
33 |
34 | @warning The first section controller that is the supplementary source decides which supplementary views get displayed.
35 | */
36 | - (instancetype)initWithSectionControllers:(NSArray *> *)sectionControllers NS_DESIGNATED_INITIALIZER;
37 |
38 | /**
39 | :nodoc:
40 | */
41 | - (instancetype)init NS_UNAVAILABLE;
42 |
43 | /**
44 | :nodoc:
45 | */
46 | + (instancetype)new NS_UNAVAILABLE;
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListSupplementaryViewSource.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | /**
15 | Conform to this protocol to provide information about a list's supplementary views. This data is used in
16 | `IGListAdapter` which then configures and maintains a `UICollectionView`. The supplementary API reflects that in
17 | `UICollectionView`, `UICollectionViewLayout`, and `UICollectionViewDataSource`.
18 | */
19 | @protocol IGListSupplementaryViewSource
20 |
21 | /**
22 | Asks the SupplementaryViewSource for an array of supported element kinds.
23 |
24 | @return An array of element kind strings that the supplementary source handles.
25 | */
26 | - (NSArray *)supportedElementKinds;
27 |
28 | /**
29 | Asks the SupplementaryViewSource for a configured supplementary view for the specified kind and index.
30 |
31 | @param elementKind The kind of supplementary view being requested
32 | @param index The index for the supplementary veiw being requested.
33 |
34 | @note This is your opportunity to do any supplementary view setup and configuration.
35 |
36 | @warning You should never allocate new views in this method. Instead deque a view from the `IGListCollectionContext`.
37 | */
38 | - (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind
39 | atIndex:(NSInteger)index;
40 |
41 | /**
42 | Asks the SupplementaryViewSource for the size of a supplementary view for the given kind and index path.
43 |
44 | @param elementKind The kind of supplementary view.
45 | @param index The index of the requested view.
46 |
47 | @return The size for the supplementary view.
48 | */
49 | - (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind
50 | atIndex:(NSInteger)index;
51 |
52 | @end
53 |
54 | NS_ASSUME_NONNULL_END
55 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/IGListWorkingRangeDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @class IGListAdapter;
13 | @class IGListSectionController;
14 |
15 | @protocol IGListSectionType;
16 |
17 | NS_ASSUME_NONNULL_BEGIN
18 |
19 | /**
20 | Implement this protocol to receive working range events for a list.
21 |
22 | The working range is a range *near* the viewport in which you can begin preparing content for display. For example,
23 | you could begin decoding images, or warming text caches.
24 | */
25 | @protocol IGListWorkingRangeDelegate
26 |
27 | /**
28 | Notifies the delegate that an section controller will enter the working range.
29 |
30 | @param listAdapter The adapter controlling the list.
31 | @param sectionController The section controller entering the range.
32 | */
33 | - (void)listAdapter:(IGListAdapter *)listAdapter sectionControllerWillEnterWorkingRange:(IGListSectionController *)sectionController;
34 |
35 | /**
36 | Notifies the delegate that an section controller exited the working range.
37 |
38 | @param listAdapter The adapter controlling the list.
39 | @param sectionController The section controller that exited the range.
40 | */
41 | - (void)listAdapter:(IGListAdapter *)listAdapter sectionControllerDidExitWorkingRange:(IGListSectionController *)sectionController;
42 |
43 | @end
44 |
45 | NS_ASSUME_NONNULL_END
46 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListAdapterProxy.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 |
14 | @class IGListAdapter;
15 |
16 | NS_ASSUME_NONNULL_BEGIN
17 |
18 | /**
19 | A proxy that sends a custom set of selectors to an IGListAdapter object and the rest to a UICollectionViewDelegate
20 | target.
21 | */
22 | IGLK_SUBCLASSING_RESTRICTED
23 | @interface IGListAdapterProxy : NSProxy
24 |
25 | /**
26 | Create a new proxy object with targets and interceptor.
27 |
28 | @param collectionViewTarget A UICollectionViewDelegate conforming object that receives unintercepted messages.
29 | @param scrollViewTarget A UIScrollViewDelegate conforming object that receives unintercepted messages.
30 | @param interceptor An IGListAdapter object that intercepts a set of messages.
31 |
32 | @return A new IGListAdapterProxy object.
33 | */
34 | - (instancetype)initWithCollectionViewTarget:(nullable id)collectionViewTarget
35 | scrollViewTarget:(nullable id)scrollViewTarget
36 | interceptor:(IGListAdapter *)interceptor;
37 |
38 | @end
39 |
40 | NS_ASSUME_NONNULL_END
41 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListDisplayHandler.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 |
14 | @class IGListAdapter;
15 | @class IGListSectionController;
16 |
17 | @protocol IGListSectionType;
18 |
19 | NS_ASSUME_NONNULL_BEGIN
20 |
21 | IGLK_SUBCLASSING_RESTRICTED
22 | @interface IGListDisplayHandler : NSObject
23 |
24 | /**
25 | Tells the handler that a cell will be displayed in the IGListKit infra.
26 |
27 | @param cell A cell that will display.
28 | @param listAdapter The adapter managing the infra.
29 | @param sectionController The section controller the cell is in.
30 | @param object The object associated with the section controller.
31 | @param indexPath The index path of the cell in the UICollectionView.
32 | */
33 | - (void)willDisplayCell:(UICollectionViewCell *)cell
34 | forListAdapter:(IGListAdapter *)listAdapter
35 | sectionController:(IGListSectionController *)sectionController
36 | object:(id)object
37 | indexPath:(NSIndexPath *)indexPath;
38 |
39 | /**
40 | Tells the handler that a cell did end display in the IGListKit infra.
41 |
42 | @param cell A cell that did end display.
43 | @param listAdapter The adapter managing the infra.
44 | @param sectionController The section controller the cell is in.
45 | @param indexPath The index path of the cell in the UICollectionView.
46 | */
47 | - (void)didEndDisplayingCell:(UICollectionViewCell *)cell
48 | forListAdapter:(IGListAdapter *)listAdapter
49 | sectionController:(IGListSectionController *)sectionController
50 | indexPath:(NSIndexPath *)indexPath;
51 |
52 | @end
53 |
54 | NS_ASSUME_NONNULL_END
55 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListIndexPathResultInternal.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface IGListIndexPathResult()
17 |
18 | - (instancetype)initWithInserts:(NSArray *)inserts
19 | deletes:(NSArray *)deletes
20 | updates:(NSArray *)updates
21 | moves:(NSArray *)moves
22 | oldIndexPathMap:(NSMapTable, NSIndexPath *> *)oldIndexPathMap
23 | newIndexPathMap:(NSMapTable, NSIndexPath *> *)newIndexPathMap;
24 |
25 | @end
26 |
27 | NS_ASSUME_NONNULL_END
28 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListIndexSetResultInternal.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface IGListIndexSetResult()
17 |
18 | - (instancetype)initWithInserts:(NSIndexSet *)inserts
19 | deletes:(NSIndexSet *)deletes
20 | updates:(NSIndexSet *)updates
21 | moves:(NSArray *)moves
22 | oldIndexMap:(NSMapTable, NSNumber *> *)oldIndexMap
23 | newIndexMap:(NSMapTable, NSNumber *> *)newIndexMap;
24 |
25 | @end
26 |
27 | NS_ASSUME_NONNULL_END
28 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListMoveIndexInternal.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface IGListMoveIndex ()
17 |
18 | - (instancetype)initWithFrom:(NSInteger)from to:(NSInteger)to NS_DESIGNATED_INITIALIZER;
19 |
20 | @end
21 |
22 | NS_ASSUME_NONNULL_END
23 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListMoveIndexPathInternal.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface IGListMoveIndexPath ()
15 |
16 | - (instancetype)initWithFrom:(NSIndexPath *)from to:(NSIndexPath *)to NS_DESIGNATED_INITIALIZER;
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListSectionControllerInternal.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "IGListSectionController.h"
11 |
12 | FOUNDATION_EXTERN void IGListSectionControllerPushThread(UIViewController *viewController, id collectionContext);
13 |
14 | FOUNDATION_EXTERN void IGListSectionControllerPopThread(void);
15 |
16 | @interface IGListSectionController()
17 |
18 | @property (nonatomic, weak, readwrite) id collectionContext;
19 |
20 | @property (nonatomic, weak, readwrite) UIViewController *viewController;
21 |
22 | @property (nonatomic, assign, readwrite) BOOL isFirstSection;
23 |
24 | @property (nonatomic, assign, readwrite) BOOL isLastSection;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListStackedSectionControllerInternal.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import "IGListStackedSectionController.h"
13 |
14 | @interface IGListStackedSectionController ()
15 | <
16 | IGListCollectionContext,
17 | IGListDisplayDelegate,
18 | IGListScrollDelegate
19 | >
20 |
21 | @property (nonatomic, strong, readonly) NSOrderedSet<__kindof IGListSectionController *> *sectionControllers;
22 |
23 | /// An array the length of the total number of items in the stack, pointing to a section controller for the item index.
24 | @property (nonatomic, copy) NSArray *> *sectionControllersForItems;
25 |
26 | /// An array of index offsets for each item in the flattened stack.
27 | @property (nonatomic, copy) NSArray *sectionControllerOffsets;
28 |
29 | /// A cached collection of the number of items summed from each section controller in the stack.
30 | @property (nonatomic, assign) NSInteger flattenedNumberOfItems;
31 |
32 | /// A counted set of the visible section controllers, used to forward granular display events to child section controllers
33 | @property (nonatomic, strong, readonly) NSCountedSet *visibleSectionControllers;
34 |
35 | - (IGListSectionController *)sectionControllerForObjectIndex:(NSInteger)itemIndex;
36 | - (NSInteger)offsetForSectionController:(IGListSectionController *)sectionController;
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/IGListWorkingRangeHandler.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @class IGListAdapter;
13 |
14 | @protocol IGListSectionType;
15 |
16 | @interface IGListWorkingRangeHandler : NSObject
17 |
18 | /**
19 | Initializes the working range handler.
20 |
21 | @param workingRangeSize the number of sections beyond the visible viewport that should be considered within the working
22 | range. Applies equally in both directions above and below the viewport.
23 | */
24 | - (instancetype)initWithWorkingRangeSize:(NSInteger)workingRangeSize;
25 |
26 | /**
27 | Tells the handler that a cell will be displayed in the IGListKit infra.
28 |
29 | @param indexPath The index path of the cell in the UICollectionView.
30 | @param listAdapter The adapter managing the infra.
31 | */
32 | - (void)willDisplayItemAtIndexPath:(NSIndexPath *)indexPath
33 | forListAdapter:(IGListAdapter *)listAdapter;
34 |
35 | /**
36 | Tells the handler that a cell did end display in the IGListKit infra.
37 |
38 | @param indexPath The index path of the cell in the UICollectionView.
39 | @param listAdapter The adapter managing the infra.
40 | */
41 | - (void)didEndDisplayingItemAtIndexPath:(NSIndexPath *)indexPath
42 | forListAdapter:(IGListAdapter *)listAdapter;
43 |
44 | @end
45 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/UICollectionView+IGListBatchUpdateData.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @class IGListBatchUpdateData;
13 |
14 | @interface UICollectionView (IGListBatchUpdateData)
15 |
16 | - (void)ig_applyBatchUpdateData:(IGListBatchUpdateData *)updateData;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/Internal/UICollectionView+IGListBatchUpdateData.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "UICollectionView+IGListBatchUpdateData.h"
11 |
12 | #import "IGListBatchUpdateData.h"
13 |
14 | @implementation UICollectionView (IGListBatchUpdateData)
15 |
16 | - (void)ig_applyBatchUpdateData:(IGListBatchUpdateData *)updateData {
17 | [self deleteItemsAtIndexPaths:[updateData.deleteIndexPaths allObjects]];
18 | [self insertItemsAtIndexPaths:[updateData.insertIndexPaths allObjects]];
19 | [self reloadItemsAtIndexPaths:[updateData.reloadIndexPaths allObjects]];
20 |
21 | for (IGListMoveIndex *move in updateData.moveSections) {
22 | [self moveSection:move.from toSection:move.to];
23 | }
24 |
25 | [self deleteSections:updateData.deleteSections];
26 | [self insertSections:updateData.insertSections];
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/NSNumber+IGListDiffable.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 |
14 | /**
15 | This category provides default `IGListDiffable` conformance for `NSNumber`.
16 | */
17 | @interface NSNumber (IGListDiffable)
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/NSNumber+IGListDiffable.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "NSNumber+IGListDiffable.h"
11 |
12 | @implementation NSNumber (IGListDiffable)
13 |
14 | - (id)diffIdentifier {
15 | return self;
16 | }
17 |
18 | - (BOOL)isEqualToDiffableObject:(id)object {
19 | return [self isEqual:object];
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/NSString+IGListDiffable.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import
13 |
14 | /**
15 | This category provides default `IGListDiffable` conformance for `NSString`.
16 | */
17 | @interface NSString (IGListDiffable)
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Pods/IGListKit/Source/NSString+IGListDiffable.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "NSString+IGListDiffable.h"
11 |
12 | @implementation NSString (IGListDiffable)
13 |
14 | - (id)diffIdentifier {
15 | return self;
16 | }
17 |
18 | - (BOOL)isEqualToDiffableObject:(id)object {
19 | return [self isEqual:object];
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/Pods/Local Podspecs/IGListKit.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IGListKit",
3 | "version": "1.0.0",
4 | "summary": "A data-driven UICollectionView framework.",
5 | "homepage": "https://github.com/Instagram/IGListKit",
6 | "documentation_url": "https://instagram.github.io/IGListKit",
7 | "description": "A data-driven UICollectionView framework for building fast and flexible lists.",
8 | "license": {
9 | "type": "BSD"
10 | },
11 | "authors": "Instagram",
12 | "social_media_url": "https://twitter.com/fbOpenSource",
13 | "source": {
14 | "git": "https://github.com/Instagram/IGListKit.git",
15 | "tag": "1.0.0",
16 | "branch": "stable"
17 | },
18 | "source_files": "Source/**/*.{h,m,mm}",
19 | "private_header_files": "Source/Internal/*.h",
20 | "requires_arc": true,
21 | "platforms": {
22 | "ios": "8.0",
23 | "tvos": "9.0"
24 | },
25 | "frameworks": "UIKit",
26 | "libraries": "c++",
27 | "pod_target_xcconfig": {
28 | "CLANG_CXX_LANGUAGE_STANDARD": "c++11",
29 | "CLANG_CXX_LIBRARY": "libc++"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - IGListKit (2.0.0)
3 |
4 | DEPENDENCIES:
5 | - IGListKit
6 |
7 | SPEC CHECKSUMS:
8 | IGListKit: ee255e4e61865bc3e01ca96b690c5435c2887849
9 |
10 | PODFILE CHECKSUM: f19cdf1c0457c987c46c6bbd1fd4c966fe79fd7b
11 |
12 | COCOAPODS: 1.1.1
13 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/IGListKit/IGListKit-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_IGListKit : NSObject
3 | @end
4 | @implementation PodsDummy_IGListKit
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/IGListKit/IGListKit-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #endif
4 |
5 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/IGListKit/IGListKit-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #endif
4 |
5 | #import "IGListAdapter.h"
6 | #import "IGListAdapterDataSource.h"
7 | #import "IGListAdapterDelegate.h"
8 | #import "IGListAdapterUpdater.h"
9 | #import "IGListAdapterUpdaterDelegate.h"
10 | #import "IGListAssert.h"
11 | #import "IGListBatchUpdateData.h"
12 | #import "IGListCollectionContext.h"
13 | #import "IGListCollectionView.h"
14 | #import "IGListDiff.h"
15 | #import "IGListDiffable.h"
16 | #import "IGListDisplayDelegate.h"
17 | #import "IGListExperiments.h"
18 | #import "IGListGridCollectionViewLayout.h"
19 | #import "IGListIndexPathResult.h"
20 | #import "IGListIndexSetResult.h"
21 | #import "IGListKit.h"
22 | #import "IGListMacros.h"
23 | #import "IGListMoveIndex.h"
24 | #import "IGListMoveIndexPath.h"
25 | #import "IGListReloadDataUpdater.h"
26 | #import "IGListScrollDelegate.h"
27 | #import "IGListSectionController.h"
28 | #import "IGListSectionType.h"
29 | #import "IGListSingleSectionController.h"
30 | #import "IGListStackedSectionController.h"
31 | #import "IGListSupplementaryViewSource.h"
32 | #import "IGListUpdatingDelegate.h"
33 | #import "IGListWorkingRangeDelegate.h"
34 | #import "NSNumber+IGListDiffable.h"
35 | #import "NSString+IGListDiffable.h"
36 |
37 | FOUNDATION_EXPORT double IGListKitVersionNumber;
38 | FOUNDATION_EXPORT const unsigned char IGListKitVersionString[];
39 |
40 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/IGListKit/IGListKit.modulemap:
--------------------------------------------------------------------------------
1 | framework module IGListKit {
2 | umbrella header "IGListKit-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/IGListKit/IGListKit.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_CXX_LANGUAGE_STANDARD = c++11
2 | CLANG_CXX_LIBRARY = libc++
3 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/IGListKit
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
6 | OTHER_LDFLAGS = -l"c++" -framework "UIKit"
7 | PODS_BUILD_DIR = $BUILD_DIR
8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
9 | PODS_ROOT = ${SRCROOT}
10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
11 | SKIP_INSTALL = YES
12 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/IGListKit/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 2.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeather/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeather/Pods-SimpleWeather-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## IGListKit
5 |
6 | BSD License
7 |
8 | For `IGListKit` software
9 |
10 | Copyright (c) 2016, Facebook, Inc. All rights reserved.
11 |
12 | Redistribution and use in source and binary forms, with or without modification,
13 | are permitted provided that the following conditions are met:
14 |
15 | * Redistributions of source code must retain the above copyright notice, this
16 | list of conditions and the following disclaimer.
17 |
18 | * Redistributions in binary form must reproduce the above copyright notice,
19 | this list of conditions and the following disclaimer in the documentation
20 | and/or other materials provided with the distribution.
21 |
22 | * Neither the name Facebook nor the names of its contributors may be used to
23 | endorse or promote products derived from this software without specific
24 | prior written permission.
25 |
26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
30 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
33 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 |
37 | Generated by CocoaPods - https://cocoapods.org
38 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeather/Pods-SimpleWeather-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_SimpleWeather : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_SimpleWeather
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeather/Pods-SimpleWeather-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #endif
4 |
5 |
6 | FOUNDATION_EXPORT double Pods_SimpleWeatherVersionNumber;
7 | FOUNDATION_EXPORT const unsigned char Pods_SimpleWeatherVersionString[];
8 |
9 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeather/Pods-SimpleWeather.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/IGListKit"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/IGListKit/IGListKit.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "IGListKit"
7 | PODS_BUILD_DIR = $BUILD_DIR
8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeather/Pods-SimpleWeather.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_SimpleWeather {
2 | umbrella header "Pods-SimpleWeather-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeather/Pods-SimpleWeather.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/IGListKit"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/IGListKit/IGListKit.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "IGListKit"
7 | PODS_BUILD_DIR = $BUILD_DIR
8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeatherTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeatherTests/Pods-SimpleWeatherTests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## IGListKit
5 |
6 | BSD License
7 |
8 | For `IGListKit` software
9 |
10 | Copyright (c) 2016, Facebook, Inc. All rights reserved.
11 |
12 | Redistribution and use in source and binary forms, with or without modification,
13 | are permitted provided that the following conditions are met:
14 |
15 | * Redistributions of source code must retain the above copyright notice, this
16 | list of conditions and the following disclaimer.
17 |
18 | * Redistributions in binary form must reproduce the above copyright notice,
19 | this list of conditions and the following disclaimer in the documentation
20 | and/or other materials provided with the distribution.
21 |
22 | * Neither the name Facebook nor the names of its contributors may be used to
23 | endorse or promote products derived from this software without specific
24 | prior written permission.
25 |
26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
30 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
33 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 |
37 | Generated by CocoaPods - https://cocoapods.org
38 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeatherTests/Pods-SimpleWeatherTests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_SimpleWeatherTests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_SimpleWeatherTests
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeatherTests/Pods-SimpleWeatherTests-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #endif
4 |
5 |
6 | FOUNDATION_EXPORT double Pods_SimpleWeatherTestsVersionNumber;
7 | FOUNDATION_EXPORT const unsigned char Pods_SimpleWeatherTestsVersionString[];
8 |
9 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeatherTests/Pods-SimpleWeatherTests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/IGListKit"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/IGListKit/IGListKit.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "IGListKit"
7 | PODS_BUILD_DIR = $BUILD_DIR
8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeatherTests/Pods-SimpleWeatherTests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_SimpleWeatherTests {
2 | umbrella header "Pods-SimpleWeatherTests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-SimpleWeatherTests/Pods-SimpleWeatherTests.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/IGListKit"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/IGListKit/IGListKit.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "IGListKit"
7 | PODS_BUILD_DIR = $BUILD_DIR
8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | fastlane documentation
2 | ================
3 | # Installation
4 | ```
5 | sudo gem install fastlane
6 | ```
7 | # Available Actions
8 | ### beta
9 | ```
10 | fastlane beta
11 | ```
12 |
13 |
14 | ----
15 |
16 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
17 | More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools).
18 | The documentation of fastlane can be found on [GitHub](https://github.com/fastlane/fastlane/tree/master/fastlane).
19 |
--------------------------------------------------------------------------------
/SimpleWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SimpleWeather.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SimpleWeather/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/.DS_Store
--------------------------------------------------------------------------------
/SimpleWeather/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/13/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | let savedLocationStore = SavedLocationStore()
17 |
18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
19 | if let nav = window?.rootViewController as? UINavigationController,
20 | let controller = nav.viewControllers.first as? SavedLocationsViewController {
21 | controller.store = savedLocationStore
22 | }
23 | return true
24 | }
25 |
26 | func applicationDidEnterBackground(_ application: UIApplication) {
27 | savedLocationStore.save()
28 | }
29 |
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Classes/.DS_Store
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Alert.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Alert.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/14/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Alert {
12 |
13 | enum Significance {
14 | case warning, watch, advisory, statement, forecast, outlook, synopsys, unknown
15 | }
16 |
17 | let description: String
18 | let date: Date
19 | let expires: Date
20 | let message: String
21 | let phenomena: String
22 | let significance: Significance
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Astronomy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Astronomy.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/25/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Astronomy {
12 |
13 | let sunrise: Date
14 | let sunset: Date
15 | let moonrise: Date
16 | let moonset: Date
17 | let moonIllumination: Double
18 | let moonAge: Int
19 | let moonPhaseDescription: String
20 | let moonHemisphere: String
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Condition+Icon.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Condition+Icon.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/26/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Condition {
12 |
13 | func icon(night: Bool = false) -> String {
14 | switch self {
15 | case .chanceflurries, .chancesnow, .chancesleet, .flurries, .sleet, .snow:
16 | return "Snow"
17 | case .chancerain, .rain:
18 | return "Mist"
19 | case .chancetstorms, .tstorms:
20 | return "Thunder-cloud"
21 | case .clear, .sunny:
22 | return night ? "Moon" : "Sun"
23 | case .cloudy:
24 | return "Cloudy"
25 | case .fog, .hazy:
26 | return "Haze"
27 | case .mostlycloudy, .partlysunny:
28 | return night ? "Mostly-sunny-night" : "Partly-sunny-day"
29 | case .partlycloudy, .mostlysunny:
30 | return night ? "Mostly-sunny-night" : "Mostly-sunny-day"
31 | case .unknown:
32 | return ""
33 | }
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Condition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsIcon.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/14/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum Condition {
12 | case chanceflurries, chancerain, chancesleet, chancesnow, chancetstorms, clear, cloudy, flurries, fog, hazy, mostlycloudy, mostlysunny, partlycloudy, partlysunny, sleet, rain, snow, sunny, tstorms, unknown
13 |
14 | static func from(string: String) -> Condition {
15 | switch string {
16 | case "chanceflurries": return .chanceflurries
17 | case "chancerain": return .chancerain
18 | case "chancesleet": return .chancesleet
19 | case "chancesnow": return .chancesnow
20 | case "chancetstorms": return .chancetstorms
21 | case "clear": return .clear
22 | case "cloudy": return .cloudy
23 | case "flurries": return .flurries
24 | case "fog": return .fog
25 | case "hazy": return .hazy
26 | case "mostlycloudy": return .mostlycloudy
27 | case "mostlysunny": return .mostlysunny
28 | case "partlycloudy": return .partlycloudy
29 | case "partlysunny": return .partlysunny
30 | case "sleet": return .sleet
31 | case "rain": return .rain
32 | case "snow": return .snow
33 | case "sunny": return .sunny
34 | case "tstorms": return .tstorms
35 | default: return .unknown
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/ConditionsSection+Forecast.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsSection+Forecast.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/11/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension ConditionsSection {
12 |
13 | static func from(forecast: Forecast?) -> ConditionsSection? {
14 | guard let observation = forecast?.observation,
15 | let today = forecast?.daily?.sorted(by: { $0.date < $1.date }).first,
16 | let astronomy = forecast?.astronomy
17 | else { return nil }
18 |
19 | return ConditionsSection(
20 | temperature: Int(observation.temp),
21 | high: today.high,
22 | low: today.low,
23 | condition: observation.condition,
24 | feelsLike: observation.feelslike,
25 | wind: observation.wind,
26 | date: observation.date,
27 | humidity: observation.humidity,
28 | dewpoint: observation.dewpoint,
29 | pressure: observation.pressure,
30 | visibility: observation.visibility,
31 | precip_1hr: observation.precip_1hr,
32 | description: observation.weather,
33 | timeOfDay: Date().timeOfDay(sunrise: astronomy.sunrise, sunset: astronomy.sunset)
34 | )
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/ConditionsSection+IGListDiffable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsSection+IGListDiffable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/11/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 |
12 | extension ConditionsSection: IGListDiffable {
13 |
14 | func diffIdentifier() -> NSObjectProtocol {
15 | return (description + date.description) as NSString
16 | }
17 |
18 | func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
19 | if self === object { return true }
20 | guard let rhs = object as? ConditionsSection else { return false }
21 | return temperature == rhs.temperature
22 | && high == rhs.high
23 | && low == rhs.low
24 | && condition == rhs.condition
25 | && feelsLike == rhs.feelsLike
26 | && wind == rhs.wind
27 | && date == rhs.date
28 | && humidity == rhs.humidity
29 | && dewpoint == rhs.dewpoint
30 | && pressure == rhs.pressure
31 | && visibility == rhs.visibility
32 | && precip_1hr == rhs.precip_1hr
33 | && description == rhs.description
34 | && timeOfDay == rhs.timeOfDay
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/ConditionsSection+ViewModels.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsSection+ViewModels.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/11/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension ConditionsSection {
12 |
13 | var viewModels: [Any] {
14 | var models = [Any]()
15 |
16 | let conditions = ConditionsCellViewModel(
17 | temperature: temperature,
18 | high: high,
19 | low: low,
20 | conditionImageName: condition.icon(night: timeOfDay == .night),
21 | feelsLike: feelsLike
22 | )
23 | models.append(conditions)
24 |
25 | models.append(ConditionsDetailCellViewModel(title: "Conditions", detail: description))
26 | models.append(ConditionsDetailCellViewModel(title: "Feels Like", detail: String(format: "%zi°", feelsLike)))
27 |
28 | let windString = wind.speed > 0 ? String(format: "%.0f mph %@", wind.speed, wind.direction) : "--"
29 | models.append(ConditionsDetailCellViewModel(title: "Wind", detail: windString))
30 |
31 | models.append(ConditionsDetailCellViewModel(title: "Humidity", detail: String(format: "%@", humidity)))
32 | models.append(ConditionsDetailCellViewModel(title: "Dewpoint", detail: String(format: "%.0f°", dewpoint)))
33 | models.append(ConditionsDetailCellViewModel(title: "Pressure", detail: String(format: "%.2f", pressure)))
34 | models.append(ConditionsDetailCellViewModel(title: "Rainfall (1h)", detail: String(format: "%.0f in", precip_1hr)))
35 |
36 | return models
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/ConditionsSection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsSection.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/11/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ConditionsSection {
12 |
13 | let temperature: Int
14 | let high: Int
15 | let low: Int
16 | let condition: Condition
17 | let feelsLike: Int
18 | let wind: Wind
19 | let date: Date
20 | let humidity: String
21 | let dewpoint: Double
22 | let pressure: Double
23 | let visibility: Double
24 | let precip_1hr: Double
25 | let description: String
26 | let timeOfDay: TimeOfDay
27 |
28 | init(
29 | temperature: Int,
30 | high: Int,
31 | low: Int,
32 | condition: Condition,
33 | feelsLike: Int,
34 | wind: Wind,
35 | date: Date,
36 | humidity: String,
37 | dewpoint: Double,
38 | pressure: Double,
39 | visibility: Double,
40 | precip_1hr: Double,
41 | description: String,
42 | timeOfDay: TimeOfDay
43 | ) {
44 | self.temperature = temperature
45 | self.high = high
46 | self.low = low
47 | self.condition = condition
48 | self.feelsLike = feelsLike
49 | self.wind = wind
50 | self.date = date
51 | self.humidity = humidity
52 | self.dewpoint = dewpoint
53 | self.pressure = pressure
54 | self.visibility = visibility
55 | self.precip_1hr = precip_1hr
56 | self.description = description
57 | self.timeOfDay = timeOfDay
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/DailyForecastSection+Forecast.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DailyForecastSection+Forecast.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/21/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension DailyForecastSection {
12 |
13 | static func from(forecast: Forecast) -> DailyForecastSection? {
14 | guard let date = forecast.observation?.date,
15 | let daily = forecast.daily?.sorted(by: { $0.date < $1.date })
16 | else { return nil }
17 |
18 | let calendar = Calendar.current
19 | let components: Set = [.day]
20 | let observationDay = calendar.dateComponents(components, from: date).day
21 |
22 | var viewModels = [ForecastDayCellViewModel]()
23 | let limit = 10
24 | for day in daily {
25 | let count = viewModels.count
26 | // dont include daily forecasts when already have observations
27 | guard count < limit else { break }
28 | guard observationDay != calendar.dateComponents(components, from: day.date).day else { continue }
29 |
30 | let isFirst = count == 0
31 | let isLast = count == limit - 2 || day == daily.last
32 | let position: ForecastDayCellPosition = isFirst ? .top : isLast ? .bottom : .none
33 |
34 | let viewModel = ForecastDayCellViewModel(
35 | date: day.date,
36 | high:
37 | day.high,
38 | low: day.low,
39 | conditionImageName: day.condition.icon(),
40 | chancePrecip: day.pop,
41 | position: position
42 | )
43 | viewModels.append(viewModel)
44 | }
45 | return DailyForecastSection(
46 | currentDate: date,
47 | viewModels: viewModels
48 | )
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/DailyForecastSection+IGListDiffable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DailyForecastSection+IGListDiffable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/20/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 |
12 | extension DailyForecastSection: IGListDiffable {
13 |
14 | func diffIdentifier() -> NSObjectProtocol {
15 | return "daily" as NSString
16 | }
17 |
18 | func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
19 | if self === object { return true }
20 | guard let object = object as? DailyForecastSection else { return false }
21 | return viewModels == object.viewModels
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/DailyForecastSection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DailyForecastSection.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/20/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class DailyForecastSection {
12 |
13 | let currentDate: Date
14 | let viewModels: [ForecastDayCellViewModel]
15 |
16 | init(currentDate: Date, viewModels: [ForecastDayCellViewModel]) {
17 | self.currentDate = currentDate
18 | self.viewModels = viewModels
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/EmbeddedSection+Forecast.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmbeddedSection+Forecast.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension EmbeddedSection {
12 |
13 | static func from(forecast: Forecast) -> EmbeddedSection? {
14 | guard let hourly = forecast.hourly?.sorted(by: { $0.date < $1.date }),
15 | let sunrise = forecast.astronomy?.sunrise,
16 | let sunset = forecast.astronomy?.sunset
17 | else { return nil }
18 |
19 | var viewModels = [ForecastHourCellViewModel]()
20 | let limit = 12
21 | for hour in hourly {
22 | let timeOfDay = hour.date.timeOfDay(sunrise: sunrise, sunset: sunset)
23 | let viewModel = ForecastHourCellViewModel(
24 | date: hour.date,
25 | temp: hour.temp,
26 | conditionImageName: hour.condition.icon(night: timeOfDay == .night),
27 | chancePrecip: hour.pop
28 | )
29 | viewModels.append(viewModel)
30 | if viewModels.count >= limit { break }
31 | }
32 | return EmbeddedSection(identifier: "hourly" as NSString, items: viewModels)
33 | }
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/EmbeddedSection+IGListDiffable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmbeddedSection+IGListDiffable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 |
12 | extension EmbeddedSection: IGListDiffable {
13 |
14 | func diffIdentifier() -> NSObjectProtocol {
15 | return identifier
16 | }
17 |
18 | func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
19 | if self === object { return true }
20 | guard let object = object as? EmbeddedSection else { return false }
21 | return identifier.isEqual(object.identifier)
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/EmbeddedSection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmbeddedSection.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 |
12 | class EmbeddedSection {
13 |
14 | let identifier: NSObjectProtocol
15 | let items: [IGListDiffable]
16 |
17 | init(identifier: NSObjectProtocol, items: [IGListDiffable]) {
18 | self.identifier = identifier
19 | self.items = items
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Forecast+JSONConvertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Forecast+JSONConvertible.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/19/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Forecast: JSONConvertible {
12 |
13 | static func fromJSON(json: [String : Any]) -> Forecast? {
14 | let observation: Observation?
15 | if let json = json["current_observation"] as? [String: Any] {
16 | observation = Observation.fromJSON(json: json)
17 | } else {
18 | observation = nil
19 | }
20 |
21 | let location: Location?
22 | if let json = json["location"] as? [String: Any] {
23 | location = Location.fromJSON(json: json)
24 | } else {
25 | location = nil
26 | }
27 |
28 | let alerts: [Alert]? = (json["alerts"] as? [ [String: Any] ])?.flatMap({ (json: [String: Any]) in
29 | return Alert.fromJSON(json: json)
30 | })
31 |
32 | let daily: [ForecastDay]? = (keypath(dict: json, path: "forecast.simpleforecast.forecastday") as [ [String: Any] ]?)?.flatMap({ (json: [String: Any]) in
33 | return ForecastDay.fromJSON(json: json)
34 | })
35 |
36 | let hourly: [ForecastHourly]? = (json["hourly_forecast"] as? [ [String: Any] ])?.flatMap({ (json: [String: Any]) in
37 | return ForecastHourly.fromJSON(json: json)
38 | })
39 |
40 | let astronomy: Astronomy?
41 | if let json = json["moon_phase"] as? [String: Any] {
42 | astronomy = Astronomy.fromJSON(json: json)
43 | } else {
44 | astronomy = nil
45 | }
46 |
47 | return Forecast(
48 | location: location,
49 | observation: observation,
50 | alerts: alerts,
51 | daily: daily,
52 | hourly: hourly,
53 | astronomy: astronomy
54 | )
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Forecast.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Forecast.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/19/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Forecast {
12 |
13 | let location: Location?
14 | let observation: Observation?
15 | let alerts: [Alert]?
16 | let daily: [ForecastDay]?
17 | let hourly: [ForecastHourly]?
18 | let astronomy: Astronomy?
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/ForecastDay+Equatable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastDay+Equatable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/10/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension ForecastDay: Equatable {
12 |
13 | public static func ==(lhs: ForecastDay, rhs: ForecastDay) -> Bool {
14 | return lhs.date == rhs.date
15 | && lhs.high == rhs.high
16 | && lhs.low == rhs.low
17 | && lhs.description == rhs.description
18 | && lhs.pop == rhs.pop
19 | && lhs.qpf_allday == rhs.qpf_allday
20 | && lhs.qpf_day == rhs.qpf_day
21 | && lhs.qpf_night == rhs.qpf_night
22 | && lhs.snow_allday == rhs.snow_allday
23 | && lhs.snow_day == rhs.snow_day
24 | && lhs.snow_night == rhs.snow_night
25 | && lhs.avehumidity == rhs.avehumidity
26 | && lhs.maxhumidity == rhs.maxhumidity
27 | && lhs.minhumidity == rhs.minhumidity
28 | && lhs.condition == rhs.condition
29 | && lhs.max_wind == rhs.max_wind
30 | && lhs.average_wind == rhs.average_wind
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/ForecastDay.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastDay.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/14/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct ForecastDay {
12 |
13 | let date: Date
14 | let high: Int
15 | let low: Int
16 | let description: String
17 | let pop: Double
18 | let qpf_allday: Double
19 | let qpf_day: Double
20 | let qpf_night: Double
21 | let snow_allday: Double
22 | let snow_day: Double
23 | let snow_night: Double
24 | let avehumidity: Int
25 | let maxhumidity: Int
26 | let minhumidity: Int
27 | let condition: Condition
28 | let max_wind: Wind?
29 | let average_wind: Wind?
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/ForecastHourly.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastHourly.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/14/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct ForecastHourly {
12 |
13 | let date: Date
14 | let temp: Int
15 | let dewpoint: Int
16 | let description: String
17 | let wx: String
18 | let uvi: Int
19 | let humidity: Int
20 | let windchill: Int
21 | let heatindex: Int
22 | let feelslike: Int
23 | let qpf: Double
24 | let snow: Double
25 | let pop: Double
26 | let mslp: Double
27 | let wind: Wind
28 | let condition: Condition
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/JSONConvertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JSONConvertible.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/15/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol JSONConvertible {
12 | static func fromJSON(json: [String: Any]) -> Self?
13 | }
14 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Location+JSONConvertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Location+JSONConvertible.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/15/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Location: JSONConvertible {
12 |
13 | static func fromJSON(json: [String : Any]) -> Location? {
14 | guard let type = json["type"] as? String,
15 | let country = json["country"] as? String,
16 | let country_iso3166 = json["country_iso3166"] as? String,
17 | let country_name = json["country_name"] as? String,
18 | let state = json["state"] as? String,
19 | let city = json["city"] as? String,
20 | let tz_short = json["tz_short"] as? String,
21 | let tz_long = json["tz_long"] as? String,
22 | let lat = (json["lat"] as? NSString)?.doubleValue,
23 | let lon = (json["lon"] as? NSString)?.doubleValue,
24 | let zip = (json["zip"] as? NSString)?.integerValue,
25 | let magic = json["magic"] as? String,
26 | let wmo = json["wmo"] as? String,
27 | let l = json["l"] as? String,
28 | let requesturl = json["requesturl"] as? String,
29 | let wuiurl = json["wuiurl"] as? String
30 | else { return nil }
31 | return Location(
32 | type: type,
33 | country: country,
34 | country_iso3166: country_iso3166,
35 | country_name: country_name,
36 | state: state,
37 | city: city,
38 | tz_short: tz_short,
39 | tz_long: tz_long,
40 | lat: lat,
41 | lon: lon,
42 | zip: zip,
43 | magic: magic,
44 | wmo: wmo,
45 | l: l,
46 | requesturl: requesturl,
47 | wuiurl: wuiurl
48 | )
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Location.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Location.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/14/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Location {
12 |
13 | let type: String
14 | let country: String
15 | let country_iso3166: String
16 | let country_name: String
17 | let state: String
18 | let city: String
19 | let tz_short: String
20 | let tz_long: String
21 | let lat: Double
22 | let lon: Double
23 | let zip: Int
24 | let magic: String
25 | let wmo: String
26 | let l: String
27 | let requesturl: String
28 | let wuiurl: String
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/MKLocalSearchCompletion+IGListDiffable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MKLocalSearchCompletion+IGListDiffable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/8/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 | import MapKit
12 |
13 | extension MKLocalSearchCompletion: IGListDiffable {
14 |
15 | public func diffIdentifier() -> NSObjectProtocol {
16 | return (title + subtitle) as NSString
17 | }
18 |
19 | public func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
20 | if self === object { return true }
21 | guard let object = object as? MKLocalSearchCompletion else { return false }
22 | return title == object.title
23 | && subtitle == object.subtitle
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Observation+JSONConvertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Conditions+JSONConvertible.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/16/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Observation: JSONConvertible {
12 |
13 | static func fromJSON(json: [String : Any]) -> Observation? {
14 | guard let epoch_string = json["observation_epoch"] as? String,
15 | let epoch_interval = TimeInterval(epoch_string),
16 | let weather = json["weather"] as? String,
17 | let temp = json["temp_f"] as? Double,
18 | let humidity = json["relative_humidity"] as? String,
19 | let wdir = json["wind_dir"] as? String,
20 | let wspd = json["wind_mph"] as? Double,
21 | let pressure = (json["pressure_in"] as? NSString)?.doubleValue,
22 | let dewpoint = json["dewpoint_f"] as? Double,
23 | let feelslike = (json["feelslike_f"] as? NSString)?.integerValue,
24 | let visibility = (json["visibility_mi"] as? NSString)?.doubleValue,
25 | let uvi = (json["UV"] as? NSString)?.integerValue,
26 | let precip_1hr = (json["precip_1hr_in"] as? NSString)?.doubleValue,
27 | let precip_day = (json["precip_today_in"] as? NSString)?.doubleValue,
28 | let icon_string = json["icon"] as? String
29 | else { return nil }
30 |
31 | let condition = Condition.from(string: icon_string)
32 | let wind = Wind(speed: max(wspd, 0), direction: wdir)
33 |
34 | return Observation(
35 | date: Date(timeIntervalSince1970: epoch_interval),
36 | weather: weather,
37 | temp: temp,
38 | humidity: humidity,
39 | wind: wind,
40 | pressure: pressure,
41 | dewpoint: dewpoint,
42 | feelslike: feelslike,
43 | visibility: max(visibility, 0),
44 | uvi: uvi,
45 | precip_1hr: max(precip_1hr, 0),
46 | precip_day: max(precip_day, 0),
47 | condition: condition
48 | )
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Observation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Conditions.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/16/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Observation {
12 |
13 | let date: Date
14 | let weather: String
15 | let temp: Double
16 | let humidity: String
17 | let wind: Wind
18 | let pressure: Double
19 | let dewpoint: Double
20 | let feelslike: Int
21 | let visibility: Double
22 | let uvi: Int
23 | let precip_1hr: Double
24 | let precip_day: Double
25 | let condition: Condition
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/RadarSection+IGListDiffable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RadarSection+IGListDiffable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/27/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 |
12 | extension RadarSection: IGListDiffable {
13 |
14 | func diffIdentifier() -> NSObjectProtocol {
15 | return self
16 | }
17 |
18 | func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
19 | return isEqual(object)
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/RadarSection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RadarSection.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/27/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 | import MapKit
12 |
13 | class RadarSection: NSObject {
14 |
15 | let center: CLLocationCoordinate2D
16 |
17 | init(center: CLLocationCoordinate2D) {
18 | self.center = center
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/SavedLocation+Equatable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SavedLocation+Equatable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/8/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // SavedLocation inherits from NSObject so not using Equatable protocol
12 | func ==(lhs: SavedLocation, rhs: SavedLocation) -> Bool {
13 | return lhs.userLocation == rhs.userLocation
14 | && lhs.latitude == rhs.latitude
15 | && lhs.longitude == rhs.longitude
16 | && lhs.name == rhs.name
17 | }
18 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/SavedLocation+IGListDiffable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SavedLocation+IGListDiffable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/18/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 |
12 | extension SavedLocation: IGListDiffable {
13 |
14 | func diffIdentifier() -> NSObjectProtocol {
15 | return name as NSString
16 | }
17 |
18 | func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
19 | if self === object { return true }
20 | guard let rhs = object as? SavedLocation else { return false }
21 | return name == rhs.name
22 | && latitude == rhs.latitude
23 | && longitude == rhs.longitude
24 | && userLocation == rhs.userLocation
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/SavedLocation+NSEncoding.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SavedLocation+NSEncoding.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/18/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | let SavedLocationNameKey = "name"
12 | let SavedLocationLatitudeKey = "latitude"
13 | let SavedLocationLongitudeKey = "longitude"
14 | let SavedLocationUserLocationKey = "userLocation"
15 |
16 | extension SavedLocation: NSCoding {
17 |
18 | convenience init?(coder aDecoder: NSCoder) {
19 | guard let name = aDecoder.decodeObject(forKey: SavedLocationNameKey) as? String
20 | else { return nil }
21 | let latitude = aDecoder.decodeDouble(forKey: SavedLocationLatitudeKey)
22 | let longitude = aDecoder.decodeDouble(forKey: SavedLocationLongitudeKey)
23 | let userLocation = aDecoder.decodeBool(forKey: SavedLocationUserLocationKey)
24 | self.init(name: name, latitude: latitude, longitude: longitude, userLocation: userLocation)
25 | }
26 |
27 | func encode(with aCoder: NSCoder) {
28 | aCoder.encode(name, forKey: SavedLocationNameKey)
29 | aCoder.encode(latitude, forKey: SavedLocationLatitudeKey)
30 | aCoder.encode(longitude, forKey: SavedLocationLongitudeKey)
31 | aCoder.encode(userLocation, forKey: SavedLocationUserLocationKey)
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/SavedLocation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SavedLocation.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/18/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | final class SavedLocation: NSObject {
12 |
13 | let name: String
14 | let latitude: Double
15 | let longitude: Double
16 | let userLocation: Bool
17 |
18 | init(name: String, latitude: Double, longitude: Double, userLocation: Bool) {
19 | self.name = name
20 | self.latitude = latitude
21 | self.longitude = longitude
22 | self.userLocation = userLocation
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Models/Wind.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Wind.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/15/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Wind {
12 |
13 | let speed: Double
14 | let direction: String
15 |
16 | }
17 |
18 | extension Wind: Equatable {
19 |
20 | public static func ==(lhs: Wind, rhs: Wind) -> Bool {
21 | return lhs.speed == rhs.speed
22 | && lhs.direction == rhs.direction
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/SectionControllers/DailyForecastSectionController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DailyForecastSectionController.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/20/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import IGListKit
11 |
12 | class DailyForecastSectionController: IGListSectionController, IGListSectionType {
13 |
14 | var model: DailyForecastSection?
15 |
16 | override init() {
17 | super.init()
18 | inset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
19 | }
20 |
21 | // MARK: IGListSectionType
22 |
23 | func numberOfItems() -> Int {
24 | return model?.viewModels.count ?? 0
25 | }
26 |
27 | func sizeForItem(at index: Int) -> CGSize {
28 | guard let size = collectionContext?.containerSize else { return .zero }
29 | return CGSize(width: size.width - inset.left - inset.right, height: 35)
30 | }
31 |
32 | func cellForItem(at index: Int) -> UICollectionViewCell {
33 | guard let context = collectionContext,
34 | let viewModel = model?.viewModels[index],
35 | let cell = context.dequeueReusableCellFromStoryboard(withIdentifier: "ForecastDayCell", for: self, at: index) as? ForecastDayCell
36 | else { return UICollectionViewCell() }
37 | cell.configure(viewModel: viewModel)
38 | return cell
39 | }
40 |
41 | func didUpdate(to object: Any) {
42 | model = object as? DailyForecastSection
43 | }
44 |
45 | func didSelectItem(at index: Int) {}
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/SectionControllers/ErrorSectionController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorSectionController.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/4/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import IGListKit
11 |
12 | protocol ErrorSectionControllerDelegate: class {
13 | func errorSectionControllerDidTapRetry(errorSectionController: ErrorSectionController)
14 | }
15 |
16 | class ErrorSectionController: IGListSectionController, IGListSectionType, RetryCellDelegate {
17 |
18 | weak var delegate: ErrorSectionControllerDelegate?
19 | var model: ErrorViewModel?
20 |
21 | // MARK: IGListSectionType
22 |
23 | func numberOfItems() -> Int {
24 | return 1
25 | }
26 |
27 | func sizeForItem(at index: Int) -> CGSize {
28 | return collectionContext?.containerSize ?? CGSize()
29 | }
30 |
31 | func cellForItem(at index: Int) -> UICollectionViewCell {
32 | guard let cell = collectionContext?.dequeueReusableCellFromStoryboard(withIdentifier: "RetryCell", for: self, at: index) as? RetryCell
33 | else { return UICollectionViewCell() }
34 | cell.delegate = self
35 | cell.titleLabel.text = model?.title
36 | cell.messageLabel.text = model?.message
37 | return cell
38 | }
39 |
40 | func didUpdate(to object: Any) {
41 | model = object as? ErrorViewModel
42 | }
43 |
44 | func didSelectItem(at index: Int) {}
45 |
46 | // MARK: RetryCellDelegate
47 |
48 | func retryCellDidTapRetry(retryCell: RetryCell) {
49 | delegate?.errorSectionControllerDidTapRetry(errorSectionController: self)
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/SectionControllers/HourlyForecastSectionController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HourlyForecastSectionController.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import IGListKit
11 |
12 | class HourlyForecastSectionController: IGListSectionController, IGListSectionType {
13 |
14 | var viewModel: ForecastHourCellViewModel?
15 |
16 | override init() {
17 | super.init()
18 | inset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 15)
19 | }
20 |
21 | // MARK: IGListSectionType
22 |
23 | func numberOfItems() -> Int {
24 | return 1
25 | }
26 |
27 | func sizeForItem(at index: Int) -> CGSize {
28 | guard let height = collectionContext?.containerSize.height else { return .zero }
29 | return CGSize(width: height, height: height)
30 | }
31 |
32 | func cellForItem(at index: Int) -> UICollectionViewCell {
33 | guard let context = collectionContext,
34 | let viewModel = viewModel,
35 | let cell = context.dequeueReusableCellFromStoryboard(withIdentifier: "ForecastHourCell", for: self, at: index) as? ForecastHourCell
36 | else { return UICollectionViewCell() }
37 | cell.configure(viewModel: viewModel)
38 | return cell
39 | }
40 |
41 | func didUpdate(to object: Any) {
42 | viewModel = object as? ForecastHourCellViewModel
43 | }
44 |
45 | func didSelectItem(at index: Int) {}
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/SectionControllers/SearchResultSectionController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchResultSectionController.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/8/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import IGListKit
11 | import MapKit
12 |
13 | func SearchResultSectionController() -> IGListSingleSectionController {
14 | let configureBlock = { (item: Any, cell: UICollectionViewCell) in
15 | guard let cell = cell as? SearchResultCell,
16 | let result = item as? MKLocalSearchCompletion else { return }
17 | cell.label.text = result.title
18 |
19 | cell.detailLabel.text = result.subtitle
20 | if result.subtitle.characters.count > 0 {
21 | cell.labelVerticalConstraint.constant = -6
22 | } else {
23 | cell.labelVerticalConstraint.constant = 0
24 | }
25 | }
26 |
27 | let sizeBlock = { (item: Any, context: IGListCollectionContext?) -> CGSize in
28 | guard let context = context else { return CGSize() }
29 | return CGSize(width: context.containerSize.width, height: 55)
30 | }
31 | return IGListSingleSectionController(
32 | storyboardCellIdentifier: "SearchResultCell",
33 | configureBlock: configureBlock,
34 | sizeBlock: sizeBlock
35 | )
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/API.swift:
--------------------------------------------------------------------------------
1 | //
2 | // API.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/13/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | let API_KEY = "36c847c1ef056205"
12 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/Date+Daytime.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date+Daytime.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/26/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum TimeOfDay {
12 | case day, night, unknown
13 | }
14 |
15 | extension Date {
16 |
17 | func timeOfDay(sunrise: Date? = nil, sunset: Date? = nil, calendar: Calendar = .current) -> TimeOfDay {
18 | let components: Set = [.day, .hour]
19 |
20 | let daytime: Bool
21 | let dateComponents = calendar.dateComponents(components, from: self)
22 | if let sunrise = sunrise,
23 | let sunset = sunset,
24 | let day = dateComponents.day,
25 | calendar.dateComponents(components, from: sunrise).day == day,
26 | calendar.dateComponents(components, from: sunset).day == day {
27 | daytime = self > sunrise && self < sunset
28 | } else if let hour = dateComponents.hour {
29 | daytime = hour > 6 && hour < 18
30 | } else {
31 | return .unknown
32 | }
33 | return daytime ? .day : .night
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/KeypathParsing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeypathParsing.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/15/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public func keypath(dict: [String: Any], path: String) -> T? {
12 | let keys: [String] = path.components(separatedBy: ".")
13 | var next = dict
14 | for key in keys {
15 | let entry = next[key]
16 | if let d = entry as? [String: Any] {
17 | next = d
18 | } else if let v = entry as? T, key == keys.last {
19 | return v
20 | }
21 | }
22 | return nil
23 | }
24 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/MKCoordinateRegion+Bounds.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MKCoordinateRegion+Bounds.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/27/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MapKit
11 |
12 | extension MKCoordinateRegion {
13 |
14 | var bounds: CGRect {
15 | return CGRect(
16 | x: center.latitude - span.latitudeDelta / 2.0,
17 | y: center.longitude - span.longitudeDelta / 2.0,
18 | width: span.latitudeDelta,
19 | height: span.longitudeDelta
20 | )
21 | }
22 |
23 | // http://stackoverflow.com/a/15683034/940936
24 | var mapRect: MKMapRect {
25 | let a = MKMapPointForCoordinate(CLLocationCoordinate2D(latitude: center.latitude + span.latitudeDelta / 2.0,
26 | longitude: center.longitude - span.longitudeDelta / 2.0))
27 | let b = MKMapPointForCoordinate(CLLocationCoordinate2D(latitude: center.latitude - span.latitudeDelta / 2.0,
28 | longitude: center.longitude + span.longitudeDelta / 2.0))
29 | return MKMapRectMake(min(a.x, b.x), min(a.y, b.y), abs(a.x - b.x), abs(a.y - b.y))
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/MKCoordinateRegion+Convenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MKCoordinateRegion+Convenience.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/14/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import MapKit
10 | import UIKit
11 |
12 | extension MKCoordinateRegion {
13 |
14 | init(center: CLLocationCoordinate2D, width: CLLocationDegrees, size: CGSize) {
15 | let ratio = size.width / size.height
16 | let span = MKCoordinateSpan(latitudeDelta: width, longitudeDelta: Double(ratio) * width)
17 | self.init(center: center, span: span)
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/URL+DiskCacheKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+DiskCacheKey.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/19/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension URL {
12 |
13 | var diskCacheKey: String {
14 | let str = absoluteString
15 | let length = Int(CC_MD5_DIGEST_LENGTH)
16 | var r = [UInt8](repeating: 0, count: length)
17 |
18 | if let d = str.data(using: String.Encoding.utf8) {
19 | _ = d.withUnsafeBytes { (body: UnsafePointer) in
20 | CC_MD5(body, CC_LONG(d.count), &r)
21 | }
22 | }
23 |
24 | let ext = pathExtension == "" ? "" : "." + pathExtension
25 | return String(format: "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%",
26 | r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14],
27 | r[15]) + ext
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/URLBuilder+Wunderground.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URLBuilder+WeatherUnderground.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/2/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | func WundergroundURL(
12 | apiKey: String,
13 | paths: [String]? = nil,
14 | query: [String: String]? = nil
15 | ) -> URL? {
16 | let apiPaths = ["api", apiKey] + (paths ?? [])
17 | return URLBuilder(base: "http://api.wunderground.com", paths: apiPaths, query: query)
18 | }
19 |
20 | func WundergroundForecastURL(
21 | apiKey: String,
22 | lat: Double,
23 | lon: Double
24 | ) -> URL? {
25 | let paths = [
26 | // order doesn't matter
27 | "forecast",
28 | "geolookup",
29 | "conditions",
30 | "forecast10day",
31 | "alerts",
32 | "hourly",
33 | "astronomy",
34 | // order matters
35 | "q",
36 | String(format: "%.4f,%.4f.json", lat, lon)] // "40.71,-74.json"
37 | return WundergroundURL(apiKey: apiKey, paths: paths)
38 | }
39 |
40 | func WundergroundRadarURL(
41 | apiKey: String,
42 | minLat: Double,
43 | maxLat: Double,
44 | minLon: Double,
45 | maxLon: Double,
46 | width: Double,
47 | height: Double
48 | ) -> URL? {
49 | let paths = [
50 | "radar",
51 | "image.png"
52 | ]
53 | let query = [
54 | "maxlat": String(format: "%.4f", maxLat),
55 | "maxlon": String(format: "%.4f", maxLon),
56 | "minlat": String(format: "%.4f", minLat),
57 | "minlon": String(format: "%.4f", minLon),
58 | "width": String(format: "%.0f", width),
59 | "height": String(format: "%.0f", height),
60 | "rainsnow": "1",
61 | ]
62 | return WundergroundURL(apiKey: apiKey, paths: paths, query: query)
63 | }
64 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/URLBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URLBuilder.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/2/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | func URLBuilder(base: String, paths: [String]? = nil, query: [String: String]? = nil) -> URL? {
12 | guard let baseURL = URL(string: base) else { preconditionFailure("Base not convertible to URL: " + base) }
13 | var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
14 | components?.queryItems = query?
15 | .map { (k, v) in URLQueryItem(name: k, value: v) }
16 |
17 | if NSClassFromString("XCTestCase") != nil {
18 | components?.queryItems = components?.queryItems?.sorted { $0.name < $1.name }
19 | }
20 |
21 | components?.path = "/" + (paths?.joined(separator: "/") ?? "")
22 | return components?.url
23 | }
24 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/URLSession+Convenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URLSession+Convenience.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/2/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension URLSession {
12 |
13 | func fetch(
14 | url: URL,
15 | request: URLSessionDataTaskResponse,
16 | completion: @escaping (URLSessionResult) -> Void
17 | ) -> URLSessionDataTask {
18 | print("Fetching parsable URL: \(url.absoluteString)")
19 |
20 | let task = dataTask(with: url, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
21 | let result = request.handle(data: data, error: error)
22 | DispatchQueue.main.async {
23 | completion(result)
24 | }
25 | })
26 | task.resume()
27 |
28 | return task
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Systems/ViewPulser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewPulser.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/26/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ViewPulser {
12 |
13 | let view: UIView
14 |
15 | init(view: UIView) {
16 | self.view = view
17 | view.isHidden = true
18 | }
19 |
20 | var enabled = true
21 |
22 | func pulse() {
23 | guard enabled, !view.isHidden else { return }
24 |
25 | let pulseAnimation = CABasicAnimation(keyPath: "opacity")
26 | pulseAnimation.duration = 0.5
27 | pulseAnimation.fromValue = 0
28 | pulseAnimation.toValue = 1
29 | pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
30 |
31 | let group = CAAnimationGroup()
32 | group.duration = 2
33 | group.repeatCount = Float.greatestFiniteMagnitude
34 | group.animations = [pulseAnimation]
35 |
36 | view.layer.add(group, forKey: "animateOpacity")
37 | }
38 |
39 | func disable() {
40 | enabled = false
41 | view.isHidden = true
42 | view.layer.removeAllAnimations()
43 | }
44 |
45 | func enable() {
46 | enabled = true
47 | view.isHidden = false
48 | pulse()
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewControllers/AlertsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlertsViewController.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/26/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class AlertsViewController: UIViewController {
12 |
13 | @IBOutlet weak var textView: UITextView!
14 |
15 | var alerts: [Alert]?
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 | let descriptions: [String]? = alerts?.flatMap({ $0.message })
20 | textView.text = descriptions?.joined(separator: "\n")
21 | textView.contentOffset = .zero
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewControllers/SavedLocationsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SavedLocationsViewController.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/18/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import IGListKit
11 |
12 | class SavedLocationsViewController: UIViewController, IGListAdapterDataSource, SavedLocationStoreListener {
13 |
14 | @IBOutlet weak var collectionView: IGListCollectionView!
15 |
16 | lazy var adapter: IGListAdapter = {
17 | return IGListAdapter(updater: IGListAdapterUpdater(), viewController: self, workingRangeSize: 0)
18 | }()
19 |
20 | var store: SavedLocationStore?
21 |
22 | override func viewDidLoad() {
23 | super.viewDidLoad()
24 | adapter.collectionView = collectionView
25 | adapter.dataSource = self
26 |
27 | store?.add(listener: self)
28 | }
29 |
30 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
31 | if let nav = segue.destination as? UINavigationController,
32 | let controller = nav.viewControllers.first as? SearchViewController {
33 | controller.store = store
34 | }
35 | }
36 |
37 | // MARK: IGListAdapterDataSource
38 |
39 | func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {
40 | return store?.locations ?? []
41 | }
42 |
43 | func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
44 | guard let store = store else { return IGListSectionController() }
45 | return SavedLocationSectionController(store: store)
46 | }
47 |
48 | func emptyView(for listAdapter: IGListAdapter) -> UIView? {
49 | return nil
50 | }
51 |
52 | // MARK: SavedLocationStoreListener
53 |
54 | func storeDidUpdate(store: SavedLocationStore) {
55 | adapter.performUpdates(animated: true)
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ConditionsCellViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsCellViewModel.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/19/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct ConditionsCellViewModel {
12 |
13 | let temperature: Int
14 | let high: Int
15 | let low: Int
16 | let conditionImageName: String
17 | let feelsLike: Int
18 |
19 | var temperatureLabelText: String {
20 | return String(format: "%zi°", temperature)
21 | }
22 |
23 | var highLowLabelText: NSAttributedString {
24 | return highLowAttributedString(high: high, low: low, size: 18)
25 | }
26 |
27 | var feelsLikeText: String? {
28 | return temperature != feelsLike ? String(format: "(%zi°)", feelsLike) : nil
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ConditionsDetailCellViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsDetailCellViewModel.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/11/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct ConditionsDetailCellViewModel {
12 |
13 | let title: String
14 | let detail: String
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ErrorViewModel+IGListDiffable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorViewModel+IGListDiffable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/4/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import IGListKit
10 |
11 | extension ErrorViewModel: IGListDiffable {
12 |
13 | func diffIdentifier() -> NSObjectProtocol {
14 | return title as NSString
15 | }
16 |
17 | func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
18 | if self === object { return true }
19 | guard let object = object as? ErrorViewModel else { return false }
20 | return type == object.type
21 | && title == object.title
22 | && message == object.message
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ErrorViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorViewModel.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/4/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum ErrorViewModelType {
12 | case location
13 | case network
14 | }
15 |
16 | class ErrorViewModel {
17 |
18 | let title: String
19 | let message: String
20 | let type: ErrorViewModelType
21 |
22 | init(title: String, message: String, type: ErrorViewModelType) {
23 | self.title = title
24 | self.message = message
25 | self.type = type
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ForecastDayCellViewModel+Equatable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastDayCellViewModel+Equatable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/21/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension ForecastDayCellViewModel: Equatable {
12 |
13 | public static func ==(lhs: ForecastDayCellViewModel, rhs: ForecastDayCellViewModel) -> Bool {
14 | return lhs.date == rhs.date
15 | && lhs.high == rhs.high
16 | && lhs.low == rhs.low
17 | && lhs.conditionImageName == rhs.conditionImageName
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ForecastDayCellViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastDayCellViewModel.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/20/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum ForecastDayCellPosition {
12 | case top, bottom, none
13 | }
14 |
15 | struct ForecastDayCellViewModel {
16 |
17 | static private let dateFormatter: DateFormatter = {
18 | let df = DateFormatter()
19 | df.dateFormat = "EEEE"
20 | return df
21 | }()
22 |
23 | let date: Date
24 | let high: Int
25 | let low: Int
26 | let conditionImageName: String
27 | let chancePrecip: Double
28 | let position: ForecastDayCellPosition
29 |
30 | var dateString: String {
31 | return ForecastDayCellViewModel.dateFormatter.string(from: date)
32 | }
33 |
34 | var highLowConditionsString: NSAttributedString {
35 | return highLowAttributedString(high: high, low: low, size: 15)
36 | }
37 |
38 | var precipString: String? {
39 | return chancePrecip >= 0.2 ? String(format: "%.0f%%", chancePrecip * 100.0) : nil
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ForecastHourCellViewModel+IGListDiffable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastHourCellViewModel+IGListDiffable.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 |
12 | extension ForecastHourCellViewModel: IGListDiffable {
13 |
14 | func diffIdentifier() -> NSObjectProtocol {
15 | return date as NSDate
16 | }
17 |
18 | func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
19 | if self === object { return true }
20 | guard let object = object as? ForecastHourCellViewModel else { return false }
21 | return date == object.date && temp == object.temp && conditionImageName == object.conditionImageName
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ForecastHourCellViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastHourCellViewModel.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ForecastHourCellViewModel {
12 |
13 | static private let dateFormatter: DateFormatter = {
14 | let df = DateFormatter()
15 | df.dateFormat = "h a"
16 | return df
17 | }()
18 |
19 | let date: Date
20 | let temp: Int
21 | let conditionImageName: String
22 | let chancePrecip: Double
23 |
24 | init(date: Date, temp: Int, conditionImageName: String, chancePrecip: Double) {
25 | self.date = date
26 | self.temp = temp
27 | self.conditionImageName = conditionImageName
28 | self.chancePrecip = chancePrecip
29 | }
30 |
31 | var dateString: String {
32 | return ForecastHourCellViewModel.dateFormatter.string(from: date)
33 | }
34 |
35 | var detailsAttributedString: NSAttributedString {
36 | let fontSize: CGFloat = 15
37 | let mAttrString = NSMutableAttributedString(
38 | string: String(format: "%zi°", temp),
39 | attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: fontSize, weight: UIFontWeightMedium)]
40 | )
41 | if chancePrecip >= 0.2 {
42 | let percipAttrStr = NSAttributedString(
43 | string: String(format: " %.0f%%", round(chancePrecip * 10.0) * 10.0),
44 | attributes: [
45 | NSFontAttributeName: UIFont.systemFont(ofSize: fontSize, weight: UIFontWeightLight),
46 | NSForegroundColorAttributeName: UIColor(red: 73/255.0, green: 130/255.0, blue: 193/255.0, alpha: 1)
47 | ]
48 | )
49 | mAttrString.append(percipAttrStr)
50 | }
51 | return mAttrString
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/ForecastHourlyDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastHourlyDataSource.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import IGListKit
11 |
12 | class ForecastHourlyDataSource: EmbeddedAdapterDataSource {
13 |
14 | func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
15 | if object is ForecastHourCellViewModel {
16 | return HourlyForecastSectionController()
17 | }
18 | return IGListSectionController()
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/RadarOverlay.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RadarOverlay.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/27/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MapKit
11 |
12 | class RadarOverlay: NSObject, MKOverlay {
13 |
14 | let coordinate: CLLocationCoordinate2D
15 | let boundingMapRect: MKMapRect
16 |
17 | init(coordinate: CLLocationCoordinate2D, boundingMapRect: MKMapRect) {
18 | self.coordinate = coordinate
19 | self.boundingMapRect = boundingMapRect
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/SavedLocationCellViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SavedLocationCellViewModel.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/14/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MapKit
11 |
12 | struct SavedLocationCellViewModel {
13 | let text: String
14 | let userLocation: Bool
15 | let region: MKCoordinateRegion
16 | }
17 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/ViewModels/WeatherNavigationViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeatherNavigationViewModel.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/4/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | func WeatherNavigationTitle(location: SavedLocation, forecast: Forecast?) -> String? {
12 | if location.userLocation == true {
13 | if let forecast = forecast {
14 | return forecast.location?.city
15 | } else {
16 | return NSLocalizedString("Loading...", comment: "")
17 | }
18 | } else {
19 | return location.name
20 | }
21 | }
22 |
23 | func WeatherNavigationShouldDisplayAlerts(forecast: Forecast?) -> Bool {
24 | return (forecast?.alerts?.count ?? 0) > 0
25 | }
26 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/BorderedButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BorderedButton.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/4/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BorderedButton: UIButton {
12 |
13 | lazy var shapeLayer: CAShapeLayer = {
14 | let l = CAShapeLayer()
15 | l.lineWidth = 1
16 | l.fillColor = nil
17 | self.layer.addSublayer(l)
18 | return l
19 | }()
20 |
21 | override func layoutSubviews() {
22 | super.layoutSubviews()
23 | shapeLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 6).cgPath
24 | shapeLayer.strokeColor = tintColor.cgColor
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/ConditionsCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsCell.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/19/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ConditionsCell: RoundedCollectionViewCell {
12 |
13 | @IBOutlet weak private var temperatureLabel: UILabel!
14 | @IBOutlet weak private var highLowLabel: UILabel!
15 | @IBOutlet weak var feelsLikeLabel: UILabel!
16 | @IBOutlet weak var iconImageView: UIImageView!
17 | @IBOutlet weak var arrowImageView: UIImageView!
18 |
19 | func configure(viewModel: ConditionsCellViewModel) {
20 | cornerOptions = .all
21 | temperatureLabel.text = viewModel.temperatureLabelText
22 | highLowLabel.attributedText = viewModel.highLowLabelText
23 | iconImageView.image = UIImage(named: viewModel.conditionImageName)
24 | feelsLikeLabel.text = viewModel.feelsLikeText
25 | }
26 |
27 | func setExpanded(expanded: Bool, animated: Bool = false) {
28 | cornerOptions = expanded ? .top : .all
29 | updateMaskIfNeeded()
30 |
31 | let color: UIColor
32 | if expanded {
33 | color = UIColor(red: 53/255.0, green: 55/255.0, blue: 63/255.0, alpha: 1)
34 | } else {
35 | color = UIColor(red: 30/255.0, green: 32/255.0, blue: 41/255.0, alpha: 1)
36 | }
37 | backgroundColor = color
38 |
39 | UIView.animate(
40 | withDuration: (animated ? 0.8 : 0),
41 | delay: 0,
42 | usingSpringWithDamping: 0.6,
43 | initialSpringVelocity: 0,
44 | options: [],
45 | animations: {
46 | self.arrowImageView.transform = expanded ? CGAffineTransform(rotationAngle: CGFloat.pi) : .identity
47 | })
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/ConditionsDetailCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConditionsDetailCell.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 12/11/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ConditionsDetailCell: RoundedCollectionViewCell {
12 |
13 | @IBOutlet weak var detailLabel: UILabel!
14 | @IBOutlet weak var titleLabel: UILabel!
15 |
16 | func configure(viewModel: ConditionsDetailCellViewModel) {
17 | titleLabel.text = viewModel.title
18 | detailLabel.text = viewModel.detail
19 | }
20 |
21 | func setIsLast(isLast: Bool) {
22 | cornerOptions = isLast ? .bottom : []
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/EmbeddedCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmbeddedCollectionViewCell.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import IGListKit
11 |
12 | class EmbeddedCollectionViewCell: UICollectionViewCell {
13 |
14 | @IBOutlet weak var collectionView: IGListCollectionView! {
15 | didSet {
16 | collectionView.bounces = true
17 | collectionView.alwaysBounceVertical = false
18 | collectionView.alwaysBounceHorizontal = true
19 | }
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/ForecastDayCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastDayCell.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/20/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ForecastDayCell: RoundedCollectionViewCell {
12 |
13 | @IBOutlet weak var dayLabel: UILabel!
14 | @IBOutlet weak var highLowConditionsLabel: UILabel!
15 | @IBOutlet weak var iconImageView: UIImageView!
16 | @IBOutlet weak var precipLabel: UILabel!
17 |
18 | func configure(viewModel: ForecastDayCellViewModel) {
19 | dayLabel.text = viewModel.dateString
20 | highLowConditionsLabel.attributedText = viewModel.highLowConditionsString
21 | iconImageView.image = UIImage(named: viewModel.conditionImageName)
22 |
23 | switch viewModel.position {
24 | case .top: cornerOptions = .top
25 | case .bottom: cornerOptions = .bottom
26 | case .none: cornerOptions = []
27 | }
28 |
29 | if let precip = viewModel.precipString {
30 | precipLabel.isHidden = false
31 | precipLabel.text = precip
32 | } else {
33 | precipLabel.isHidden = true
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/ForecastHourCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForecastHourCell.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/24/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ForecastHourCell: RoundedCollectionViewCell {
12 |
13 | @IBOutlet weak var timeLabel: UILabel!
14 | @IBOutlet weak var tempLabel: UILabel!
15 | @IBOutlet weak var iconImageView: UIImageView!
16 |
17 | func configure(viewModel: ForecastHourCellViewModel) {
18 | cornerOptions = .all
19 | timeLabel.text = viewModel.dateString
20 | tempLabel.attributedText = viewModel.detailsAttributedString
21 | iconImageView.image = UIImage(named: viewModel.conditionImageName)
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/HighLowAttributedString.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HighLowAttributedString.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/27/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | func highLowAttributedString(high: Int, low: Int, size: CGFloat = 15) -> NSAttributedString {
12 | let font = UIFont.systemFont(ofSize: size, weight: UIFontWeightMedium)
13 | let mstr = NSMutableAttributedString(
14 | string: String(format: "%zi°", high),
15 | attributes: [
16 | NSFontAttributeName: font,
17 | NSForegroundColorAttributeName: UIColor.white
18 | ])
19 | mstr.append(NSAttributedString(
20 | string: String(format: " %zi°", low),
21 | attributes: [
22 | NSFontAttributeName: font,
23 | NSForegroundColorAttributeName: UIColor(white: 0.733333333, alpha: 1)
24 | ]))
25 | return mstr
26 | }
27 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/LoadingView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoadingView.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/4/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class LoadingView: UIView {
12 |
13 | let spinner = UIActivityIndicatorView(activityIndicatorStyle: .white)
14 |
15 | override init(frame: CGRect) {
16 | super.init(frame: frame)
17 | spinner.startAnimating()
18 | addSubview(spinner)
19 | }
20 |
21 | required init?(coder aDecoder: NSCoder) {
22 | fatalError("init(coder:) has not been implemented")
23 | }
24 |
25 | override func layoutSubviews() {
26 | super.layoutSubviews()
27 | spinner.center = CGPoint(x: bounds.midX, y: bounds.midY)
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/MapCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapCell.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/27/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MapKit
11 |
12 | class MapCell: UICollectionViewCell {
13 |
14 | @IBOutlet weak var mapView: MKMapView!
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/RadarOverlayView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RadarOverlayView.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/27/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MapKit
11 |
12 | class RadarOverlayView: MKOverlayRenderer {
13 |
14 | let image: UIImage
15 |
16 | init(overlay: MKOverlay, image: UIImage) {
17 | self.image = image
18 | super.init(overlay: overlay)
19 | }
20 |
21 | override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
22 | // https://www.raywenderlich.com/87008/overlay-views-mapkit-swift-tutorial
23 | guard let imageRef = image.cgImage else { return }
24 | let theMapRect = overlay.boundingMapRect
25 | let theRect = rect(for: theMapRect)
26 | context.scaleBy(x: 1, y: -1)
27 | context.translateBy(x: 0.0, y: -theRect.size.height)
28 | context.setAlpha(0.5)
29 | context.draw(imageRef, in: theRect)
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/RetryCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RetryCell.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/4/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol RetryCellDelegate: class {
12 | func retryCellDidTapRetry(retryCell: RetryCell)
13 | }
14 |
15 | class RetryCell: UICollectionViewCell {
16 |
17 | @IBOutlet weak var titleLabel: UILabel!
18 | @IBOutlet weak var messageLabel: UILabel!
19 | @IBOutlet weak var retryButton: BorderedButton!
20 |
21 | weak var delegate: RetryCellDelegate?
22 |
23 | @IBAction func onRetry(_ sender: Any) {
24 | delegate?.retryCellDidTapRetry(retryCell: self)
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/SavedLocationContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SavedLocationContentView.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/14/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MapKit
11 |
12 | class SavedLocationContentView: UIView {
13 |
14 | let mapView: MKMapView = {
15 | let view = MKMapView()
16 | view.showsScale = false
17 | view.showsCompass = false
18 | view.showsTraffic = false
19 | view.showsBuildings = false
20 | view.showsUserLocation = false
21 | view.showsPointsOfInterest = false
22 | view.isUserInteractionEnabled = false
23 | return view
24 | }()
25 |
26 | let label: UILabel = {
27 | let view = UILabel()
28 | view.font = UIFont.boldSystemFont(ofSize: 15)
29 | view.textColor = .white
30 | view.backgroundColor = .clear
31 | return view
32 | }()
33 |
34 | let disclosure = UIImageView(image: #imageLiteral(resourceName: "Disclosure"))
35 |
36 | override init(frame: CGRect) {
37 | super.init(frame: frame)
38 | layer.cornerRadius = 8
39 | clipsToBounds = true
40 | addSubview(mapView)
41 | addSubview(label)
42 | addSubview(disclosure)
43 | backgroundColor = UIColor(red: 30/255.0, green: 32/255.0, blue: 41/255.0, alpha: 1)
44 | }
45 |
46 | required init?(coder aDecoder: NSCoder) {
47 | fatalError("init(coder:) has not been implemented")
48 | }
49 |
50 | override func layoutSubviews() {
51 | super.layoutSubviews()
52 | let divide = bounds.divided(atDistance: 40, from: .minYEdge)
53 | label.frame = divide.slice.insetBy(dx: 15, dy: 0)
54 | mapView.frame = divide.remainder
55 | disclosure.center = CGPoint(x: bounds.width - disclosure.bounds.width / 2 - 15, y: label.center.y)
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/SavedLocationDeleteView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SavedLocationDeleteView.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/15/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SavedLocationDeleteView: UIView {
12 |
13 | let label: UILabel = {
14 | let view = UILabel()
15 | view.backgroundColor = .clear
16 | view.text = NSLocalizedString("Delete", comment: "")
17 | view.textAlignment = .center
18 | view.textColor = .white
19 | view.font = UIFont.systemFont(ofSize: 13)
20 | view.sizeToFit()
21 | return view
22 | }()
23 |
24 | let imageView = UIImageView(image: #imageLiteral(resourceName: "Delete"))
25 |
26 | override init(frame: CGRect) {
27 | super.init(frame: frame)
28 | addSubview(label)
29 | addSubview(imageView)
30 | }
31 |
32 | required init?(coder aDecoder: NSCoder) {
33 | fatalError("init(coder:) has not been implemented")
34 | }
35 |
36 | override func layoutSubviews() {
37 | super.layoutSubviews()
38 | let height = imageView.bounds.height + 8 + label.bounds.height
39 | imageView.center = CGPoint(x: bounds.midX, y: (bounds.height - height + imageView.bounds.height) / 2)
40 | label.center = CGPoint(x: bounds.midX, y: (bounds.height + height - label.bounds.height) / 2)
41 | }
42 |
43 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
44 | super.touchesBegan(touches, with: event)
45 | alpha = 0.5
46 | }
47 |
48 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
49 | super.touchesEnded(touches, with: event)
50 | alpha = 1
51 | }
52 |
53 | override func touchesMoved(_ touches: Set, with event: UIEvent?) {
54 | super.touchesMoved(touches, with: event)
55 | alpha = 1
56 | }
57 |
58 | override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
59 | super.touchesCancelled(touches, with: event)
60 | alpha = 1
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/SimpleWeather/Classes/Views/SearchResultCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchResultCell.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 1/8/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SearchResultCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var label: UILabel!
14 | @IBOutlet weak var detailLabel: UILabel!
15 |
16 | @IBOutlet weak var labelVerticalConstraint: NSLayoutConstraint!
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/SimpleWeather/Resources/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/.DS_Store
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x-1.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x-2.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Arrow.imageset/Arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Arrow.imageset/Arrow.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Arrow.imageset/Arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Arrow.imageset/Arrow@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Arrow.imageset/Arrow@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Arrow.imageset/Arrow@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Arrow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Arrow.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Arrow@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Arrow@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Cloudy.imageset/Cloudy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Cloudy.imageset/Cloudy.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Cloudy.imageset/Cloudy@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Cloudy.imageset/Cloudy@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Cloudy.imageset/Cloudy@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Cloudy.imageset/Cloudy@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Cloudy.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Cloudy.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Cloudy@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Cloudy@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Delete.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Delete.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Delete@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Delete@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Delete.imageset/Delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Delete.imageset/Delete.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Delete.imageset/Delete@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Delete.imageset/Delete@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Delete.imageset/Delete@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Delete.imageset/Delete@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Disclosure.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Disclosure.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Disclosure@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Disclosure@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Disclosure.imageset/Disclosure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Disclosure.imageset/Disclosure.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Disclosure.imageset/Disclosure@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Disclosure.imageset/Disclosure@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Disclosure.imageset/Disclosure@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Disclosure.imageset/Disclosure@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Haze.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Haze.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Haze@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Haze@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Haze.imageset/Haze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Haze.imageset/Haze.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Haze.imageset/Haze@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Haze.imageset/Haze@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Haze.imageset/Haze@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Haze.imageset/Haze@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mist.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Mist.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Mist@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Mist@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mist.imageset/Mist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mist.imageset/Mist.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mist.imageset/Mist@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mist.imageset/Mist@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mist.imageset/Mist@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mist.imageset/Mist@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Moon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Moon.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Moon@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Moon@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Moon.imageset/Moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Moon.imageset/Moon.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Moon.imageset/Moon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Moon.imageset/Moon@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Moon.imageset/Moon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Moon.imageset/Moon@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-day.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Mostly-sunny-day.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Mostly-sunny-day@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Mostly-sunny-day@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-day.imageset/Mostly-sunny-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-day.imageset/Mostly-sunny-day.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-day.imageset/Mostly-sunny-day@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-day.imageset/Mostly-sunny-day@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-day.imageset/Mostly-sunny-day@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-day.imageset/Mostly-sunny-day@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-night.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Mostly-sunny-night.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Mostly-sunny-night@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Mostly-sunny-night@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-night.imageset/Mostly-sunny-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-night.imageset/Mostly-sunny-night.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-night.imageset/Mostly-sunny-night@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-night.imageset/Mostly-sunny-night@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-night.imageset/Mostly-sunny-night@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Mostly-sunny-night.imageset/Mostly-sunny-night@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Partly-sunny-day.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Partly-sunny-day.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Partly-sunny-day@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Partly-sunny-day@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Partly-sunny-day.imageset/Partly-sunny-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Partly-sunny-day.imageset/Partly-sunny-day.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Partly-sunny-day.imageset/Partly-sunny-day@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Partly-sunny-day.imageset/Partly-sunny-day@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Partly-sunny-day.imageset/Partly-sunny-day@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Partly-sunny-day.imageset/Partly-sunny-day@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Snow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Snow.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Snow@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Snow@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Snow.imageset/Snow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Snow.imageset/Snow.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Snow.imageset/Snow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Snow.imageset/Snow@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Snow.imageset/Snow@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Snow.imageset/Snow@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Sun.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Sun.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Sun@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Sun@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Sun.imageset/Sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Sun.imageset/Sun.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Sun.imageset/Sun@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Sun.imageset/Sun@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Sun.imageset/Sun@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Sun.imageset/Sun@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Thunder-cloud.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Thunder-cloud.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Thunder-cloud@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Thunder-cloud@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Thunder-cloud.imageset/Thunder-cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Thunder-cloud.imageset/Thunder-cloud.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Thunder-cloud.imageset/Thunder-cloud@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Thunder-cloud.imageset/Thunder-cloud@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Thunder-cloud.imageset/Thunder-cloud@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Thunder-cloud.imageset/Thunder-cloud@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Wind.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Wind.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Wind@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Wind@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Wind.imageset/Wind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Wind.imageset/Wind.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Wind.imageset/Wind@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Wind.imageset/Wind@2x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Assets.xcassets/Wind.imageset/Wind@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rnystrom/SimpleWeather/2138788038358ffabb01fd6bda6a0b1277cedcc1/SimpleWeather/Resources/Assets.xcassets/Wind.imageset/Wind@3x.png
--------------------------------------------------------------------------------
/SimpleWeather/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Weather+
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | NSLocationUsageDescription
31 | This helps us find the weather at your location.
32 | NSLocationWhenInUseUsageDescription
33 | This helps us find the weather at your location.
34 | UILaunchStoryboardName
35 | LaunchScreen
36 | UIMainStoryboardFile
37 | Main
38 | UIRequiredDeviceCapabilities
39 |
40 | armv7
41 |
42 | UIStatusBarStyle
43 | UIStatusBarStyleLightContent
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 |
48 | UISupportedInterfaceOrientations~ipad
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationPortraitUpsideDown
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 | UIViewControllerBasedStatusBarAppearance
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/SimpleWeather/SimpleWeather-Briding-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleWeather-Briding-Header.h.h
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/19/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
--------------------------------------------------------------------------------
/SimpleWeatherTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SimpleWeatherTests/KeypathParsingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeypathParsingTests.swift
3 | // SimpleWeather
4 | //
5 | // Created by Ryan Nystrom on 11/15/16.
6 | // Copyright © 2016 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class KeypathParsingTests: XCTestCase {
12 |
13 | func test_whenKeypathExists_withProperValueCast_thatValueReturned() {
14 | let dict = [ "a": [ "b" : "1" ] ]
15 | let result: String? = keypath(dict: dict, path: "a.b")
16 | XCTAssertEqual(result, "1")
17 | }
18 |
19 | func test_whenKeypathExists_withIncorrectValueCast_thatValueReturned() {
20 | let dict = [ "a": [ "b" : "1" ] ]
21 | let result: Int? = keypath(dict: dict, path: "a.b")
22 | XCTAssertNil(result)
23 | }
24 |
25 | func test_whenKeypathNotDeepEnough_thatNilReturned() {
26 | let dict = [ "a": [ "b" : "1" ] ]
27 | let result: String? = keypath(dict: dict, path: "a")
28 | XCTAssertNil(result)
29 | }
30 |
31 | func test_whenKeypathDoesNotExist_withTooDeep_thatNilReturned() {
32 | let dict = [ "a": [ "b" : "1" ] ]
33 | let result: String? = keypath(dict: dict, path: "a.b.c")
34 | XCTAssertNil(result)
35 | }
36 |
37 | func test_whenKeypathDoesNotExist_withCorrectDepth_thatValueReturned() {
38 | let dict = [ "a": [ "b" : "1" ] ]
39 | let result: String? = keypath(dict: dict, path: "a.c")
40 | XCTAssertNil(result)
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/SimpleWeatherUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SimpleWeatherUITests/SimpleWeatherUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleWeatherUITests.swift
3 | // SimpleWeatherUITests
4 | //
5 | // Created by Ryan Nystrom on 1/15/17.
6 | // Copyright © 2017 Ryan Nystrom. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class SimpleWeatherUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 |
16 | continueAfterFailure = false
17 |
18 | let app = XCUIApplication()
19 | setupSnapshot(app)
20 | app.launch()
21 | }
22 |
23 | override func tearDown() {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | super.tearDown()
26 | }
27 |
28 | func test_weather_screen() {
29 | XCUIApplication().collectionViews.scrollViews.otherElements.containing(.staticText, identifier:"New York").children(matching: .other).element.children(matching: .other).element.children(matching: .map).element.tap()
30 | sleep(2)
31 | snapshot("01WeatherScreen")
32 | }
33 |
34 | func test_locations_screen() {
35 | sleep(2)
36 | snapshot("02LocationsScreen")
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/fastlane/Appfile:
--------------------------------------------------------------------------------
1 | # The Appfile can be used to specify information that's used across all fastlane
2 | # tools, like your username or the app's bundle identifier.
3 | #
4 | # For more details, check out the documentation at:
5 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md
6 |
7 | app_identifier "com.whoisryannystrom.weather" # the bundle identifier of your app
8 | apple_id "rnystrom@whoisryannystrom.com" # Your Apple ID
9 |
--------------------------------------------------------------------------------
/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | # More documentation about how to customize your build
2 | # can be found here:
3 | # https://docs.fastlane.tools
4 | fastlane_version "1.109.0"
5 |
6 | # This value helps us track success metrics for Fastfiles
7 | # we automatically generate. Feel free to remove this line
8 | # once you get things running smoothly!
9 | generated_fastfile_id "959e5fcd-45e0-4847-b130-f09163fab837"
10 |
11 | default_platform :ios
12 |
13 | # Fastfile actions accept additional configuration, but
14 | # don't worry, fastlane will prompt you for required
15 | # info which you can add here later
16 | lane :beta do
17 | cert
18 | sigh
19 | gym(scheme: "SimpleWeather",
20 | workspace: "SimpleWeather.xcworkspace")
21 | pilot(skip_waiting_for_build_processing: true)
22 | end
23 |
24 | lane :appstore do
25 | snapshot
26 | match(type: "appstore")
27 | gym(scheme: "SimpleWeather",
28 | workspace: "SimpleWeather.xcworkspace")
29 | appstore
30 | end
31 |
--------------------------------------------------------------------------------
/fastlane/README.md:
--------------------------------------------------------------------------------
1 | fastlane documentation
2 | ================
3 | # Installation
4 | ```
5 | sudo gem install fastlane
6 | ```
7 | # Available Actions
8 | ### beta
9 | ```
10 | fastlane beta
11 | ```
12 |
13 |
14 | ----
15 |
16 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
17 | More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools).
18 | The documentation of fastlane can be found on [GitHub](https://github.com/fastlane/fastlane/tree/master/fastlane).
19 |
--------------------------------------------------------------------------------
/fastlane/Snapfile:
--------------------------------------------------------------------------------
1 | # Uncomment the lines below you want to change by removing the # in the beginning
2 |
3 | # A list of devices you want to take the screenshots from
4 | devices([
5 | "iPhone 6",
6 | "iPhone 6 Plus",
7 | "iPhone 5"
8 | ])
9 |
10 | languages([
11 | "en-US"
12 | ])
13 |
14 | # The name of the scheme which contains the UI Tests
15 | scheme "SimpleWeather"
16 |
17 | # Where should the resulting screenshots be stored?
18 | # output_directory "./screenshots"
19 |
20 | # clear_previous_screenshots true # remove the '#' to clear all previously generated screenshots before creating new ones
21 |
22 | # Choose which project/workspace to use
23 | # project "./Project.xcodeproj"
24 | workspace "./SimpleWeather.xcworkspace"
25 |
26 | # Arguments to pass to the app on launch. See https://github.com/fastlane/snapshot#launch-arguments
27 | # launch_arguments(["-favColor red"])
28 |
29 | # For more information about all available options run
30 | # fastlane snapshot --help
31 |
--------------------------------------------------------------------------------
/report.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------