TRAVIS_OS_NAME=osx default= <<<<<< ENV .codecov.yml .codebeatignore .createDocs.sh .downloadNpsData.sh .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 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/Data/parks.json Sources/NatParkSwiftKit/Data/visitorcenters.json Sources/NatParkSwiftKit/DataService.swift Sources/NatParkSwiftKit/DataServiceError.swift Sources/NatParkSwiftKit/Internal/Internal.swift Sources/NatParkSwiftKit/Internal/RequestUrlFactory.swift Sources/NatParkSwiftKit/Model/Address.swift Sources/NatParkSwiftKit/Model/Alert.swift Sources/NatParkSwiftKit/Model/Asset.swift Sources/NatParkSwiftKit/Model/ContactInformation.swift Sources/NatParkSwiftKit/Model/Fee.swift Sources/NatParkSwiftKit/Model/NewsRelease.swift Sources/NatParkSwiftKit/Model/NpsImage.swift Sources/NatParkSwiftKit/Model/OperatingHour.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| |// 2| |// DataService.swift 3| |// NatParkSwiftKit 4| |// 5| |// Created by Eidinger, Marco on 1/18/20. 6| |// 7| |import Foundation 8| |import Combine 9| | 10| |/// Main API class to interact with the National Park Service API 11| |public class DataService { 12| | 13| | /// Required API key which can be requested for free from NPS Developer website 14| | public let apiKey: String 15| | 16| | private let urlFactory: RequestUrlFactory = RequestUrlFactory() 17| | 18| 1| private var errorTransformer: (Error) -> DataServiceError = { error in 19| 1| switch error { 20| 1| case DataServiceError.invalidApiKey: 21| 1| return DataServiceError.invalidApiKey 22| 1| default: 23| 0| return DataServiceError.cannotDecodeContent(error: error) 24| 1| } 25| 1| } 26| | 27| 10| private var responseTransformer: (Data, URLResponse) throws -> Data = { data, response -> Data in 28| 10| guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { 29| 1| throw DataServiceError.invalidApiKey 30| 9| } 31| 9| return data 32| 10| } 33| | 34| | /// Initializer 35| 11| public init(apiKey: String) { 36| 11| self.apiKey = apiKey 37| 11| } 38| | 39| | // MARK: public functions 40| | 41| | /** 42| | fetch single park information from the National Park Service Data API 43| | 44| | - 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. 45| | 46| | - Returns: a respective publisher which outputs an array of parks 47| | */ 48| 2| public func fetchPark(_ parkCode: String) -> AnyPublisher { 49| 2| return self.fetchParks(by: [parkCode], in: nil, nil) 50| 2| .map { $0.data.first } 51| 2| .eraseToAnyPublisher() 52| 2| } 53| | 54| | /** 55| | fetch park information from the National Park Service Data API 56| | 57| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 58| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 59| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 60| | - Returns: a respective publisher which outputs a tuple containing an array of parks as well as the total count of matching items on the server 61| | */ 62| 6| public func fetchParks(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<(data: [Park], total: Int), DataServiceError> { 63| 6| 64| 6| return self.fetch(Resources.self, by: parkCodes, in: states, honoring: requestOptions) 65| 6| } 66| | 67| | /** 68| | fetch alert release information from the National Park Service Data API 69| | 70| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 71| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 72| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 73| | - Returns: a respective publisher which outputs a tuple containing an array of alerts as well as the total count of matching items on the server 74| | */ 75| 1| public func fetchAlerts(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<(data: [Alert], total: Int), DataServiceError> { 76| 1| 77| 1| return self.fetch(Resources.self, by: parkCodes, in: states, honoring: requestOptions) 78| 1| } 79| | 80| | /** 81| | fetch news release information from the National Park Service Data API 82| | 83| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 84| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 85| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 86| | - Returns: a respective publisher which outputs a tuple containing an array of news releases as well as the total count of matching items on the server 87| | */ 88| 1| public func fetchNewsReleases(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<(data: [NewsRelease], total: Int), DataServiceError> { 89| 1| 90| 1| return self.fetch(Resources.self, by: parkCodes, in: states, honoring: requestOptions) 91| 1| } 92| | 93| | /** 94| | fetch visitor center information from the National Park Service Data API 95| | 96| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 97| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 98| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 99| | - Returns: a respective publisher which outputs a tuple containing an array of visitor centers as well as the total count of matching items on the server 100| | */ 101| 1| public func fetchVisitorCenters(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<(data: [VisitorCenter], total: Int), DataServiceError> { 102| 1| 103| 1| return self.fetch(Resources.self, by: parkCodes, in: states, honoring: requestOptions) 104| 1| } 105| | 106| | /** 107| | fetch asset (place) information from the National Park Service Data API 108| | 109| | - Parameter parkCodes: to limit results for certain parks only. Array of park codes, e.g. ["yell"]. Can be nil or empty 110| | - Parameter states: to limit results for certain states only. Array of US states, e.g [.california]. Can be nil or empty 111| | - Parameter requestOptions: to specify result amount (default: 50) and further influence search critierias 112| | - Returns: a respective publisher which outputs a tuple containing an array of assets as well as the total count of matching items on the server 113| | */ 114| 1| public func fetchAssets(by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions? = nil) -> AnyPublisher<(data: [Asset], total: Int), DataServiceError> { 115| 1| 116| 1| return self.fetch(Resources.self, by: parkCodes, in: states, honoring: requestOptions) 117| 1| } 118| | 119| | // MARK: private functions 120| 10| private func url(_ endpoint: DataServiceEndpoint, by parkCodes: [String]? = [], in states: [StateInUSA]? = [], _ requestOptions: RequestOptions?) -> URL? { 121| 10| 122| 10| var urlComponents = self.urlFactory.apiUrlComponents(for: endpoint.rawValue, authorizedBy: self.apiKey) 123| 10| 124| 10| let parkCodeQueryParameterValue = parkCodes?.joined(separator: ",") 125| 10| if parkCodeQueryParameterValue != nil { 126| 7| urlComponents.queryItems?.append(URLQueryItem(name: "parkCode", value: parkCodeQueryParameterValue)) 127| 10| } 128| 10| 129| 10| let statesQueryParameterValue = states?.map { $0.rawValue }.joined(separator: ",") 130| 10| if statesQueryParameterValue != nil { 131| 5| urlComponents.queryItems?.append(URLQueryItem(name: "stateCode", value: statesQueryParameterValue)) 132| 10| } 133| 10| 134| 10| self.urlFactory.add(requestOptions, to: &urlComponents) 135| 10| 136| 10| return urlComponents.url 137| 10| } 138| | 139| 10| private func endpoint(of target: Decodable.Type) -> DataServiceEndpoint? { 140| 10| switch target { 141| 10| case is Park.Type: 142| 6| return .parks 143| 10| case is Alert.Type: 144| 1| return .alerts 145| 10| case is NewsRelease.Type: 146| 1| return .newsRelease 147| 10| case is VisitorCenter.Type: 148| 1| return .visitorCenters 149| 10| case is Asset.Type: 150| 1| return .assets 151| 10| default: 152| 0| return nil 153| 10| } 154| 10| } 155| | 156| 10| private func fetch(_ value: T.Type, by parkCodes: [String]? = [], in states: [StateInUSA]? = [], honoring requestOptions: RequestOptions? = nil) -> AnyPublisher<(data: [T.NatParkServiceEntity], total: Int), DataServiceError> { 157| 10| 158| 10| guard let decodedAssociatedType = value.NatParkServiceEntity.self as? Decodable.Type else { 159| 0| return Fail(error: DataServiceError.badURL).eraseToAnyPublisher() 160| 10| } 161| 10| 162| 10| guard let endpoint = self.endpoint(of: decodedAssociatedType) else { 163| 0| return Fail(error: DataServiceError.badURL).eraseToAnyPublisher() 164| 10| } 165| 10| 166| 10| guard let validUrl = self.url(endpoint, by: parkCodes, in: states, requestOptions) else { 167| 0| return Fail(error: DataServiceError.badURL).eraseToAnyPublisher() 168| 10| } 169| 10| 170| 10| return URLSession.shared.dataTaskPublisher(for: validUrl) 171| 10| .tryMap(responseTransformer) 172| 10| .decode(type: T.self, decoder: JSONDecoder()) 173| 10| .map { ($0.data, Int($0.total) ?? 0) } 174| 10| .mapError(self.errorTransformer) 175| 10| .eraseToAnyPublisher() 176| 10| } 177| |} /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| |enum DataServiceEndpoint: String { 12| | case parks = "/parks" 13| | case alerts = "/alerts" 14| | case newsRelease = "/newsreleases" 15| | case visitorCenters = "/visitorcenters" 16| | case assets = "/places" 17| |} 18| | 19| |protocol IResource: Decodable { 20| | associatedtype NatParkServiceEntity 21| | var total: String { get } 22| | var data: [NatParkServiceEntity] { get } 23| |} 24| | 25| |struct Resources: IResource { 26| | let total: String 27| | let data: [NatParkServiceEntity] 28| |} 29| | 30| |extension String { 31| 305| func toURL() -> URL? { 32| 305| return URL(string: self) 33| 305| } 34| | 35| 50| func toDate() -> Date? { 36| 50| var releaseDateAsString = self 37| 50| releaseDateAsString = String(releaseDateAsString.dropLast()) 38| 50| releaseDateAsString = String(releaseDateAsString.dropLast()) 39| 50| let releaseDateString: String = releaseDateAsString 40| 50| let dateFor: DateFormatter = DateFormatter() 41| 50| dateFor.dateFormat = "yyyy-mm-dd HH:mm:ss" 42| 50| dateFor.locale = Locale(identifier: "en_US_POSIX") 43| 50| return dateFor.date(from: releaseDateString) 44| 50| } 45| | 46| 66| func toLocation() -> CLLocation? { 47| 66| guard let lat = self.toLatitude(), 48| 66| let long = self.toLongitude() else { 49| 5| return nil 50| 61| } 51| 61| return CLLocation(latitude: lat, longitude: long) 52| 66| } 53| | 54| 66| func toLatitude() -> Double? { 55| 66| guard let latitudeSeparator = self.firstIndex(of: ":") else { 56| 5| return nil 57| 61| } 58| 61| let startLat = self.index(latitudeSeparator, offsetBy: +1) 59| 61| guard let endLat = self.firstIndex(of: ",") else { 60| 0| return nil 61| 61| } 62| 61| return Double(String(self[startLat.. Double? { 66| 61| guard let longitudeSeparator = self.firstIndex(of: ",") else { 67| 0| return nil 68| 61| } 69| 61| // default format "lat:44.59824417, long:-110.5471695" 70| 61| var endLong = self.endIndex 71| 61| var distance = +7 72| 61| // fallback format "{lat:63.7308262, lng:-148.9171829}" 73| 61| if self.contains("lng") { 74| 10| distance = +6 75| 10| endLong = self.index(self.endIndex, offsetBy: -1) 76| 61| } 77| 61| let startLong = self.index(longitudeSeparator, offsetBy: distance) 78| 61| return Double(String(self[startLong.. [StateInUSA] { 82| 55| var states: [StateInUSA] = [] 83| 55| let statesAsStringArray = self.components(separatedBy: ",") 84| 79| statesAsStringArray.forEach { (potentialState) in 85| 79| guard let state = StateInUSA(rawValue: potentialState) else { 86| 3| return 87| 76| } 88| 76| states.append(state) 89| 76| } 90| 55| return states 91| 55| } 92| |} /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| 10| func apiUrlComponents(for endpoint: String, authorizedBy apiKey: String) -> URLComponents { 13| 10| var urlComponents = URLComponents() 14| 10| urlComponents.scheme = "https" 15| 10| urlComponents.host = "developer.nps.gov" 16| 10| urlComponents.path = "/api/v1" + endpoint 17| 10| urlComponents.queryItems = [ 18| 10| URLQueryItem(name: "api_key", value: apiKey) 19| 10| ] 20| 10| return urlComponents 21| 10| } 22| | 23| 10| func add(_ requestOptions: RequestOptions?, to urlComponents: inout URLComponents) { 24| 10| if let limit = requestOptions?.limit { 25| 3| urlComponents.queryItems?.append(URLQueryItem(name: "limit", value: String(limit))) 26| 10| } 27| 10| 28| 10| if let start = requestOptions?.start { 29| 3| urlComponents.queryItems?.append(URLQueryItem(name: "start", value: String(start))) 30| 10| } 31| 10| 32| 10| if let query = requestOptions?.searchQuery { 33| 1| urlComponents.queryItems?.append(URLQueryItem(name: "q", value: query)) 34| 10| } 35| 10| 36| 10| if let fields = requestOptions?.fields { 37| 8| let fieldsQueryParameterValue = fields.map { $0.fieldName }.joined(separator: ",") 38| 2| if fieldsQueryParameterValue.isEmpty == false { 39| 2| urlComponents.queryItems?.append(URLQueryItem(name: "fields", value: fieldsQueryParameterValue)) 40| 2| } 41| 10| } 42| 10| } 43| |} /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| |// Park.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| | /// Park addresses (physical and mailing) 20| | case addresses 21| | /// Hours and seasons when the park is open or closed 22| | case operatingHours 23| | /// Information about contacting staff at the facility 24| | case contacts 25| |} 26| | 27| |/// 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. 28| |public struct Park: Decodable, Identifiable, Hashable { 29| | 30| | enum CodingKeys: CodingKey { 31| | case id, parkCode, name, fullName, description, url, designation, states, latLong, directionsInfo, directionsUrl, weatherInfo, images, entranceFees, entrancePasses, addresses, operatingHours, contacts 32| | } 33| | 34| | /// Park identification string 35| | public let id: String 36| | /// A variable width character code used to identify a specific park 37| | public let parkCode: String 38| | /// Short park name (no designation) 39| | public let name: String 40| | /// Full park name (with designation) 41| | public let fullName: String 42| | /// Introductory paragraph from the park homepage 43| | public let description: String 44| | /// Type of designation (eg, national park, national monument, national recreation area, etc) 45| | public let designation: ParkUnitDesignation 46| | /// State(s) the park is located in 47| | public let states: [StateInUSA] 48| | /// Park GPS cordinates 49| | public let gpsLocation: CLLocation? 50| | /// General overview of how to get to the park 51| | public let directionsInfo: String 52| | /// Link to page, if available, that provides additional detail on getting to the park 53| | public let directionsUrl: URL? 54| | /// General description of the weather in the park over the course of a year 55| | public let weatherInfo: String? 56| | /// Park Website 57| | public let url: URL? 58| | /// Park images 59| | public let images: [NpsImage]? 60| | /// Fee for entering the park 61| | public let entranceFees: [Fee]? 62| | /// Passes available to provide entry into the park 63| | public let entrancePasses: [Fee]? 64| | /// Park addresses (physical and mailing) 65| | public let addresses: [Address]? 66| | /// Hours and seasons when the park is open or closed 67| | public let operatingHours: [OperatingHour]? 68| | /// Information about contacting staff at the facility 69| | public let contacts: ContactInformation? 70| | 71| | /// Memberwise Initializer 72| 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]?, addresses: [Address]?, operatingHours: [OperatingHour]?, contacts: ContactInformation?) { 73| 4| self.id = id 74| 4| self.parkCode = parkCode 75| 4| self.name = name 76| 4| self.fullName = fullName 77| 4| self.description = description 78| 4| self.designation = designation 79| 4| self.states = states 80| 4| self.gpsLocation = gpsLocation 81| 4| self.directionsInfo = directionsInfo 82| 4| self.directionsUrl = directionsUrl 83| 4| self.weatherInfo = weatherInfo 84| 4| self.url = url 85| 4| self.images = images 86| 4| self.entranceFees = entranceFees 87| 4| self.entrancePasses = entrancePasses 88| 4| self.addresses = addresses 89| 4| self.operatingHours = operatingHours 90| 4| self.contacts = contacts 91| 4| } 92| | 93| | /// Park can be compared for equality using the equal-to operator (`==`) or inequality using the not-equal-to operator (`!=`) 94| 2| public static func == (lhs: Park, rhs: Park) -> Bool { 95| 2| return lhs.id == rhs.id 96| 2| } 97| | 98| | /// Park conforms to the Hashable protocol and hence can be used in a set or as a dictionary key 99| 3| public func hash(into hasher: inout Hasher) { 100| 3| hasher.combine(self.id) 101| 3| } 102| |} 103| | 104| |extension Park { 105| | /// Decoderwise Initializer 106| 55| public init(from decoder: Decoder) throws { 107| 55| let values = try decoder.container(keyedBy: CodingKeys.self) 108| 55| 109| 55| id = try values.decode(String.self, forKey: .id) 110| 55| parkCode = try values.decode(String.self, forKey: .parkCode) 111| 55| name = try values.decode(String.self, forKey: .name) 112| 55| fullName = try values.decode(String.self, forKey: .fullName) 113| 55| description = try values.decode(String.self, forKey: .description) 114| 55| url = try values.decodeIfPresent(String.self, forKey: .url)?.toURL() 115| 55| designation = try ParkUnitDesignation(validDesignationOrUnknown: values.decodeIfPresent(String.self, forKey: .designation)) 116| 55| directionsInfo = try values.decode(String.self, forKey: .directionsInfo) 117| 55| directionsUrl = try values.decodeIfPresent(String.self, forKey: .directionsUrl)?.toURL() 118| 55| weatherInfo = try values.decodeIfPresent(String.self, forKey: .weatherInfo) 119| 55| states = try values.decode(String.self, forKey: .states).toStates() 120| 55| gpsLocation = try values.decodeIfPresent(String.self, forKey: .latLong)?.toLocation() 121| 55| images = try values.decodeIfPresent([NpsImage].self, forKey: .images) 122| 55| entranceFees = try values.decodeIfPresent([Fee].self, forKey: .entranceFees) 123| 55| entrancePasses = try values.decodeIfPresent([Fee].self, forKey: .entrancePasses) 124| 55| addresses = try values.decodeIfPresent([Address].self, forKey: .addresses) 125| 55| operatingHours = try values.decodeIfPresent([OperatingHour].self, forKey: .operatingHours) 126| 55| contacts = try values.decodeIfPresent(ContactInformation.self, forKey: .contacts) 127| 55| } 128| |} /Users/travis/build/MarcoEidinger/npsapi-swift/Sources/NatParkSwiftKit/Model/ParkUnitDesignation.swift: 1| |// 2| |// UnitDesignation.swift 3| |// NatParkSwiftKit 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| 57| public init(validDesignationOrUnknown: String?) { 109| 57| guard let input = validDesignationOrUnknown else { 110| 0| self = ParkUnitDesignation.unknown 111| 0| return 112| 57| } 113| 57| self = ParkUnitDesignation(rawValue: input) ?? ParkUnitDesignation.unknown 114| 57| } 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| 8| var fieldName: String { 18| 8| return String(describing: self.rawValue) 19| 8| } 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, Decodable { 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| 2| public var name: String { 115| 2| let caseName = String(describing: self) 116| 2| var caseNameMissingFirstCharacter: String = String(caseName.dropFirst()) 117| 2| 118| 15| for character in caseNameMissingFirstCharacter where character.isUppercase { 119| 15| if let insertIndex = caseNameMissingFirstCharacter.firstIndex(of: character) { 120| 1| caseNameMissingFirstCharacter.insert(" ", at: insertIndex) 121| 15| } 122| 15| } 123| 2| 124| 2| let capitalizedCaseName = caseName.prefix(1).capitalized + caseNameMissingFirstCharacter 125| 2| 126| 2| return capitalizedCaseName 127| 2| } 128| | 129| | /// Returns 2 character state code, e.g. "CA" for .california 130| 1| public var postalAbbreviation: String { 131| 1| return self.rawValue 132| 1| } 133| |} /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| | /// Visitor Center addresses (physical and mailing) 14| | case addresses 15| | /// Hours and seasons when the visitor center is open or closed 16| | case operatingHours 17| | 18| | case contacts 19| |} 20| | 21| |/// 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 22| |public struct VisitorCenter: Decodable, Identifiable { 23| | enum CodingKeys: String, CodingKey { 24| | case id, parkCode, name, description, directionsInfo, directionsUrl, latLong, addresses, operatingHours, contacts 25| | } 26| | 27| | /// ID 28| | public let id: String 29| | /// A variable width character code used to identify a specific park 30| | public let parkCode: String 31| | /// News release title 32| | public let name: String 33| | /// General description of the facility 34| | public let description: String 35| | /// Date news release was released 36| | public let directionsInfo: String? 37| | /// Link to full news release 38| | public let directionsUrl: URL? 39| | /// News release image 40| | public let gpsLocation: CLLocation? 41| | /// Visitor Center addresses (physical and mailing) 42| | public let addresses: [Address]? 43| | /// Hours and seasons when the visitor center is open or closed 44| | public let operatingHours: [OperatingHour]? 45| | 46| | public let contacts: ContactInformation? 47| |} 48| | 49| |extension VisitorCenter { 50| 11| public init(from decoder: Decoder) throws { 51| 11| let values = try decoder.container(keyedBy: CodingKeys.self) 52| 11| id = try values.decode(String.self, forKey: .id) 53| 11| parkCode = try values.decode(String.self, forKey: .parkCode) 54| 11| name = try values.decode(String.self, forKey: .name) 55| 11| description = try values.decode(String.self, forKey: .description) 56| 11| directionsInfo = try values.decodeIfPresent(String.self, forKey: .directionsInfo) 57| 11| directionsUrl = try values.decodeIfPresent(String.self, forKey: .directionsUrl)?.toURL() 58| 11| gpsLocation = try values.decodeIfPresent(String.self, forKey: .latLong)?.toLocation() 59| 11| addresses = try values.decodeIfPresent([Address].self, forKey: .addresses) 60| 11| operatingHours = try values.decodeIfPresent([OperatingHour].self, forKey: .operatingHours) 61| 11| contacts = try values.decodeIfPresent(ContactInformation.self, forKey: .contacts) 62| 11| } 63| |} <<<<<< 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| 10| override func setUp() { 9| 10| super.setUp() 10| 10| 11| 10| guard let data = Data(base64Encoded: "Y3hKaE96ZFBERzg1YjRITnBBYm85MzJHc1Y3c3Q3a1BVUVhObmQ0Vw==") else { 12| 0| XCTFail("invalid api key for testing") 13| 0| return 14| 10| } 15| 10| 16| 10| guard let testKey = String(data: data, encoding: .utf8) else { 17| 0| XCTFail("invalid api key for testing") 18| 0| return 19| 10| } 20| 10| 21| 10| self.api = DataService(apiKey: testKey) 22| 10| } 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| }) { (resultTuple) in 93| 1| let (parks, totalCount) = resultTuple 94| 1| XCTAssertTrue(totalCount > 0, "We have parks") 95| 1| XCTAssertTrue(parks.count > 0, "We have parks") 96| 1| XCTAssertEqual(totalCount, parks.count, "Shall be same in this case as parks in california are less than default paging (50)") 97| 1| guard let firstState = parks.first?.states.first else { 98| 0| XCTFail("No state") 99| 0| return 100| 1| } 101| 1| XCTAssertTrue(firstState == .california, "We have a state") 102| 1| } 103| 1| XCTAssertNotNil(subscription) 104| 1| wait(for: [expectation], timeout: 45.0) 105| 1| } 106| | 107| 1| func testFetchParksWithRequestOption() { 108| 1| let expectation = XCTestExpectation(description: "Download Parks") 109| 1| let publisher = api.fetchParks(by: nil, in: [.california], RequestOptions.init(limit: 5, searchQuery: "Yosemite National Park", fields: [.images, .entranceFees, .entrancePasses, .addresses, .contacts])) 110| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 111| 1| switch completion { 112| 1| case .finished: 113| 1| expectation.fulfill() 114| 1| case .failure(let error): 115| 0| print(error) 116| 0| XCTFail("subscription returned error unexpectedly") 117| 1| } 118| 1| }) { (resultTuple) in 119| 1| let (parks, totalCount) = resultTuple 120| 1| XCTAssertEqual(totalCount, 1, "We have parks") 121| 1| XCTAssertEqual(parks.count, 1, "We have parks") 122| 1| XCTAssertEqual(parks.first?.parkCode, ParkCodeConstants.yosemite) 123| 1| XCTAssertNotNil(parks.first?.images, "We should have images") 124| 1| XCTAssertNotNil(parks.first?.images?.first?.url, "We should have image url") 125| 1| XCTAssertNotNil(parks.first?.entranceFees, "We should not have receive a non default field without requesting it") 126| 1| XCTAssertNotNil(parks.first?.entrancePasses, "We should not have receive a non default field without requesting it") 127| 1| XCTAssertNotNil(parks.first?.addresses, "We should not have receive a non default field without requesting it") 128| 1| XCTAssertNotNil(parks.first?.contacts, "We should not have receive a non default field without requesting it") 129| 1| } 130| 1| XCTAssertNotNil(subscription) 131| 1| wait(for: [expectation], timeout: 45.0) 132| 1| } 133| 1| func testFetchParksWithPagination() { 134| 1| let expectation = XCTestExpectation(description: "Download Parks") 135| 1| let publisher = api.fetchParks(by: nil, in: nil, RequestOptions.init(limit: 20, start: 2, searchQuery: nil, fields: nil)) 136| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 137| 1| switch completion { 138| 1| case .finished: 139| 1| expectation.fulfill() 140| 1| case .failure(let error): 141| 0| print(error) 142| 0| XCTFail("subscription returned error unexpectedly") 143| 1| } 144| 1| }) { (resultTuple) in 145| 1| let (parks, totalCount) = resultTuple 146| 1| XCTAssertGreaterThanOrEqual(totalCount, 497, "There are currently 497 parks in NPS") 147| 1| XCTAssertEqual(parks.count, 20, "Only 20 were fetched from server") 148| 1| XCTAssertEqual(parks.first?.parkCode, "bepa", "Not avia but bepa is first park due to the start parameter") 149| 1| } 150| 1| XCTAssertNotNil(subscription) 151| 1| wait(for: [expectation], timeout: 45.0) 152| 1| } 153| | 154| 1| func testFetchAlertsByParkCode() { 155| 1| let expectation = XCTestExpectation(description: "Download Alerts") 156| 1| let publisher = api.fetchAlerts(by: [ParkCodeConstants.acadia]) 157| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 158| 1| switch completion { 159| 1| case .finished: 160| 1| expectation.fulfill() 161| 1| case .failure(let error): 162| 0| print(error) 163| 0| XCTFail("subscription returned error unexpectedly") 164| 1| } 165| 1| }) { (resultTuple) in 166| 1| let (alerts, _) = resultTuple 167| 1| XCTAssertTrue(alerts.count > 0, "We have alerts") 168| 1| } 169| 1| XCTAssertNotNil(subscription) 170| 1| wait(for: [expectation], timeout: 45.0) 171| 1| } 172| | 173| 1| func testFetchNewsReleaseByParkCode() { 174| 1| let expectation = XCTestExpectation(description: "Download NewsReleases") 175| 1| let publisher = api.fetchNewsReleases() 176| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 177| 1| switch completion { 178| 1| case .finished: 179| 1| expectation.fulfill() 180| 1| case .failure(let error): 181| 0| print(error) 182| 0| XCTFail("subscription returned error unexpectedly") 183| 1| } 184| 1| }) { (resultTuple) in 185| 1| let (alerts, _) = resultTuple 186| 1| XCTAssertTrue(alerts.count > 0, "We have news releaes") 187| 1| XCTAssertNotNil(alerts.first?.id) 188| 1| XCTAssertNotNil(alerts.first?.title) 189| 1| XCTAssertNotNil(alerts.first?.abstract) 190| 1| XCTAssertNotNil(alerts.first?.parkCode) 191| 1| XCTAssertNotNil(alerts.first?.releaseDate) 192| 1| XCTAssertNotNil(alerts.first?.url) 193| 1| } 194| 1| XCTAssertNotNil(subscription) 195| 1| wait(for: [expectation], timeout: 45.0) 196| 1| } 197| | 198| 1| func testFetchVisitorCentersByParkCode() { 199| 1| let expectation = XCTestExpectation(description: "Download Visitor Centers") 200| 1| let publisher = api.fetchVisitorCenters(by: [ParkCodeConstants.yellowstone], in: nil, RequestOptions.init(searchQuery: nil, fields: [.addresses, .operatingHours, .contacts])) 201| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 202| 1| switch completion { 203| 1| case .finished: 204| 1| expectation.fulfill() 205| 1| case .failure(let error): 206| 0| print(error) 207| 0| XCTFail("subscription returned error unexpectedly") 208| 1| } 209| 1| }) { (resultTuple) in 210| 1| let (visitorCenters, _) = resultTuple 211| 1| XCTAssertTrue(visitorCenters.count > 0, "We have visitor centers") 212| 1| XCTAssertNotNil(visitorCenters.first?.id) 213| 1| XCTAssertNotNil(visitorCenters.first?.name) 214| 1| XCTAssertNotNil(visitorCenters.first?.description) 215| 1| XCTAssertNotNil(visitorCenters.first?.parkCode) 216| 1| XCTAssertNotNil(visitorCenters.first?.directionsInfo) 217| 1| XCTAssertNotNil(visitorCenters.first?.gpsLocation) 218| 1| XCTAssertNotNil(visitorCenters.first?.addresses) 219| 1| XCTAssertEqual(visitorCenters.first?.addresses?.first?.stateCode, StateInUSA.wyoming) 220| 1| XCTAssertNotNil(visitorCenters.first?.operatingHours) 221| 1| XCTAssertNotNil(visitorCenters.first?.contacts) 222| 1| } 223| 1| XCTAssertNotNil(subscription) 224| 1| wait(for: [expectation], timeout: 45.0) 225| 1| } 226| | 227| 1| func testFetchAssetsByParkCode() { 228| 1| let expectation = XCTestExpectation(description: "Download Assets") 229| 1| let publisher = api.fetchAssets(by: [ParkCodeConstants.yellowstone], in: nil, nil) 230| 1| let subscription = publisher.sink(receiveCompletion: { (completion) in 231| 1| switch completion { 232| 1| case .finished: 233| 1| expectation.fulfill() 234| 1| case .failure(let error): 235| 0| print(error) 236| 0| XCTFail("subscription returned error unexpectedly") 237| 1| } 238| 1| }) { (resultTuple) in 239| 1| let (assets, _) = resultTuple 240| 1| XCTAssertTrue(assets.count > 0, "We have assets / places") 241| 1| XCTAssertNotNil(assets.first?.id) 242| 1| XCTAssertNotNil(assets.first?.listingImage.url) 243| 1| XCTAssertNotNil(assets.first?.listingImage.altText) 244| 1| } 245| 1| XCTAssertNotNil(subscription) 246| 1| wait(for: [expectation], timeout: 45.0) 247| 1| } 248| |} /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: [], addresses: [], operatingHours: [], contacts: nil) 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: [], addresses: [], operatingHours: [], contacts: nil) 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: [], addresses: [], operatingHours: [], contacts: nil) 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: [], addresses: [], operatingHours: [], contacts: nil) 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 testSimpleName() { 18| 1| let cali: StateInUSA = .california 19| 1| XCTAssertEqual(cali.name, "California") 20| 1| } 21| | 22| 1| func testDoubleName() { 23| 1| let newyork: StateInUSA = .newYork 24| 1| XCTAssertEqual(newyork.name, "New York") 25| 1| } 26| | 27| 1| func testAbbreviation() { 28| 1| let cali: StateInUSA = .california 29| 1| XCTAssertEqual(cali.postalAbbreviation, "CA") 30| 1| } 31| | 32| |} <<<<<< EOF