Skip to content

Commit

Permalink
[SSDK-622] Add language parameter to offline tileset descriptor creat…
Browse files Browse the repository at this point in the history
…ion (#202)

### Description
Fixes [SSDK-622](https://mapbox.atlassian.net/browse/SSDK-622)

Add optional `language` parameter that has two modes:
1. Absent language parameter / `nil` value will use existing behavior of _just_ the dataset name.
2. A given language parameter will be appended to the dataset name.

### Checklist
- [x] Update `CHANGELOG`
- [x] Add a non-English language test after #199 is merged
  • Loading branch information
aokj4ck authored Apr 9, 2024
1 parent 2ada7a4 commit 2ed6263
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 12 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Guide: https://keepachangelog.com/en/1.0.0/

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

- [Offline] Add optional `language` parameter to SearchOfflineManager.createTilesetDescriptor and SearchOfflineManager.createPlacesTilesetDescriptor functions.
- [Tests] Add Spanish language offline search test.

- [Offline] Added OfflineIndexObserver which accepts two blocks for indexChanged or error events. This can be assigned to the offline search engine to receive state updates.

- [Offline] Change default tileset name to `mbx-main`
Expand Down
57 changes: 53 additions & 4 deletions Sources/MapboxSearch/InternalAPI/CoreSearchEngineStatics.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,60 @@
import Foundation

enum CoreSearchEngineStatics {
static func createTilesetDescriptor(dataset: String, version: String) -> MapboxCommon.TilesetDescriptor {
CoreSearchEngine.createTilesetDescriptor(forDataset: dataset, version: version)
enum Constants {
static let delimiter = "_"
}

static func createPlacesTilesetDescriptor(dataset: String, version: String) -> MapboxCommon.TilesetDescriptor {
CoreSearchEngine.createPlacesTilesetDescriptor(forDataset: dataset, version: version)
static func createTilesetDescriptor(dataset: String, version: String, language: String? = nil) -> MapboxCommon
.TilesetDescriptor {
let identifier: String
if let language {
if ISOLanguages.contains(language: language) {
identifier = dataset + Constants.delimiter + language
} else {
_Logger.searchSDK
.warning(
"Provided language code '\(language)' for tileset is non-ISO. Dataset '\(dataset)' without language will be used."
)
identifier = dataset
}
} else {
identifier = dataset
}
return CoreSearchEngine.createTilesetDescriptor(forDataset: identifier, version: version)
}

static func createPlacesTilesetDescriptor(dataset: String, version: String, language: String? = nil) -> MapboxCommon
.TilesetDescriptor {
let identifier: String
if let language {
if ISOLanguages.contains(language: language) {
identifier = dataset + Constants.delimiter + language
} else {
_Logger.searchSDK
.warning(
"Provided language code '\(language)' for places tileset is non-ISO. Dataset '\(dataset)' without language will be used."
)
identifier = dataset
}
} else {
identifier = dataset
}
return CoreSearchEngine.createPlacesTilesetDescriptor(forDataset: identifier, version: version)
}
}

enum ISOLanguages {
static func contains(language: String) -> Bool {
var validLanguage: Bool
if #available(iOS 16, *) {
validLanguage = Locale.LanguageCode.isoLanguageCodes
.map(\.identifier)
.contains(language)
} else {
validLanguage = Locale.isoLanguageCodes
.contains(language)
}
return validLanguage
}
}
37 changes: 32 additions & 5 deletions Sources/MapboxSearch/PublicAPI/Offline/SearchOfflineManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,53 @@ public class SearchOfflineManager {
engine.setTileStore(searchTileStore.commonTileStore, completion: completion)
}

/// Creates TilesetDescriptor for offline search index data with provided dataset name and version.
// MARK: - Tileset with name, version, and language parameters

/// Creates TilesetDescriptor for offline search index data with provided dataset name, version, and language.
/// Providing nil or excluding the language parameter will use the dataset name as-is.
/// Providing a language will append it to the name.
/// - Parameters:
/// - dataset: dataset name
/// - version: dataset version
/// - language: Provide a ISO 639-1 Code language from NSLocale. Values will be appended to the place dataset
/// name.
/// - Returns: TilesetDescriptor for TileStore
public static func createTilesetDescriptor(dataset: String, version: String? = nil) -> MapboxCommon
public static func createTilesetDescriptor(
dataset: String,
version: String? = nil,
language: String? = nil
) -> MapboxCommon
.TilesetDescriptor {
CoreSearchEngineStatics.createTilesetDescriptor(dataset: dataset, version: version ?? "")
CoreSearchEngineStatics.createTilesetDescriptor(
dataset: dataset,
version: version ?? "",
language: language
)
}

/// Creates TilesetDescriptor for offline search boundaries with provided dataset name and version.
/// Providing nil or excluding the language parameter will use the places dataset name as-is.
/// Providing a language will append it to the name.
/// - Parameters:
/// - dataset: dataset name
/// - version: dataset version
/// - language: Provide a ISO 639-1 Code language from NSLocale. Values will be appended to the dataset name.
/// - Returns: TilesetDescriptor for TileStore
public static func createPlacesTilesetDescriptor(dataset: String, version: String? = nil) -> MapboxCommon
public static func createPlacesTilesetDescriptor(
dataset: String,
version: String? = nil,
language: String? = nil
) -> MapboxCommon
.TilesetDescriptor {
CoreSearchEngineStatics.createPlacesTilesetDescriptor(dataset: dataset, version: version ?? "")
CoreSearchEngineStatics.createPlacesTilesetDescriptor(
dataset: dataset,
version: version ?? "",
language: language
)
}

// MARK: - Default tileset

/// Creates TilesetDescriptor for offline search index data using default dataset name.
/// - Returns: TilesetDescriptor for TileStore
public static func createDefaultTilesetDescriptor() -> MapboxCommon.TilesetDescriptor {
Expand Down
59 changes: 56 additions & 3 deletions Tests/MapboxSearchIntegrationTests/OfflineIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ class OfflineIntegrationTests: MockServerIntegrationTestCase<SBSMockResponse> {
wait(for: [setTileStoreExpectation], timeout: 10)
}

func loadData(completion: @escaping (Result<MapboxCommon.TileRegion, MapboxSearch.TileRegionError>) -> Void)
func loadData(
tilesetDescriptor: TilesetDescriptor? = nil,
completion: @escaping (Result<MapboxCommon.TileRegion, MapboxSearch.TileRegionError>) -> Void
)
-> SearchCancelable {
/// This will use the default dataset defined at ``SearchOfflineManager.defaultDatasetName``
let descriptor = SearchOfflineManager.createDefaultTilesetDescriptor()
/// A nil tilesetDescriptor parameter will fallback to the default dataset defined at
/// ``SearchOfflineManager.defaultDatasetName``
let descriptor = tilesetDescriptor ?? SearchOfflineManager.createDefaultTilesetDescriptor()

let dcLocationValue = NSValue(mkCoordinate: dcLocation)
let options = MapboxCommon.TileRegionLoadOptions.build(
geometry: Geometry(point: dcLocationValue),
Expand Down Expand Up @@ -104,6 +109,54 @@ class OfflineIntegrationTests: MockServerIntegrationTestCase<SBSMockResponse> {
XCTAssertFalse(searchEngine.suggestions.isEmpty)
}

func testSpanishLanguageSupport() throws {
clearData()

// Set up index observer before the fetch starts to validate changes after it completes
let indexChangedExpectation = expectation(description: "Received offline index changed event")
let offlineIndexObserver = OfflineIndexObserver(onIndexChangedBlock: { changeEvent in
_Logger.searchSDK.info("Index changed: \(changeEvent)")
indexChangedExpectation.fulfill()
}, onErrorBlock: { error in
_Logger.searchSDK.error("Encountered error in OfflineIndexObserver \(error)")
XCTFail(error.debugDescription)
})
searchEngine.offlineManager.engine.addOfflineIndexObserver(for: offlineIndexObserver)

// Perform the offline fetch
let spanishTileset = SearchOfflineManager.createTilesetDescriptor(
dataset: "mbx-main",
language: "es"
)
let loadDataExpectation = expectation(description: "Load Data")
_ = loadData(tilesetDescriptor: spanishTileset) { result in
switch result {
case .success(let region):
XCTAssert(region.id == self.regionId)
XCTAssert(region.completedResourceCount > 0)
XCTAssertEqual(region.requiredResourceCount, region.completedResourceCount)
case .failure(let error):
XCTFail("Unable to load Region, \(error.localizedDescription)")
}
loadDataExpectation.fulfill()
}
wait(
for: [loadDataExpectation, indexChangedExpectation],
timeout: 200,
enforceOrder: true
)

let offlineUpdateExpectation = delegate.offlineUpdateExpectation
searchEngine.search(query: "café")
wait(for: [offlineUpdateExpectation], timeout: 10)

XCTAssertNil(delegate.error)
XCTAssertNil(delegate.error?.localizedDescription)
XCTAssertNotNil(searchEngine.responseInfo)
XCTAssertFalse(delegate.resolvedResults.isEmpty)
XCTAssertFalse(searchEngine.suggestions.isEmpty)
}

func testNoData() {
clearData()

Expand Down

0 comments on commit 2ed6263

Please sign in to comment.