TRAVIS_OS_NAME=osx default= <<<<<< ENV .codecov.yml .codebeatignore .createDocs.sh .downloadNpsData.sh LICENSE NatParkSwiftKit.xcodeproj/NatParkSwiftKitTests_Info.plist NatParkSwiftKit.xcodeproj/NatParkSwiftKit_Info.plist NatParkSwiftKit.xcodeproj/project.pbxproj NatParkSwiftKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata NatParkSwiftKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist NatParkSwiftKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings NatParkSwiftKit.xcodeproj/xcshareddata/xcschemes/NatParkSwiftKit-Package.xcscheme Package.swift Sources/NatParkSwiftKit/DataService.swift Sources/NatParkSwiftKit/DataServiceError.swift Sources/NatParkSwiftKit/Internal/Internal.swift Sources/NatParkSwiftKit/Internal/RequestUrlFactory.swift Sources/NatParkSwiftKit/Model/Alert.swift Sources/NatParkSwiftKit/Model/Asset.swift Sources/NatParkSwiftKit/Model/Fee.swift Sources/NatParkSwiftKit/Model/NewsRelease.swift Sources/NatParkSwiftKit/Model/NpsImage.swift Sources/NatParkSwiftKit/Model/Park.swift Sources/NatParkSwiftKit/Model/ParkCodeConstants.swift Sources/NatParkSwiftKit/Model/ParkUnitDesignation.swift Sources/NatParkSwiftKit/Model/RequestOption.swift Sources/NatParkSwiftKit/Model/RequestableField.swift Sources/NatParkSwiftKit/Model/StateInUSA.swift Sources/NatParkSwiftKit/Model/VisitorCenter.swift Tests/LinuxMain.swift Tests/NatParkSwiftKitTests/DataServiceTests.swift Tests/NatParkSwiftKitTests/ParkTests.swift Tests/NatParkSwiftKitTests/ParkUnitDesignationTests.swift Tests/NatParkSwiftKitTests/StateInUSATests.swift Tests/NatParkSwiftKitTests/XCTestManifests.swift docs/badge.svg docs/css/highlight.css docs/css/jazzy.css docs/docsets/.docset/Contents/Info.plist docs/docsets/.docset/Contents/Resources/Documents/css/highlight.css docs/docsets/.docset/Contents/Resources/Documents/css/jazzy.css docs/docsets/.docset/Contents/Resources/Documents/js/jazzy.js docs/docsets/.docset/Contents/Resources/Documents/js/jquery.min.js docs/docsets/.docset/Contents/Resources/Documents/search.json docs/docsets/.docset/Contents/Resources/docSet.dsidx docs/docsets/.tgz docs/docsets/.xml docs/js/jazzy.js docs/js/jquery.min.js docs/search.json docs/undocumented.json <<<<<< network # path=NatParkSwiftKit.framework.coverage.txt /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/DataService.swift: 1| |import Foundation 2| |import Combine 3| | 4| |private enum DataServiceEndpoint: String { 5| | case parks = "/parks" 6| | case alerts = "/alerts" 7| | case newsRelease = "/newsreleases" 8| | case visitorCenters = "/visitorcenters" 9| | case assets = "/places" 10| |} 11| | 12| |/// Main API class to interact with the National Park Service API 13| |public class DataService { 14| | 15| | /// Required API key which can be requested for free from NPS Developer website 16| | public let apiKey: String 17| | 18| | private let urlFactory: RequestUrlFactory = RequestUrlFactory() 19| | 20| 1| private var errorTransformer: (Error) -> DataServiceError = { error in 21| 1| switch error { 22| 1| case DataServiceError.invalidApiKey: 23| 1| return DataServiceError.invalidApiKey 24| 1| default: 25| 0| return DataServiceError.cannotDecodeContent(error: error) 26| 1| } 27| 1| } 28| | 29| 9| private var responseTransformer: (Data, URLResponse) throws -> Data = { data, response -> Data in 30| 9| guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { 31| 1| throw DataServiceError.invalidApiKey 32| 8| } 33| 8| return data 34| 9| } 35| | 36| | /// Initializer 37| 10| public init(apiKey: String) { 38| 10| self.apiKey = apiKey 39| 10| } 40| | 41| | // MARK: public functions 42| | 43| | /** 44| | fetch single park information from the National Park Service Data API 45| | 46| | - Parameter parkCode: The National Park Service uses four letter codes - Alpha Codes - to abbreviate the names of its parks. If a park has one name in its title, like Yosemite National Park, the code word would be the first four letters of the name - YOSE. If the park has two names or more in its title, like Grand Canyon National Park, the code word would be the first two letters of each name - GRCA. 47| | 48| | - Returns: a respective publisher 49| | */ 50| 2| public func fetchPark(_ parkCode: String) -> AnyPublisher { 51| 2| return self.fetchParks(by: [parkCode], in: nil, nil) 52| 2| .map { $0.first } 53| 2| .eraseToAnyPublisher() 54| 2| } 55| | 56| | /** 57| | fetch park information from the National Park Service Data API 58| | 59| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 60| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 61| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 62| | - Returns: a respective publisher 63| | */ 64| 5| public func fetchParks(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<[Park], DataServiceError> { 65| 5| 66| 5| guard let validUrl = self.url(.parks, by: parkCodes, in: states, requestOptions) else { 67| 0| return Fail(error: DataServiceError.badURL).eraseToAnyPublisher() 68| 5| } 69| 5| 70| 5| return URLSession.shared.dataTaskPublisher(for: validUrl) 71| 5| .tryMap(responseTransformer) 72| 5| .decode(type: Parks.self, decoder: JSONDecoder()) 73| 5| .map { $0.data } 74| 5| .mapError(self.errorTransformer) 75| 5| .eraseToAnyPublisher() 76| 5| } 77| | 78| | /** 79| | fetch alert release information from the National Park Service Data API 80| | 81| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 82| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 83| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 84| | - Returns: a respective publisher 85| | */ 86| 1| public func fetchAlerts(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<[Alert], DataServiceError> { 87| 1| 88| 1| guard let validUrl = self.url(.alerts, by: parkCodes, in: states, requestOptions) else { 89| 0| return Fail(error: DataServiceError.badURL).eraseToAnyPublisher() 90| 1| } 91| 1| 92| 1| return URLSession.shared.dataTaskPublisher(for: validUrl) 93| 1| .tryMap(responseTransformer) 94| 1| .decode(type: Alerts.self, decoder: JSONDecoder()) 95| 1| .map { $0.data } 96| 1| .mapError(self.errorTransformer) 97| 1| .eraseToAnyPublisher() 98| 1| } 99| | 100| | /** 101| | fetch news release information from the National Park Service Data API 102| | 103| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 104| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 105| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 106| | - Returns: a respective publisher 107| | */ 108| 1| public func fetchNewsReleases(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<[NewsRelease], DataServiceError> { 109| 1| 110| 1| guard let validUrl = self.url(.newsRelease, by: parkCodes, in: states, requestOptions) else { 111| 0| return Fail(error: DataServiceError.badURL).eraseToAnyPublisher() 112| 1| } 113| 1| 114| 1| return URLSession.shared.dataTaskPublisher(for: validUrl) 115| 1| .tryMap(responseTransformer) 116| 1| .decode(type: NewsReleases.self, decoder: JSONDecoder()) 117| 1| .map { $0.data } 118| 1| .mapError(self.errorTransformer) 119| 1| .eraseToAnyPublisher() 120| 1| } 121| | 122| | /** 123| | fetch visitor center information from the National Park Service Data API 124| | 125| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 126| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 127| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 128| | - Returns: a respective publisher 129| | */ 130| 1| public func fetchVisitorCenters(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<[VisitorCenter], DataServiceError> { 131| 1| 132| 1| guard let validUrl = self.url(.visitorCenters, by: parkCodes, in: states, requestOptions) else { 133| 0| return Fail(error: DataServiceError.badURL).eraseToAnyPublisher() 134| 1| } 135| 1| 136| 1| return URLSession.shared.dataTaskPublisher(for: validUrl) 137| 1| .tryMap(responseTransformer) 138| 1| .decode(type: VisitorCenters.self, decoder: JSONDecoder()) 139| 1| .map { $0.data } 140| 1| .mapError(self.errorTransformer) 141| 1| .eraseToAnyPublisher() 142| 1| } 143| | 144| | /** 145| | fetch asset (place) information from the National Park Service Data API 146| | 147| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 148| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 149| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 150| | - Returns: a respective publisher 151| | */ 152| 1| public func fetchAssets(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<[Asset], DataServiceError> { 153| 1| 154| 1| guard let validUrl = self.url(.assets, by: parkCodes, in: states, requestOptions) else { 155| 0| return Fail(error: DataServiceError.badURL).eraseToAnyPublisher() 156| 1| } 157| 1| 158| 1| return URLSession.shared.dataTaskPublisher(for: validUrl) 159| 1| .tryMap(responseTransformer) 160| 1| .decode(type: Assets.self, decoder: JSONDecoder()) 161| 1| .map { $0.data } 162| 1| .mapError(self.errorTransformer) 163| 1| .eraseToAnyPublisher() 164| 1| } 165| | 166| | // MARK: private functions 167| 9| private func url(_ endpoint: DataServiceEndpoint, by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions?) -> URL? { 168| 9| 169| 9| var urlComponents = self.urlFactory.apiUrlComponents(for: endpoint.rawValue, authorizedBy: self.apiKey) 170| 9| 171| 9| let parkCodeQueryParameterValue = parkCodes?.joined(separator: ",") 172| 9| if parkCodeQueryParameterValue != nil { 173| 7| urlComponents.queryItems?.append(URLQueryItem(name: "parkCode", value: parkCodeQueryParameterValue)) 174| 9| } 175| 9| 176| 9| let statesQueryParameterValue = states?.map { $0.rawValue }.joined(separator: ",") 177| 9| if statesQueryParameterValue != nil { 178| 5| urlComponents.queryItems?.append(URLQueryItem(name: "stateCode", value: statesQueryParameterValue)) 179| 9| } 180| 9| 181| 9| self.urlFactory.add(requestOptions, to: &urlComponents) 182| 9| 183| 9| return urlComponents.url 184| 9| } 185| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Internal/Internal.swift: 1| |// 2| |// Internal.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/21/20. 6| |// 7| | 8| |import Foundation 9| |import CoreLocation 10| | 11| |struct Parks: Decodable { 12| | let total: String 13| | let data: [Park] 14| |} 15| | 16| |struct Alerts: Decodable { 17| | let total: String 18| | let data: [Alert] 19| |} 20| | 21| |struct NewsReleases: Decodable { 22| | let total: String 23| | let data: [NewsRelease] 24| |} 25| | 26| |struct VisitorCenters: Decodable { 27| | let total: String 28| | let data: [VisitorCenter] 29| |} 30| | 31| |struct Assets: Decodable { 32| | let total: String 33| | let data: [Asset] 34| |} 35| | 36| |extension String { 37| 265| func toURL() -> URL? { 38| 265| return URL(string: self) 39| 265| } 40| | 41| 50| func toDate() -> Date? { 42| 50| var releaseDateAsString = self 43| 50| releaseDateAsString = String(releaseDateAsString.dropLast()) 44| 50| releaseDateAsString = String(releaseDateAsString.dropLast()) 45| 50| let releaseDateString: String = releaseDateAsString 46| 50| let dateFor: DateFormatter = DateFormatter() 47| 50| dateFor.dateFormat = "yyyy-mm-dd HH:mm:ss" 48| 50| dateFor.locale = Locale(identifier: "en_US_POSIX") 49| 50| return dateFor.date(from: releaseDateString) 50| 50| } 51| | 52| 46| func toLocation() -> CLLocation? { 53| 46| guard let lat = self.toLatitude(), 54| 46| let long = self.toLongitude() else { 55| 5| return nil 56| 41| } 57| 41| return CLLocation(latitude: lat, longitude: long) 58| 46| } 59| | 60| 46| func toLatitude() -> Double? { 61| 46| guard let latitudeSeparator = self.firstIndex(of: ":") else { 62| 5| return nil 63| 41| } 64| 41| let startLat = self.index(latitudeSeparator, offsetBy: +1) 65| 41| guard let endLat = self.firstIndex(of: ",") else { 66| 0| return nil 67| 41| } 68| 41| return Double(String(self[startLat.. Double? { 72| 41| guard let longitudeSeparator = self.firstIndex(of: ",") else { 73| 0| return nil 74| 41| } 75| 41| // default format "lat:44.59824417, long:-110.5471695" 76| 41| var endLong = self.endIndex 77| 41| var distance = +7 78| 41| // fallback format "{lat:63.7308262, lng:-148.9171829}" 79| 41| if self.contains("lng") { 80| 10| distance = +6 81| 10| endLong = self.index(self.endIndex, offsetBy: -1) 82| 41| } 83| 41| let startLong = self.index(longitudeSeparator, offsetBy: distance) 84| 41| return Double(String(self[startLong.. [StateInUSA] { 88| 35| var states: [StateInUSA] = [] 89| 35| let statesAsStringArray = self.components(separatedBy: ",") 90| 58| statesAsStringArray.forEach { (potentialState) in 91| 58| guard let state = StateInUSA(rawValue: potentialState) else { 92| 0| return 93| 58| } 94| 58| states.append(state) 95| 58| } 96| 35| return states 97| 35| } 98| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Internal/RequestUrlFactory.swift: 1| |// 2| |// RequestUrlFactory.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/18/20. 6| |// 7| | 8| |import Foundation 9| | 10| |class RequestUrlFactory { 11| | 12| 9| func apiUrlComponents(for endpoint: String, authorizedBy apiKey: String) -> URLComponents { 13| 9| var urlComponents = URLComponents() 14| 9| urlComponents.scheme = "https" 15| 9| urlComponents.host = "developer.nps.gov" 16| 9| urlComponents.path = "/api/v1" + endpoint 17| 9| urlComponents.queryItems = [ 18| 9| URLQueryItem(name: "api_key", value: apiKey) 19| 9| ] 20| 9| return urlComponents 21| 9| } 22| | 23| 9| func add(_ requestOptions: RequestOptions?, to urlComponents: inout URLComponents) { 24| 9| if let limit = requestOptions?.limit { 25| 1| urlComponents.queryItems?.append(URLQueryItem(name: "limit", value: String(limit))) 26| 9| } 27| 9| 28| 9| if let query = requestOptions?.searchQuery { 29| 1| urlComponents.queryItems?.append(URLQueryItem(name: "q", value: query)) 30| 9| } 31| 9| 32| 9| if let fields = requestOptions?.fields { 33| 3| let fieldsQueryParameterValue = fields.map { $0.fieldName }.joined(separator: ",") 34| 1| if fieldsQueryParameterValue.isEmpty == false { 35| 1| urlComponents.queryItems?.append(URLQueryItem(name: "fields", value: fieldsQueryParameterValue)) 36| 1| } 37| 9| } 38| 9| } 39| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/Alert.swift: 1| |// 2| |// Alert.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/18/20. 6| |// 7| | 8| |import Foundation 9| | 10| |/// Requestable attribute of Park model 11| |public enum RequestableAlertField: String, RequestableField { 12| | case void 13| |} 14| | 15| |/// Alerts communicate information about hazardous, potentially hazardous, or changing conditions that may affect a visit to a national park. Alert data includes the type of alert, title, description, and optional link to additional information. 16| |public struct Alert: Decodable, Identifiable { 17| | enum CodingKeys: CodingKey { 18| | case id, parkCode, title, description, category, url 19| | } 20| | 21| | /// Unique identifier for an alert record 22| | public let id: String 23| | /// A variable width character code used to identify a specific park 24| | public let parkCode: String 25| | /// Alert title 26| | public let title: String 27| | /// Alert description 28| | public let description: String 29| | /// Alert type: danger, caution, information, or park closure 30| | public let category: String 31| | /// Link to more information about the alert, if available 32| | public let url: URL? 33| |} 34| | 35| |extension Alert { 36| 1| public init(from decoder: Decoder) throws { 37| 1| let values = try decoder.container(keyedBy: CodingKeys.self) 38| 1| 39| 1| id = try values.decode(String.self, forKey: .id) 40| 1| parkCode = try values.decode(String.self, forKey: .parkCode) 41| 1| title = try values.decode(String.self, forKey: .title) 42| 1| description = try values.decode(String.self, forKey: .description) 43| 1| category = try values.decode(String.self, forKey: .category) 44| 1| url = try values.decodeIfPresent(String.self, forKey: .url)?.toURL() 45| 1| } 46| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/Asset.swift: 1| |// 2| |// Asset.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/24/20. 6| |// 7| | 8| |import Foundation 9| | 10| |/// Requestable attribute of Asset model 11| |public enum RequestableAssetField: String, RequestableField { 12| | case void 13| |} 14| | 15| |/// Places are shared content assets that are tagged so they can appear in a variety of places on NPS.gov. Data includes a title, image, short description of the content, and link to more information about the asset. 16| |public struct Asset: Decodable, Identifiable { 17| | enum CodingKeys: String, CodingKey { 18| | case id, listingDescription = "listingdescription", listingImage = "listingimage", title, url 19| | } 20| | 21| | /// Uniquely identifies place record 22| | public let id: String 23| | /// Short description of the content 24| | public let listingDescription: String 25| | /// mall image that accompanies the short description 26| | public let listingImage: NpsImage 27| | /// Asset title 28| | public let title: String 29| | /// Link to more information about the asset, if available 30| | public let url: URL? 31| |} 32| | 33| |extension Asset { 34| 36| public init(from decoder: Decoder) throws { 35| 36| let values = try decoder.container(keyedBy: CodingKeys.self) 36| 36| 37| 36| id = try values.decode(String.self, forKey: .id) 38| 36| listingDescription = try values.decode(String.self, forKey: .listingDescription) 39| 36| listingImage = try values.decode(NpsImage.self, forKey: .listingImage) 40| 36| title = try values.decode(String.self, forKey: .title) 41| 36| url = try values.decodeIfPresent(String.self, forKey: .url)?.toURL() 42| 36| } 43| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/NewsRelease.swift: 1| |// 2| |// NewsRelease.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/21/20. 6| |// 7| | 8| |import Foundation 9| | 10| |/// Requestable attribute of NewsRelease model 11| |public enum RequestableNewsReleaseField: String, RequestableField { 12| | case void 13| |} 14| | 15| |/// News release data includes a title, abstract, and link to national park news releases, as well as an optional image. 16| |public struct NewsRelease: Decodable, Identifiable { 17| | enum CodingKeys: String, CodingKey { 18| | case id, parkCode, title, releaseDate = "releasedate", abstract, url, image 19| | } 20| | 21| | /// Unique identifier for the news release 22| | public let id: String 23| | /// A variable width character code used to identify a specific park 24| | public let parkCode: String 25| | /// News release title 26| | public let title: String 27| | /// Short description of news release content 28| | public let abstract: String 29| | /// Date news release was released 30| | public let releaseDate: Date? 31| | /// Link to full news release 32| | public let url: URL? 33| | /// News release image 34| | public let image: NpsImage? 35| |} 36| | 37| |extension NewsRelease { 38| 50| public init(from decoder: Decoder) throws { 39| 50| let values = try decoder.container(keyedBy: CodingKeys.self) 40| 50| id = try values.decode(String.self, forKey: .id) 41| 50| parkCode = try values.decode(String.self, forKey: .parkCode) 42| 50| title = try values.decode(String.self, forKey: .title) 43| 50| abstract = try values.decode(String.self, forKey: .abstract) 44| 50| releaseDate = try values.decodeIfPresent(String.self, forKey: .releaseDate)?.toDate() 45| 50| url = try values.decodeIfPresent(String.self, forKey: .url)?.toURL() 46| 50| image = try values.decodeIfPresent(NpsImage.self, forKey: .image) 47| 50| } 48| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/NpsImage.swift: 1| |// 2| |// NpsImage.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/21/20. 6| |// 7| | 8| |import Foundation 9| | 10| |/// Image information provided by National Park Service 11| |public struct NpsImage: Decodable, Identifiable { 12| | enum CodingKeys: String, CodingKey { 13| | case id, title, description, caption, altText, credit, url 14| | } 15| | 16| | /// Unique identifier for the news release 17| | public let id: String 18| | /// Image Title 19| | public let title: String? 20| | /// Image Title 21| | public let description: String? 22| | /// Image Caption 23| | public let caption: String? 24| | /// Alternative Text for Image 25| | public let altText: String? 26| | /// Credit to acknowledge contributor / creator of Image 27| | public let credit: String? 28| | /// Link to Image 29| | public let url: URL? 30| |} 31| | 32| |extension NpsImage { 33| 97| public init(from decoder: Decoder) throws { 34| 97| let values = try decoder.container(keyedBy: CodingKeys.self) 35| 97| id = try (values.decodeIfPresent(String.self, forKey: .id) ?? NSUUID().uuidString) 36| 97| title = try values.decodeIfPresent(String.self, forKey: .title) 37| 97| description = try values.decodeIfPresent(String.self, forKey: .description) 38| 97| caption = try values.decode(String.self, forKey: .caption) 39| 97| altText = try values.decodeIfPresent(String.self, forKey: .altText) 40| 97| credit = try values.decodeIfPresent(String.self, forKey: .credit) 41| 97| url = try values.decodeIfPresent(String.self, forKey: .url)?.toURL() 42| 97| } 43| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/Park.swift: 1| |// 2| |// File.swift 3| |// 4| |// 5| |// Created by Eidinger, Marco on 1/16/20. 6| |// 7| | 8| |import Foundation 9| |import CoreLocation 10| | 11| |/// Requestable attribute of Park model 12| |public enum RequestableParkField: String, RequestableField { 13| | /// Park images 14| | case images 15| | /// Fee for entering the park 16| | case entranceFees 17| | /// Passes available to provide entry into the park 18| | case entrancePasses 19| |} 20| | 21| |/// Park basics data includes location, contact, operating hours, and entrance fee/pass information for each national park. At least five photos of each park are also available. 22| |public struct Park: Decodable, Identifiable, Hashable { 23| | 24| | /// Park can be compared for equality using the equal-to operator (`==`) or inequality using the not-equal-to operator (`!=`) 25| 2| public static func == (lhs: Park, rhs: Park) -> Bool { 26| 2| return lhs.id == rhs.id 27| 2| } 28| | 29| | /// Park conforms to the Hashable protocol and hence can be used in a set or as a dictionary key 30| 3| public func hash(into hasher: inout Hasher) { 31| 3| hasher.combine(self.id) 32| 3| } 33| | 34| | enum CodingKeys: CodingKey { 35| | case id, parkCode, name, fullName, description, url, designation, states, latLong, directionsInfo, directionsUrl, weatherInfo, images, entranceFees, entrancePasses 36| | } 37| | 38| | /// Park identification string 39| | public let id: String 40| | /// A variable width character code used to identify a specific park 41| | public let parkCode: String 42| | /// Short park name (no designation) 43| | public let name: String 44| | /// Full park name (with designation) 45| | public let fullName: String 46| | /// Introductory paragraph from the park homepage 47| | public let description: String 48| | /// Type of designation (eg, national park, national monument, national recreation area, etc) 49| | public let designation: ParkUnitDesignation 50| | /// State(s) the park is located in 51| | public let states: [StateInUSA] 52| | /// Park GPS cordinates 53| | public let gpsLocation: CLLocation? 54| | /// General overview of how to get to the park 55| | public let directionsInfo: String 56| | /// Link to page, if available, that provides additional detail on getting to the park 57| | public let directionsUrl: URL? 58| | /// General description of the weather in the park over the course of a year 59| | public let weatherInfo: String? 60| | /// Park Website 61| | public let url: URL? 62| | /// Park images 63| | public let images: [NpsImage]? 64| | /// Fee for entering the park 65| | public let entranceFees: [Fee]? 66| | /// Passes available to provide entry into the park 67| | public let entrancePasses: [Fee]? 68| | 69| | /// Memberwise Initializer 70| 4| public init(id: String, parkCode: String, name: String, fullName: String, description: String, designation: ParkUnitDesignation, states: [StateInUSA], gpsLocation: CLLocation?, directionsInfo: String, directionsUrl: URL?, weatherInfo: String?, url: URL?, images: [NpsImage]?, entranceFees: [Fee]?, entrancePasses: [Fee]?) { 71| 4| self.id = id 72| 4| self.parkCode = parkCode 73| 4| self.name = name 74| 4| self.fullName = fullName 75| 4| self.description = description 76| 4| self.designation = designation 77| 4| self.states = states 78| 4| self.gpsLocation = gpsLocation 79| 4| self.directionsInfo = directionsInfo 80| 4| self.directionsUrl = directionsUrl 81| 4| self.weatherInfo = weatherInfo 82| 4| self.url = url 83| 4| self.images = images 84| 4| self.entranceFees = entranceFees 85| 4| self.entrancePasses = entrancePasses 86| 4| } 87| |} 88| | 89| |extension Park { 90| | /// Decoderwise Initializer 91| 35| public init(from decoder: Decoder) throws { 92| 35| let values = try decoder.container(keyedBy: CodingKeys.self) 93| 35| 94| 35| id = try values.decode(String.self, forKey: .id) 95| 35| parkCode = try values.decode(String.self, forKey: .parkCode) 96| 35| name = try values.decode(String.self, forKey: .name) 97| 35| fullName = try values.decode(String.self, forKey: .fullName) 98| 35| description = try values.decode(String.self, forKey: .description) 99| 35| url = try values.decodeIfPresent(String.self, forKey: .url)?.toURL() 100| 35| designation = try ParkUnitDesignation(validDesignationOrUnknown: values.decodeIfPresent(String.self, forKey: .designation)) 101| 35| directionsInfo = try values.decode(String.self, forKey: .directionsInfo) 102| 35| directionsUrl = try values.decodeIfPresent(String.self, forKey: .directionsUrl)?.toURL() 103| 35| weatherInfo = try values.decodeIfPresent(String.self, forKey: .weatherInfo) 104| 35| states = try values.decode(String.self, forKey: .states).toStates() 105| 35| gpsLocation = try values.decodeIfPresent(String.self, forKey: .latLong)?.toLocation() 106| 35| images = try values.decodeIfPresent([NpsImage].self, forKey: .images) 107| 35| entranceFees = try values.decodeIfPresent([Fee].self, forKey: .entranceFees) 108| 35| entrancePasses = try values.decodeIfPresent([Fee].self, forKey: .entrancePasses) 109| 35| } 110| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/ParkUnitDesignation.swift: 1| |// 2| |// UnitDesignation.swift 3| |// npsapi-swift 4| |// 5| |// Created by Eidinger, Marco on 1/23/20. 6| |// 7| | 8| |import Foundation 9| | 10| |/// Designation of a unit belonging to the National Park Service 11| |public enum ParkUnitDesignation: String, CaseIterable, Decodable, Hashable { 12| | /// National Park 13| | case nationalPark = "National Park" 14| | /// National Monument 15| | case nationalMonument = "National Monument" 16| | /// National Memorial 17| | case nationalMemorial = "National Memorial" 18| | /// National Historic Trail 19| | case nationalHistoricTrail = "National Historic Trail" 20| | /// National Historic Site 21| | case nationalHistoricSite = "National Historic Site" 22| | /// National Historical Park 23| | case nationalHistoricalPark = "National Historical Park" 24| | /// National Recreation Area 25| | case nationalRecreationArea = "National Recreation Area" 26| | /// National Preserve 27| | case nationalPreserve = "National Preserve" 28| | /// National Seashore 29| | case nationalSeashore = "National Seashore" 30| | /// National and State Parks 31| | case nationalAndStateParks = "National and State Parks" 32| | /// National Battlefield 33| | case nationalBattlefield = "National Battlefield" 34| | /// National Lakeshore 35| | case nationalLakeshore = "National Lakeshore" 36| | /// National Scenic Trail 37| | case nationalScenicTrail = "National Scenic Trail" 38| | /// National Heritage Area 39| | case nationalHeritageArea = "National Heritage Area" 40| | /// National Military Park 41| | case nationalMilitaryPark = "National Military Park" 42| | /// National Historical Reserve 43| | case nationalHistoricalReserve = "National Historical Reserve" 44| | /// National Heritage Corridor 45| | case nationalHeritageCorridor = "National Heritage Corridor" 46| | /// National Park & Preserve 47| | case nationalParkAndPreserve = "National Park & Preserve" 48| | /// National Geologic Trail 49| | case nationalGeologicTrail = "National Geologic Trail" 50| | /// National River 51| | case nationalRiver = "National River" 52| | /// National Wild and Scenic River 53| | case nationalWildAndScenicRiver = "National Wild and Scenic River" 54| | /// National Scenic River 55| | case nationalScenicRiver = "National Scenic River" 56| | /// National Scenic Riverway 57| | case nationalScenicRiverway = "National Scenic Riverway" 58| | /// National Scenic Riverways 59| | case nationalScenicRiverways = "National Scenic Riverways" 60| | /// National Recreational River 61| | case nationalRecreationalRiver = "National Recreational River" 62| | /// National Historical Park and Preserve 63| | case nationalHistoricalParkAndPreserve = "National Historical Park and Preserve" 64| | /// National Monument & Preserve 65| | case nationalMonumentAndPreserve = "National Monument & Preserve" 66| | /// National Historical Park and Ecological Preserve 67| | case nationalHistoricalParkAndEcologicalPreserve = "National Historical Park and Ecological Preserve" 68| | /// National Reserve 69| | case nationalReserve = "National Reserve" 70| | 71| | /// Heritage Area 72| | case heritageArea = "Heritage Area" 73| | /// Park 74| | case park = "Park" 75| | /// Parkway 76| | case parkway = "Parkway" 77| | /// Memorial 78| | case memorial = "Memorial" 79| | /// Memorial Parkway 80| | case memorialParkway = "Memorial Parkway" 81| | /// Cultural Heritage Corridor 82| | case culturalHeritageCorridor = "Cultural Heritage Corridor" 83| | /// Part of Colonial National Historical Park 84| | case partOfColonialNationalHistoricalPark = "Part of Colonial National Historical Park" 85| | /// Part of Statue of Liberty National Monument 86| | case partOfStatueOLibertyNationalMonument = "Part of Statue of Liberty National Monument" 87| | 88| | /// Scenic & Recreational River 89| | case scenicAndRecreationalRiver = "Scenic & Recreational River" 90| | /// Wild & Scenic River 91| | case wildAndScenicRiver = "Wild & Scenic River" 92| | /// Wild River 93| | case wildRiver = "Wild River" 94| | 95| | /// International Park 96| | case internationalPark = "International Park" 97| | /// International Historic Site 98| | case internationalHistoricSite = "International Historic Site" 99| | /// National Parks 100| | case nationalParks = "National Parks" 101| | /// Ecological & Historic Preserve 102| | case ecologicalAndHistoricPreserve = "Ecological & Historic Preserve" 103| | 104| | /// Unknown; either record does not have designation or designation is incorrectly spelled 105| | case unknown = "" 106| | 107| | /// Preferred initializer; cannot return null; will map to .unknown if necessary 108| 37| public init(validDesignationOrUnknown: String?) { 109| 37| guard let input = validDesignationOrUnknown else { 110| 0| self = ParkUnitDesignation.unknown 111| 0| return 112| 37| } 113| 37| self = ParkUnitDesignation(rawValue: input) ?? ParkUnitDesignation.unknown 114| 37| } 115| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/RequestableField.swift: 1| |// 2| |// RequestableField.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/18/20. 6| |// 7| | 8| |import Foundation 9| | 10| |/// Field which can explicitly be requested for an entity, e.g Park 11| |public protocol RequestableField: RawRepresentable { 12| | /// Name of the field 13| | var fieldName: String { get } 14| |} 15| | 16| |public extension RequestableField { 17| 3| var fieldName: String { 18| 3| return String(describing: self.rawValue) 19| 3| } 20| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/StateInUSA.swift: 1| |// 2| |// File.swift 3| |// 4| |// 5| |// Created by Eidinger, Marco on 1/16/20. 6| |// 7| | 8| |import Foundation 9| | 10| |/// Constituent political entity in the United States 11| |public enum StateInUSA: String, CaseIterable { 12| | /// Alabama 13| | case alabama = "AL" 14| | /// Alaska 15| | case alaska = "AK" 16| | /// Arizona 17| | case arizona = "AZ" 18| | /// Arkansas 19| | case arkansas = "AR" 20| | /// California 21| | case california = "CA" 22| | /// Colorado 23| | case colorado = "CO" 24| | /// Connecticut 25| | case connecticut = "CT" 26| | /// Delaware 27| | case delaware = "DE" 28| | /// Florida 29| | case florida = "FL" 30| | /// Georgia 31| | case georgia = "GA" 32| | /// Hawaii 33| | case hawaii = "HI" 34| | /// Idaho 35| | case idaho = "ID" 36| | /// Illinois 37| | case illinois = "IL" 38| | /// Indiana 39| | case indiana = "IN" 40| | /// Iowa 41| | case iowa = "IA" 42| | /// Kansas 43| | case kansas = "KS" 44| | /// Kentucky 45| | case kentucky = "KY" 46| | /// Louisiana 47| | case louisiana = "LA" 48| | /// Maine 49| | case maine = "ME" 50| | /// Maryland 51| | case maryland = "MD" 52| | /// Massachusetts 53| | case massachusetts = "MA" 54| | /// Michigan 55| | case michigan = "MI" 56| | /// Minnesota 57| | case minnesota = "MN" 58| | /// Mississippi 59| | case mississippi = "MS" 60| | /// Missouri 61| | case missouri = "MO" 62| | /// Montana 63| | case montana = "MT" 64| | /// Nebraska 65| | case nebraska = "NE" 66| | /// Nevada 67| | case nevada = "NV" 68| | /// New Hampshire 69| | case newhampshire = "NH" 70| | /// New Jersey 71| | case newjersey = "NJ" 72| | /// New Mexcio 73| | case newmexico = "NM" 74| | /// New York 75| | case newyork = "NY" 76| | /// North Carolina 77| | case northCarolina = "NC" 78| | /// North Dakota 79| | case northDakota = "ND" 80| | /// Ohio 81| | case ohio = "OH" 82| | /// Oklahoma 83| | case oklahoma = "OK" 84| | /// Oregon 85| | case oregon = "OR" 86| | //// Pennsylvania 87| | case pennsylvania = "PA" 88| | /// Rhode Island 89| | case rhodeIsland = "RI" 90| | /// South Carolina 91| | case southCarolina = "SC" 92| | //// South Dakota 93| | case southDakota = "SD" 94| | /// Tennessee 95| | case tennessee = "TN" 96| | /// Texas 97| | case texax = "TX" 98| | /// Utah 99| | case utah = "UT" 100| | /// Vermont 101| | case vermont = "VT" 102| | /// Virginia 103| | case virgina = "VA" 104| | /// Washington 105| | case washington = "WA" 106| | /// West Virginia 107| | case westVirginia = "WV" 108| | /// Wisconsin 109| | case wisconsin = "WI" 110| | /// Wyoming 111| | case wyoming = "WY" 112| | 113| | /// Returns name of the state, e.g. "California" for .california 114| 1| public var name: String { 115| 1| let caseName = String(describing: self) 116| 1| let capitlizedCaseName = caseName.prefix(1).capitalized + caseName.dropFirst() 117| 1| return capitlizedCaseName 118| 1| } 119| | 120| | /// Returns 2 character state code, e.g. "CA" for .california 121| 1| public var postalAbbreviation: String { 122| 1| return self.rawValue 123| 1| } 124| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/VisitorCenter.swift: 1| |// 2| |// VisitorCenter.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/23/20. 6| |// 7| | 8| |import Foundation 9| |import CoreLocation 10| | 11| |/// Requestable attribute of VisitorCenter model 12| |public enum RequestableVisitorCenterField: String, RequestableField { 13| | case void 14| |} 15| | 16| |/// Visitor center data includes location, contact, and operating hours information for visitor centers and other visitor contact facilities in national parks At least one visitor center is listed for each park; some parks with multiple visitor centers may include information about more than one 17| |public struct VisitorCenter: Decodable, Identifiable { 18| | enum CodingKeys: String, CodingKey { 19| | case id, parkCode, name, description, directionsInfo, directionsUrl, latLong 20| | } 21| | 22| | /// ID 23| | public let id: String 24| | /// A variable width character code used to identify a specific park 25| | public let parkCode: String 26| | /// News release title 27| | public let name: String 28| | /// General description of the facility 29| | public let description: String 30| | /// Date news release was released 31| | public let directionsInfo: String? 32| | /// Link to full news release 33| | public let directionsUrl: URL? 34| | /// News release image 35| | public let gpsLocation: CLLocation? 36| |} 37| | 38| |extension VisitorCenter { 39| 11| public init(from decoder: Decoder) throws { 40| 11| let values = try decoder.container(keyedBy: CodingKeys.self) 41| 11| id = try values.decode(String.self, forKey: .id) 42| 11| parkCode = try values.decode(String.self, forKey: .parkCode) 43| 11| name = try values.decode(String.self, forKey: .name) 44| 11| description = try values.decode(String.self, forKey: .description) 45| 11| directionsInfo = try values.decodeIfPresent(String.self, forKey: .directionsInfo) 46| 11| directionsUrl = try values.decodeIfPresent(String.self, forKey: .directionsUrl)?.toURL() 47| 11| gpsLocation = try values.decodeIfPresent(String.self, forKey: .latLong)?.toLocation() 48| 11| } 49| |} <<<<<< EOF # path=NatParkSwiftKitTests.xctest.coverage.txt /Users/travis/build/MarcoEidinger/npsapi-swift/Tests/NatParkSwiftKitTests/DataServiceTests.swift: 1| |import XCTest 2| |@testable import NatParkSwiftKit 3| | 4| |final class DataServiceTests: XCTestCase { 5| | 6| | private var api: DataService! = nil 7| | 8| 9| override func setUp() { 9| 9| super.setUp() 10| 9| 11| 9| guard let data = Data(base64Encoded: "Y3hKaE96ZFBERzg1YjRITnBBYm85MzJHc1Y3c3Q3a1BVUVhObmQ0Vw==") else { 12| 0| XCTFail("invalid api key for testing") 13| 0| return 14| 9| } 15| 9| 16| 9| guard let testKey = String(data: data, encoding: .utf8) else { 17| 0| XCTFail("invalid api key for testing") 18| 0| return 19| 9| } 20| 9| 21| 9| self.api = DataService(apiKey: testKey) 22| 9| } 23| | 24| 1| func testErrorHandlingInvalidApiKey() { 25| 1| self.api = DataService(apiKey: "InvalidKey") 26| 1| let expectation = XCTestExpectation(description: "Download Parks") 27| 1| let publisher = api.fetchParks(by: [ParkCodeConstants.acadia]) 28| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 29| 1| switch completion { 30| 1| case .finished: 31| 0| XCTFail("subscription finished unexpectedly") 32| 1| case .failure(let error): 33| 1| XCTAssertNotNil(error, "We should have an error object") 34| 1| expectation.fulfill() 35| 1| } 36| 1| }) { (parks) in 37| 0| XCTFail("We recevied data unexpectedly") 38| 0| } 39| 1| XCTAssertNotNil(subscription) 40| 1| wait(for: [expectation], timeout: 45.0) 41| 1| } 42| | 43| 1| func testFetchParkWithNoResult() { 44| 1| let expectation = XCTestExpectation(description: "Will not find park") 45| 1| let publisher = api.fetchPark("DOES_NOT_EXIST") 46| 1| let subscription = publisher 47| 1| .replaceError(with: nil) 48| 1| .sink { (park) in 49| 1| XCTAssertNil(park, "No park should have been found for that key") 50| 1| expectation.fulfill() 51| 1| } 52| 1| XCTAssertNotNil(subscription) 53| 1| wait(for: [expectation], timeout: 45.0) 54| 1| } 55| | 56| 1| func testFetchParkByParkCode() { 57| 1| let expectation = XCTestExpectation(description: "Download specific park") 58| 1| let publisher = api.fetchPark(ParkCodeConstants.acadia) 59| 1| let subscription = publisher 60| 1| .replaceError(with: nil) 61| 1| .sink { (park) in 62| 1| guard let park = park else { 63| 0| XCTFail("No park information found") 64| 0| return 65| 1| } 66| 1| guard let firstState = park.states.first else { 67| 0| XCTFail("No state") 68| 0| return 69| 1| } 70| 1| XCTAssertTrue(firstState == .maine, "We have a state") 71| 1| XCTAssertNotNil(park.url, "Park has to have url") 72| 1| XCTAssertNotNil(park.gpsLocation, "Park has to have gps coordinates") 73| 1| XCTAssertNil(park.entranceFees, "We should not have receive a non default field without requesting it") 74| 1| XCTAssertNil(park.entrancePasses, "We should not have receive a non default field without requesting it") 75| 1| expectation.fulfill() 76| 1| } 77| 1| XCTAssertNotNil(subscription) 78| 1| wait(for: [expectation], timeout: 45.0) 79| 1| } 80| | 81| 1| func testFetchParksByState() { 82| 1| let expectation = XCTestExpectation(description: "Download Parks") 83| 1| let publisher = api.fetchParks(by: nil, in: [.california], nil) 84| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 85| 1| switch completion { 86| 1| case .finished: 87| 1| expectation.fulfill() 88| 1| case .failure(let error): 89| 0| print(error) 90| 0| XCTFail("subscription returned error unexpectedly") 91| 1| } 92| 1| }) { (parks) in 93| 1| XCTAssertTrue(parks.count > 0, "We have parks") 94| 1| guard let firstState = parks.first?.states.first else { 95| 0| XCTFail("No state") 96| 0| return 97| 1| } 98| 1| XCTAssertTrue(firstState == .california, "We have a state") 99| 1| } 100| 1| XCTAssertNotNil(subscription) 101| 1| wait(for: [expectation], timeout: 45.0) 102| 1| } 103| | 104| 1| func testFetchParksWithRequestOption() { 105| 1| let expectation = XCTestExpectation(description: "Download Parks") 106| 1| let publisher = api.fetchParks(by: nil, in: [.california], RequestOptions.init(limit: 5, searchQuery: "Yosemite National Park", fields: [.images, .entranceFees, .entrancePasses])) 107| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 108| 1| switch completion { 109| 1| case .finished: 110| 1| expectation.fulfill() 111| 1| case .failure(let error): 112| 0| print(error) 113| 0| XCTFail("subscription returned error unexpectedly") 114| 1| } 115| 1| }) { (parks) in 116| 1| XCTAssertTrue(parks.count == 1, "We have parks") 117| 1| XCTAssertEqual(parks.first?.parkCode, ParkCodeConstants.yosemite) 118| 1| XCTAssertNotNil(parks.first?.images, "We should have images") 119| 1| XCTAssertNotNil(parks.first?.images?.first?.url, "We should have image url") 120| 1| XCTAssertNotNil(parks.first?.entranceFees, "We should not have receive a non default field without requesting it") 121| 1| XCTAssertNotNil(parks.first?.entrancePasses, "We should not have receive a non default field without requesting it") 122| 1| } 123| 1| XCTAssertNotNil(subscription) 124| 1| wait(for: [expectation], timeout: 45.0) 125| 1| } 126| | 127| 1| func testFetchAlertsByParkCode() { 128| 1| let expectation = XCTestExpectation(description: "Download Alerts") 129| 1| let publisher = api.fetchAlerts(by: [ParkCodeConstants.acadia]) 130| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 131| 1| switch completion { 132| 1| case .finished: 133| 1| expectation.fulfill() 134| 1| case .failure(let error): 135| 0| print(error) 136| 0| XCTFail("subscription returned error unexpectedly") 137| 1| } 138| 1| }) { (alerts) in 139| 1| XCTAssertTrue(alerts.count > 0, "We have alerts") 140| 1| } 141| 1| XCTAssertNotNil(subscription) 142| 1| wait(for: [expectation], timeout: 45.0) 143| 1| } 144| | 145| 1| func testFetchNewsReleaseByParkCode() { 146| 1| let expectation = XCTestExpectation(description: "Download NewsReleases") 147| 1| let publisher = api.fetchNewsReleases() 148| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 149| 1| switch completion { 150| 1| case .finished: 151| 1| expectation.fulfill() 152| 1| case .failure(let error): 153| 0| print(error) 154| 0| XCTFail("subscription returned error unexpectedly") 155| 1| } 156| 1| }) { (alerts) in 157| 1| XCTAssertTrue(alerts.count > 0, "We have news releaes") 158| 1| XCTAssertNotNil(alerts.first?.id) 159| 1| XCTAssertNotNil(alerts.first?.title) 160| 1| XCTAssertNotNil(alerts.first?.abstract) 161| 1| XCTAssertNotNil(alerts.first?.parkCode) 162| 1| XCTAssertNotNil(alerts.first?.releaseDate) 163| 1| XCTAssertNotNil(alerts.first?.url) 164| 1| } 165| 1| XCTAssertNotNil(subscription) 166| 1| wait(for: [expectation], timeout: 45.0) 167| 1| } 168| | 169| 1| func testFetchVisitorCentersByParkCode() { 170| 1| let expectation = XCTestExpectation(description: "Download Visitor Centers") 171| 1| let publisher = api.fetchVisitorCenters(by: [ParkCodeConstants.yellowstone], in: nil, nil) 172| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 173| 1| switch completion { 174| 1| case .finished: 175| 1| expectation.fulfill() 176| 1| case .failure(let error): 177| 0| print(error) 178| 0| XCTFail("subscription returned error unexpectedly") 179| 1| } 180| 1| }) { (visitorCenters) in 181| 1| XCTAssertTrue(visitorCenters.count > 0, "We have visitor centers") 182| 1| XCTAssertNotNil(visitorCenters.first?.id) 183| 1| XCTAssertNotNil(visitorCenters.first?.name) 184| 1| XCTAssertNotNil(visitorCenters.first?.description) 185| 1| XCTAssertNotNil(visitorCenters.first?.parkCode) 186| 1| XCTAssertNotNil(visitorCenters.first?.directionsInfo) 187| 1| XCTAssertNotNil(visitorCenters.first?.gpsLocation) 188| 1| } 189| 1| XCTAssertNotNil(subscription) 190| 1| wait(for: [expectation], timeout: 45.0) 191| 1| } 192| | 193| 1| func testFetchAssetsByParkCode() { 194| 1| let expectation = XCTestExpectation(description: "Download Assets") 195| 1| let publisher = api.fetchAssets(by: [ParkCodeConstants.yellowstone], in: nil, nil) 196| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 197| 1| switch completion { 198| 1| case .finished: 199| 1| expectation.fulfill() 200| 1| case .failure(let error): 201| 0| print(error) 202| 0| XCTFail("subscription returned error unexpectedly") 203| 1| } 204| 1| }) { (assets) in 205| 1| XCTAssertTrue(assets.count > 0, "We have assets / places") 206| 1| XCTAssertNotNil(assets.first?.id) 207| 1| XCTAssertNotNil(assets.first?.listingImage.url) 208| 1| XCTAssertNotNil(assets.first?.listingImage.altText) 209| 1| } 210| 1| XCTAssertNotNil(subscription) 211| 1| wait(for: [expectation], timeout: 45.0) 212| 1| } 213| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Tests/NatParkSwiftKitTests/ParkTests.swift: 1| |// 2| |// ParkTests.swift 3| |// NatParkSwiftKitTests 4| |// 5| |// Created by Eidinger, Marco on 1/29/20. 6| |// 7| | 8| |import XCTest 9| |import NatParkSwiftKit 10| | 11| |class ParkTests: XCTestCase { 12| | 13| 1| func testMemberwiseInitializer() { 14| 1| let park = Park(id: "123", parkCode: "yell", name: "Yellowstone", fullName: "Yellowstone National Park", description: "Left", designation: .nationalPark, states: [.wyoming], gpsLocation: nil, directionsInfo: "", directionsUrl: nil, weatherInfo: "", url: nil, images: [], entranceFees: [], entrancePasses: []) 15| 1| XCTAssertNotNil(park) 16| 1| } 17| | 18| 1| func testEqualityComparision() { 19| 1| let park1 = Park(id: "123", parkCode: "yell", name: "Yellowstone", fullName: "Yellowstone National Park", description: "Left", designation: .nationalPark, states: [.wyoming], gpsLocation: nil, directionsInfo: "", directionsUrl: nil, weatherInfo: "", url: nil, images: [], entranceFees: [], entrancePasses: []) 20| 1| let park2 = Park(id: "123", parkCode: "yell", name: "Yellowstone", fullName: "Yellowstone National Park", description: "Left", designation: .nationalPark, states: [.wyoming], gpsLocation: nil, directionsInfo: "", directionsUrl: nil, weatherInfo: "", url: nil, images: [], entranceFees: [], entrancePasses: []) 21| 1| XCTAssertEqual(park1, park2) 22| 1| } 23| | 24| 1| func testHashableConformance() { 25| 1| let park = Park(id: "123", parkCode: "yell", name: "Yellowstone", fullName: "Yellowstone National Park", description: "Left", designation: .nationalPark, states: [.wyoming], gpsLocation: nil, directionsInfo: "", directionsUrl: nil, weatherInfo: "", url: nil, images: [], entranceFees: [], entrancePasses: []) 26| 1| var dictionary: [Park:String] = [:] 27| 1| dictionary[park] = "Cool" 28| 1| XCTAssertEqual(dictionary[park], "Cool") 29| 1| } 30| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Tests/NatParkSwiftKitTests/ParkUnitDesignationTests.swift: 1| |// 2| |// ParkUnitDesignationTests.swift 3| |// NatParkSwiftKitTests 4| |// 5| |// Created by Eidinger, Marco on 1/24/20. 6| |// 7| | 8| |import XCTest 9| |import NatParkSwiftKit 10| | 11| |class ParkUnitDesignationTests: XCTestCase { 12| | 13| 1| func testAllCases() { 14| 45| let values = ParkUnitDesignation.allCases.map {$0.rawValue} 15| 1| XCTAssertEqual(ParkUnitDesignation.allCases.count, values.count) 16| 1| } 17| | 18| 1| func testValidInit() { 19| 1| XCTAssertNotNil(ParkUnitDesignation(validDesignationOrUnknown: "National Park")) 20| 1| } 21| | 22| 1| func testInitFallbackToUnknown() { 23| 1| XCTAssertEqual(ParkUnitDesignation(validDesignationOrUnknown: "XXXX"), ParkUnitDesignation.unknown) 24| 1| } 25| | 26| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Tests/NatParkSwiftKitTests/StateInUSATests.swift: 1| |// 2| |// StateInUSATests.swift 3| |// NatParkSwiftKitTests 4| |// 5| |// Created by Eidinger, Marco on 1/23/20. 6| |// 7| | 8| |import XCTest 9| |import NatParkSwiftKit 10| | 11| |class StateInUSATests: XCTestCase { 12| | 13| 1| func testCompleteness() { 14| 1| XCTAssertEqual(StateInUSA.allCases.count, 50) 15| 1| } 16| | 17| 1| func testName() { 18| 1| let cali: StateInUSA = .california 19| 1| XCTAssertEqual(cali.name, "California") 20| 1| } 21| | 22| 1| func testAbbreviation() { 23| 1| let cali: StateInUSA = .california 24| 1| XCTAssertEqual(cali.postalAbbreviation, "CA") 25| 1| } 26| | 27| |} <<<<<< EOF