Tworzenie prostej aplikacji do nawigacji na iOS w języku Swift za pomocą pakietu Google Maps Platform Navigation SDK

1. Zanim zaczniesz

Z tego ćwiczenia w Codelabs dowiesz się, jak utworzyć prostą aplikację na iOS, która korzysta z Navigation SDK w Google Maps Platform, aby nawigować do wstępnie skonfigurowanego miejsca docelowego.

Tak będzie wyglądać Twoja aplikacja po zakończeniu pracy.

7e7c194a98d6dfa4.png

Wymagania wstępne

Czego się nauczysz

  • Jak utworzyć prostą aplikację na iOS w języku Swift, która korzysta z pakietu Navigation SDK do nawigowania do miejsca docelowego.
  • Jak zintegrować Navigation SDK ze zdalnego repozytorium Cocoapods.
  • Jak zarządzać uprawnieniami do lokalizacji i umową użytkownika z Warunkami dotyczącymi użytkowników końcowych pakietu Navigation SDK.
  • Jak zainicjować pakiet SDK.
  • Jak ustawić cel podróży i rozpocząć nawigację.

Czego potrzebujesz

2. Konfiguracja

Jeśli nie masz jeszcze konta Google Cloud Platform i projektu w chmurze z włączonymi płatnościami, skonfiguruj projekt Google Cloud, postępując zgodnie z instrukcjami dotyczącymi pierwszych kroków z Google Maps Platform.

Wybieranie projektu Google Cloud w konsoli

W konsoli Google Cloud kliknij menu projektu i wybierz projekt, którego chcesz użyć w tym module.

Menu wyboru projektu w konsoli Google Cloud.

Włączanie Navigation SDK w projekcie

Włącz interfejsy API i pakiety SDK Google Maps Platform wymagane w tym samouczku w Google Cloud Marketplace.

W konsoli Google Cloud otwórz Interfejsy API i usługi > Biblioteka i wyszukaj „Navigation SDK”.

Powinien wyświetlić się 1 wynik wyszukiwania.

Ekran Biblioteka interfejsów API w konsoli Google Cloud z wyświetloną stroną pakietu Navigation SDK.

Kliknij Navigation SDK, aby otworzyć stronę z informacjami o produkcie. Kliknij Włącz, aby włączyć pakiet SDK w projekcie.

Powtórz ten proces w przypadku Google Maps SDK na iOS.

Utwórz klucz interfejsu API

Wygeneruj klucz interfejsu API na stronie Dane logowania w konsoli Cloud. Wszystkie żądania wysyłane do Google Maps Platform wymagają klucza interfejsu API. Na stronie Dane logowania w konsoli. U góry strony kliknij „+ Utwórz dane logowania” i wybierz „Klucz interfejsu API”.

W przypadku użycia produkcyjnego zalecamy ustawienie ograniczenia aplikacji dla klucza interfejsu API, ale w tym ćwiczeniu jest to opcjonalne.

3. Pobieranie przykładowych plików projektu

W tej sekcji opisujemy, jak skonfigurować podstawowy pusty projekt aplikacji Xcode, klonując pliki z repozytorium GitHub dla tego laboratorium. Repozytorium GitHub zawiera wersje kodu modułu przed i po zmianach. Ten przewodnik zaczyna się od pustego szablonu projektu i prowadzi do jego ukończenia. Jeśli utkniesz, możesz użyć gotowego projektu w repozytorium jako materiału referencyjnego.

Sklonuj repozytorium lub pobierz kod

Przejdź do katalogu, w którym chcesz przechowywać codelab.

Następnie sklonuj repozytorium lub pobierz kod:

git clone https://github.com/googlemaps-samples/codelab-navigation-101-ios-swift

Jeśli nie masz zainstalowanego narzędzia git, kliknij ten przycisk, aby pobrać kod:

Aby jak najszybciej zacząć, w repozytorium znajdziesz w folderze Starter kod startowy, który pomoże Ci w wykonaniu tego ćwiczenia. Dostępny jest też gotowy Solution projekt, jeśli chcesz przejść dalej lub w dowolnym momencie sprawdzić swoje postępy. Aby użyć projektu rozwiązania, postępuj zgodnie z instrukcjami „Instalacja za pomocą Cocoapods” poniżej, a następnie uruchom polecenie „pod update” z folderu solution/Navigation SDK Codelab.

Po sklonowaniu repozytorium lokalnie otwórz folder Starter jako istniejący projekt w Xcode. Sprawdź, czy projekt się kompiluje i działa.

Podłącz urządzenie lub skonfiguruj symulator Xcode

4. Dodawanie pakietu Navigation SDK do aplikacji

Pakiet Navigation SDK można zintegrować z projektem Xcode na 3 sposoby. W tym samouczku używamy CocoaPods. Szczegółowe informacje o integracji za pomocą Swift Package Manager lub ręcznej instalacji przez pobranie pakietu SDK znajdziesz w artykule Tworzenie projektu Xcode i instalowanie pakietu Navigation SDK w dokumentacji pakietu Navigation SDK.

Instalowanie za pomocą Cocoapods

Jeśli nie masz jeszcze narzędzia CocoaPods, zainstaluj je w systemie macOS, uruchamiając w terminalu to polecenie: Więcej informacji znajdziesz w przewodniku dla początkujących dotyczącym CocoaPods.

sudo gem install cocoapods

Utwórz nowy plik o nazwie Podfile w folderze projektu, w folderze starter/Navigation SDK Codelab (w Xcode kliknij File > New > File > Other > Empty i zapisz jako „Podfile”).

Dodaj do pliku Podfile te wiersze:

source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '15.0'

target 'Navigation SDK Codelab' do
  pod 'GoogleNavigation', '9.1.1'
end

Zaoszczędź Podfile.

Otwórz terminal i przejdź do katalogu, w którym został zapisany plik Podfile (powinien to być folder „starter/Navigation SDK Codelab” w repozytorium codelab).

cd "<path-to-starter-project-folder>/Navigation SDK Codelab"

Uruchom polecenie pod install. Spowoduje to zainstalowanie interfejsów API określonych w Podfile wraz z wszelkimi zależnościami.

pod install

Zamknij Xcode, a potem otwórz plik .xcworkspace projektu, aby uruchomić Xcode. Od tego momentu do otwierania projektu musisz używać pliku .xcworkspace.

Sprawdź, czy do struktury projektu dodano katalog Pods i czy zawiera on komponenty Pod „GoogleMaps” i „GoogleNavigation”.

6e81772ee067d452.png

Dodawanie klucza interfejsu API

Dodaj klucz interfejsu API do AppDelegate.swift w ten sposób:

  1. Dodaj te instrukcje importu:
import GoogleMaps
import GoogleNavigation
  1. Dodaj do metody application(_:didFinishLaunchingWithOptions:) te informacje:
GMSServices.provideAPIKey("YOUR_API_KEY")

Zastąp „YOUR_API_KEY” kluczem API utworzonym w poprzednim kroku.

Zbuduj projekt i popraw ewentualne błędy.

5. Konfigurowanie uprawnień aplikacji

Pakiet Navigation SDK korzysta z sygnałów GPS, aby podawać lokalizację dopasowaną do drogi i szczegółowe wskazówki dojazdu, więc aplikacja będzie musiała poprosić użytkownika o przyznanie dostępu do dokładnych danych o lokalizacji.

Aby to zrobić, dodaj do pliku Info.plist aplikacji w Xcode kilka właściwości, dodaj do aplikacji kod, który będzie prosić użytkownika o uprawnienia w czasie działania aplikacji, i obsłuż wszelkie błędy, np. brak przyznania uprawnień lub niedostępność lokalizacji.

Otwórz plik Info.plist w Xcode. Powinien on wyglądać mniej więcej tak.

6532a85bd9ac8fb4.png

Prośba o dostęp do dokładnej lokalizacji

Aby dodać nowe wartości, najedź wskaźnikiem myszy na wiersz „Lista właściwości informacji”, aż pojawi się ikona „+”. Kliknij „+”, aby wyświetlić okno z sugerowanymi nazwami usług. Pamiętaj, że możesz też dodawać usługi ręcznie.

Dodaj do pliku Info.plist te właściwości i wartości:

Właściwość

Wartość

Prywatność – lokalizacja zawsze i podczas używania – opis zastosowania

„Ta aplikacja wymaga dostępu do lokalizacji urządzenia, aby podawać szczegółowe wskazówki dojazdu”.

Prywatność – lokalizacja podczas korzystania z aplikacji – opis użycia

„Ta aplikacja wymaga dostępu do lokalizacji urządzenia, aby podawać szczegółowe wskazówki dojazdu”.

allowsBackgroundLocationUpdates

TAK

Prośba o dostęp do lokalizacji w tle

Dodaj do pliku Info.plist te właściwości i wartości:

UIBackgroundModes > Dodaj wiersz > Item 0: App registers for location updates (wybierz tę wartość z listy sugestii)

Po zakończeniu plik Info.plist powinien wyglądać mniej więcej tak:

3b0c49018451d0ff.png

Wysyłanie prośby o dostęp do lokalizacji w czasie działania

Dodaj do pliku ViewController.swift te instrukcje importu:

import GoogleNavigation

Dodaj do klasy ViewController tę deklarację:

var locationManager: CLLocationManager!

Dodaj zastąpienie metody dla loadView() i wywołaj locationManager.requestAlwaysAuthorization():

override func loadView() {
        locationManager = CLLocationManager()
        locationManager.requestAlwaysAuthorization()

Aplikacja poprosi użytkownika o dostęp do lokalizacji i udostępni go, jeśli użytkownik wyrazi na to zgodę .

Prośba o zezwolenie na wyświetlanie powiadomień

Aby poprosić użytkownika o zezwolenie na wyświetlanie powiadomień (które będą potrzebne do wyświetlania instrukcji manewrów nawigacyjnych), dodaj do funkcji loadView() ten kod:

UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) {
        granted, error in
        // Handle denied authorization to display notifications.
          if !granted || error != nil {
              print("User rejected request to display notifications.")
          }
        }

Skompiluj i uruchom aplikację, a następnie sprawdź, czy wyświetla się prośba o udostępnienie lokalizacji i włączenie powiadomień.

ad5f665a21170c49.png

6. Dodawanie interfejsu użytkownika do nawigacji

W tym kroku dodasz mapę i skonfigurujesz ją tak, aby wyświetlała lokalizację. Następnie wyświetl użytkownikowi okno z warunkami korzystania z pakietu SDK do nawigacji.

Dodawanie widoku mapy do aplikacji

Dodaj ten wiersz, aby zadeklarować zmienną GMSMapView w kontrolerze widoku.

var mapView: GMSMapView!

Aby zainicjować mapę, dodaj ten kod do pliku loadView() w pliku Viewcontroller.swift.

let camera = GMSCameraPosition.camera(withLatitude: 51.483174, longitude: -0.177369, zoom: 14)
let options = GMSMapViewOptions()
options.camera = camera
options.frame = .zero
        
mapView = GMSMapView(options: options)
view = mapView

Zbuduj i uruchom aplikację. Powinna się wyświetlić mapa wyśrodkowana na południowo-zachodnim Londynie.

1d46ce5c0851cae3.png

Wyświetlanie okna z warunkami korzystania z usługi Navigation SDK

Dodaj do pliku ViewController.swift ten kod w ramach tej samej metody loadView() co poprzedni kod. Wyświetlą się warunki korzystania z pakietu Navigation SDK. Jeśli nie zostanie zaakceptowana, nawigacja nie zostanie włączona.

// Show the terms and conditions.
let companyName = "Navigation SDK Codelab"
GMSNavigationServices.showTermsAndConditionsDialogIfNeeded(withCompanyName: companyName) { termsAccepted in
  if termsAccepted {
    // Enable navigation if the user accepts the terms.
    self.mapView.isNavigationEnabled = true
    // Request authorization for alert notifications which deliver guidance instructions
    // in the background.
  } else {
    // Handle the case when the user rejects the terms and conditions.
  }
}

Zbuduj i uruchom aplikację, aby zobaczyć okno.

29f17ae5b4c07c9f.png

7. Dodawanie detektorów kluczowych zdarzeń nawigacyjnych

Z tego kroku dowiesz się, jak skonfigurować odbiorniki kluczowych zdarzeń, takich jak przybycie do miejsca docelowego lub zmiana trasy przez kierowcę.

Aby nasłuchiwać tych zdarzeń, kontroler widoku musi przyjąć protokół GMSNavigatorListener.

Dodaj ten protokół do definicji klasy w ViewController.swift.

class ViewController: UIViewController,
                      GMSNavigatorListener {

Teraz dodaj wiersz kodu, aby skonfigurować odbiornik w loadView():

// Add a listener for GMSNavigator.
mapView.navigator?.add(self)

Na koniec dodaj do klasy 2 metody obsługi wywoływanych zdarzeń.

// Listener to handle arrival events.
func navigator(_ navigator: GMSNavigator, didArriveAt waypoint: GMSNavigationWaypoint) {
  print("You have arrived at: \(waypoint.title)")
}

// Listener for route change events.
func navigatorDidChangeRoute(_ navigator: GMSNavigator) {
  print("The route has changed.")
}

8. Ustawianie miejsca docelowego i rozpoczynanie nawigacji

Z tej sekcji dowiesz się, jak ustawić miejsce docelowe i rozpocząć nawigację.

Utwórz nową funkcję logiki nawigacji.

Najpierw dodaj do pliku ViewController nową funkcję o nazwie startNav(). Będzie ona zawierać logikę ustawiania miejsca docelowego i rozpoczynania nawigacji.

// Create a route and start guidance.
@objc func startNav() {
}

Utwórz Waypoint dla miejsca docelowego.

Następnie utwórz tablicę miejsc docelowych z jednym punktem pośrednim.

// Create a route and start guidance.
@objc func startNav() {
  var destinations = [GMSNavigationWaypoint]()
  destinations.append(
    GMSNavigationWaypoint.init(
      placeID: "ChIJH-tBOc4EdkgRJ8aJ8P1CUxo",
      title: "Trafalgar Square")!)
}

Wywołaj setDestinations()i obsłuż odpowiedź.

Następnie wywołaj funkcję setDestinations i obsłuż zwrócony obiekt GMSRouteStatus.

Jeśli GMSRouteStatus ma wartość „OK”, rozpocznij nawigację, ustawiając isGuidanceActive=true na obiekcie navigator elementu mapView. W przeciwnym razie wydrukuj komunikat o błędzie.

Jeśli zwrócona wartość GMSRouteStatus to „OK”, rozpocznij symulację jazdy po trasie, wywołując mapView.locationSimulator.simulateLocationsAlongExistingRoute().

// Create a route and start guidance.
@objc func startNav() {
  var destinations = [GMSNavigationWaypoint]()
    destinations.append(
      GMSNavigationWaypoint.init(
        placeID: "ChIJH-tBOc4EdkgRJ8aJ8P1CUxo",
          title: "Trafalgar Square")!)
      
  mapView.navigator?.setDestinations(
    destinations
  ) { routeStatus in
    guard routeStatus == .OK else {
      print("Handle route statuses that are not OK.")
      return
    }
    //If routeStatus is OK, start guidance.
    self.mapView.navigator?.isGuidanceActive = true
    //start simulating driving along the route. self.mapView.locationSimulator?.simulateLocationsAlongExistingRoute()
    self.mapView.cameraMode = .following
  }
}

Obsługa typowych stanów błędów

Warto bardziej szczegółowo obsługiwać błędy GMSRouteStatus, zwłaszcza podczas debugowania początkowych problemów z nową aplikacją. Na przykład na początku możesz częściej otrzymywać błędy związane z dostępem do lokalizacji, kluczem interfejsu API lub „nie znaleziono trasy” ze względu na konfigurację debugowania, więc warto obsługiwać te stany błędów.

Dodaj kod, który obsługuje te konkretne przypadki i wyświetla w konsoli odpowiedni komunikat.

mapView.navigator?.setDestinations(
  destinations
) { routeStatus in
    guard routeStatus == .OK else {
      print("Handle route statuses that are not OK.")
      switch routeStatus {
       case .locationUnavailable:
        print("Location unavailable.") //check permissions
      case .noRouteFound:
        print("No route found.") //check start location and destination
      case .waypointError:
        print("Waypoint error") //check Place ID
      default:
        print("Not sure what happened")
      }
    return
  }

Dodawanie przycisku do uruchamiania wskazówek nawigacyjnych

Na koniec dodaj przycisk do interfejsu i połącz go z metodą startNav. Utwórz metodę o nazwie makeButton() z tym kodem. Wywołaj funkcję makeButton() z poziomu loadView().

// Add a button to the view.
func makeButton() {
  // A button to start navigation.
  let navButton = UIButton(frame: CGRect(x: 5, y: 150, width: 200, height: 35))
  navButton.backgroundColor = .blue
  navButton.alpha = 0.5
  navButton.setTitle("Start navigation", for: .normal)
  navButton.addTarget(self, action: #selector(startNav), for: .touchUpInside)
  self.mapView.addSubview(navButton)
}

Skompiluj i uruchom aplikację.

Uwaga: uruchomienie kodu w

startNav()

zadzwoni do

setDestinations()

metoda, która generuje opłaty po wykorzystaniu pierwszych 1000 miejsc docelowych. Więcej informacji znajdziesz w artykule Wykorzystanie i rozliczenia.

9. Gratulacje!

Gratulacje – dotarliśmy do celu!

7a69dcb75c904d7.png

Masz prostą aplikację, która za pomocą pakietu Navigation SDK Google Maps Platform wyświetla szczegółowe wskazówki dojazdu do miejsca docelowego.

Masz skonfigurowane uprawnienia aplikacji i okno dialogowe z warunkami korzystania z pakietu Navigation SDK dla użytkowników końcowych oraz określone miejsce docelowe za pomocą identyfikatora miejsca. W aplikacji zostały obsłużone różne stany powodzenia i błędu.

10. Możesz zrobić jeszcze więcej

Jeśli chcesz dalej rozwijać swoją aplikację, zapoznaj się z poniższymi tematami, aby znaleźć inspirację.

  • Nasłuchiwanie większej liczby zdarzeń nawigacyjnych Dodaj kod, aby wyświetlać komunikat, jeśli pozostały czas lub odległość przekraczają próg.
  • Dostosowywanie interfejsu nawigacji
  • Jeśli chcesz podjąć większe wyzwanie, spróbuj dodać selektor miejsca z interfejsu Places API, aby umożliwić użytkownikowi ustawienie miejsca docelowego. Wskazówka: przykładowe implementacje znajdziesz w aplikacjach demonstracyjnych pakietu Navigation SDK. Aby wyświetlić kod, uruchom pod try GoogleNavigation w folderze projektu.