diff --git a/.gitignore b/.gitignore index d16aced..cbb606b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ Carthage/Build # `pod install` in .travis.yml # Example/Pods/ + +# documentation build path +docs/ +*.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index c359d6b..8656dcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. All non released changes should be in CHANGELOG_UNRELEASED.md file --------- +## [0.2.0] - 2022-02-21 +### Added +* Added protocol OnNavigationListener and a method setOnNavigationListener on SitumMapsLibrary. As a developer you can +set a listener and implement the protocol methods to get notified of events during navigation (onNavigationRequested, +onNavigationError and onNavigationFinished). Each of the protocol methods will receive an object that complies to +protocol Navigation. +* Added protocol Navigation that holds information about the current status of the navigation and the navigation +destination. + ## [0.1.22] - 2022-02-21 # Added * Added method navigateToLocation(floor, lat, lng) on SitumMapLibrary to navigate to a location in the current building. diff --git a/CHANGELOG_UNRELEASED.md b/CHANGELOG_UNRELEASED.md index e69de29..139597f 100644 --- a/CHANGELOG_UNRELEASED.md +++ b/CHANGELOG_UNRELEASED.md @@ -0,0 +1,2 @@ + + diff --git a/Example/Podfile.lock b/Example/Podfile.lock index eead7db..364cafa 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -8,7 +8,7 @@ PODS: - SitumSDK (2.52.0): - Protobuf (~> 3.7) - SSZipArchive (~> 2.4) - - SitumWayfinding (0.1.22): + - SitumWayfinding (0.2.0): - GoogleMaps (~> 4.2.0) - SitumSDK (~> 2.52.0) - SSZipArchive (2.4.3) @@ -31,9 +31,9 @@ SPEC CHECKSUMS: GoogleMaps: eb03e327edfd70b06de1e6e321653f73712df7ad Protobuf: 235750e4696ff59fb07d949a9dbbc92b3c0700fe SitumSDK: cc0a5b48a90669c463622b12ae6f07588863f5af - SitumWayfinding: 9f56c9004b7136580ab154830c758416fb977919 + SitumWayfinding: 5a7a574f799580866b751b57522a224b04fa8ec6 SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef PODFILE CHECKSUM: 181eb894514efe3e0119ec84dae5349934aaaa20 -COCOAPODS: 1.11.2 +COCOAPODS: 1.10.2 diff --git a/Example/SitumWayfinding/ViewController.swift b/Example/SitumWayfinding/ViewController.swift index 2121ed0..6ff774a 100644 --- a/Example/SitumWayfinding/ViewController.swift +++ b/Example/SitumWayfinding/ViewController.swift @@ -90,7 +90,7 @@ class ViewController: UIViewController { action = .navigateToLocation(floor: floor, lat: lat, lng: lng) self.performSegue(withIdentifier: "loadWayfindingSegue", sender: self) } - + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "loadWayfindingSegue" { if let vc = segue.destination as? WayfindingController { diff --git a/Example/SitumWayfinding/WayfindingController.swift b/Example/SitumWayfinding/WayfindingController.swift index ca3efb3..7f3268b 100644 --- a/Example/SitumWayfinding/WayfindingController.swift +++ b/Example/SitumWayfinding/WayfindingController.swift @@ -12,7 +12,7 @@ import SitumWayfinding import SitumSDK import GoogleMaps -class WayfindingController: UIViewController, OnPoiSelectionListener, OnFloorChangeListener, OnMapReadyListener { +class WayfindingController: UIViewController { @IBOutlet var containerView: UIView! @@ -45,6 +45,7 @@ class WayfindingController: UIViewController, OnPoiSelectionListener, OnFloorCha self.library?.setOnPoiSelectionListener(listener: self) self.library?.setOnFloorChangeListener(listener: self) self.library?.setOnMapReadyListener(listener: self) + self.library?.setOnNavigationListener(listener: self) do { try self.library!.load() @@ -58,19 +59,26 @@ class WayfindingController: UIViewController, OnPoiSelectionListener, OnFloorCha override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } - - // MARK: Wayfinding Delegate +} + +// MARK: Wayfinding Delegates +extension WayfindingController: OnPoiSelectionListener { func onPoiDeselected(building: SITBuilding) { print("onPoiDeselected app") } - + func onPoiSelected(poi: SITPOI, level: SITFloor, building: SITBuilding) { print("onPoiSelected") } +} + +extension WayfindingController: OnFloorChangeListener { func onFloorChanged(from: SITFloor, to: SITFloor, building: SITBuilding) { print("onFloorChanged from \(from.floor) to \(to.floor)") } +} +extension WayfindingController: OnMapReadyListener { func onMapReady(map: SitumMap) { print("map ready to interact \(map)") @@ -102,8 +110,8 @@ class WayfindingController: UIViewController, OnPoiSelectionListener, OnFloorCha switch error { case .invalidPOI: print("POI: selection error, invalid POI \(error))") - case .unknown: - print("POI: unknown error \(error))") + default: + print("POI: wayfinding error \(error)") } } else { print("POI: generic error \(error))") @@ -111,4 +119,18 @@ class WayfindingController: UIViewController, OnPoiSelectionListener, OnFloorCha } } +extension WayfindingController: OnNavigationListener { + func onNavigationRequested(navigation: Navigation) { + print("Navigation: starts with destination \(navigation.destination)") + } + + func onNavigationError(navigation: Navigation, error: Error) { + print("Navigation: to \(navigation.destination) fails with error \(error)") + } + + func onNavigationFinished(navigation: Navigation) { + print("Navigation: finished with status \(navigation.status)") + } +} + diff --git a/SitumWayfinding.podspec b/SitumWayfinding.podspec index 31cd7c1..8c5f186 100644 --- a/SitumWayfinding.podspec +++ b/SitumWayfinding.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SitumWayfinding' - s.version = '0.1.22' + s.version = '0.2.0' s.summary = 'Indoor Location for iOS.' s.static_framework = true diff --git a/SitumWayfinding/Classes/Positioning/PositioningPresenter.swift b/SitumWayfinding/Classes/Positioning/PositioningPresenter.swift index 8dfef5b..3ecb537 100644 --- a/SitumWayfinding/Classes/Positioning/PositioningPresenter.swift +++ b/SitumWayfinding/Classes/Positioning/PositioningPresenter.swift @@ -10,13 +10,6 @@ import Foundation import SitumSDK import GoogleMaps -enum SITDirectionsRequestValidity{ - case SITValidDirectionsRequest - case SITNotOriginError - case SITOutdorOriginError - case SITNotDestinationError -} - class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate, SITNavigationDelegate { var view: PositioningView? @@ -83,12 +76,12 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate self.locationManagerUserLocation = nil self.lastOOBAlert = 0.0 self.lastCalibrationAlert = 0.0 - view?.stop() + view?.cleanLocationUI() + view?.stopNavigation(status: .canceled) } - - func stopNavigation() { + + func resetLastOutsideRouteAlert() { self.lastOutsideRouteAlert = 0.0 - view?.stopNavigation() } public func shouldShowFakeLocSelector() -> Bool { @@ -275,16 +268,15 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate } public func requestDirections(to position: SITPoint!) { - let directionsRequestValidity = checkDirectionsRequestValidity(origin: userLocation?.position ?? nil, destination: position) - if (directionsRequestValidity == .SITValidDirectionsRequest){ - var request: SITDirectionsRequest = RequestBuilder.buildDirectionsRequest(userLocation: userLocation!, destination: position) - request = self.interceptorsManager.onDirectionsRequest(request) - SITDirectionsManager.sharedInstance().delegate = self - SITDirectionsManager.sharedInstance().requestDirections(request) - }else{ - view?.stopNavigation() - self.alertUserOfInvalidDirectionsRequest(error: directionsRequestValidity) + if let navigationError = checkDirectionsRequestValidity(origin: userLocation?.position ?? nil, destination: position) { + view?.stopNavigation(status: .error(navigationError)) + return } + + var request: SITDirectionsRequest = RequestBuilder.buildDirectionsRequest(userLocation: userLocation!, destination: position) + request = self.interceptorsManager.onDirectionsRequest(request) + SITDirectionsManager.sharedInstance().delegate = self + SITDirectionsManager.sharedInstance().requestDirections(request) } func isUserIndoor() -> Bool{ @@ -298,47 +290,32 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate return SITNavigationManager.shared().isRunning() } - func checkDirectionsRequestValidity(origin: SITPoint!, destination: SITPoint!) -> SITDirectionsRequestValidity{ - let originValidity = checkDirectionsRequestOriginValidity(origin: origin) - let destinationValidity = self.checkDirectionsRequestDestinationValidity(destination: destination) - if (originValidity != .SITValidDirectionsRequest){ - return originValidity - } - return destinationValidity - } - - func alertUserOfInvalidDirectionsRequest(error: SITDirectionsRequestValidity){ - switch error { - case .SITNotOriginError: - view?.showAlertMessage(title: "Position unknown", message: "User actual location is unknown, please activate the positioning before computing a route and try again.", alertType: .otherAlert) - case .SITOutdorOriginError: - view?.showAlertMessage(title: "Position outdoor", message: "User actual location is outdoor, navegation is only avaialble indoor.", alertType: .otherAlert) - case .SITNotDestinationError: - view?.showAlertMessage(title: "No destination selected", message: "There is no destination currently selected, the navigation cannot be started. Please select a POI (or longpress to create a custom one) and try again.", alertType: .otherAlert) - case .SITValidDirectionsRequest: - //No need to do anything - break + func checkDirectionsRequestValidity(origin: SITPoint!, destination: SITPoint!) -> NavigationError? { + let originError = checkIfOriginIsValid(origin: origin) + if (originError != nil){ + return originError } + let destinationError = self.checkIfDestinationIsValid(destination: destination) + return destinationError } - func checkDirectionsRequestOriginValidity(origin:SITPoint!) -> SITDirectionsRequestValidity{ + func checkIfOriginIsValid(origin:SITPoint!) -> NavigationError? { if (origin == nil){ //Theoretically this shouldnt happen as positioning is started when a route is requested if it was stopped - return .SITNotOriginError; + return .positionUnknown } if (origin.isOutdoor()) { - return .SITOutdorOriginError; + return .outdoorOrigin } - return .SITValidDirectionsRequest - + return nil } - func checkDirectionsRequestDestinationValidity(destination: SITPoint!) -> SITDirectionsRequestValidity{ + func checkIfDestinationIsValid(destination: SITPoint!) -> NavigationError? { if (destination == nil){ - return .SITNotDestinationError + return .noDestinationSelected } - return .SITValidDirectionsRequest + return nil } func requestNavigation(route: SITRoute) { @@ -392,8 +369,8 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate func locationManager(_ locationManager: SITLocationInterface, didFailWithError error: Error?) { Logger.logErrorMessage("Location error problem: \(error.debugDescription)") - view?.stop() - view?.showAlertMessage(title: "Error", message: error!.localizedDescription, alertType: .otherAlert) + view?.cleanLocationUI() + view?.stopNavigation(status: .error(NavigationError.locationError(error))) } func locationManager(_ locationManager: SITLocationInterface, didUpdate state: SITLocationState) { @@ -415,7 +392,7 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate case .userNotInBuilding: stateName = "User not in building" if isUserNavigating(){ - view?.stopNavigation() + view?.stopNavigation(status: .error(NavigationError.outsideBuilding)) } showAlertIfNeeded(type: .outOfBuilding, title: self.oobAlertTitle, message: "The user is currently outside of the building. Positioning will resume when the user returns.") break; @@ -426,16 +403,14 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate //MARK: DirectionsDelegate methods func directionsManager(_ manager: SITDirectionsInterface, didFailProcessingRequest request: SITDirectionsRequest, withError error: Error?) { - view?.showAlertMessage(title: "Unable to compute route", message: "An unexpected error was found while computing the route. Please try again.", alertType: .otherAlert) Logger.logErrorMessage("directions request failed with error: \(error.debugDescription)"); - self.stopNavigation() + self.view?.stopNavigation(status: .error(NavigationError.unableToComputeRoute)) } func directionsManager(_ manager: SITDirectionsInterface, didProcessRequest request: SITDirectionsRequest, withResponse route: SITRoute) { if (route.routeSteps.count == 0) { - view?.showAlertMessage(title: "Unable to compute route", message: "There is no route between the selected locations. Try to compute a different route or to switch accessibility mode", alertType: .otherAlert) Logger.logDebugMessage("Unable to find a path for request: \(request.debugDescription)") - self.stopNavigation() + self.view?.stopNavigation(status: .error(NavigationError.noAvailableRoute)) } else { view?.showRoute(route: route) self.directionsRequest = request @@ -449,7 +424,7 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate func navigationManager(_ navigationManager: SITNavigationInterface, didFailWithError error: Error) { Logger.logErrorMessage("Navigation error: \(error)") - self.stopNavigation() + self.view?.stopNavigation(status: .error(error)) } func navigationManager(_ navigationManager: SITNavigationInterface, didUpdate progress: SITNavigationProgress, on route: SITRoute) { @@ -460,8 +435,7 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate func navigationManager(_ navigationManager: SITNavigationInterface, destinationReachedOn route: SITRoute) { Logger.logDebugMessage("Destination reached") - view?.showAlertMessage(title: "Destination Reached", message: "You've arrived to your destination", alertType: .otherAlert) - view?.stopNavigation() + self.view?.stopNavigation(status: .destinationReached) } func navigationManager(_ navigationManager: SITNavigationInterface, userOutsideRoute route: SITRoute) { @@ -470,8 +444,7 @@ class PositioningPresenter: NSObject, SITLocationDelegate, SITDirectionsDelegate if isUserIndoor(){ showAlertIfNeeded(type: .outsideRoute, title: self.outsideRouteAlertTitle, message: "The user is not currently detected on the route. Please go back to resume navigation.") }else{ - view?.stopNavigation() - alertUserOfInvalidDirectionsRequest(error: .SITOutdorOriginError) + view?.stopNavigation(status: .error(NavigationError.outsideBuilding)) } } diff --git a/SitumWayfinding/Classes/Positioning/PositioningViewController.swift b/SitumWayfinding/Classes/Positioning/PositioningViewController.swift index 498b0e5..a3b67a6 100644 --- a/SitumWayfinding/Classes/Positioning/PositioningViewController.swift +++ b/SitumWayfinding/Classes/Positioning/PositioningViewController.swift @@ -774,13 +774,23 @@ class PositioningViewController: UIViewController, GMSMapViewDelegate, UITableVi @IBAction func navigationButtonPressed(_ sender: Any) { Logger.logInfoMessage("Navigation Button Has Been pressed") - startNavigation() + startNavigationByUser() + } + + func startNavigationByUser() { + self.startNavigation() + if let category = getCategoryFromMarker(marker: self.lastSelectedMarker) { + let navigation = WYFNavigation(status: .requested, destination: WYFDestination(category: category)) + self.delegateNotifier?.navigationDelegate?.onNavigationRequested(navigation: navigation) + } } func startNavigation(to poi: SITPOI) { do { try self.select(poi: poi) { [weak self] in self?.startNavigation() + let navigation = WYFNavigation(status: .requested, destination: WYFDestination(category: .poi(poi))) + self?.delegateNotifier?.navigationDelegate?.onNavigationRequested(navigation: navigation) } } catch { Logger.logErrorMessage("poi \(poi) is not a valid poi in this building") @@ -793,8 +803,14 @@ class PositioningViewController: UIViewController, GMSMapViewDelegate, UITableVi } select(floor: indexPath) let gsmMarker = self.createMarker(withCoordinate: location, floorId: floor.identifier) - select(marker: SitumMarker(from: gsmMarker)) { [weak self] in + let marker = SitumMarker(from: gsmMarker) + select(marker: marker) { [weak self] in + guard let positioningVC = self else { return } self?.startNavigation() + let point = SITPoint(building: positioningVC.buildingInfo!.building, floorIdentifier: floor.identifier, + coordinate: location) + let navigation = WYFNavigation(status: .requested, destination: WYFDestination(category: .location(point))) + self?.delegateNotifier?.navigationDelegate?.onNavigationRequested(navigation: navigation) } } @@ -831,7 +847,7 @@ class PositioningViewController: UIViewController, GMSMapViewDelegate, UITableVi @IBAction func stopNavigatingButtonPressed(_ sender: UIButton) { - presenter?.stopNavigation() + stopNavigationByUser() } @IBAction @@ -880,7 +896,7 @@ class PositioningViewController: UIViewController, GMSMapViewDelegate, UITableVi func showAlertMessage(title: String, message: String, alertType:AlertType) { - let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { _ in self.presenter?.alertViewClosed(alertType) @@ -1054,17 +1070,21 @@ class PositioningViewController: UIViewController, GMSMapViewDelegate, UITableVi } //MARK: Stop methods - - func stop() { + + func stopNavigationByUser() { + stopNavigation(status: .canceled) + } + + func cleanLocationUI() { self.makeUserMarkerVisible(visible: false) self.numberBeaconsRangedView.isHidden = true self.reloadFloorPlansTableViewData() self.hideCenterButton() self.change(.stopped, centerCamera: false) - self.stopNavigation() } - func stopNavigation() { + func stopNavigation(status: NavigationStatus) { + presenter?.resetLastOutsideRouteAlert() SITNavigationManager.shared().removeUpdates() self.indicationsView.isHidden = true self.changeCancelNavigationButtonVisibility(isVisible: false) @@ -1076,12 +1096,63 @@ class PositioningViewController: UIViewController, GMSMapViewDelegate, UITableVi self.displayPois(onFloor: orderedFloors(buildingInfo: buildingInfo)?[self.selectedLevelIndex].identifier) self.removeLastCustomMarker() self.destinationMarker?.setMapView(mapView: nil) + self.notifyEndOfNavigation(status: status, marker: self.destinationMarker) self.destinationMarker = nil // hide selected point self.mapView.selectedMarker = nil } + private func notifyEndOfNavigation(status: NavigationStatus, marker: SitumMarker?) { + guard let category = self.getCategoryFromMarker(marker: self.destinationMarker) else { return } + let navigation = WYFNavigation(status: status, destination: WYFDestination(category: category)) + + if case .error(let error) = status { + self.delegateNotifier?.navigationDelegate?.onNavigationError(navigation: navigation, error: error) + if let error = error as? NavigationError { + switch error { + case .positionUnknown: + self.showAlertMessage(title: "Position unknown", message: error.localizedDescription, alertType: .otherAlert) + case .outdoorOrigin: + self.showAlertMessage(title: "Position outdoor", message: error.localizedDescription, alertType: .otherAlert) + case .noDestinationSelected: + self.showAlertMessage(title: "No destination selected", message: error.localizedDescription, alertType: .otherAlert) + case .unableToComputeRoute: + self.showAlertMessage(title: "Unable to compute route", message: error.localizedDescription, alertType: .otherAlert) + case .noAvailableRoute: + self.showAlertMessage(title: "Unable to compute route", message: error.localizedDescription, alertType: .otherAlert) + case .outsideBuilding: + self.showAlertMessage(title: "Unable to compute route", message: error.localizedDescription, alertType: .otherAlert) + case .locationError(let error): + let errorMessage = error?.localizedDescription ?? WayfindingError.unknown.localizedDescription + self.showAlertMessage(title: "Error", message: errorMessage, alertType: .otherAlert) + } + } + } else { + if case .destinationReached = status { + self.showAlertMessage(title: "Destination Reached", message: "You've arrived to your destination", alertType: .otherAlert) + } + self.delegateNotifier?.navigationDelegate?.onNavigationFinished(navigation: navigation) + } + } + + private func getCategoryFromMarker(marker: SitumMarker?) -> DestinationCategory? { + guard let marker = marker else { return nil } + + if marker.isPoiMarker() { + return .poi(marker.poi!) + } else { + let coordinate = CLLocationCoordinate2D( + latitude: marker.gmsMarker.position.latitude, + longitude: marker.gmsMarker.position.longitude + ) + let floorId = getFloorIdFromMarker(marker: marker) + let point = SITPoint(building: buildingInfo!.building, floorIdentifier: floorId, + coordinate: coordinate) + return .location(point) + } + } + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let identifier = segue.identifier { if identifier == "mapContainerSegueID"{ diff --git a/SitumWayfinding/Classes/Positioning/Protocols/PositioningViewProtocol.swift b/SitumWayfinding/Classes/Positioning/Protocols/PositioningViewProtocol.swift index 79e4d3e..c7882af 100644 --- a/SitumWayfinding/Classes/Positioning/Protocols/PositioningViewProtocol.swift +++ b/SitumWayfinding/Classes/Positioning/Protocols/PositioningViewProtocol.swift @@ -13,7 +13,7 @@ protocol PositioningView { func change(_ state: SITLocationState, centerCamera: Bool) - func stop() + func cleanLocationUI() func showNumberOfBeaconsRanged(text: Int) @@ -27,7 +27,7 @@ protocol PositioningView { func updateProgress(progress: SITNavigationProgress) - func stopNavigation() + func stopNavigation(status: NavigationStatus) func reloadFloorPlansTableViewData() diff --git a/SitumWayfinding/Classes/WayfindingLibrary/Internal/WYFNavigation.swift b/SitumWayfinding/Classes/WayfindingLibrary/Internal/WYFNavigation.swift new file mode 100644 index 0000000..366e97d --- /dev/null +++ b/SitumWayfinding/Classes/WayfindingLibrary/Internal/WYFNavigation.swift @@ -0,0 +1,37 @@ +// +// WYFNavigation.swift +// SitumWayfinding +// +// Created by Lapisoft on 18/2/22. +// + +import Foundation + +internal struct WYFNavigation: Navigation { + var status: NavigationStatus + var destination: Destination +} + +internal struct WYFDestination: Destination { + var category: DestinationCategory + var point: SITPoint { + switch category { + case .poi(let poi): + return poi.position() + case .location(let point): + return point + } + } + var identifier: String? { + guard case .poi(let poi) = category else { return nil } + return poi.identifier + } + var name: String? { + guard case .poi(let poi) = category else { return nil } + return poi.name + } + + init(category: DestinationCategory) { + self.category = category + } +} diff --git a/SitumWayfinding/Classes/WayfindingLibrary/Internal/WayfindingDelegatesNotifier.swift b/SitumWayfinding/Classes/WayfindingLibrary/Internal/WayfindingDelegatesNotifier.swift index 2282c23..e5ae188 100644 --- a/SitumWayfinding/Classes/WayfindingLibrary/Internal/WayfindingDelegatesNotifier.swift +++ b/SitumWayfinding/Classes/WayfindingLibrary/Internal/WayfindingDelegatesNotifier.swift @@ -10,6 +10,7 @@ class WayfindingDelegatesNotifier{ var poiSelectionDelegate: OnPoiSelectionListener? var floorChangeDelegate: OnFloorChangeListener? var mapReadyDelegate: OnMapReadyListener? + var navigationDelegate: OnNavigationListener? /** Method that notifies when a POI has been selected. There are several actions that can result on a POI being selected. diff --git a/SitumWayfinding/Classes/WayfindingLibrary/Protocols/OnNavigationListener.swift b/SitumWayfinding/Classes/WayfindingLibrary/Protocols/OnNavigationListener.swift new file mode 100644 index 0000000..bf1406c --- /dev/null +++ b/SitumWayfinding/Classes/WayfindingLibrary/Protocols/OnNavigationListener.swift @@ -0,0 +1,186 @@ +// OnNavigationListener.swift +// ios-app-wayfindingExample +// +// Created by Lapisoft on 09/02/2022. +// Copyright © 2019 Situm Technologies. All rights reserved. +// + + +import Foundation +import SitumSDK + +/** + Delegate that get notified about navigation events + */ +public protocol OnNavigationListener { + /** + Called when a navigation request was made either by user or by the library. Status of navigation object will be + requested + - Parameter navigation: navigation object + */ + func onNavigationRequested(navigation: Navigation) + /** + Called when navigation fails due an error. Status of navigation object will be error + - Parameter navigation: navigation object + - Parameter error: error that makes navigation fail + */ + func onNavigationError(navigation: Navigation, error: Error) + /** + Called when navigation finishes either by user cancelation or user reaching the destination. Status of navigation + object will be destinationReached or canceled + - Parameter navigation: navigation object + */ + func onNavigationFinished(navigation: Navigation) +} + + +/** + Object that contains information about navigation events + */ +public protocol Navigation { + /** + Current status of the ongoing navigation + */ + var status: NavigationStatus { get set } + /** + Destination of the current navigation + */ + var destination: Destination { get set } +} + +/** + This represent the destination of a navigation towards a POI or a location + */ +public protocol Destination { + /** + Either a POI or a location + */ + var category: DestinationCategory { get set } + /** + Point of the current destination + */ + var point: SITPoint { get } + /** + If navigation goes towards a POI this holds the identifier of the POI + */ + var identifier: String? { get } + /** + If navigation goes towards a POI this holds the name of the POI + */ + var name: String? { get } +} + +/** + Type of destination + */ +public enum DestinationCategory { + /** + Destination is a POI with a SITPOI inside + */ + case poi(SITPOI) + /** + Destination is a location with a SITPoint inside + */ + case location(SITPoint) +} + +/** + Current status of the navigation + */ +public enum NavigationStatus { + /** + Navigation was requested by user/developer + */ + case requested + /** + An error has occurred on the ongoing Navigation + */ + case error(Error) + /** + The destination was reached by user + */ + case destinationReached + /** + The ongoing navigation was cancelled by the user/developer + */ + case canceled +} + +/** + Error that navigation could raise + */ +public enum NavigationError: Error { + /** + Error raised when actual position of user is unknown + */ + case positionUnknown + /** + Error raised when actual location of user is outdoor, navigation is only available indoor + */ + case outdoorOrigin + /** + Error raised when user request navigation without select a valid destination + */ + case noDestinationSelected + /** + Error raised when SITUM could not calculate route to destination due an internal error + */ + case unableToComputeRoute + /** + Error raised when there is no route available between user position and destination + */ + case noAvailableRoute + /** + Error raised when a user goes outside the current route and is located out of building + */ + case outsideBuilding + /** + Error raised when a problem with location service happened. Contains the inner location error + */ + case locationError(Error?) +} + +extension NavigationError: LocalizedError { + /** + Description of error + */ + public var errorDescription: String? { + switch self { + case .positionUnknown: + return "User actual location is unknown, please activate the positioning before computing a route and try again." + case .outdoorOrigin: + return "User actual location is outdoor, navegation is only avaialble indoor." + case .noDestinationSelected: + return "There is no destination currently selected, the navigation cannot be started. Please select a POI (or longpress to create a custom one) and try again." + case .unableToComputeRoute: + return "An unexpected error was found while computing the route. Please try again." + case .noAvailableRoute: + return "There is no route between the selected locations. Try to compute a different route or to switch accessibility mode" + case .outsideBuilding: + return "The user is not currently detected on the route and is out of the building. Please go back to resume navigation." + case .locationError(let error): + return error?.localizedDescription + } + } + /** + Code of error + */ + public var _code: Int { + switch self { + case .positionUnknown: + return 10_101 + case .outdoorOrigin: + return 10_102 + case .noDestinationSelected: + return 10_103 + case .unableToComputeRoute: + return 10_104 + case .noAvailableRoute: + return 10_105 + case .outsideBuilding: + return 10_106 + case .locationError: + return 10_107 + } + } +} diff --git a/SitumWayfinding/Classes/WayfindingLibrary/SitumMap.swift b/SitumWayfinding/Classes/WayfindingLibrary/SitumMap.swift index 8e42222..8c3e101 100644 --- a/SitumWayfinding/Classes/WayfindingLibrary/SitumMap.swift +++ b/SitumWayfinding/Classes/WayfindingLibrary/SitumMap.swift @@ -69,6 +69,13 @@ public protocol SitumMap { */ func setOnMapReadyListener(listener: OnMapReadyListener?) + /** + Sets a delegate that get notified with events related to Navigation + + - parameter listener: OnNavigationChangeListener + */ + func setOnNavigationListener(listener: OnNavigationListener?) + /** Select a given poi. This method will perform the proper actions over the User Interface to make that Poi the selected one diff --git a/SitumWayfinding/Classes/WayfindingLibrary/SitumMapsLibrary.swift b/SitumWayfinding/Classes/WayfindingLibrary/SitumMapsLibrary.swift index a0ea7a6..b457e19 100644 --- a/SitumWayfinding/Classes/WayfindingLibrary/SitumMapsLibrary.swift +++ b/SitumWayfinding/Classes/WayfindingLibrary/SitumMapsLibrary.swift @@ -119,10 +119,10 @@ import GoogleMaps } /** - Stops Stum Navigation + Stops Situm Navigation */ @objc public func stopNavigation(){ - self.toPresentViewController?.presenter?.stopNavigation() + self.toPresentViewController?.stopNavigation(status: .canceled) } /** @@ -215,6 +215,15 @@ import GoogleMaps delegatesNotifier.mapReadyDelegate = listener } + /** + Sets a delegate that get notified with events related to Navigation + + - parameter listener: OnNavigationChangeListener + */ + public func setOnNavigationListener(listener: OnNavigationListener?) { + delegatesNotifier.navigationDelegate = listener + } + /** Start the navigation to a poi in the current building. This will: * Start the positioning if needed diff --git a/SitumWayfinding/Classes/WayfindingLibrary/WayfindingError.swift b/SitumWayfinding/Classes/WayfindingLibrary/WayfindingError.swift index ab29f96..b265313 100644 --- a/SitumWayfinding/Classes/WayfindingLibrary/WayfindingError.swift +++ b/SitumWayfinding/Classes/WayfindingLibrary/WayfindingError.swift @@ -7,7 +7,7 @@ import Foundation /** Errors WayfindinLibrary could raise */ -public enum WayfindingError: Error { +public enum WayfindingError: LocalizedError { /** Select a POI on selectPoi() inside SitumMapsLibrary could return and invalid POI error when this poi do no belong to the current building @@ -18,3 +18,28 @@ public enum WayfindingError: Error { */ case unknown } + +extension WayfindingError { + /** + Description of error + */ + public var errorDescription: String? { + switch self { + case .invalidPOI: + return "The poi selected is not valid in this building" + case .unknown: + return "An unknown error happened" + } + } + /** + Code of error + */ + public var _code: Int { + switch self { + case .invalidPOI: + return 10_001 + case .unknown: + return 10_002 + } + } +} diff --git a/scripts/framework.properties b/scripts/framework.properties index d9e3e22..a9a7a46 100644 --- a/scripts/framework.properties +++ b/scripts/framework.properties @@ -1 +1 @@ -frameworkVersion=0.1.22 +frameworkVersion=0.2.0