Quickstart
Basic Integration
Let's make a quick demo application to show off the main features of Naurt.
For this demo, we want to make an app that displays the user location on the screen. So, we need to make an object that is both
- A delegate of the
NaurtLocationManager
- An
ObservableObject
so changes to the object can update our Views
import SwiftUI;
import NaurtSDK; // Importing Naurt will give you access to all the classes and protocols defined later in this document
class LocationDelegate: ObservableObject, NaurtDelegate {
var naurt: NaurtLocationManager;
@Published public var latitude: Double? = nil;
@Published public var longitude: Double? = nil;
init() {
self.naurt = NaurtLocationManager(apiKey: "<YOUR KEY HERE>");
self.naurt.delegate = self; // We can set self as the delegate for Naurt since this object conforms to NaurtDelegate
self.naurt.requestWhenInUseAuthorization(); // It's important that we have location permissions!
}
}
Since our LocationDelegate
method conforms to NaurtDelegate
the NaurtLocationManager
will invoke the functions of the LocationDelegate
. However, this code will not compile
since we need to add some mandatory methods to LocationDelegate
, as defined by
NaurtDelegate
class LocationDelegate: ObservableObject, NaurtDelegate {
// ...
func didUpdateLocation(_ naurtPoint: NaurtLocation) {
// NaurtLocationManager will call this method to give us the latest location
DispatchQueue.main.async {
self.latitude = naurtPoint.latitude;
self.longitude = naurtPoint.longitude;
}
}
func onAppClose() {
self.naurt.onAppClose(); // Make sure you clean up the memory!
}
}
Now the NaurtLocationManager
is able to pass NaurtLocation
to us when the location
updates. We extract the latitude and longitude from this.
Now, we want to represent this on the screen, so we create a simple view
struct ContentView: View {
@ObservedObject var naurtDelegate = LocationDelegate();
var body: some View {
GeometryReader {geometry in
HStack {
let latitude = self.naurtDelegate.latitude;
let longitude = self.naurtDelegate.longitude;
if latitude == nil || longitude == nil {
Text("No position yet!");
} else {
Text("Latitude: \(latitude!) Longitude: \(longitude!)");
}
}
}
}
}
Although you could build this app with the simulator, it might never produce location fixes. To really see Naurt in action, try building this on a real iPhone and going for a walk outside!
Now, as NaurtLocationManager
produces new locations, the location will be updated
on the screen.
Checking the Validation Status
Let's try displaying more information on the screen. Since the NaurtSDK needs to be
unlocked with a key, there is some delay between the NaurtLocationManager
being
created and it being validated by the Naurt servers. When the NaurtLocationManager
is not validated, it will simple pass through the default location from the device.
In other words, you don't have to worry about Naurt not validating causing your
app to crash - you will still get locations.
The NaurtLocationManager
has two ways for you to check if it is valid. The first
(inferior) way is to use the getIsValidated
method. This isn't really reccomended
for general use outside of debugging.
Remember, that Naurt validation is asynchronous - so the following behaviour is expected
var naurt = NaurtLocationManager(apiKey: "<YOUR KEY HERE>");
print(naurt.getIsValidated()) // false
// wait some time...
print(naurt.getIsValidated()) // true
However, this isn't useful in the context of an ObservableObject
, so let's
instead use the delegate.
First, we'll add a new Published
property to our LocationDelegate
and also
add the optional didChangeValidated
method from the NaurtDelegate
protocol.
Now, when the validation status changes, the NaurtLocationManager
will call that
method
class LocationDelegate: ObservableObject, NaurtDelegate {
// ...
@Published public var validated = false;
func didChangeValidated(_ isValidated: Bool) {
DispatchQueue.main.async {
self.validated = isValidated;
}
}
}
And then we can display this on the screen by updating our ContentView
struct ContentView: View {
@ObservedObject var naurtDelegate = LocationDelegate();
var body: some View {
GeometryReader {geometry in
VStack {
HStack {
let isValid = self.naurtDelegate.validated;
Text("is Validated:")
if isValid {
Image(systemName:"checkmark.circle.fill").foregroundColor(.green);
}else{
Image(systemName:"xmark.circle.fill").foregroundColor(.red)
}
}
HStack {
let latitude = self.naurtDelegate.latitude;
let longitude = self.naurtDelegate.longitude;
if latitude == nil || longitude == nil {
Text("No position yet!");
} else {
Text("Latitude: \(latitude!) Longitude: \(longitude!)");
}
}
}
}
}
}
Now, we'll get a green check or a red cross telling us the status of the Naurt validation
Automatic POIs
Let's interact with the POI system now. The NaurtLocationManager
will automatically
make POIs for you when your users park, if some metadata is provided. You can then
later search for these POIs using that metadata via the POI API. The
SDK also contains a wrapper for this API.
In order to set metadata, you use the newDestination
method. You should add
metadata that describes the place where you expect your user to be parking.
For instance, if they are travelling to an address for a delivery, you could
add that address into the metadata.
Important!
ThenewDestination
method enables automatic POI creation
which can be retrieved later with the POI API.
It is crucial to maintain consistent
metadata with this function, allowing accurate POI creation and future searches.
Here's how it worksExample 1: On Demand Food Delivery
- Order Received: You’re running a food delivery app and receive an order for a restaurant. Call newDestination with the restaurant’s metadata.
- Driver Arrives at Restaurant: Upon parking, a parking Point Of Interest (POI) is created automatically.
- En Route to Final Destination: Call
newDestination
again, this time with the delivery destination’s metadata, once the driver has collected the food. - Delivery Completed: After the food is delivered, call
newDestination
withnil
as the metadata. This ensures that no unnecessary parking POIs are created with irrelevant metadata.
Example 2: Pre-Planned Mail Delivery
- Start of Workday: If you’re a mail delivery company with
pre-planned routes, call
newDestination
with the metadata of the first delivery address as the driver starts their workday. - Parcel Delivery: For each delivery, call newDestination with the next delivery address. This keeps the metadata up-to-date with the driver’s next parking location.
Summary
Make sure that the metadata provided to thenewDestination
method matches the state of the delivery and aligns with where drivers
are expected to park. This will allow Naurt to automatically create
POIs for you that can be searched later.
Let's say that you're a company with example 2 from the above pullout. We want a button on the screen the driver can press to switch to the next address. Let's add the button first
class LocationDelegate: ObservableObject, NaurtDelegate {
// ...
func newDestination(metadata: NSDictionary?) {
self.naurt.newDestination(metadata: metadata);
}
}
struct ContentView: View {
@ObservedObject var naurtDelegate = LocationDelegate();
var body: some View {
GeometryReader {geometry in
VStack {
HStack {
Text("is Validated:")
if naurtDelegate.isValidated{
Image(systemName:"checkmark.circle.fill").foregroundColor(.green);
}else{
Image(systemName:"xmark.circle.fill").foregroundColor(.red)
}
}
HStack {
let latitude = self.naurtDelegate.latitude;
let longitude = self.naurtDelegate.longitude;
if latitude == nil || longitude == nil {
Text("No position yet!");
} else {
Text("Latitude: \(latitude!) Longitude: \(longitude!)");
}
}
HStack {
Button("Next Address", action: nextAddress);
}
}
}
}
}
Now we need to create that nextAddress
method
class AddressManager {
private var addresses: [AddressJson] = [
AddressJson(unit: "flat 12", house_name: nil, street_number: "15", street: "high street", city: "london", county: "essesx", state: "england", country: "uk", postalcode: "sw1 43g"),
AddressJson(unit: "flat 6", house_name: nil, street_number: "4", street: "main street", city: "london", county: "essesx", state: "england", country: "uk", postalcode: "sw1 23a"),
AddressJson(unit: nil, house_name: nil, street_number: "24", street: "low street", city: "london", county: "essesx", state: "england", country: "uk", postalcode: "sw2 44h")
];
private var index = 0;
public func getNextAddress() -> AddressJson? {
if index > addresses.count - 1 {
return nil;
}
let tmpIndex = self.index;
self.index += 1;
return self.addresses[tmpIndex];
}
}
struct ContentView: View {
@ObservedObject var naurtDelegate = LocationDelegate();
var addressManager = AddressManager();
// ...
private func nextAddress() {
let next_address: AddressJson? = self.addressManager.getNextAddress();
let metadata: NSDictionary = ["some": "metadata"]; // optional
self.naurtDelegate.newDestination(addressJson: next_address, metadata: metadata);
}
}
In this example, we've hard coded the addresses. Obviously, in a real application these need to be able to change somehow! Perhaps they can come from a web request?
Finally, let's interact quickly with the POI API. This will only be a quick
overview, you can read a lot more about it from the POI system.
Manually interacting with the POI API requires the POIManager
class. Let's add
that to our LocationDelegate
since it's already handling our Naurt classes.
We'll also add a search method
class LocationDelegate: ObservableObject, NaurtDelegate {
// ...
var poiManager = POIManager(apiKey: "<YOUR KEY HERE>");
var currnetPois: [String: [PoiEntry]]? = nil;
func search(address: AddressJson) {
self.poiManager.getPoi(poiId: nil, latitude: nil, longitude: nil, poiTypes: ["*"], metadata: nil, distanceFilter: nil, address_json: address, address_string: nil, additional_results: nil) {pois, statusCode, error, errorString in
self.currentPois = pois;
}
}
}
In this example, we aren't going to do anything with the POIs except store them in this object. Maybe you could display them on a map?
We'll use this search
method in the nextAddress
method
struct ContentView: View {
@ObservedObject var naurtDelegate = LocationDelegate();
var addressManager = AddressManager();
// ...
private func nextAddress() {
let address: AddressJson? = self.addressManager.getNextAddress();
if address != nil {
self.naurtDelegate.search(address: address!);
}
self.naurtDelegate.newDestination(addressJson: address, metadata: nil);
}
}