Skip to content

Commit d1b168a

Browse files
committed
Merge branch 'master' into swift3
Also removed usage of Locale.languageCode, which is incompatible with iOS 9.
2 parents 7841fcb + b07b4cd commit d1b168a

File tree

13 files changed

+205
-45
lines changed

13 files changed

+205
-45
lines changed

MapboxGeocoder.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
DA2E03F21CB0FE0200D1269A /* MBRectangularRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2E03F11CB0FE0200D1269A /* MBRectangularRegion.swift */; };
2121
DA2EC05C1CED72E900D4BA5D /* MBPlacemarkScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2EC05B1CED72E900D4BA5D /* MBPlacemarkScope.swift */; };
2222
DA2EC05E1CED732F00D4BA5D /* MBGeocodeOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2EC05D1CED732F00D4BA5D /* MBGeocodeOptions.swift */; };
23+
DA4D90071DD63AEC006EC71A /* PlacemarkScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4D90061DD63AEC006EC71A /* PlacemarkScopeTests.swift */; };
24+
DA4D90081DD63AEC006EC71A /* PlacemarkScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4D90061DD63AEC006EC71A /* PlacemarkScopeTests.swift */; };
25+
DA4D90091DD63AEC006EC71A /* PlacemarkScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4D90061DD63AEC006EC71A /* PlacemarkScopeTests.swift */; };
2326
DA5170AD1CF1B1DB00CD6DCF /* MapboxGeocoder.h in Headers */ = {isa = PBXBuildFile; fileRef = DDC229581A36073B006BE405 /* MapboxGeocoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
2427
DA5170AE1CF1B1E000CD6DCF /* MBGeocodeOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2EC05D1CED732F00D4BA5D /* MBGeocodeOptions.swift */; };
2528
DA5170AF1CF1B1E000CD6DCF /* MBGeocoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC229591A36073B006BE405 /* MBGeocoder.swift */; };
@@ -171,6 +174,7 @@
171174
DA2E03F11CB0FE0200D1269A /* MBRectangularRegion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MBRectangularRegion.swift; sourceTree = "<group>"; };
172175
DA2EC05B1CED72E900D4BA5D /* MBPlacemarkScope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MBPlacemarkScope.swift; sourceTree = "<group>"; };
173176
DA2EC05D1CED732F00D4BA5D /* MBGeocodeOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MBGeocodeOptions.swift; sourceTree = "<group>"; };
177+
DA4D90061DD63AEC006EC71A /* PlacemarkScopeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlacemarkScopeTests.swift; sourceTree = "<group>"; };
174178
DA5170961CF1B18F00CD6DCF /* MapboxGeocoder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MapboxGeocoder.framework; sourceTree = BUILT_PRODUCTS_DIR; };
175179
DA51709F1CF1B18F00CD6DCF /* MapboxGeocoderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapboxGeocoderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
176180
DA5170C11CF253EE00CD6DCF /* MapboxGeocoder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MapboxGeocoder.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -402,6 +406,7 @@
402406
DA701C001CB1292C00B0E520 /* GeocoderTests.swift */,
403407
DA210BAA1CB4BE73008088FD /* ForwardGeocodingTests.swift */,
404408
DDF1E84C1BD6F7BA00C40C78 /* ReverseGeocodingTests.swift */,
409+
DA4D90061DD63AEC006EC71A /* PlacemarkScopeTests.swift */,
405410
DDF1E84E1BD6F7BA00C40C78 /* Info.plist */,
406411
DDF1E8571BD700EB00C40C78 /* Fixtures */,
407412
);
@@ -1043,6 +1048,7 @@
10431048
buildActionMask = 2147483647;
10441049
files = (
10451050
DA5170B61CF1B1EF00CD6DCF /* ReverseGeocodingTests.swift in Sources */,
1051+
DA4D90081DD63AEC006EC71A /* PlacemarkScopeTests.swift in Sources */,
10461052
DA5170B41CF1B1EF00CD6DCF /* GeocoderTests.swift in Sources */,
10471053
DA5170B51CF1B1EF00CD6DCF /* ForwardGeocodingTests.swift in Sources */,
10481054
);
@@ -1065,6 +1071,7 @@
10651071
buildActionMask = 2147483647;
10661072
files = (
10671073
DA5170E11CF2542800CD6DCF /* ReverseGeocodingTests.swift in Sources */,
1074+
DA4D90091DD63AEC006EC71A /* PlacemarkScopeTests.swift in Sources */,
10681075
DA5170DF1CF2542800CD6DCF /* GeocoderTests.swift in Sources */,
10691076
DA5170E01CF2542800CD6DCF /* ForwardGeocodingTests.swift in Sources */,
10701077
);
@@ -1118,6 +1125,7 @@
11181125
buildActionMask = 2147483647;
11191126
files = (
11201127
DDF1E84D1BD6F7BA00C40C78 /* ReverseGeocodingTests.swift in Sources */,
1128+
DA4D90071DD63AEC006EC71A /* PlacemarkScopeTests.swift in Sources */,
11211129
DA210BAB1CB4BE73008088FD /* ForwardGeocodingTests.swift in Sources */,
11221130
DA701C011CB1292C00B0E520 /* GeocoderTests.swift in Sources */,
11231131
);

MapboxGeocoder.xcodeproj/xcshareddata/xcschemes/Example (Objective-C).xcscheme

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,36 @@
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
2929
shouldUseLaunchSchemeArgsEnv = "YES">
3030
<Testables>
31+
<TestableReference
32+
skipped = "NO">
33+
<BuildableReference
34+
BuildableIdentifier = "primary"
35+
BlueprintIdentifier = "DDF1E8491BD6F7BA00C40C78"
36+
BuildableName = "MapboxGeocoderTests.xctest"
37+
BlueprintName = "MapboxGeocoderTests"
38+
ReferencedContainer = "container:MapboxGeocoder.xcodeproj">
39+
</BuildableReference>
40+
</TestableReference>
41+
<TestableReference
42+
skipped = "NO">
43+
<BuildableReference
44+
BuildableIdentifier = "primary"
45+
BlueprintIdentifier = "DA51709E1CF1B18F00CD6DCF"
46+
BuildableName = "MapboxGeocoderTests.xctest"
47+
BlueprintName = "MapboxGeocoderMacTests"
48+
ReferencedContainer = "container:MapboxGeocoder.xcodeproj">
49+
</BuildableReference>
50+
</TestableReference>
51+
<TestableReference
52+
skipped = "NO">
53+
<BuildableReference
54+
BuildableIdentifier = "primary"
55+
BlueprintIdentifier = "DA5170C91CF253EE00CD6DCF"
56+
BuildableName = "MapboxGeocoderTests.xctest"
57+
BlueprintName = "MapboxGeocoderTVTests"
58+
ReferencedContainer = "container:MapboxGeocoder.xcodeproj">
59+
</BuildableReference>
60+
</TestableReference>
3161
</Testables>
3262
<MacroExpansion>
3363
<BuildableReference

MapboxGeocoder.xcodeproj/xcshareddata/xcschemes/Example (Swift).xcscheme

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,26 @@
3838
ReferencedContainer = "container:MapboxGeocoder.xcodeproj">
3939
</BuildableReference>
4040
</TestableReference>
41+
<TestableReference
42+
skipped = "NO">
43+
<BuildableReference
44+
BuildableIdentifier = "primary"
45+
BlueprintIdentifier = "DA51709E1CF1B18F00CD6DCF"
46+
BuildableName = "MapboxGeocoderTests.xctest"
47+
BlueprintName = "MapboxGeocoderMacTests"
48+
ReferencedContainer = "container:MapboxGeocoder.xcodeproj">
49+
</BuildableReference>
50+
</TestableReference>
51+
<TestableReference
52+
skipped = "NO">
53+
<BuildableReference
54+
BuildableIdentifier = "primary"
55+
BlueprintIdentifier = "DA5170C91CF253EE00CD6DCF"
56+
BuildableName = "MapboxGeocoderTests.xctest"
57+
BlueprintName = "MapboxGeocoderTVTests"
58+
ReferencedContainer = "container:MapboxGeocoder.xcodeproj">
59+
</BuildableReference>
60+
</TestableReference>
4161
</Testables>
4262
<MacroExpansion>
4363
<BuildableReference

MapboxGeocoder/MBGeocodeOptions.swift

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ open class GeocodeOptions: NSObject {
2626
open var allowedISOCountryCodes: [String]?
2727

2828
/**
29-
A loation to use as a hint when looking up the specified address.
29+
A location to use as a hint when looking up the specified address.
3030

3131
This property prioritizes results that are close to a specific location, which is typically the user’s current location. If the value of this property is `nil` – which it is by default – no specific location is prioritized.
3232
*/
@@ -46,6 +46,11 @@ open class GeocodeOptions: NSObject {
4646
*/
4747
open var allowedRegion: RectangularRegion?
4848

49+
/**
50+
Limit the number of results returned. The default is `5` for forward geocoding and `1` for reverse geocoding.
51+
*/
52+
public var maximumResultCount: UInt
53+
4954
// MARK: Specifying the Output Format
5055

5156
/**
@@ -59,7 +64,10 @@ open class GeocodeOptions: NSObject {
5964
*/
6065
open var locale: Locale?
6166

62-
fileprivate override init() {}
67+
fileprivate override init() {
68+
self.maximumResultCount = 0
69+
super.init()
70+
}
6371

6472
/**
6573
An array of geocoding query strings to include in the request URL.
@@ -87,7 +95,10 @@ open class GeocodeOptions: NSObject {
8795
if let allowedRegion = allowedRegion {
8896
params.append(URLQueryItem(name: "bbox", value: String(describing: allowedRegion)))
8997
}
90-
if let languageCode = locale?.languageCode {
98+
if maximumResultCount > 0 {
99+
params.append(URLQueryItem(name: "limit", value: String(maximumResultCount)))
100+
}
101+
if let languageCode = (locale as NSLocale?)?.object(forKey: .languageCode) as? String {
91102
params.append(URLQueryItem(name: "language", value: languageCode))
92103
}
93104
return params
@@ -105,10 +116,11 @@ open class ForwardGeocodeOptions: GeocodeOptions {
105116
If true, a resulting placemark’s name may contain a word that begins with the query string. If false, the query string must match a whole word or phrase in the placemark’s name. The default value of this property is true, which is best suited for continuous search fields.
106117
*/
107118
open var autocompletesQuery = true
108-
119+
109120
fileprivate init(queries: [String]) {
110121
super.init()
111122
self.queries = queries
123+
self.maximumResultCount = 5
112124
}
113125

114126
/**
@@ -151,10 +163,11 @@ open class ReverseGeocodeOptions: GeocodeOptions {
151163
An array of coordinates to search for.
152164
*/
153165
open var coordinates: [CLLocationCoordinate2D]
154-
166+
155167
fileprivate init(coordinates: [CLLocationCoordinate2D]) {
156168
self.coordinates = coordinates
157169
super.init()
170+
self.maximumResultCount = 1
158171
queries = coordinates.map { String(format: "%.5f,%.5f", $0.longitude, $0.latitude) }
159172
}
160173

MapboxGeocoder/MBGeocoder.swift

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -284,23 +284,22 @@ open class Geocoder: NSObject {
284284
/**
285285
Returns an error that supplements the given underlying error with additional information from the an HTTP response’s body or headers.
286286
*/
287-
fileprivate static func descriptiveError(_ json: JSONDictionary, response: URLResponse?, underlyingError error: NSError?) -> NSError {
287+
static func descriptiveError(_ json: JSONDictionary, response: URLResponse?, underlyingError error: NSError?) -> NSError {
288288
var userInfo = error?.userInfo ?? [:]
289289
if let response = response as? HTTPURLResponse {
290290
var failureReason: String? = nil
291291
var recoverySuggestion: String? = nil
292292
switch response.statusCode {
293293
case 429:
294-
if let timeInterval = response.allHeaderFields["x-rate-limit-interval"] as? TimeInterval, let maximumCountOfRequests = response.allHeaderFields["x-rate-limit-limit"] as? UInt {
294+
if let timeInterval = response.rateLimitInterval, let maximumCountOfRequests = response.rateLimit {
295295
let intervalFormatter = DateComponentsFormatter()
296296
intervalFormatter.unitsStyle = .full
297-
let formattedInterval = intervalFormatter.string(from: timeInterval)
297+
let formattedInterval = intervalFormatter.string(from: timeInterval) ?? "\(timeInterval) seconds"
298298
let formattedCount = NumberFormatter.localizedString(from: maximumCountOfRequests as NSNumber, number: .decimal)
299299
failureReason = "More than \(formattedCount) requests have been made with this access token within a period of \(formattedInterval)."
300300
}
301-
if let rolloverTimestamp = response.allHeaderFields["x-rate-limit-reset"] as? Double {
302-
let date = Date(timeIntervalSince1970: rolloverTimestamp)
303-
let formattedDate = DateFormatter.localizedString(from: date, dateStyle: .long, timeStyle: .full)
301+
if let rolloverTime = response.rateLimitResetTime {
302+
let formattedDate = DateFormatter.localizedString(from: rolloverTime, dateStyle: .long, timeStyle: .full)
304303
recoverySuggestion = "Wait until \(formattedDate) before retrying."
305304
}
306305
default:
@@ -309,7 +308,41 @@ open class Geocoder: NSObject {
309308
userInfo[NSLocalizedFailureReasonErrorKey] = failureReason ?? userInfo[NSLocalizedFailureReasonErrorKey] ?? HTTPURLResponse.localizedString(forStatusCode: error?.code ?? -1)
310309
userInfo[NSLocalizedRecoverySuggestionErrorKey] = recoverySuggestion ?? userInfo[NSLocalizedRecoverySuggestionErrorKey]
311310
}
312-
userInfo[NSUnderlyingErrorKey] = error
311+
if let error = error {
312+
userInfo[NSUnderlyingErrorKey] = error
313+
}
313314
return NSError(domain: error?.domain ?? MBGeocoderErrorDomain, code: error?.code ?? -1, userInfo: userInfo)
314315
}
315316
}
317+
318+
extension HTTPURLResponse {
319+
320+
static let rateLimitIntervalHeaderKey = "X-Rate-Limit-Interval"
321+
static let rateLimitLimitHeaderKey = "X-Rate-Limit-Limit"
322+
static let rateLimitResetHeaderKey = "X-Rate-Limit-Reset"
323+
324+
var rateLimit: UInt? {
325+
guard let limit = allHeaderFields[HTTPURLResponse.rateLimitLimitHeaderKey] as? String else {
326+
return nil
327+
}
328+
return UInt(limit)
329+
}
330+
331+
var rateLimitInterval: TimeInterval? {
332+
guard let interval = allHeaderFields[HTTPURLResponse.rateLimitIntervalHeaderKey] as? String else {
333+
return nil
334+
}
335+
return TimeInterval(interval)
336+
}
337+
338+
var rateLimitResetTime: Date? {
339+
guard let resetTime = allHeaderFields[HTTPURLResponse.rateLimitResetHeaderKey] as? String else {
340+
return nil
341+
}
342+
guard let resetTimeNumber = Double(resetTime) else {
343+
return nil
344+
}
345+
return Date(timeIntervalSince1970: resetTimeNumber)
346+
}
347+
348+
}

MapboxGeocoder/MBPlacemark.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,9 @@ open class Placemark: NSObject, NSSecureCoding {
161161
The scope offers a general indication of the size or importance of the feature represented by the placemark – in other words, how local the feature is.
162162
*/
163163
open var scope: PlacemarkScope {
164-
let components = identifier.characters.split(separator: ".")
165-
assert(components.count == 2)
166-
let scopeCharacters = identifier.characters.split(separator: ".").first!
167-
return PlacemarkScope(descriptions: [String(scopeCharacters)])
164+
let components = identifier.components(separatedBy: ".")
165+
assert(components.count > 0)
166+
return PlacemarkScope(descriptions: [components.prefix(2).joined(separator: ".")]) ?? PlacemarkScope(descriptions: [components.first!]) ?? []
168167
}
169168

170169
/**

MapboxGeocoder/MBPlacemarkScope.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ typedef NS_OPTIONS(NSUInteger, MBPlacemarkScope) {
2222
MBPlacemarkScopeNeighborhood = (1 << 7),
2323
/// A physical address, such as to a business or residence.
2424
MBPlacemarkScopeAddress = (1 << 8),
25-
/// A point of interest, such as a business or school.
26-
MBPlacemarkScopePointOfInterest = (1 << 9),
27-
/// Subset of regular points of interest. Landmarks are particularly notable or long-lived features like parks, museums, and places of worship.
28-
MBPlacemarkScopePointOfInterestLandmark = (1 << 10),
29-
25+
26+
/// A particularly notable or long-lived point of interest, such as a park, museum, or place of worship.
27+
MBPlacemarkScopeLandmark = (1 << 10),
28+
/// A point of interest, such as a business or store.
29+
MBPlacemarkScopePointOfInterest = MBPlacemarkScopeLandmark | (1 << 9),
30+
3031
/// All scopes.
3132
MBPlacemarkScopeAll = 0x0ffffUL,
3233
};

MapboxGeocoder/MBPlacemarkScope.swift

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ extension PlacemarkScope: CustomStringConvertible {
44
/**
55
Initializes a placemark scope bitmask corresponding to the given array of string representations of scopes.
66
*/
7-
public init(descriptions: [String]) {
7+
public init?(descriptions: [String]) {
88
var scope: PlacemarkScope = []
99
for description in descriptions {
1010
switch description {
@@ -24,48 +24,46 @@ extension PlacemarkScope: CustomStringConvertible {
2424
scope.update(with: .neighborhood)
2525
case "address":
2626
scope.update(with: .address)
27+
28+
case "poi.landmark":
29+
scope.update(with: .landmark)
2730
case "poi":
2831
scope.update(with: .pointOfInterest)
29-
case "poi.landmark":
30-
scope.update(with: .pointOfInterestLandmark)
3132
default:
32-
break
33+
return nil
3334
}
3435
}
3536
self.init(rawValue: scope.rawValue)
3637
}
3738

3839
public var description: String {
3940
var descriptions: [String] = []
40-
if contains(PlacemarkScope.country) {
41+
if contains(.country) {
4142
descriptions.append("country")
4243
}
43-
if contains(PlacemarkScope.region) {
44+
if contains(.region) {
4445
descriptions.append("region")
4546
}
46-
if contains(PlacemarkScope.district) {
47+
if contains(.district) {
4748
descriptions.append("district")
4849
}
49-
if contains(PlacemarkScope.postalCode) {
50+
if contains(.postalCode) {
5051
descriptions.append("postcode")
5152
}
52-
if contains(PlacemarkScope.place) {
53+
if contains(.place) {
5354
descriptions.append("place")
5455
}
55-
if contains(PlacemarkScope.locality) {
56+
if contains(.locality) {
5657
descriptions.append("locality")
5758
}
58-
if contains(PlacemarkScope.neighborhood) {
59+
if contains(.neighborhood) {
5960
descriptions.append("neighborhood")
6061
}
61-
if contains(PlacemarkScope.address) {
62+
if contains(.address) {
6263
descriptions.append("address")
6364
}
64-
if contains(PlacemarkScope.pointOfInterest) {
65-
descriptions.append("poi")
66-
}
67-
if contains(PlacemarkScope.pointOfInterestLandmark) {
68-
descriptions.append("poi.landmark")
65+
if contains(.landmark) {
66+
descriptions.append(contains(.pointOfInterest) ? "poi" : "poi.landmark")
6967
}
7068
return descriptions.joined(separator: ",")
7169
}

MapboxGeocoder/MBRectangularRegion.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ open class RectangularRegion: CLRegion {
4747
&& northEast.latitude == object.northEast.latitude && northEast.longitude == object.northEast.longitude)
4848
}
4949

50+
open override var description: String {
51+
return "\(southWest.longitude),\(southWest.latitude),\(northEast.longitude),\(northEast.latitude)"
52+
}
53+
5054
/**
5155
Returns a Boolean value indicating whether the bounding box contains the specified coordinate.
5256
*/

MapboxGeocoderTests/ForwardGeocodingTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class ForwardGeocodingTests: XCTestCase {
2323
var addressPlacemark: GeocodedPlacemark! = nil
2424
let options = ForwardGeocodeOptions(query: "1600 pennsylvania ave")
2525
options.allowedISOCountryCodes = ["CA"]
26+
options.allowedRegion = RectangularRegion(southWest: CLLocationCoordinate2D(latitude: -85, longitude: -179), northEast: CLLocationCoordinate2D(latitude: 85, longitude: 179))
2627
let task = geocoder.geocode(options) { (placemarks, attribution, error) in
2728
XCTAssertEqual(placemarks?.count, 4, "forward geocode should have 4 results")
2829
addressPlacemark = placemarks![0]
@@ -81,7 +82,6 @@ class ForwardGeocodingTests: XCTestCase {
8182
options.allowedISOCountryCodes = ["NC"]
8283
let task = geocoder.geocode(options) { (placemarks, attribution, error) in
8384
XCTAssertEqual(placemarks?.count, 0, "forward geocode should return no results for invalid query")
84-
8585
XCTAssertEqual(attribution, "NOTICE: © 2016 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained.")
8686

8787
expectation.fulfill()

0 commit comments

Comments
 (0)