Skip to content

Commit

Permalink
[SSDK-667] Add offline demo view controller (#219)
Browse files Browse the repository at this point in the history
### Description
Fixes SSDK-667

- Add dedicated fifth tab to MapboxSearch.xcodeproj > Demo application for trying out offline search
- Remove support for `--offline` launch argument in Demo application

### Checklist
- [x] Update `CHANGELOG`
  • Loading branch information
aokj4ck authored Apr 26, 2024
1 parent 038508e commit 2d4a2a4
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 58 deletions.
7 changes: 0 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,6 @@ fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/

# End of https://www.gitignore.io/api/swift

.DS_Store
Expand All @@ -92,4 +86,3 @@ Products/
/output/
/node_modules
/muter_logs
Sources/Demo/offline
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 -->

- [Demo] Add OfflineDemoViewController to MapboxSearch.xcodeproj > Demo application.
- [Demo] Remove support for `--offline` launch argument.

- [SearchResult] Add support for `mapboxId` field when availalbe.
- [FavoriteRecord] Add support for `mapboxId` field when availalbe.
- [HistoryRecord] Add support for `mapboxId` field when availalbe.
Expand Down
12 changes: 12 additions & 0 deletions MapboxSearch.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
04C0848D2B4C82F3002F9C69 /* SdkInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C0848C2B4C82F3002F9C69 /* SdkInformation.swift */; };
04C127552B62F6BC00884325 /* ApiType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C127542B62F6BC00884325 /* ApiType.swift */; };
04C127582B62FFDB00884325 /* ApiType+Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04C127572B62FFDB00884325 /* ApiType+Core.swift */; };
04DAF7582BDAAB4C002719E2 /* OfflineDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DAF7572BDAAB4C002719E2 /* OfflineDemoViewController.swift */; };
04DFB40C2B8CF1ED00231830 /* search-box-suggestions-categories.json in Resources */ = {isa = PBXBuildFile; fileRef = 04DFB40B2B8CF1ED00231830 /* search-box-suggestions-categories.json */; };
04DFB40D2B8CF1ED00231830 /* search-box-suggestions-categories.json in Resources */ = {isa = PBXBuildFile; fileRef = 04DFB40B2B8CF1ED00231830 /* search-box-suggestions-categories.json */; };
04DFB40E2B8CF1ED00231830 /* search-box-suggestions-categories.json in Resources */ = {isa = PBXBuildFile; fileRef = 04DFB40B2B8CF1ED00231830 /* search-box-suggestions-categories.json */; };
Expand Down Expand Up @@ -551,6 +552,7 @@
04C0848C2B4C82F3002F9C69 /* SdkInformation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SdkInformation.swift; sourceTree = "<group>"; };
04C127542B62F6BC00884325 /* ApiType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiType.swift; sourceTree = "<group>"; };
04C127572B62FFDB00884325 /* ApiType+Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ApiType+Core.swift"; sourceTree = "<group>"; };
04DAF7572BDAAB4C002719E2 /* OfflineDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineDemoViewController.swift; sourceTree = "<group>"; };
04DFB40B2B8CF1ED00231830 /* search-box-suggestions-categories.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "search-box-suggestions-categories.json"; sourceTree = "<group>"; };
04DFB40F2B8CF46D00231830 /* search-box-retrieve-categories.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "search-box-retrieve-categories.json"; sourceTree = "<group>"; };
04DFB4132B8CFD4700231830 /* search-box-suggestions-san-francisco.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "search-box-suggestions-san-francisco.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1071,6 +1073,14 @@
path = Engine;
sourceTree = "<group>";
};
04DAF7562BDAAB38002719E2 /* Offline */ = {
isa = PBXGroup;
children = (
04DAF7572BDAAB4C002719E2 /* OfflineDemoViewController.swift */,
);
path = Offline;
sourceTree = "<group>";
};
04E5FF972B48829200DADC18 /* Region */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1933,6 +1943,7 @@
FEEDD3B32508E3CD00DC0A98 /* Demo */ = {
isa = PBXGroup;
children = (
04DAF7562BDAAB38002719E2 /* Offline */,
14F7186829A1335F00D5BC2E /* Place Autocomplete */,
14B92D5C298BFD04006003C1 /* Category */,
1440BF4B28FD7591009B3679 /* Address Autofill */,
Expand Down Expand Up @@ -2855,6 +2866,7 @@
buildActionMask = 2147483647;
files = (
1440BF4F290019AD009B3679 /* AddressAutofillResultViewController.swift in Sources */,
04DAF7582BDAAB4C002719E2 /* OfflineDemoViewController.swift in Sources */,
1440BF4D28FD75A9009B3679 /* AddressAutofillMainViewController.swift in Sources */,
041DAFD92BCDA45B0071F9EB /* DiscoverViewController.swift in Sources */,
FEEDD3C32508E3CD00DC0A98 /* AppDelegate.swift in Sources */,
Expand Down
28 changes: 28 additions & 0 deletions Sources/Demo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,34 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
ServiceProvider.shared.localHistoryProvider.deleteAll()
}

setUpProgrammaticUI(application: application)

return true
}

/// Set up the fifth tab 'Offline' programmatically.
private func setUpProgrammaticUI(application: UIApplication) {
let tabBarControllers = application.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap(\.windows)
.compactMap { window in
window.backgroundColor = UIColor.systemBackground
return window.rootViewController as? UITabBarController
}

let offlineDemoViewController = OfflineDemoViewController()
offlineDemoViewController.tabBarItem = UITabBarItem(
title: "Offline",
image: UIImage(systemName: "icloud.and.arrow.down"),
tag: 0
)

for tabBarController in tabBarControllers {
guard var viewControllers = tabBarController.viewControllers else {
break
}
viewControllers.append(offlineDemoViewController)
tabBarController.setViewControllers(viewControllers, animated: false)
}
}
}
51 changes: 0 additions & 51 deletions Sources/Demo/MapRootController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,13 @@ class MapRootController: UIViewController {
let panelController = MapboxPanelController(rootViewController: searchController)
addChild(panelController)

if ProcessInfo.processInfo.arguments.contains("--offline") {
enableOfflineSearch()
}

// Enabling jp/ja search options for testing Japanese Address Search.
// Setting Japanese into the list of preferred languages is a way to activate it.
if Locale.preferredLanguages.contains(where: { $0.contains("ja") }) {
searchController.searchOptions = SearchOptions(countries: ["jp"], languages: ["ja"])
}
}

func enableOfflineSearch() {
let engine = searchController.searchEngine

engine.setOfflineMode(.enabled) {
let descriptor = SearchOfflineManager.createDefaultTilesetDescriptor()

let dcLocation = NSValue(mkCoordinate: CLLocationCoordinate2D(
latitude: 38.89992081005698,
longitude: -77.03399849939174
))

guard let options = MapboxCommon.TileRegionLoadOptions.build(
geometry: Geometry(point: dcLocation),
descriptors: [descriptor],
acceptExpired: true
) else {
assertionFailure()
return
}

_ = engine.offlineManager.tileStore.loadTileRegion(id: "dc", options: options, progress: nil) { result in
switch result {
case .success(let region):
assert(region.id == "dc")
case .failure(let error):
print(error.localizedDescription)
assertionFailure()
}
}
}
}

let locationManager = CLLocationManager()

override func viewDidAppear(_ animated: Bool) {
Expand Down Expand Up @@ -114,18 +78,3 @@ extension MapRootController: SearchControllerDelegate {
showAnnotation([annotation], isPOI: true)
}
}

extension Style {
static let clown = Style(
primaryTextColor: .white,
primaryBackgroundColor: .red,
secondaryBackgroundColor: .systemBlue,
separatorColor: .systemBlue,
primaryAccentColor: .orange,
primaryInactiveElementColor: .yellow,
panelShadowColor: .green,
panelHandlerColor: .black,
iconTintColor: .cyan,
activeSegmentTitleColor: .black
)
}
145 changes: 145 additions & 0 deletions Sources/Demo/Offline/OfflineDemoViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import CoreLocation
import MapboxSearch
import MapboxSearchUI
import MapKit
import UIKit

/// Demonstrate how to use Offline Search in the Demo app
class OfflineDemoViewController: UIViewController {
private var mapView = MKMapView()
private var messageLabel = UILabel()
private lazy var searchController = MapboxSearchController()

override func viewDidLoad() {
super.viewDidLoad()

setUpLayout()

searchController.delegate = self
let panelController = MapboxPanelController(rootViewController: searchController)
addChild(panelController)

enableOfflineSearch()

// Enabling jp/ja search options for testing Japanese Address Search.
// Setting Japanese into the list of preferred languages is a way to activate it.
if Locale.preferredLanguages.contains(where: { $0.contains("ja") }) {
searchController.searchOptions = SearchOptions(countries: ["jp"], languages: ["ja"])
}
}

func enableOfflineSearch() {
let engine = searchController.searchEngine

engine.setOfflineMode(.enabled) {
let descriptor = SearchOfflineManager.createDefaultTilesetDescriptor()

let dcLocation = NSValue(mkCoordinate: CLLocationCoordinate2D(
latitude: 38.89992081005698,
longitude: -77.03399849939174
))

guard let options = MapboxCommon.TileRegionLoadOptions.build(
geometry: Geometry(point: dcLocation),
descriptors: [descriptor],
acceptExpired: true
) else {
assertionFailure()
return
}

_ = engine.offlineManager.tileStore.loadTileRegion(id: "dc", options: options, progress: nil) { result in
switch result {
case .success(let region):
assert(region.id == "dc")
case .failure(let error):
print(error.localizedDescription)
assertionFailure()
}
}
}
}

let locationManager = CLLocationManager()

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

locationManager.requestWhenInUseAuthorization()
}

private func setUpLayout() {
// Set up the Map and programmatic layout
for subview in [messageLabel, mapView] {
view.addSubview(subview)
subview.translatesAutoresizingMaskIntoConstraints = false
}

NSLayoutConstraint.activate([
messageLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
messageLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
messageLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),

mapView.topAnchor.constraint(equalTo: messageLabel.bottomAnchor),
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mapView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])

// Set up the help message in the navigation bar
let message =
"Offline search is available as a premium feature.\nContact Mapbox sales team for more information."

messageLabel.textAlignment = .center
messageLabel.numberOfLines = 0
messageLabel.text = message
}

func showAnnotation(_ annotations: [MKAnnotation], isPOI: Bool) {
mapView.removeAnnotations(mapView.annotations)

guard !annotations.isEmpty else { return }
mapView.addAnnotations(annotations)

if annotations.count == 1, let annotation = annotations.first {
let delta = isPOI ? 0.005 : 0.5
let span = MKCoordinateSpan(latitudeDelta: delta, longitudeDelta: delta)
let region = MKCoordinateRegion(center: annotation.coordinate, span: span)
mapView.setRegion(region, animated: true)
} else {
mapView.showAnnotations(annotations, animated: true)
}
}
}

extension OfflineDemoViewController: SearchControllerDelegate {
func categorySearchResultsReceived(category: SearchCategory, results: [SearchResult]) {
let annotations = results.map { searchResult -> MKPointAnnotation in
let annotation = MKPointAnnotation()
annotation.coordinate = searchResult.coordinate
annotation.title = searchResult.name
annotation.subtitle = searchResult.address?.formattedAddress(style: .medium)
return annotation
}

showAnnotation(annotations, isPOI: false)
}

func searchResultSelected(_ searchResult: SearchResult) {
let annotation = MKPointAnnotation()
annotation.coordinate = searchResult.coordinate
annotation.title = searchResult.name
annotation.subtitle = searchResult.address?.formattedAddress(style: .medium)

showAnnotation([annotation], isPOI: searchResult.type == .POI)
}

func userFavoriteSelected(_ userFavorite: FavoriteRecord) {
let annotation = MKPointAnnotation()
annotation.coordinate = userFavorite.coordinate
annotation.title = userFavorite.name
annotation.subtitle = userFavorite.address?.formattedAddress(style: .medium)

showAnnotation([annotation], isPOI: true)
}
}

0 comments on commit 2d4a2a4

Please sign in to comment.