Skip to content

Commit

Permalink
[SSDK-399] Add Discover.Options(country, proximity, origin) (#167)
Browse files Browse the repository at this point in the history
### Description

**Ticket**: [SSDK-399](https://mapbox.atlassian.net/browse/SSDK-399) 

- Add Country.default: Self? to auto-detect the current device region and provide a value if found
- Add proximity and origin CLLocationCoordinate2D fields to Discover.Option and forward these to SearchOption parameters
- This fixes an issue when using search-along-route to query category results by providing a country, proximity, and origin parameters to Discover.Options.

### Checklist
- [x] Update `CHANGELOG`

[SSDK-399]: https://mapbox.atlassian.net/browse/SSDK-399
  • Loading branch information
aokj4ck authored Feb 15, 2024
1 parent 70ca06e commit 70b8a50
Show file tree
Hide file tree
Showing 9 changed files with 814 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Guide: https://keepachangelog.com/en/1.0.0/

<!-- Add changes for active work here -->

- [Discover] Add support for country, proximity, and origin parameters in Discover.Options search parameters. This fixes an issue when using search-along-route to query category results.

- [SearchUI] Add `distanceFormatter` field to Configuration to support changing the search suggestions distance format. Nil values will use the default behavior.

- [Core] Add xcprivacy for MapboxSearch and MapboxSearchUI
Expand Down
12 changes: 12 additions & 0 deletions MapboxSearch.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
042477C62B72CCB000D870D5 /* geocoding-reverse-geocoding.json in Resources */ = {isa = PBXBuildFile; fileRef = 042477C42B72CCB000D870D5 /* geocoding-reverse-geocoding.json */; };
042477C72B72CCB000D870D5 /* geocoding-reverse-geocoding.json in Resources */ = {isa = PBXBuildFile; fileRef = 042477C42B72CCB000D870D5 /* geocoding-reverse-geocoding.json */; };
043A3D4D2B30F38300DB681B /* CoreAddress+AddressComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043A3D4C2B30F38300DB681B /* CoreAddress+AddressComponents.swift */; };
048823482B6B0A9D00C770AA /* category-hotel-search-along-route-jp.json in Resources */ = {isa = PBXBuildFile; fileRef = 04AB0B7C2B6B043C00FDE7D5 /* category-hotel-search-along-route-jp.json */; };
048823492B6B0A9D00C770AA /* category-hotel-search-along-route-jp.json in Resources */ = {isa = PBXBuildFile; fileRef = 04AB0B7C2B6B043C00FDE7D5 /* category-hotel-search-along-route-jp.json */; };
0488234A2B6B0A9E00C770AA /* category-hotel-search-along-route-jp.json in Resources */ = {isa = PBXBuildFile; fileRef = 04AB0B7C2B6B043C00FDE7D5 /* category-hotel-search-along-route-jp.json */; };
04AB0B7B2B6AF43E00FDE7D5 /* DiscoverIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AB0B792B6AF37800FDE7D5 /* DiscoverIntegrationTests.swift */; };
04970F8D2B7A97C900213763 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 04970F8C2B7A97C900213763 /* PrivacyInfo.xcprivacy */; };
04AB0B4B2B6AADB700FDE7D5 /* mapbox.places.san.francisco.json in Resources */ = {isa = PBXBuildFile; fileRef = 04AB0B4A2B6AADB700FDE7D5 /* mapbox.places.san.francisco.json */; };
04AB0B4C2B6AADB700FDE7D5 /* mapbox.places.san.francisco.json in Resources */ = {isa = PBXBuildFile; fileRef = 04AB0B4A2B6AADB700FDE7D5 /* mapbox.places.san.francisco.json */; };
Expand Down Expand Up @@ -494,6 +498,8 @@
042477C12B7290E700D870D5 /* SearchEngineGeocodingIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchEngineGeocodingIntegrationTests.swift; sourceTree = "<group>"; };
042477C42B72CCB000D870D5 /* geocoding-reverse-geocoding.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "geocoding-reverse-geocoding.json"; sourceTree = "<group>"; };
043A3D4C2B30F38300DB681B /* CoreAddress+AddressComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreAddress+AddressComponents.swift"; sourceTree = "<group>"; };
04AB0B792B6AF37800FDE7D5 /* DiscoverIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoverIntegrationTests.swift; sourceTree = "<group>"; };
04AB0B7C2B6B043C00FDE7D5 /* category-hotel-search-along-route-jp.json */ = {isa = PBXFileReference; explicitFileType = text.json; path = "category-hotel-search-along-route-jp.json"; sourceTree = "<group>"; };
04970F8C2B7A97C900213763 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
04AB0B4A2B6AADB700FDE7D5 /* mapbox.places.san.francisco.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = mapbox.places.san.francisco.json; sourceTree = "<group>"; };
04BBC6332B61898F00E24E99 /* LocalhostMockServiceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalhostMockServiceProvider.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1400,6 +1406,7 @@
2CE1B9FA2A13D412005B043F /* address-retrieve-san-francisco.json */,
2CE1B9F92A13D412005B043F /* address-suggestions-san-francisco.json */,
2CD8AC5129F28D6900C47BE4 /* retrieve-poi.json */,
04AB0B7C2B6B043C00FDE7D5 /* category-hotel-search-along-route-jp.json */,
2CD8AC4029F1D38800C47BE4 /* suggestions-category-with-coordinates.json */,
2CD8AC4129F1D38800C47BE4 /* suggestions-with-coordinates.json */,
2CD8AC4229F1D38800C47BE4 /* suggestions-with-mixed-coordinates.json */,
Expand Down Expand Up @@ -1521,6 +1528,7 @@
F9ACA6162642C18200F50CD4 /* SearchEngineIntegrationTests.swift */,
042477C12B7290E700D870D5 /* SearchEngineGeocodingIntegrationTests.swift */,
F99190422645ABE6009927A6 /* CategorySearchEngineIntegrationTests.swift */,
04AB0B792B6AF37800FDE7D5 /* DiscoverIntegrationTests.swift */,
F9C5572C2670C88E00BE8B94 /* Info.plist */,
);
path = MapboxSearchIntegrationTests;
Expand Down Expand Up @@ -2104,6 +2112,7 @@
042477C62B72CCB000D870D5 /* geocoding-reverse-geocoding.json in Resources */,
2CD8AC4429F1D38800C47BE4 /* suggestions-category-with-coordinates.json in Resources */,
F9B62CCB264BCC2600492999 /* suggestions-empty.json in Resources */,
048823492B6B0A9D00C770AA /* category-hotel-search-along-route-jp.json in Resources */,
04AB0B4C2B6AADB700FDE7D5 /* mapbox.places.san.francisco.json in Resources */,
F9ACA6182642C48C00F50CD4 /* recursion.json in Resources */,
F9B62CC8264BC61600492999 /* category-cafe.json in Resources */,
Expand Down Expand Up @@ -2134,6 +2143,7 @@
F907440F261B00000091899C /* suggestions-san-francisco.json in Resources */,
F94FFA4826453D410019ED9B /* reverse-geocoding-sbs.json in Resources */,
2CD8AC4B29F1D38800C47BE4 /* suggestions-with-mixed-coordinates.json in Resources */,
0488234A2B6B0A9E00C770AA /* category-hotel-search-along-route-jp.json in Resources */,
2CD8AC5429F28D6900C47BE4 /* retrieve-poi.json in Resources */,
042477C72B72CCB000D870D5 /* geocoding-reverse-geocoding.json in Resources */,
F9074425261B0DF70091899C /* retrieve-san-francisco.json in Resources */,
Expand All @@ -2154,6 +2164,7 @@
042477C52B72CCB000D870D5 /* geocoding-reverse-geocoding.json in Resources */,
2CD8AC4329F1D38800C47BE4 /* suggestions-category-with-coordinates.json in Resources */,
F9C557A52670CB0400BE8B94 /* suggestions-empty.json in Resources */,
048823482B6B0A9D00C770AA /* category-hotel-search-along-route-jp.json in Resources */,
04AB0B4B2B6AADB700FDE7D5 /* mapbox.places.san.francisco.json in Resources */,
F9C557A62670CB0400BE8B94 /* recursion.json in Resources */,
F9C557A72670CB0400BE8B94 /* category-cafe.json in Resources */,
Expand Down Expand Up @@ -2670,6 +2681,7 @@
F9C557C12670CD8C00BE8B94 /* CoreSearchOptions+Samples.swift in Sources */,
1420F31C29A2800300D4A511 /* CoreSearchResultStub+Samples.swift in Sources */,
F9C557B72670CC5600BE8B94 /* CoreSearchResultStub.swift in Sources */,
04AB0B7B2B6AF43E00FDE7D5 /* DiscoverIntegrationTests.swift in Sources */,
F9C557BF2670CD4300BE8B94 /* CoreRequestOptions+Samples.swift in Sources */,
F9C557BB2670CCC000BE8B94 /* SearchResultType+Extensions.swift in Sources */,
F9C557BA2670CCAB00BE8B94 /* TestDataProviderRecord.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
public struct Country: Equatable {
public let countryCode: String

/// Country model initializier
/// Country model initializer
/// - Parameter countryCode: Permitted values are ISO 3166-1 alpha 2 country codes (e.g. US, DE, GB)
public init?(countryCode: String) {
guard ISO3166_1_alpha2(rawValue: countryCode.uppercased()) != nil else {
Expand All @@ -12,4 +12,23 @@ public struct Country: Equatable {

self.countryCode = countryCode.lowercased()
}

init(code: ISO3166_1_alpha2) {
self.countryCode = code.rawValue.lowercased()
}

/// Detect the system region ISO3166\_1\_Alpha2 identifier and return an instance for it
static var `default`: Self? {
if #available(iOS 16, *) {
return Country(countryCode: Locale.current.region?.identifier ?? "")
} else {
let regionComponents = Locale.current.identifier.components(separatedBy: "_")
if regionComponents.count >= 2 {
let countryIdentifier = regionComponents[1]
return Country(countryCode: countryIdentifier)
} else {
return nil
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ extension Discover {
userActivityReporter.reportActivity(forComponent: "discover-search-nearby")

let searchOptions = SearchOptions(
countries: [options.country?.countryCode].compactMap { $0 },
languages: [options.language.languageCode],
limit: options.limit,
proximity: proximity
proximity: proximity,
origin: options.origin
)

search(for: query, with: searchOptions, completion: completion)
Expand All @@ -85,10 +87,12 @@ extension Discover {
userActivityReporter.reportActivity(forComponent: "discover-search-in-area")

let searchOptions = SearchOptions(
countries: [options.country?.countryCode].compactMap { $0 },
languages: [options.language.languageCode],
limit: options.limit,
proximity: proximity,
boundingBox: region
proximity: proximity ?? options.proximity,
boundingBox: region,
origin: options.origin
)

search(for: query, with: searchOptions, completion: completion)
Expand All @@ -110,8 +114,11 @@ extension Discover {
userActivityReporter.reportActivity(forComponent: "discover-search-along-the-route")

let searchOptions = SearchOptions(
countries: [options.country?.countryCode].compactMap { $0 },
languages: [options.language.languageCode],
limit: options.limit,
proximity: options.proximity,
origin: options.origin,
routeOptions: route
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,31 @@ extension Discover {
/// English (en) language parameter, but Frankreich (“France”) with a German (de) language parameter.
public let language: Language

/// See ``MapboxSearch.Country.ISO3166_1_alpha2`` for the list of ISO 3166 alpha 2 country codes.
/// The default value is nil.
public let country: Country?

/// Bias the response to favor results that are closer to a specific location.
/// When both proximity and origin are provided, origin is interpreted as the target of a route, while proximity
/// indicates the current user location.
public let proximity: CLLocationCoordinate2D?

/// The location from which to calculate distance. When both proximity and origin are provided, origin is
/// interpreted as the target of a route, while proximity indicates the current user location.
public let origin: CLLocationCoordinate2D?

public init(
limit: Int = 10,
language: Language? = nil
language: Language? = nil,
country: Country? = nil,
proximity: CLLocationCoordinate2D? = nil,
origin: CLLocationCoordinate2D? = nil
) {
self.limit = limit
self.language = language ?? .default
self.country = country
self.proximity = proximity
self.origin = origin
}
}
}
46 changes: 46 additions & 0 deletions Tests/MapboxSearchIntegrationTests/DiscoverIntegrationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import CoreLocation
@testable import MapboxSearch
import XCTest

class DiscoverIntegrationTests: MockServerIntegrationTestCase {
lazy var searchEngine = Discover(locationProvider: DefaultLocationProvider())

func testCategorySearchAlongRouteWithCountryProximityOrigin() throws {
try server.setResponse(.categoryHotelSearchAlongRoute_JP)
let expectation = XCTestExpectation(description: "Expecting results")

let coordinate1 = CLLocationCoordinate2D(latitude: 35.655614, longitude: 139.7081684)
let coordinate2 = CLLocationCoordinate2D(latitude: 35.6881616, longitude: 139.6994339)
let coordinates = [coordinate1, coordinate2]

let mapboxSearchRoute = MapboxSearch.Route(coordinates: coordinates)
let rOptions: MapboxSearch.RouteOptions = RouteOptions(route: mapboxSearchRoute, time: 1000)

let discoverOptions = Discover.Options(
limit: 10,
language: nil,
country: Country(countryCode: "jp"),
proximity: CLLocationCoordinate2D(
latitude: 35.6634363,
longitude: 139.7394536
),
origin: CLLocationCoordinate2D(latitude: 35.66580, longitude: 139.74609)
)

searchEngine.search(
for: Discover.Query.Category.canonicalName("hotel"),
route: rOptions,
options: discoverOptions
) { result in
switch result {
case .success(let searchResults):
XCTAssertFalse(searchResults.isEmpty)
expectation.fulfill()
case .failure:
XCTFail("Error not expected")
}
expectation.fulfill()
}
wait(for: [expectation], timeout: 10)
}
}
14 changes: 14 additions & 0 deletions Tests/MapboxSearchUITests/MockWebServer/MockResponse.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import Foundation

enum MockResponse {
enum Endpoint: String {
case suggest
case retrieve
case reverse
case multiRetrieve = "retrieve/multi"
case categoryCafe = "cafe"
case categoryHotel = "hotel"
case addressSuggest = "autofill/suggest"
case addressRetrieve = "autofill/retrieve"
}

case suggestEmpty
case suggestMinsk
case suggestSanFrancisco
Expand All @@ -22,6 +33,7 @@ enum MockResponse {
case reverseGeocoding
case reverseGeocodingSBS
case categoryCafe
case categoryHotelSearchAlongRoute_JP

var filepath: String {
let bundle = Bundle(for: MockWebServer.self)
Expand Down Expand Up @@ -60,6 +72,8 @@ enum MockResponse {
return bundle.path(forResource: "retrieve-multi", ofType: "json")!
case .categoryCafe:
return bundle.path(forResource: "category-cafe", ofType: "json")!
case .categoryHotelSearchAlongRoute_JP:
return bundle.path(forResource: "category-hotel-search-along-route-jp", ofType: "json")!
case .suggestAddressSanFrancisco:
return bundle.path(forResource: "address-suggestions-san-francisco", ofType: "json")!
case .retrieveAddressSanFrancisco:
Expand Down
5 changes: 3 additions & 2 deletions Tests/MapboxSearchUITests/MockWebServer/MockWebServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ extension MockWebServer {
.recursion,
.reverseGeocoding,
.reverseGeocodingSBS,
.categoryCafe:
.categoryCafe,
.categoryHotelSearchAlongRoute_JP:
return .get

case .multiRetrieve,
Expand Down Expand Up @@ -127,7 +128,7 @@ extension MockWebServer {
case .multiRetrieve:
path += "/retrieve/multi"

case .categoryCafe:
case .categoryCafe, .categoryHotelSearchAlongRoute_JP:
path += "/category/:category"
}

Expand Down
Loading

0 comments on commit 70b8a50

Please sign in to comment.