diff --git a/.gitignore b/.gitignore index cefceff..db7fe03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# Created by https://www.toptal.com/developers/gitignore/api/swift,xcode -# Edit at https://www.toptal.com/developers/gitignore?templates=swift,xcode +# Created by https://www.toptal.com/developers/gitignore/api/xcode,swift +# Edit at https://www.toptal.com/developers/gitignore?templates=xcode,swift ### Swift ### # Xcode @@ -64,9 +64,9 @@ playground.xcworkspace Carthage/Build/ -# Add this lines if you are using Accio dependency management (Deprecated since Xcode 12) -# Dependencies/ -# .accio/ +# Accio dependency management +Dependencies/ +.accio/ # fastlane # It is recommended to not store the screenshots in the git repo. @@ -86,23 +86,18 @@ fastlane/test_output iOSInjectionProject/ ### Xcode ### -# Xcode -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - - - -## Gcc Patch -/*.gcno +## Xcode 8 and earlier ### Xcode Patch ### *.xcodeproj/* !*.xcodeproj/project.pbxproj !*.xcodeproj/xcshareddata/ !*.xcworkspace/contents.xcworkspacedata +/*.gcno **/xcshareddata/WorkspaceSettings.xcsettings -# End of https://www.toptal.com/developers/gitignore/api/swift,xcode +# End of https://www.toptal.com/developers/gitignore/api/xcode,swift ## Custom additions diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index e332d37..5976464 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -52,6 +52,7 @@ Sources/StadiaMaps/Models/LocateEdge.swift Sources/StadiaMaps/Models/LocateEdgeInfo.swift Sources/StadiaMaps/Models/LocateNode.swift Sources/StadiaMaps/Models/LocateObject.swift +Sources/StadiaMaps/Models/LowSpeedVehicleCostingOptions.swift Sources/StadiaMaps/Models/ManeuverSign.swift Sources/StadiaMaps/Models/ManeuverSignElement.swift Sources/StadiaMaps/Models/MapMatchCostingModel.swift @@ -64,6 +65,7 @@ Sources/StadiaMaps/Models/MatrixCostingModel.swift Sources/StadiaMaps/Models/MatrixDistance.swift Sources/StadiaMaps/Models/MatrixRequest.swift Sources/StadiaMaps/Models/MatrixResponse.swift +Sources/StadiaMaps/Models/MatrixWaypoint.swift Sources/StadiaMaps/Models/MotorScooterCostingOptions.swift Sources/StadiaMaps/Models/MotorcycleCostingOptions.swift Sources/StadiaMaps/Models/NearestRoadsRequest.swift @@ -150,6 +152,7 @@ docs/LocateEdge.md docs/LocateEdgeInfo.md docs/LocateNode.md docs/LocateObject.md +docs/LowSpeedVehicleCostingOptions.md docs/ManeuverSign.md docs/ManeuverSignElement.md docs/MapMatchCostingModel.md @@ -162,6 +165,7 @@ docs/MatrixCostingModel.md docs/MatrixDistance.md docs/MatrixRequest.md docs/MatrixResponse.md +docs/MatrixWaypoint.md docs/MotorScooterCostingOptions.md docs/MotorcycleCostingOptions.md docs/NearestRoadsRequest.md diff --git a/.openapi-generator/VERSION b/.openapi-generator/VERSION index ba7f754..18bb418 100644 --- a/.openapi-generator/VERSION +++ b/.openapi-generator/VERSION @@ -1 +1 @@ -7.4.0 +7.5.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 58ac770..5c2227a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Version 3.0.0 - 2024-04-30 + +### Added + +- Add support for low-speed vehicle routing +- The matrix endpoint now accepts its own model rather than coordinate. This includes a search cutoff and paves the way for future expansion. + +### Changed + +- Improved the documentation of the matrix endpoint failure modes + +### Fixed + +- The time and distance field on matrix source to target models are now marked as nullable + ## Version 2.1.0 - 2024-03-21 ### Added diff --git a/Sources/StadiaMaps/APIHelper.swift b/Sources/StadiaMaps/APIHelper.swift index e189ccf..6aa3a4f 100644 --- a/Sources/StadiaMaps/APIHelper.swift +++ b/Sources/StadiaMaps/APIHelper.swift @@ -89,7 +89,7 @@ public enum APIHelper { if destination.isEmpty { return nil } - return destination + return destination.sorted { $0.name < $1.name } } /// maps all values from source to query parameters @@ -112,6 +112,6 @@ public enum APIHelper { if destination.isEmpty { return nil } - return destination + return destination.sorted { $0.name < $1.name } } } diff --git a/Sources/StadiaMaps/Models/CostingModel.swift b/Sources/StadiaMaps/Models/CostingModel.swift index 2e61113..e5626e3 100644 --- a/Sources/StadiaMaps/Models/CostingModel.swift +++ b/Sources/StadiaMaps/Models/CostingModel.swift @@ -10,7 +10,7 @@ import Foundation import AnyCodable #endif -/** Costing models for determining the most optimal route to take. Note that bikeshare and motorcycle are still in beta. While Valhalla supports multimodal routing, we do not currently process transit data and have excluded it from the list. See https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/#costing-models for detailed descriptions of each model. */ +/** A model which influences the routing based on the type of travel. The costing model affects parameters ranging from which roads are legally accessible to preferences based on comfort or speed. See https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/#costing-models for in-depth descriptions of each costing model. */ public enum CostingModel: String, Codable, CaseIterable { case auto case bus @@ -21,4 +21,5 @@ public enum CostingModel: String, Codable, CaseIterable { case motorScooter = "motor_scooter" case motorcycle case pedestrian + case lowSpeedVehicle = "low_speed_vehicle" } diff --git a/Sources/StadiaMaps/Models/CostingOptions.swift b/Sources/StadiaMaps/Models/CostingOptions.swift index 8c671cb..678d11d 100644 --- a/Sources/StadiaMaps/Models/CostingOptions.swift +++ b/Sources/StadiaMaps/Models/CostingOptions.swift @@ -19,8 +19,9 @@ public struct CostingOptions: Codable, JSONEncodable, Hashable { public var motorScooter: MotorScooterCostingOptions? public var motorcycle: MotorcycleCostingOptions? public var pedestrian: PedestrianCostingOptions? + public var lowSpeedVehicle: LowSpeedVehicleCostingOptions? - public init(auto: AutoCostingOptions? = nil, bus: AutoCostingOptions? = nil, taxi: AutoCostingOptions? = nil, truck: TruckCostingOptions? = nil, bicycle: BicycleCostingOptions? = nil, motorScooter: MotorScooterCostingOptions? = nil, motorcycle: MotorcycleCostingOptions? = nil, pedestrian: PedestrianCostingOptions? = nil) { + public init(auto: AutoCostingOptions? = nil, bus: AutoCostingOptions? = nil, taxi: AutoCostingOptions? = nil, truck: TruckCostingOptions? = nil, bicycle: BicycleCostingOptions? = nil, motorScooter: MotorScooterCostingOptions? = nil, motorcycle: MotorcycleCostingOptions? = nil, pedestrian: PedestrianCostingOptions? = nil, lowSpeedVehicle: LowSpeedVehicleCostingOptions? = nil) { self.auto = auto self.bus = bus self.taxi = taxi @@ -29,6 +30,7 @@ public struct CostingOptions: Codable, JSONEncodable, Hashable { self.motorScooter = motorScooter self.motorcycle = motorcycle self.pedestrian = pedestrian + self.lowSpeedVehicle = lowSpeedVehicle } public enum CodingKeys: String, CodingKey, CaseIterable { @@ -40,6 +42,7 @@ public struct CostingOptions: Codable, JSONEncodable, Hashable { case motorScooter = "motor_scooter" case motorcycle case pedestrian + case lowSpeedVehicle = "low_speed_vehicle" } // Encodable protocol methods @@ -54,5 +57,6 @@ public struct CostingOptions: Codable, JSONEncodable, Hashable { try container.encodeIfPresent(motorScooter, forKey: .motorScooter) try container.encodeIfPresent(motorcycle, forKey: .motorcycle) try container.encodeIfPresent(pedestrian, forKey: .pedestrian) + try container.encodeIfPresent(lowSpeedVehicle, forKey: .lowSpeedVehicle) } } diff --git a/Sources/StadiaMaps/Models/LowSpeedVehicleCostingOptions.swift b/Sources/StadiaMaps/Models/LowSpeedVehicleCostingOptions.swift new file mode 100644 index 0000000..760c128 --- /dev/null +++ b/Sources/StadiaMaps/Models/LowSpeedVehicleCostingOptions.swift @@ -0,0 +1,110 @@ +// +// LowSpeedVehicleCostingOptions.swift +// +// Generated by openapi-generator +// https://openapi-generator.tech +// + +import Foundation +#if canImport(AnyCodable) + import AnyCodable +#endif + +public struct LowSpeedVehicleCostingOptions: Codable, JSONEncodable, Hashable { + public enum VehicleType: String, Codable, CaseIterable { + case lowSpeedVehicle = "low_speed_vehicle" + case golfCart = "golf_cart" + } + + static let useLivingStreetsRule = NumericRule(minimum: 0, exclusiveMinimum: false, maximum: 1, exclusiveMaximum: false, multipleOf: nil) + static let useFerryRule = NumericRule(minimum: 0, exclusiveMinimum: false, maximum: 1, exclusiveMaximum: false, multipleOf: nil) + static let topSpeedRule = NumericRule(minimum: 20, exclusiveMinimum: false, maximum: 60, exclusiveMaximum: false, multipleOf: nil) + static let maxAllowedSpeedLimitRule = NumericRule(minimum: 20, exclusiveMinimum: false, maximum: 80, exclusiveMaximum: false, multipleOf: nil) + /** A penalty (in seconds) applied when transitioning between roads (determined by name). */ + public var maneuverPenalty: Int? = 5 + /** The estimated cost (in seconds) when a gate is encountered. */ + public var gateCost: Int? = 15 + /** A penalty (in seconds) applied to the route cost when a gate is encountered. This penalty can be used to reduce the likelihood of suggesting a route with gates unless absolutely necessary. */ + public var gatePenalty: Int? = 300 + /** The estimated cost (in seconds) when encountering an international border. */ + public var countryCrossingCost: Int? = 600 + /** A penalty applied to transitions to international border crossings. This penalty can be used to reduce the likelihood of suggesting a route with border crossings unless absolutely necessary. */ + public var countryCrossingPenalty: Int? = 0 + /** A penalty applied to transitions to service roads. This penalty can be used to reduce the likelihood of suggesting a route with service roads unless absolutely necessary. The default penalty is 15 for cars, busses, motor scooters, and motorcycles; and zero for others. */ + public var servicePenalty: Int? + /** A factor that multiplies the cost when service roads are encountered. The default is 1.2 for cars and busses, and 1 for trucks, motor scooters, and motorcycles. */ + public var serviceFactor: Double? = 1 + /** A measure of willingness to take living streets. Values near 0 attempt to avoid them, and values near 1 will favour them. Note that as some routes may be impossible without living streets, 0 does not guarantee avoidance of them. The default value is 0 for trucks; 0.1 for other motor vehicles; 0.5 for bicycles; and 0.6 for pedestrians. */ + public var useLivingStreets: Double? + /** A measure of willingness to take ferries. Values near 0 attempt to avoid ferries, and values near 1 will favour them. Note that as some routes may be impossible without ferries, 0 does not guarantee avoidance of them. */ + public var useFerry: Double? = 0.5 + /** If set to true, ignores any restrictions (eg: turn and conditional restrictions). Useful for matching GPS traces to the road network regardless of restrictions. */ + public var ignoreRestrictions: Bool? + /** If set to true, ignores most restrictions (eg: turn and conditional restrictions), but still respects restrictions that impact vehicle safety such as weight and size. */ + public var ignoreNonVehicularRestrictions: Bool? + /** If set to true, ignores directional restrictions on roads. Useful for matching GPS traces to the road network regardless of restrictions. */ + public var ignoreOneways: Bool? + /** The type of vehicle: * low_speed_vehicle (BETA): a low-speed vehicle which falls under a different regulatory and licensing regime than automobiles (ex: LSV in the US and Canada, Quadricycles in the EU, etc.) * golf_cart: a street legal golf cart that is under a similar regulator regime as the generic LSV laws, but may need to follow special paths when available or abide by restrictions specific to golf carts. */ + public var vehicleType: VehicleType? = .lowSpeedVehicle + /** The top speed (in kph) that the vehicle is capable of travelling. This impacts travel time calculations as well as which roads are preferred. A very low speed vehicle will tend to prefer lower speed roads even in the presence of other legal routes. */ + public var topSpeed: Int? = 35 + /** The maximum speed limit for highways on which it is legal for the vehicle to travel. Defaults to 57 (kph; around 35 mph). Acceptable values range from 20 to 80. Highways with *tagged* speed limits higher than this value will not be routed over (some caveats apply; this feature is still BETA). */ + public var maxAllowedSpeedLimit: Int? = 57 + + public init(maneuverPenalty: Int? = 5, gateCost: Int? = 15, gatePenalty: Int? = 300, countryCrossingCost: Int? = 600, countryCrossingPenalty: Int? = 0, servicePenalty: Int? = nil, serviceFactor: Double? = 1, useLivingStreets: Double? = nil, useFerry: Double? = 0.5, ignoreRestrictions: Bool? = nil, ignoreNonVehicularRestrictions: Bool? = nil, ignoreOneways: Bool? = nil, vehicleType: VehicleType? = .lowSpeedVehicle, topSpeed: Int? = 35, maxAllowedSpeedLimit: Int? = 57) { + self.maneuverPenalty = maneuverPenalty + self.gateCost = gateCost + self.gatePenalty = gatePenalty + self.countryCrossingCost = countryCrossingCost + self.countryCrossingPenalty = countryCrossingPenalty + self.servicePenalty = servicePenalty + self.serviceFactor = serviceFactor + self.useLivingStreets = useLivingStreets + self.useFerry = useFerry + self.ignoreRestrictions = ignoreRestrictions + self.ignoreNonVehicularRestrictions = ignoreNonVehicularRestrictions + self.ignoreOneways = ignoreOneways + self.vehicleType = vehicleType + self.topSpeed = topSpeed + self.maxAllowedSpeedLimit = maxAllowedSpeedLimit + } + + public enum CodingKeys: String, CodingKey, CaseIterable { + case maneuverPenalty = "maneuver_penalty" + case gateCost = "gate_cost" + case gatePenalty = "gate_penalty" + case countryCrossingCost = "country_crossing_cost" + case countryCrossingPenalty = "country_crossing_penalty" + case servicePenalty = "service_penalty" + case serviceFactor = "service_factor" + case useLivingStreets = "use_living_streets" + case useFerry = "use_ferry" + case ignoreRestrictions = "ignore_restrictions" + case ignoreNonVehicularRestrictions = "ignore_non_vehicular_restrictions" + case ignoreOneways = "ignore_oneways" + case vehicleType = "vehicle_type" + case topSpeed = "top_speed" + case maxAllowedSpeedLimit = "max_allowed_speed_limit" + } + + // Encodable protocol methods + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(maneuverPenalty, forKey: .maneuverPenalty) + try container.encodeIfPresent(gateCost, forKey: .gateCost) + try container.encodeIfPresent(gatePenalty, forKey: .gatePenalty) + try container.encodeIfPresent(countryCrossingCost, forKey: .countryCrossingCost) + try container.encodeIfPresent(countryCrossingPenalty, forKey: .countryCrossingPenalty) + try container.encodeIfPresent(servicePenalty, forKey: .servicePenalty) + try container.encodeIfPresent(serviceFactor, forKey: .serviceFactor) + try container.encodeIfPresent(useLivingStreets, forKey: .useLivingStreets) + try container.encodeIfPresent(useFerry, forKey: .useFerry) + try container.encodeIfPresent(ignoreRestrictions, forKey: .ignoreRestrictions) + try container.encodeIfPresent(ignoreNonVehicularRestrictions, forKey: .ignoreNonVehicularRestrictions) + try container.encodeIfPresent(ignoreOneways, forKey: .ignoreOneways) + try container.encodeIfPresent(vehicleType, forKey: .vehicleType) + try container.encodeIfPresent(topSpeed, forKey: .topSpeed) + try container.encodeIfPresent(maxAllowedSpeedLimit, forKey: .maxAllowedSpeedLimit) + } +} diff --git a/Sources/StadiaMaps/Models/MatrixCostingModel.swift b/Sources/StadiaMaps/Models/MatrixCostingModel.swift index 9b15ba9..2181829 100644 --- a/Sources/StadiaMaps/Models/MatrixCostingModel.swift +++ b/Sources/StadiaMaps/Models/MatrixCostingModel.swift @@ -20,4 +20,5 @@ public enum MatrixCostingModel: String, Codable, CaseIterable { case motorScooter = "motor_scooter" case motorcycle case pedestrian + case lowSpeedVehicle = "low_speed_vehicle" } diff --git a/Sources/StadiaMaps/Models/MatrixDistance.swift b/Sources/StadiaMaps/Models/MatrixDistance.swift index 06f3fc5..6033cdb 100644 --- a/Sources/StadiaMaps/Models/MatrixDistance.swift +++ b/Sources/StadiaMaps/Models/MatrixDistance.swift @@ -11,16 +11,16 @@ import Foundation #endif public struct MatrixDistance: Codable, JSONEncodable, Hashable { - /** The distance (in `units`) between the location in `sources` at `from_index` and the location in `targets` at `to_index`. */ - public var distance: Double - /** The travel time (in seconds) between the location in `sources` at `from_index` and the location in `targets` at `to_index`. */ - public var time: Int + /** The distance (in `units`) between the location in `sources` at `from_index` and the location in `targets` at `to_index`. This value may be 0 in the case that the source and destination are the same, and `null` if no route was found between the locations. */ + public var distance: Double? + /** The travel time (in seconds) between the location in `sources` at `from_index` and the location in `targets` at `to_index`. This value may be 0 in the case that the source and destination are the same, and `null` if no route was found between the locations. */ + public var time: Int? /** The index of the start location in the `sources` array. */ public var fromIndex: Int /** The index of the end location in the `targets` array. */ public var toIndex: Int - public init(distance: Double, time: Int, fromIndex: Int, toIndex: Int) { + public init(distance: Double?, time: Int?, fromIndex: Int, toIndex: Int) { self.distance = distance self.time = time self.fromIndex = fromIndex diff --git a/Sources/StadiaMaps/Models/MatrixRequest.swift b/Sources/StadiaMaps/Models/MatrixRequest.swift index 5b2fe4c..461657e 100644 --- a/Sources/StadiaMaps/Models/MatrixRequest.swift +++ b/Sources/StadiaMaps/Models/MatrixRequest.swift @@ -24,15 +24,15 @@ public struct MatrixRequest: Codable, JSONEncodable, Hashable { /** An identifier to disambiguate requests (echoed by the server). */ public var id: String? /** The list of starting locations */ - public var sources: [Coordinate] + public var sources: [MatrixWaypoint] /** The list of ending locations */ - public var targets: [Coordinate] + public var targets: [MatrixWaypoint] public var costing: MatrixCostingModel public var costingOptions: CostingOptions? /** Only applicable to one-to-many or many-to-one requests. This defaults to all locations. When specified explicitly, this option allows a partial result to be returned. This is basically equivalent to \"find the closest/best locations out of the full set.\" This can have a dramatic improvement for large requests. */ public var matrixLocations: Int? - public init(units: DistanceUnit? = nil, language: ValhallaLanguages? = nil, directionsType: DirectionsType? = .instructions, id: String? = nil, sources: [Coordinate], targets: [Coordinate], costing: MatrixCostingModel, costingOptions: CostingOptions? = nil, matrixLocations: Int? = nil) { + public init(units: DistanceUnit? = nil, language: ValhallaLanguages? = nil, directionsType: DirectionsType? = .instructions, id: String? = nil, sources: [MatrixWaypoint], targets: [MatrixWaypoint], costing: MatrixCostingModel, costingOptions: CostingOptions? = nil, matrixLocations: Int? = nil) { self.units = units self.language = language self.directionsType = directionsType diff --git a/Sources/StadiaMaps/Models/MatrixResponse.swift b/Sources/StadiaMaps/Models/MatrixResponse.swift index b998398..cb5aa65 100644 --- a/Sources/StadiaMaps/Models/MatrixResponse.swift +++ b/Sources/StadiaMaps/Models/MatrixResponse.swift @@ -13,9 +13,9 @@ import Foundation public struct MatrixResponse: Codable, JSONEncodable, Hashable { /** An identifier to disambiguate requests (echoed by the server). */ public var id: String? - /** The list of starting locations */ + /** The list of starting locations determined by snapping to the nearest appropriate point on the road network for the costing model. All locations appear in the same order as the input. */ public var sources: [Coordinate] - /** The list of ending locations */ + /** The list of ending locations determined by snapping to the nearest appropriate point on the road network for the costing model. All locations appear in the same order as the input. */ public var targets: [Coordinate] /** The matrix of starting and ending locations, along with the computed distance and travel time. The array is row-ordered. This means that the time and distance from the first location to all others forms the first row of the array, followed by the time and distance from the second source location to all target locations, etc. */ public var sourcesToTargets: [[MatrixDistance]] diff --git a/Sources/StadiaMaps/Models/MatrixWaypoint.swift b/Sources/StadiaMaps/Models/MatrixWaypoint.swift new file mode 100644 index 0000000..a12a12f --- /dev/null +++ b/Sources/StadiaMaps/Models/MatrixWaypoint.swift @@ -0,0 +1,43 @@ +// +// MatrixWaypoint.swift +// +// Generated by openapi-generator +// https://openapi-generator.tech +// + +import Foundation +#if canImport(AnyCodable) + import AnyCodable +#endif + +public struct MatrixWaypoint: Codable, JSONEncodable, Hashable { + static let latRule = NumericRule(minimum: -90, exclusiveMinimum: false, maximum: 90, exclusiveMaximum: false, multipleOf: nil) + static let lonRule = NumericRule(minimum: -180, exclusiveMinimum: false, maximum: 180, exclusiveMaximum: false, multipleOf: nil) + /** The latitude of a point in the shape. */ + public var lat: Double + /** The longitude of a point in the shape. */ + public var lon: Double + /** The cutoff (in meters) at which we will assume the input is too far away from civilisation to be worth correlating to the nearest graph elements. The default is 35 km. */ + public var searchCutoff: Int? + + public init(lat: Double, lon: Double, searchCutoff: Int? = nil) { + self.lat = lat + self.lon = lon + self.searchCutoff = searchCutoff + } + + public enum CodingKeys: String, CodingKey, CaseIterable { + case lat + case lon + case searchCutoff = "search_cutoff" + } + + // Encodable protocol methods + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(lat, forKey: .lat) + try container.encode(lon, forKey: .lon) + try container.encodeIfPresent(searchCutoff, forKey: .searchCutoff) + } +} diff --git a/Sources/StadiaMaps/URLSessionImplementations.swift b/Sources/StadiaMaps/URLSessionImplementations.swift index ed347d2..b1448d6 100644 --- a/Sources/StadiaMaps/URLSessionImplementations.swift +++ b/Sources/StadiaMaps/URLSessionImplementations.swift @@ -8,6 +8,9 @@ import Foundation #if !os(macOS) import MobileCoreServices #endif +#if canImport(UniformTypeIdentifiers) + import UniformTypeIdentifiers +#endif public protocol URLSessionProtocol { func dataTask(with request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask @@ -540,10 +543,21 @@ private class FormDataEncoding: ParameterEncoding { func mimeType(for url: URL) -> String { let pathExtension = url.pathExtension - if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue(), - let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() - { - return mimetype as String + if #available(iOS 15, macOS 11, *) { + #if canImport(UniformTypeIdentifiers) + if let utType = UTType(filenameExtension: pathExtension) { + return utType.preferredMIMEType ?? "application/octet-stream" + } + #else + return "application/octet-stream" + #endif + } else { + if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue(), + let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() + { + return mimetype as String + } + return "application/octet-stream" } return "application/octet-stream" } diff --git a/project.yml b/project.yml index 45c0def..4970782 100644 --- a/project.yml +++ b/project.yml @@ -7,7 +7,7 @@ targets: sources: [Sources] info: path: ./Info.plist - version: 6.1.0 + version: 6.3.0 settings: APPLICATION_EXTENSION_API_ONLY: true scheme: {} diff --git a/tests/GeocodingAPIIntegrationTestCase.swift b/tests/GeocodingAPIIntegrationTestCase.swift index 3e80c19..6836c41 100644 --- a/tests/GeocodingAPIIntegrationTestCase.swift +++ b/tests/GeocodingAPIIntegrationTestCase.swift @@ -26,7 +26,7 @@ final class GeocodingAPIIntegrationTestCase: IntegrationXCTestCase { } func testReverse() async throws { - let res = try await GeocodingAPI.reverse(pointLat: 59.444351, pointLon: 24.750645) + let res = try await GeocodingAPI.reverse(pointLat: 59.444351, pointLon: 24.750645, layers: [.address, .localadmin]) XCTAssert(!res.features.isEmpty) XCTAssertEqual(res.features.first?.properties?.country, "Estonia") XCTAssertEqual(res.features.first?.properties?.layer, .address) diff --git a/tests/RoutingAPITestCase.swift b/tests/RoutingAPITestCase.swift index 8a1dcb9..682847f 100644 --- a/tests/RoutingAPITestCase.swift +++ b/tests/RoutingAPITestCase.swift @@ -79,13 +79,14 @@ final class RoutingAPITestCase: IntegrationXCTestCase { func testTimeDistanceMatrix() async throws { let req = MatrixRequest(id: "matrix", sources: [ - locationA, + MatrixWaypoint(lat: 58.891957, lon: 22.726262), + MatrixWaypoint(lat: 59.1558, lon: 23.762758), ], targets: [ - locationB, - locationC, + MatrixWaypoint(lat: 59.176153, lon: 23.846605), + MatrixWaypoint(lat: 59.562853, lon: 23.096114) ], - costing: .pedestrian) + costing: .bicycle) let res = try await RoutingAPI.timeDistanceMatrix(matrixRequest: req) XCTAssertEqual(req.id, res.id) XCTAssertEqual(req.sources.count, res.sources.count)