

# Create an iOS app for Amazon Location Service
<a name="qs-ios"></a>

In this section, you will create an iOS application with the ability to search at a location and tracking in the foreground. First, you will create your Amazon Location resources, and an Amazon Cognito identity for your application.

**Topics**
+ [Create Amazon Location resources for your app](qs-create-resources-ios.md)
+ [Set up authentication for your Amazon Location application](qs-setup-authentication-ios.md)
+ [Create the base iOS application to use Amazon Location](qs-ios-create-app.md)
+ [Set up the initial code for Amazon Location](qs-ios-create-app-initial-code.md)
+ [Add an Amazon Location interactive map to your application](qs-ios-add-map.md)
+ [Add Amazon Location search to your application](qs-ios-add-search.md)
+ [Add Amazon Location tracking to your application](qs-ios-add-tracking.md)

# Create Amazon Location resources for your app
<a name="qs-create-resources-ios"></a>

If you do not already have them, you must create the Amazon Location resources that your application will use. You will create a map resource to display maps in your application, a place index to search for locations on the map, and a tracker to track an object across the map.

**To add location resources to your application**

1. Choose the map style that you want to use.

   1. In the Amazon Location console, on the [Maps](https://console.aws.amazon.com/location/maps/home) page, choose **Create map** to preview map styles.

   1. Add a **Name** and **Description** for the new map resource. Make a note of the name that you use for the map resource. You will need it when creating your script file later in the tutorial.

   1. Choose a map.
**Note**  
Choosing a map style also chooses which map data provider that you will use. If your application is tracking or routing assets that you use in your business, such as delivery vehicles or employees, you may only use HERE as your geolocation provider. For more information, see section 82 of the [AWS service terms](https://aws.amazon.com/service-terms). 

   1. Agree to the **Amazon Location Terms and Conditions**, then choose **Create map**. You can interact with the map that you've chosen: zoom in, zoom out, or pan in any direction.

   1. Make a note of the Amazon Resource Name (ARN) that is shown for your new map resource. You'll use it to create the correct authentication later in this tutorial.

1. Choose the place index that you want to use.

   1. In the Amazon Location console on the [https://console.aws.amazon.com/location/places/home](https://console.aws.amazon.com/location/places/home) page, choose **Create place index**.

   1. Add a **Name** and **Description** for the new place index resource. Make a note of the name that you use for the place index resource. You will need it when creating your script file later in the tutorial.

   1. Choose a data provider.
**Note**  
In most cases, choose the data provider that matches the map provider that you already chose. This helps to ensure that the searches will match the maps.  
If your application is tracking or routing assets that you use in your business, such as delivery vehicles or employees, you may only use HERE as your geolocation provider. For more information, see section 82 of the [AWS service terms](https://aws.amazon.com/service-terms). 

   1. Choose the **Data storage option**. For this tutorial, the results are not stored, so you can choose ** No, single use only**.

   1. Agree to the **Amazon Location Terms and Conditions**, then choose **Create place index**.

   1. Make a note of the ARN that is shown for your new place index resource. You'll use it to create the correct authentication in the next section of this tutorial.

1. To create a tracker using the Amazon Location console.

   1.  Open the [Amazon Location Service console](https://console.aws.amazon.com/location/).

   1.  In the left navigation pane, choose **Trackers**.

   1.  Choose **Create tracker**.

   1.  Fill in the all the required fields.

   1.  Under **Position filtering**, we recommend you use the default setting: **TimeBased**.

   1.  Choose **Create tracker** to finish.

# Set up authentication for your Amazon Location application
<a name="qs-setup-authentication-ios"></a>

The application that you create in this tutorial has anonymous usage, meaning that your users are not required to sign into AWS to use the application. However, the Amazon Location Service APIs require authentication to use. You will use Amazon Cognito to provide authentication and authorization for anonymous users. This tutorial will use Amazon Cognito to authenticate your application.

**Note**  
For more information about using Amazon Cognito with Amazon Location Service, see [Grant access to Amazon Location Service](how-to-access.md).

The following tutorials show you how to set up authentication for the map, the place index, and tracker you created in as well setting up permissions for Amazon Location.

**Create an IAM policy for tracking**

1. Sign in to the IAM console at https://console.aws.amazon.com/iam/ with your user that has administrator permissions. 

1. In the navigation pane, choose Policies. 

1. In the content pane, choose Create policy. 

1. Choose the **JSON** option, then copy and paste this JSON policy into the JSON text box.

   ```
   {
           "Version": "2012-10-17",		 	 	 
           "Statement": [
               {
                   "Effect": "Allow",
                   "Action": [
                       "geo:GetMapTile",
                       "geo:GetMapStyleDescriptor",
                       "geo:GetMapSprites",
                       "geo:GetMapGlyphs",
                       "geo:SearchPlaceIndexForPosition",
                       "geo:GetDevicePositionHistory",
                       "geo:BatchUpdateDevicePosition"
                   ],
                   "Resource": [
                       "arn:aws:geo:{Region}:{Account}:map/{MapName}",
                       "arn:aws:geo:{Region}:{Account}:place-index/{IndexName}",
                       "arn:aws:geo:{Region}:{Account}:tracker/{TrackerName}"
                   ]
               }
           ]
       }
   ```

   This is a policy example for Tracking. To use the example for your own policy, replace the `Region`, `Account`, `IndexName`, `MapName` and **TrackerName** placeholders.
**Note**  
While unauthenticated identity pools are intended for exposure on unsecured internet sites, note that they will be exchanged for standard, time-limited AWS credentials.  
It's important to scope the IAM roles associated with unauthenticated identity pools appropriately. For more information about using and appropriately scoping policies in Amazon Cognito with Amazon Location Service, see [Granting access to Amazon Location Service](location/previous/developerguide/how-to-access.html).

1. On the Review and Create page, provide a name for the policy name field. Review the permissions granted by your policy, and then choose Create Policy to save your work.

The new policy appears in the list of managed policies and is ready to attach.

**Set up authentication for your tracking**

1. Set up authentication for your map application in the [Amazon Cognito console](https://console.aws.amazon.com/cognito/home/).

1. Open the **Identity pools** page.
**Note**  
The pool that you create must be in the same AWS account and AWS Region as the Amazon Location Service resources that you created in the previous section.

1. Choose **Create Identity pool**.

1. Starting with the **Configure identity pool trust** step. For user access authentication, select **Guest access**, and press next.

1. On the **Configure permissions** page select the **Use an existing IAM role** and enter the name of the IAM role you created in the previous step. When ready press next to move on to the next step.

1. On the **Configure properties** page, provide a name for your identity pool. Then press **Next**.

1. On the **Review and create** page, review all the information present then press **Create identity pool**.

1. Open the **Identity pools** page, and select the identity pool you just created. Then copy or write down the IdentityPoolId that you will use later in your browser script.

# Create the base iOS application to use Amazon Location
<a name="qs-ios-create-app"></a>

In this tutorial, you will create an iOS application that embeds a map, and allows the user to find what's at a location on the map. 

First, let's create a Swift application using Xcode's project wizard.

**To create an empty application (Xcode)**

1. Open Xcode, and from the menu, choose **File**, **New**, **New Project**.

1. From the **iOS** tab, select **App**, and then choose **Next**.

1. Provide a **Product Name**, an **Organization Identifier**, and in the **Interface** field input `SwiftUI`. Choose **Next** to finalize the selection.

1. Select a location where you will save your project and press **create** button to create the empty application.

Once you have creating the base application, you will need to install the required packages for the sample app.

**Installing required dependencies**

1. In Xcode, right-click on the **project** and choose **Add Packages...**. This will open the Packages window, where you can add packages to your project.

1. In the Packages window, add the following packages:
   + For the Maplibre native package, use this URL: [https://github.com/maplibre/maplibre-gl-native-distribution](https://github.com/maplibre/maplibre-gl-native-distribution). From the URL, add these packages: `maplibre-gl-native-distribution`, and `Mapbox`.
   + For the Amazon Location authentication iOS SDK, use this URL: [https://github.com/aws-geospatial/amazon-location-mobile-auth-sdk-ios](https://github.com/aws-geospatial/amazon-location-mobile-auth-sdk-ios). From the URL, add these packages: `amazon-location-mobile-auth-sdk-ios`, and `AmazonLocationiOSAuthSDK`.
   + For the Amazon Location tracking iOS SDK, use this URL: [https://github.com/aws-geospatial/amazon-location-mobile-tracking-sdk-ios](https://github.com/aws-geospatial/amazon-location-mobile-tracking-sdk-ios). From the URL, add these packages: `amazon-location-mobile-tracking-sdk-ios`, and `AmazonLocationiOSTrackingSDK`.

# Set up the initial code for Amazon Location
<a name="qs-ios-create-app-initial-code"></a>

This page provides the initial code for a sample iOS application that integrates with the Amazon Location Service. With this code as a starting point, you can build location-aware iOS apps leveraging features like maps, geocoding, geofencing, tracking, and routing.

**Enable Location permissions in your app**

1. Open your Xcode project.

1. Locate the project's `Info.plist` file.

1. Add the necessary keys for location permissions based on your app's requirements. Here are the keys:
   + `NSLocationWhenInUseUsageDescription`: Description of why your app needs location access when it's in use.
   + `NSLocationAlwaysAndWhenInUseUsageDescription`: Description of why your app needs continuous location access.

Now you will need to configure resource values in your app. Add a new file named `Config.xcconfig` and fill out the values that you had created previously in the Amazon console.

```
REGION =
INDEX_NAME =
MAP_NAME =
IDENTITY_POOL_ID =
TRACKER_NAME =
```

1. From the left side navigator section, select the project.

1. Under the targets section, select your app and click on the info tab.

1. Add info properties with values like the below:

1. Add the `Config.swift` file with the contents below, which will read config values from the Bundle info file.

   ```
   import Foundation
   
   enum Config {
       static let region = Bundle.main.object(forInfoDictionaryKey: "Region") as! String
       static let mapName = Bundle.main.object(forInfoDictionaryKey: "MapName") as! String
       static let indexName = Bundle.main.object(forInfoDictionaryKey: "IndexName") as! String
       static let identityPoolId = Bundle.main.object(forInfoDictionaryKey: "IdentityPoolId") as! String
       static let trackerName = Bundle.main.object(forInfoDictionaryKey: "TrackerName") as! String
   }
   ```

1. Create a new folder with the name `ViewModel` and add a `TrackingViewModel.swift` file inside it.

   ```
   import SwiftUI
   import AmazonLocationiOSAuthSDK
   import MapLibre
   
   final class TrackingViewModel : ObservableObject {
       @Published var trackingButtonText = NSLocalizedString("StartTrackingLabel", comment: "")
       @Published var trackingButtonColor = Color.blue
       @Published var trackingButtonIcon = "play.circle"
       @Published var region : String
       @Published var mapName : String
       @Published var indexName : String
       @Published var identityPoolId : String
       @Published var trackerName : String
       @Published var showAlert = false
       @Published var alertTitle = ""
       @Published var alertMessage = ""
       @Published var centerLabel = ""
   
       var clientIntialised: Bool
       var client: LocationTracker!
       var authHelper: AuthHelper
       var credentialsProvider: LocationCredentialsProvider?
       var mlnMapView: MLNMapView?
       var mapViewDelegate: MapViewDelegate?
       var lastGetTrackingTime: Date?
       var trackingActive: Bool
       
       init(region: String, mapName: String, indexName: String, identityPoolId: String, trackerName: String) {
           self.region = region
           self.mapName = mapName
           self.indexName = indexName
           self.identityPoolId = identityPoolId
           self.trackerName = trackerName
           self.authHelper = AuthHelper()
           self.trackingActive = false
           self.clientIntialised = false
       }
       
       func authWithCognito(identityPoolId: String?) {
           guard let identityPoolId = identityPoolId?.trimmingCharacters(in: .whitespacesAndNewlines)
           else {
               alertTitle = NSLocalizedString("Error", comment: "")
               alertMessage = NSLocalizedString("NotAllFieldsAreConfigured", comment: "")
               showAlert = true
               return
           }
           credentialsProvider = authHelper.authenticateWithCognitoUserPool(identityPoolId: identityPoolId)
           initializeClient()
       }
       
       func initializeClient() {
           client = LocationTracker(provider: credentialsProvider!, trackerName: trackerName)
           clientIntialised = true
       }
   }
   ```

# Add an Amazon Location interactive map to your application
<a name="qs-ios-add-map"></a>

You will now add the map control to your application. This tutorial uses MapLibre and the AWS API for managing the map view in the application. The map control itself is part of the [MapLibre GL Native iOS](https://docs.maptiler.com/maplibre-gl-native-ios/) library.

1. Add `MapView.swift` file under the **Views** folder with the following code:

   ```
   import SwiftUI
   import MapLibre
   
   struct MapView: UIViewRepresentable {
       var onMapViewAvailable: ((MLNMapView) -> Void)?
       var mlnMapView: MLNMapView?
       var trackingViewModel: TrackingViewModel
       
       func makeCoordinator() -> MapView.Coordinator {
           return Coordinator(self, trackingViewModel: trackingViewModel)
       }
       
       func makeUIView(context: Context) -> MLNMapView {
           let styleURL = URL(string: "https://maps.geo.\(trackingViewModel.region).amazonaws.com/maps/v0/maps/\(trackingViewModel.mapName)/style-descriptor")
           let mapView = MLNMapView(frame: .zero, styleURL: styleURL)
           mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
           mapView.setZoomLevel(15, animated: true)
           mapView.showsUserLocation = true
           mapView.userTrackingMode = .follow
           context.coordinator.mlnMapView = mapView
           mapView.delegate = context.coordinator
   
           mapView.logoView.isHidden = true
           context.coordinator.addCenterMarker()
           
           onMapViewAvailable?(mapView)
           trackingViewModel.mlnMapView = mapView
           return mapView
       }
       
       func updateUIView(_ uiView: MLNMapView, context: Context) {
       }
       
       class Coordinator: NSObject, MLNMapViewDelegate, MapViewDelegate {
           var control: MapView
           var mlnMapView: MLNMapView?
           var trackingViewModel: TrackingViewModel
           var centerMarker: MLNPointAnnotation?
           
           public init(_ control: MapView, trackingViewModel: TrackingViewModel) {
               self.control = control
               self.trackingViewModel = trackingViewModel
               super.init()
               self.trackingViewModel.mapViewDelegate = self
           }
   
           func mapViewDidFinishRenderingMap(_ mapView: MLNMapView, fullyRendered: Bool) {
               if(fullyRendered) {
                   mapView.accessibilityIdentifier = "MapView"
                   mapView.isAccessibilityElement = false
               }
           }
           
           func addCenterMarker() {
               guard let mlnMapView = mlnMapView else {
                   return
               }
   
               let centerCoordinate = mlnMapView.centerCoordinate
               let marker = MLNPointAnnotation()
               marker.coordinate = centerCoordinate
               marker.accessibilityLabel = "CenterMarker"
               mlnMapView.addAnnotation(marker)
               centerMarker = marker
   
               trackingViewModel.reverseGeocodeCenter(centerCoordinate: centerCoordinate, marker: marker)
           }
           
           func mapView(_ mapView: MLNMapView, regionDidChangeAnimated animated: Bool) {
               if let marker = centerMarker {
                                 DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                       mapView.deselectAnnotation(marker, animated: false)
                       marker.coordinate = mapView.centerCoordinate
                       let centerCoordinate = mapView.centerCoordinate
                       self.trackingViewModel.reverseGeocodeCenter(centerCoordinate: centerCoordinate, marker: marker)
                   }
               }
           }
       }
   }
   ```

1. Add `AWSSignatureV4Delegate` file under the **ViewModel** folder. This file is used to sign with all the MapView http requests to render the map:

   ```
   import MapLibre
   import AmazonLocationiOSAuthSDK
    
   class AWSSignatureV4Delegate : NSObject, MLNOfflineStorageDelegate {
       private let awsSigner: AWSSigner
     
       init(credentialsProvider: LocationCredentialsProvider) {
           self.awsSigner = DENY LIST ERROR , serviceName: "geo")
           super.init()
       }
    
       func offlineStorage(_ storage: MLNOfflineStorage, urlForResourceOf kind: MLNResourceKind, with url: URL) -> URL {
           if url.host?.contains("amazonaws.com") != true {
               return url
           }
           let signedURL = awsSigner.signURL(url: url, expires: .hours(1))
           
           return signedURL
       }
   }
   ```

1. Add `UserLocationView.swift` file under **Views** folder. This adds a button which centers the map to the user's location

   ```
   import SwiftUI
   
   struct UserLocationView: View {
       @ObservedObject var trackingViewModel: TrackingViewModel
       var body: some View {
               Button(action: {
                   trackingViewModel.locateMe()
               }) {
                   Image(systemName: "scope")
                       .resizable()
                       .frame(width: 24, height: 24)
                       .padding(5)
                       .background(Color.white)
                       .foregroundColor(.blue)
                       .clipShape(RoundedRectangle(cornerRadius: 8))
                       .shadow(color: Color.black.opacity(0.3), radius: 3, x: 0, y: 2)
               }
               .accessibility(identifier: "LocateMeButton")
               .padding(.trailing, 10)
               .padding(.bottom, 10)
               .frame(maxWidth: .infinity, alignment: .trailing)
       }
   }
   ```

1. Add the `TrackingView.swift` file with the following code:

   ```
   import SwiftUI
   
   struct TrackingView: View {
       @ObservedObject var trackingViewModel: TrackingViewModel
       var body: some View {
           ZStack(alignment: .bottom) {
               MapView(trackingViewModel: trackingViewModel)
               VStack {
                   UserLocationView(trackingViewModel: trackingViewModel)
               }
           }
           .onAppear() {
               if !trackingViewModel.identityPoolId.isEmpty {
                   trackingViewModel.authWithCognito(identityPoolId: trackingViewModel.identityPoolId)
               }
           }
       }
   }
   ```

You can now build the application. To run it, you may have to set up a device to emulate it in Xcode or use the app on your device. Use this app to see how the map control behaves. You can pan by dragging on the map and pinch to zoom. On your own, you can change how the map control works to customize it to the needs of your application.

# Add Amazon Location search to your application
<a name="qs-ios-add-search"></a>

 You now will add reverse geocoding search to the application, where you find the items at a location. To simplify the use of an iOS app, we will search the center of the screen. To find a new location, move the map to where you want to search. We will place a marker at the center of the map to show where we are searching.

1. Add the following code in `TrackingViewModel.swift` file which is related to the reverse geocoding search

   ```
   func reverseGeocodeCenter(centerCoordinate: CLLocationCoordinate2D, marker: MLNPointAnnotation) {
       let position = [NSNumber(value: centerCoordinate.longitude), NSNumber(value: centerCoordinate.latitude)]
       searchPositionAPI(position: position, marker: marker)
   }
   
   func searchPositionAPI(position: [Double], marker: MLNPointAnnotation) {
       if let amazonClient = authHelper.getLocationClient() {
           Task {
               let searchRequest = SearchPlaceIndexForPositionInput(indexName: indexName, language: "en" , maxResults: 10, position: position)
               let searchResponse = try? await amazonClient.searchPosition(indexName: indexName, input: searchRequest)
               DispatchQueue.main.async {
                   self.centerLabel = searchResponse?.results?.first?.place?.label ?? ""
                   self.mlnMapView?.selectAnnotation(marker, animated: true, completionHandler: {})
               }
           }
       }
   }
   ```

1. Update `TrackingView.swift` file with the following code which will show the mapview's centered location's address

   ```
   import SwiftUI
   
   struct TrackingView: View {
       @ObservedObject var trackingViewModel: TrackingViewModel
       var body: some View {
           ZStack(alignment: .bottom) {
               if trackingViewModel.mapSigningIntialised {
                   MapView(trackingViewModel: trackingViewModel)
                   VStack {
                       UserLocationView(trackingViewModel: trackingViewModel)
                       CenterAddressView(trackingViewModel: trackingViewModel)
                   }
               }
               else {
                   Text("Loading...") 
               }
           }
           .onAppear() {
               if !trackingViewModel.identityPoolId.isEmpty {
                   Task {
                       do {
                           try await trackingViewModel.authWithCognito(identityPoolId: trackingViewModel.identityPoolId)
                       }
                       catch {
                           print(error)
                       }
                   }
               }
           }
       }
   }
   ```

# Add Amazon Location tracking to your application
<a name="qs-ios-add-tracking"></a>

The last step for your application is to add tracking functionality to your app. In this case, you will add start tracking, stop tracking, fetch and display tracker points on your app.

1. Add the `TrackingBottomView.swift` file in your project. Which has a button that starts and stops tracking user locations and shows tracking points on the map.

   ```
   import SwiftUI
   
   struct TrackingBottomView: View {
       @ObservedObject var trackingViewModel: TrackingViewModel
       var body: some View {
              Button(action: {
                  Task {
                      if(trackingViewModel.trackingButtonText == NSLocalizedString("StartTrackingLabel", comment: "")) {
                          trackingViewModel.startTracking()
                      } else {
                          trackingViewModel.stopTracking()
                      }
                  }
              }) {
                  HStack {
                      Spacer()
                      Text("Tracking")
                          .foregroundColor(trackingViewModel.trackingButtonColor)
                          .background(.white)
                          .cornerRadius(15.0)
                      
                      Image(systemName: trackingViewModel.trackingButtonIcon)
                          .resizable()
                          .frame(width: 24, height: 24)
                          .padding(5)
                          .background(.white)
                          .foregroundColor(trackingViewModel.trackingButtonColor)
   
                  }
              }
              .accessibility(identifier: "TrackingButton")
              .background(.white)
              .clipShape(RoundedRectangle(cornerRadius: 8))
              .padding(.trailing, 10)
              .padding(.bottom, 40)
              .frame(width: 130, alignment: .trailing)
              .shadow(color: Color.black.opacity(0.3), radius: 3, x: 0, y: 2)
          }
   }
   ```

1. Update `TrackingView.swift` file with the following code

   ```
   import SwiftUI
   
   struct TrackingView: View {
       @ObservedObject var trackingViewModel: TrackingViewModel
       var body: some View {
           ZStack(alignment: .bottom) {
               if trackingViewModel.mapSigningIntialised {
                   MapView(trackingViewModel: trackingViewModel)
                   VStack {
                       UserLocationView(trackingViewModel: trackingViewModel)
                       CenterAddressView(trackingViewModel: trackingViewModel)
                       TrackingBottomView(trackingViewModel: trackingViewModel)
                   }
               }
               else {
                   Text("Loading...") 
               }
           }
           .onAppear() {
               if !trackingViewModel.identityPoolId.isEmpty {
                   Task {
                       do {
                           try await trackingViewModel.authWithCognito(identityPoolId: trackingViewModel.identityPoolId)
                       }
                       catch {
                           print(error)
                       }
                   }
               }
           }
       }
   }
   ```

1. Add the following code in `TrackingViewModel.swift` file. These functions are responsible for start and stop tracking. It will also show an error alert if user location permission is denied.

1. To implement foreground tracking copy paste the following code example:

   ```
   func showLocationDeniedRationale() {
           alertTitle = NSLocalizedString("locationManagerAlertTitle", comment: "")
           alertMessage = NSLocalizedString("locationManagerAlertText", comment: "")
           showAlert = true
       }
       
       // Required in info.plist: Privacy - Location When In Use Usage Description
       func startTracking() {
           do {
               print("Tracking Started...")
               if(client == nil) {
                   initializeClient()
               }
               try client.startTracking()
               DispatchQueue.main.async { [self] in
                   self.trackingButtonText = NSLocalizedString("StopTrackingLabel", comment: "")
                   self.trackingButtonColor = .red
                   self.trackingButtonIcon = "pause.circle"
                   trackingActive = true
               }
           } catch TrackingLocationError.permissionDenied {
               showLocationDeniedRationale()
           } catch {
               print("error in tracking")
           }
       }
       
       func stopTracking() {
           print("Tracking Stopped...")
           client.stopTracking()
           trackingButtonText = NSLocalizedString("StartTrackingLabel", comment: "")
           trackingButtonColor = .blue
           trackingButtonIcon = "play.circle"
           trackingActive = false
       }
   ```
**Note**  
The `startTracking` will ask for the user's location permission. The application must use **When In Use** or **Only Once** permissions. Otherwise, the application will throw a permission denied error.

To get and display tracking locations, follow this procedure:

1. To get the locations from the user's device, you need to provide the start and end date and time. A single call returns a maximum of 100 tracking locations, but if there are more than 100 tracking locations, it will return a `nextToken` value. You will need to call subsequent `getTrackerDeviceLocation` calls with `nextToken` to load more tracking points for the given start and end time.

   ```
    func getTrackingPoints(nextToken: String? = nil) async throws {
           guard trackingActive else {
               return
           }
           // Initialize startTime to 24 hours ago from the current date and time.
           let startTime: Date = Date().addingTimeInterval(-86400)
           var endTime: Date = Date()
           if lastGetTrackingTime != nil {
               endTime = lastGetTrackingTime!
           }
           let result = try await client?.getTrackerDeviceLocation(nextToken: nextToken, startTime: startTime, endTime: endTime)
           if let trackingData = result {
               
               lastGetTrackingTime = Date()
               let devicePositions = trackingData.devicePositions
   
               let positions = devicePositions!.sorted { (pos1: LocationClientTypes.DevicePosition, pos2: LocationClientTypes.DevicePosition) -> Bool in
                   guard let date1 = pos1.sampleTime,
                         let date2 = pos2.sampleTime else {
                       return false
                   }
                   return date1 < date2
               }
   
               let trackingPoints = positions.compactMap { position -> CLLocationCoordinate2D? in
                   guard let latitude = position.position!.last, let longitude = position.position!.first else {
                       return nil
                   }
                   return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
               }
               DispatchQueue.main.async {
                   self.mapViewDelegate!.drawTrackingPoints( trackingPoints: trackingPoints)
               }
               if let nextToken = trackingData.nextToken {
                   try await getTrackingPoints(nextToken: nextToken)
               }
           }
       }
   ```

1. Now replace the code in the `MapView.swift` file with the following code:

   ```
   import SwiftUI
   import MapLibre
   
   struct MapView: UIViewRepresentable {
       var onMapViewAvailable: ((MLNMapView) -> Void)?
       var mlnMapView: MLNMapView?
       var trackingViewModel: TrackingViewModel
       
       func makeCoordinator() -> MapView.Coordinator {
           return Coordinator(self, trackingViewModel: trackingViewModel)
       }
       
       func makeUIView(context: Context) -> MLNMapView {
           let styleURL = URL(string: "https://maps.geo.\(trackingViewModel.region).amazonaws.com/maps/v0/maps/\(trackingViewModel.mapName)/style-descriptor")
           let mapView = MLNMapView(frame: .zero, styleURL: styleURL)
           mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
           mapView.setZoomLevel(15, animated: true)
           mapView.showsUserLocation = true
           mapView.userTrackingMode = .follow
           context.coordinator.mlnMapView = mapView
           mapView.delegate = context.coordinator
   
           mapView.logoView.isHidden = true
           context.coordinator.addCenterMarker()
           
           onMapViewAvailable?(mapView)
           trackingViewModel.mlnMapView = mapView
           return mapView
       }
       
       func updateUIView(_ uiView: MLNMapView, context: Context) {
       }
       
       class Coordinator: NSObject, MLNMapViewDelegate, MapViewDelegate {
           var control: MapView
           var mlnMapView: MLNMapView?
           var trackingViewModel: TrackingViewModel
           var centerMarker: MLNPointAnnotation?
           
           public init(_ control: MapView, trackingViewModel: TrackingViewModel) {
               self.control = control
               self.trackingViewModel = trackingViewModel
               super.init()
               self.trackingViewModel.mapViewDelegate = self
           }
   
           func mapViewDidFinishRenderingMap(_ mapView: MLNMapView, fullyRendered: Bool) {
               if(fullyRendered) {
                   mapView.accessibilityIdentifier = "MapView"
                   mapView.isAccessibilityElement = false
               }
           }
           
           func addCenterMarker() {
               guard let mlnMapView = mlnMapView else {
                   return
               }
   
               let centerCoordinate = mlnMapView.centerCoordinate
               let marker = MLNPointAnnotation()
               marker.coordinate = centerCoordinate
               marker.accessibilityLabel = "CenterMarker"
               mlnMapView.addAnnotation(marker)
               centerMarker = marker
   
               trackingViewModel.reverseGeocodeCenter(centerCoordinate: centerCoordinate, marker: marker)
           }
           
           func mapView(_ mapView: MLNMapView, regionDidChangeAnimated animated: Bool) {
               if let marker = centerMarker {
                   DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                       mapView.deselectAnnotation(marker, animated: false)
                       marker.coordinate = mapView.centerCoordinate
                       let centerCoordinate = mapView.centerCoordinate
                       self.trackingViewModel.reverseGeocodeCenter(centerCoordinate: centerCoordinate, marker: marker)
                   }
               }
           }
           
           func mapView(_ mapView: MLNMapView, viewFor annotation: MLNAnnotation) -> MLNAnnotationView? {
               guard let pointAnnotation = annotation as? MLNPointAnnotation else {
                   return nil
               }
   
               let reuseIdentifier: String
               var color: UIColor = .black
               if pointAnnotation.accessibilityLabel == "Tracking" {
                   reuseIdentifier = "TrackingAnnotation"
                   color = UIColor(red: 0.00784313725, green: 0.50588235294, blue: 0.58039215686, alpha: 1)
               } else if pointAnnotation.accessibilityLabel == "LocationChange" {
                   reuseIdentifier = "LocationChange"
                   color = .gray
               } else {
                   reuseIdentifier = "DefaultAnnotationView"
               }
   
               var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
   
               if annotationView == nil {
                   if reuseIdentifier != "DefaultAnnotationView" {
                       annotationView = MLNAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
                       //If point annotation is an uploaded Tracking point the radius is 20 and color is blue, otherwise radius is 10 and color is gray
                       let radius = pointAnnotation.accessibilityLabel == "Tracking" ? 20:10
                       annotationView?.frame = CGRect(x: 0, y: 0, width: radius, height: radius)
                       annotationView?.backgroundColor = color
                       annotationView?.layer.cornerRadius = 10
                       
                       if pointAnnotation.accessibilityLabel == "Tracking" {
                           annotationView?.layer.borderColor = UIColor.white.cgColor
                           annotationView?.layer.borderWidth = 2.0
                           annotationView?.layer.shadowColor = UIColor.black.cgColor
                           annotationView?.layer.shadowOffset = CGSize(width: 0, height: 2)
                           annotationView?.layer.shadowRadius = 3
                           annotationView?.layer.shadowOpacity = 0.2
                           annotationView?.clipsToBounds = false
                       }
                   }
                   else {
                       return nil
                   }
               }
   
               return annotationView
           }
           
           func mapView(_ mapView: MLNMapView, didUpdate userLocation: MLNUserLocation?) {
               if (userLocation?.location) != nil {
                   if trackingViewModel.trackingActive {
                       let point = MLNPointAnnotation()
                       point.coordinate = (userLocation?.location!.coordinate)!
                       point.accessibilityLabel = "LocationChange"
                       mapView.addAnnotation(point)
                       Task {
                           do {
                               try await trackingViewModel.getTrackingPoints()
                           }
                           catch {
                               print(error)
                           }
                       }
                   }
               }
           }
           
           func checkIfTrackingAnnotationExists(on mapView: MLNMapView, at coordinates: CLLocationCoordinate2D) -> Bool {
               let existingAnnotation = mapView.annotations?.first(where: { annotation in
                   guard let annotation = annotation as? MLNPointAnnotation else { return false }
                   return annotation.coordinate.latitude == coordinates.latitude &&
                   annotation.coordinate.longitude == coordinates.longitude && annotation.accessibilityLabel == "Tracking" })
               return existingAnnotation != nil
           }
           
           public func drawTrackingPoints(trackingPoints: [CLLocationCoordinate2D]?) {
               guard let mapView = mlnMapView, let newTrackingPoints = trackingPoints, !newTrackingPoints.isEmpty else {
                   return
               }
   
               let uniqueCoordinates = newTrackingPoints.filter { coordinate in
                   !checkIfTrackingAnnotationExists(on: mapView, at: coordinate)
               }
   
               let points = uniqueCoordinates.map { coordinate -> MLNPointAnnotation in
                   let point = MLNPointAnnotation()
                   point.coordinate = coordinate
                   point.accessibilityLabel = "Tracking"
                   return point
               }
               mapView.addAnnotations(points)
           }
       }
   }
   
   protocol MapViewDelegate: AnyObject {
       func drawTrackingPoints(trackingPoints: [CLLocationCoordinate2D]?)
   }
   ```

To localize string values , use the following procedure.

1. Create and add a new file called `Localizable.xcstrings`.

1. Right-click on the `Localizable.xcstrings` file and open it as **Source Code**.

1. Replace its content with the following:

   ```
   {
     "sourceLanguage" : "en",
     "strings" : {
       "Cancel" : {
         "extractionState" : "manual",
         "localizations" : {
           "en" : {
             "stringUnit" : {
               "state" : "translated",
               "value" : "Cancel"
             }
           }
         }
       },
       "Error" : {
         "extractionState" : "manual",
         "localizations" : {
           "en" : {
             "stringUnit" : {
               "state" : "translated",
               "value" : "Error"
             }
           }
         }
       },
       "Loading..." : {
   
       },
       "locationManagerAlertText" : {
         "extractionState" : "manual",
         "localizations" : {
           "en" : {
             "stringUnit" : {
               "state" : "translated",
               "value" : "Allow \\\"Quick Start App\\\" to use your location"
             }
           }
         }
       },
       "locationManagerAlertTitle" : {
         "extractionState" : "manual",
         "localizations" : {
           "en" : {
             "stringUnit" : {
               "state" : "translated",
               "value" : "We need your location to detect your location in map"
             }
           }
         }
       },
       "NotAllFieldsAreConfigured" : {
         "extractionState" : "manual",
         "localizations" : {
           "en" : {
             "stringUnit" : {
               "state" : "translated",
               "value" : "Not all the fields are configured"
             }
           }
         }
       },
       "OK" : {
         "extractionState" : "manual",
         "localizations" : {
           "en" : {
             "stringUnit" : {
               "state" : "translated",
               "value" : "OK"
             }
           }
         }
       },
       "StartTrackingLabel" : {
         "localizations" : {
           "en" : {
             "stringUnit" : {
               "state" : "translated",
               "value" : "Start Tracking"
             }
           }
         }
       },
       "StopTrackingLabel" : {
         "localizations" : {
           "en" : {
             "stringUnit" : {
               "state" : "translated",
               "value" : "Stop Tracking"
             }
           }
         }
       },
       "Tracking" : {
   
       }
     },
     "version" : "1.0"
   }
   ```

1. Save your files, and build and run your app to preview the functionality.

1. Allow the location permission and tap on the tracking button. The app will start uploading user locations and upload them to the Amazon Location tracker. It will also show user location changes, tracking points, and current address on the map.

Your quick-start application is complete. This tutorial has shown you how to create an iOS application that:
+ Creates a map that users can interact with.
+ Handles several map events associated with the user changing the map view.
+ Calls an Amazon Location Service API, specifically to search the map at a location, using Amazon Location's searchByPosition API.

## What's next
<a name="qs-ios-whats-next"></a>

You have completed the quick start tutorial, and should have an idea of how Amazon Location Service is used to build applications.

The source code for this application is available on [GitHub](https://github.com/aws-geospatial/amazon-location-samples-ios/tree/main/quick-start).

To get more out of Amazon Location, you can check out the following resources:
+ Dive deeper into the [concepts of Amazon Location Service](how-it-works.md)
+ Get more information about [how to use Amazon Location features and functionality](using-amazon-location.md)
+ See how to expand on this sample and build more complex applications by looking at [code examples using Amazon Location](samples.md)