Erfassen Sie Ihre Fortschritte
Trailhead-Startseite
Trailhead-Startseite

Add Geolocation

One of the best things about the iPhone is its rich set of on-board sensors. As developers we can harness them to make the user experience better. In this step, we work with the iPhone's GPS sensor.

Your team would like to capture the GPS coordinates of the accident location. However, GPS coordinates aren’t the most user-friendly. So we ask iOS to reverse geocode the latitude and longitude into a street address we can recognize. Yes, you can do that.

Then we ask iOS to create a snapshot of the map image, resize it, and upload to Salesforce as an attachment to the claim. Along the way, we gain experience with Apple’s MapKit and MKMapSnapshotter, and the SDK’s methods for attaching files.

Reverse-Geolocation

Let’s start by looking at how we reverse geocode coordinates to a street address. To do this, we use the CoreLocation Geocoder service class ( CLGeocode). 

1. Open the redwoods-project project in Xcode if it isn’t already.

2. Open the NewClaimViewController+Map.swift file from the left-hand pane.

3. Find the function Geocode(_ location: CLLocation), you can easily do this by typing command+f and entering it in the search field.

The goal is to convert the location from coordinates to a street address. To get you started, the code is already doing a couple of things:

  • The iOS geolocation service can only geolocate one thing at a time. This is why you have the cancelGeocode method within the function. Just like it suggests, it cancels any geocoding that's happing at the moment.
  • We need to reverse geocode, because the standard goes from street address to coordinates. For the insurance app, we need it to do the opposite. This is why you have the reverseGeocodeLocation method in the next line. This part is asynchronous.

In this completion block, let’s validate the geocoding was successful, and create a street address from the results. 

4. We can validate the reverse geocoding with a guard block. Add this in a new line just under the geoCoder.reverseGeocodeLocation method.

guard let placemark = placemarks?.first else { return }
Note

Note

Guard blocks are a Swift construct that attempts a given let assignment. If it succeeds, the rest of the block has access to that variable. If that let assignment fails, the else block is called. 

In this case, we use guard to assign the variable placemark to the first element of the closure’s parameter placemarks. The placemark object has all the properties you need to construct the street address string, but they’re not named like Salesforce address fields. 

We capture the street address and translate these variables to integrate with Salesforce in the next section. 

Integrate Variables

To get the street address, you need to write several let statements that uses ??, the nil coalescing operator. ?? works by either unwrapping the optional on the left, or returning the value on the right. 

Here’s an example:

let number = placemark.subThoroughfare ?? ""

So, if placemark.subThoroughfare has a value, that is what number will be. If it's blank, or null, it will be empty string. Using the above line as a guide, build out the rest of the address’s components from placemark. 

Salesforce Metadata
Apple Geolocation API
number
subThoroughfare
street
thoroughfare
city
locality
state
administrativeArea
zip
postalCode
country
isoCountryCode

When you’re done creating the address lines your code should look like this:

func geocode(_ location: CLLocation) {
        // Only one reverse geocoding can be in progress at a time, hence we need
        // to cancel any existing ones if we are getting location updates.
           geoCoder.cancelGeocode()
           geoCoder.reverseGeocodeLocation(location) { placemarks, _ in
                guard let placemark = placemarks?.first else { return }
                self.geoCodedAddress = placemark
                let number = placemark.subThoroughfare ?? ""
                let street = placemark.thoroughfare ?? ""
                let city = placemark.locality ?? ""
                let state = placemark.administrativeArea ?? ""
                let zip = placemark.postalCode ?? ""
                let country = placemark.isoCountryCode ?? ""
                let address = number + " " + street + " " + city + " "
+ state + ". " + zip + " " + country
                self.addressLabel.text = address
                self.geoCodedAddressText = address
          }
  }

Remember to Save your work.

Note the last two lines of the method. We’ve provided them for you, and they do a couple of things. 

  • Set the UI’s address label (self.addressLabel.text) to the address string we created.
  • Set a controller variable (self.geoCodedAddressText) that’s used to when we upload the case. Now that the app can get the street address where the accident happened, let’s look at incorporating a snapshot of the map with the claim.

Generate and Upload the Location Snapshot

In the NewClaimViewController+Uploading.swift file find the method called uploadMapImage(forCaseId caseID: String). The first iteration of the app provides most of this method’s functionality—taking the GPS sensor data and uploading the resulting map image.

Check out the trailing closure for snapshotter.start. In this case, the trailing closure functions as a completion block. When we get to the end of our closure, we have a .png file ready to go. This is the file we want to upload.

1. Under let mapImage = UIGraphicsGetImageFromCurrentImageContext ()!, add the following to the end of your snapshotter.start’s completion block:

               let attachmentRequest = RestClient.shared.requestForCreatingImageAttachment(from: mapImage,relatingToCaseID: caseID, fileName: "MapSnapshot.png")

Your code should look like this:

let mapImage = UIGraphicsGetImageFromCurrentImageContext()!
               let attachmentRequest =
RestClient.shared.requestForCreatingImageAttachment(from: mapImage, relatingToCaseID: caseID, fileName: "MapSnapshot.png")
               UIGraphicsEndImageContext()

2. Once we’ve generated the request, it’s time to send it. To accomplish this, we use the RestClient.shared.send method. Put the following code after the marker //Insert your attachmentRequest here:

RestClient.shared.send(request: attachmentRequest, onFailure:
self.handleError) { _, _ in
                       SalesforceLogger.d(type(of: self), message:
"Completed uploading map image. Now uploading photos.")
                      self.uploadPhotos (forCaseID: caseID)
                    }

3. Notice we add the self.uploadPhotos closure at the end. We need to make sure this works within the asynchronous chain. Refactor the createCaseContacts method to call uploadMapImage like this (hint, adjust the closure at the end):

private func createCaseContacts(withContactIDs contactIDs:
[String], forCaseID caseID: String) {
          let associationRequest =
RestClient.shared.compositeRequestForCreatingAssociations(fromContactI
Ds: contactIDs, toCaseID: caseID)
           RestClient.shared.sendCompositeRequest(associationRequest,
onFailure: handleError) { _ in
                 SalesforceLogger.d(type(of: self), message: "Completed
creating \(contactIDs.count) case contact record(s). Optionally
uploading map image as attachment.")
                self.uploadMapImage(forCaseID: caseID)
           }
     }

Run the Mobile App Again

Let’s test the app’s new functionality.

  1. Click the Build and Run button Run icon to verify that all of your code is correct and execute your app.
  2. You should be taken directly to the Open Claims view. If not, open the app and navigate there.
  3. Click on the + button to add a new claim.
  4. Zoom in on a location. Notice that it now populates an address above the app if you center on a street. You did that! Incident Location section in the mobile app with an approximate address location above the map.
  5. Go ahead and click Submit to log the new claim. After it submits, you should be taken back to the Open Claims view, with the new claim at the top of the list.

You just added some heavy-duty functionality with reverse geolocation. Don’t forget to save your work! In the next step, you work closer with the Salesforce Mobile SDK for iOS. Time for dynamic updates, something that is sure to make your customers’s lives easier.