Getting Current Indoor Position¶
For use cases that require getting users location (e.g. mark your car), the following code snippets can be used. At first, you need to make sure that the Pointr instance is up and running. We can have a helper function that handles Pointr initialization and starting:
func startPointr(onComplete: @escaping (PointrState) -> Void) {
print("starting pointr")
if (Pointr.shared.state == .running) {
print("already running")
onComplete(Pointr.shared.state)
return
}
// check for terminal states
guard Pointr.shared.state != .failedInvalidDeepLinkUrl, Pointr.shared.state != .failedValidation, Pointr.shared.state != .failedRegistration, Pointr.shared.state != .failedRegistration, Pointr.shared.state != .failedNoInternet else {
print("failed to start")
onComplete(Pointr.shared.state)
return
}
if (Pointr.shared.state.rawValue > 0) {
print("start operation is in progress")
let listener = MyPointrStateChangeListener(onComplete: onComplete)
// means start operation is in progress
// add another listener to forward the result
Pointr.shared.addListener(listener)
return
}
let params = PTRParams()
params.loggerLevel = .all
params.mode = PointrDebugMode()
params.baseUrl = "https://sample-app-v8-api.pointr.cloud"
params.clientIdentifier = "72df7035-cdea-4c19-93b8-6deff5177894"
params.licenseKey = "c93f0101-36ed-4129-8f95-558f4cd8e82f"
print("pointr is off...")
// SDK only works when started from main thread for react-native, ionic and flutter
// but our function is not a blocking function
Pointr.shared.start(with: params) { state in
print("\(PTRPointrStateToString(state))")
if (state.rawValue > 0 && state != .running) { return }
onComplete(state)
}
}
class MyPointrStateChangeListener: NSObject, PointrStateChangeListener {
let onComplete: (PointrState) -> Void
init(onComplete: @escaping (PointrState) -> Void) {
self.onComplete = onComplete
}
func pointrStateDidChange(to state: PointrState) {
print("state: \(PTRPointrStateToString(state))")
// start in progress
if (state.rawValue > 0 && state.rawValue < PointrState.running.rawValue) { return }
Pointr.shared.removeListener(self)
onComplete(state)
}
}
fun startPointr(context: Context, onComplete: (Pointr.State) -> Unit) {
Pointr.getPointr()?.let { pointr ->
if (pointr.state == Pointr.State.RUNNING) {
// pointr is already running
onComplete(Pointr.State.RUNNING)
return
}
if (pointr.state.`val` < 0) {
// failed to start
onComplete(pointr.state)
return
}
if (pointr.state.`val` > 0) {
// means start operation is in progress
// add another listener to forward the result
pointr.addListener { state ->
if (state.`val` < Pointr.State.RUNNING.`val` && state.`val` > 0) {
return@addListener
}
Pointr.getPointr()?.removeListener(this)
onComplete(state)
}
return
}
}
val params = PTRParams(
"72df7035-cdea-4c19-93b8-6deff5177894",
"c93f0101-36ed-4129-8f95-558f4cd8e82f",
"https://sample-app-v8-api.pointr.cloud"
)
params.logLevel = Plog.LogLevel.VERBOSE
Pointr.with(context, params)
Pointr.getPointr()?.addListener { state ->
if (state.`val` < Pointr.State.RUNNING.`val` && state.`val` > 0) {
return@addListener
}
// remove and complete when a terminal state has been reached
Pointr.getPointr()?.removeListener(this)
onComplete(state)
}
Pointr.getPointr?.start()
}
This method initializes and starts the Pointr instance if it is not already started. It returns the state asynchronously. Callback function onComplete
must be waited upon before proceeding to next event.
A listener is needed for special case where Pointr is in a state that is between running and off such as a scenario where multiple calls to start the SDK could be done.
Listener here removes itself from Pointr instance once start operation ends with a terminal state.
At this point, if we have the Pointr instance running, we can wait for position updates by adding PositionManager
a listener
let dispatchGroup = DispatchGroup()
let positionManagerDelegate = MyPositionManagerDelegate(dispatchGroup: dispatchGroup)
dispatchGroup.enter()
Pointr.shared.positionManager?.addListener(positionManagerDelegate)
print("wait for position update")
let result = dispatchGroup.wait(wallTimeout: .now() + DispatchTimeInterval.seconds(60))
if result != .success {
print("unable to get location within defined interval")
Pointr.shared.positionManager?.removeListener(positionManagerDelegate)
}
return Pointr.shared.positionManager?.currentCalculatedLocation
MyPositionManagerDelegate
is a delegate that takes the DispatchGroup
instance in the constructor. class MyPositionManagerDelegate: NSObject, PTRPositionManagerDelegate {
let dispatchGroup: DispatchGroup
init(dispatchGroup: DispatchGroup) {
self.dispatchGroup = dispatchGroup
}
func onPositionManagerCalculatedLocation(_ calculatedLocation: PTRCalculatedLocation) {
print("position update: \(calculatedLocation.coordinate.latitude), \(calculatedLocation.coordinate.longitude)")
if (!calculatedLocation.isValid()) { return }
// remove it self as listener when done
Pointr.shared.positionManager?.removeListener(self)
dispatchGroup.leave()
}
}
It may take some time to fetch the site and content data if Pointr SDK is doing a fresh start. Because of this, a DispatchGroup
is used to wait for callback from PositionManagerDelegate
. You can define a timeout period, which is 60 seconds as in this example.
val semaphore = Semaphore(1)
semaphore.acquire()
Pointr.getPointr()?.positionManager?.addListener(object: PositionManager.Listener {
override fun onPositionManagerCalculatedLocation(p0: CalculatedLocation?) {
val calculatedLocation = p0 ?: return
if (!calculatedLocation.isValid()) return
Pointr.getPointr()?.positionManager?.removeListener(this)
semaphore.release()
}
override fun onPositionManagerDetectedPositionLevelChange(p0: Level) { }
override fun onPositionManagerPositionIsFading() { }
override fun onPositionManagerPositionIsLost() { }
override fun onPositionManagerPositioningServiceStateChangedTo(p0: PositioningTypes.PositioningServiceState?) { }
})
Plog.i("wait for position update")
val result = semaphore.tryAcquire(60, java.util.concurrent.TimeUnit.SECONDS)
if (!result) {
Plog.e("unable to get location within defined interval")
Pointr.getPointr()?.removeListener(this)
}
return Pointr.getPointr()?.positionManager?.currentCalculatedLocation
It may take some time to fetch the site and content data if Pointr SDK is doing a fresh start. Because of this, a Semaphore
is used to wait for callback from PositionManager.Listener
. You can define a timeout period, which is 60 seconds as in this example.
Based on business uses cases, a geographically valid location (valid latitude and longitude) can be enough. In this case, isGeoValid
function can be used to check the validity of the outdoor location. But in this example an indoor location is expected which can be verified with the isValid
check to have a valid level, building and site information.
You can serialize and save the location to storage if it is going to be utilized later
Note
App needs to have all the necessary permissions to be able to get a position. Checkout out permission page for details.