1. Omówienie
Witamy w usłudze Friendly Chat. Z tego ćwiczenia w Codelab dowiesz się, jak korzystać z platformy Firebase do tworzenia aplikacji na iOS. Zaimplementujesz klienta czatu i będziesz monitorować jego skuteczność za pomocą Firebase.
Czego się nauczysz
- Zezwalaj użytkownikom na logowanie się.
- Synchronizowanie danych przy użyciu Bazy danych czasu rzeczywistego Firebase.
- Przechowuj pliki binarne w Firebase Storage.
Czego potrzebujesz
- Xcode
- CocoaPods
- urządzenie testowe z systemem iOS 8.0 lub nowszym albo symulator.
Jak będziesz korzystać z tego samouczka?
Jak oceniasz tworzenie aplikacji na iOS?
2. Pobieranie przykładowego kodu
Sklonuj repozytorium GitHub z poziomu wiersza poleceń.
$ git clone https://github.com/firebase/codelab-friendlychat-ios
3. Tworzenie aplikacji startowej
Aby utworzyć aplikację startową:
- W oknie terminala przejdź do katalogu
ios-starter/swift-starter
z pobranego przykładowego kodu. - Uruchom
pod install --repo-update
- Otwórz plik friendChatSwift.xcworkspace, by otworzyć projekt w Xcode.
- Kliknij przycisk Uruchom.
Po kilku sekundach powinien wyświetlić się ekran główny znajomego czatu. Powinien pojawić się interfejs. Nie możesz jednak zalogować się, wysyłać ani odbierać wiadomości. Aplikacja zostanie przerwana w wyjątku, dopóki nie wykonasz kolejnego kroku.
4. Utwórz projekt w konsoli Firebase
Utwórz projekt
W konsoli Firebase wybierz Dodaj projekt.
Nazwij projekt FriendlyChat
, a potem kliknij Utwórz projekt.
Przechodzenie na wyższą wersję abonamentu Firebase
Aby korzystać z Cloud Storage dla Firebase, projekt Firebase musi być w abonamentem Blaze (opłaty według wykorzystania), co oznacza, że jest połączony z kontem rozliczeniowym Cloud.
- Do konta rozliczeniowego Cloud wymagana jest forma płatności, np. karta kredytowa.
- Jeśli dopiero zaczynasz korzystać z Firebase i Google Cloud, sprawdź, czy kwalifikujesz się do otrzymania środków w wysokości 300 USD i bezpłatnego okresu próbnego konta rozliczeniowego Cloud.
- Jeśli wykonujesz to Codelab w ramach wydarzenia, zapytaj organizatora, czy są dostępne jakieś kredyty Cloud.
Aby przenieść projekt na abonament Blaze, wykonaj te czynności:
- W konsoli Firebase wybierz Przejdź na większy pakiet.
- Wybierz abonament Blaze. Postępuj zgodnie z instrukcjami wyświetlanymi na ekranie, aby połączyć konto rozliczeniowe Cloud z projektem.
Jeśli w ramach tego przejścia na wyższy poziom abonamentu musisz utworzyć konto rozliczeniowe Cloud, konieczne może być powrót do procesu przejścia w konsoli Firebase, aby go dokończyć.
Łączenie aplikacji na iOS
- Na ekranie „Przegląd projektu” nowego projektu kliknij Add Firebase to your iOS app (Dodaj Firebase do swojej aplikacji na iOS).
- Wpisz identyfikator pakietu w postaci „
com.google.firebase.codelab.FriendlyChatSwift
”. - Wpisz „
123456
” jako identyfikator App Store. - Kliknij Zarejestruj aplikację.
Dodawanie do aplikacji pliku GoogleService-Info.plist
Na drugim ekranie kliknij Download GoogleService-Info.plist, aby pobrać plik konfiguracyjny, który zawiera wszystkie niezbędne metadane Firebase dotyczące Twojej aplikacji. Skopiuj ten plik do swojej aplikacji i dodaj go do celu friendlyChatSwift.
Możesz teraz kliknąć „x” w prawym górnym rogu wyskakującego okienka, aby je zamknąć (pomijając kroki 3 i 4 – ponieważ będziesz wykonywać te czynności tutaj).
Zaimportuj moduł Firebase
Najpierw sprawdź, czy moduł Firebase
został zaimportowany.
AppDelegate.swift, FCViewController.swift
import Firebase
Konfigurowanie Firebase w pliku AppDelegate
Aby skonfigurować podstawowe usługi Firebase z pliku plist, użyj metody „configure” w klasie FirebaseApp w ramach funkcji application:didFinishLaunchingWithOptions.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().delegate = self
return true
}
5. Identyfikowanie użytkowników
Używanie reguł do ograniczania dostępu do uwierzytelnionych użytkowników
Dodamy teraz regułę, która będzie wymagać uwierzytelnienia przed odczytaniem lub zapisaniem wiadomości. W tym celu dodajemy do obiektu danych wiadomości te reguły. W sekcji Baza danych w konsoli Firebase wybierz Realtime Database, a potem kliknij kartę Reguły. Następnie zaktualizuj reguły, aby wyglądały tak:
{
"rules": {
"messages": {
".read": "auth != null",
".write": "auth != null"
}
}
}
Więcej informacji o tym, jak to działa (w tym dokumentację zmiennej „auth”) znajdziesz w dokumentacji zabezpieczeń Firebase.
Konfigurowanie interfejsów API uwierzytelniania
Aby aplikacja miała dostęp do interfejsów API uwierzytelniania Firebase w imieniu użytkowników, musisz ją włączyć
- Otwórz konsolę Firebase i wybierz projekt.
- Wybierz Authentication (Uwierzytelnianie).
- Wybierz kartę Metoda logowania.
- Ustaw przełącznik Google w pozycji włączonej (niebieskiej).
- W oknie, które się pojawi, kliknij Zapisz.
Jeśli w późniejszej części tego ćwiczenia pojawią się błędy z komunikatem „CONFIGURATION_NOT_FOUND”, wróć do tego kroku i sprawdź dokładnie swoją pracę.
Potwierdź zależność od Uwierzytelniania Firebase
Sprawdź, czy w pliku Podfile
występują zależności funkcji uwierzytelniania Firebase.
Podfile
pod 'Firebase/Auth'
Skonfiguruj plik Info.plist na potrzeby logowania przez Google.
Musisz dodać niestandardowy schemat URL do projektu XCode.
- Otwórz konfigurację projektu: kliknij dwukrotnie nazwę projektu w widoku drzewa po lewej stronie. Wybierz swoją aplikację w sekcji CELE, kliknij kartę Informacje i rozwiń sekcję Typy adresów URL.
- Kliknij przycisk + i dodaj schemat adresu URL dla odwrotnego identyfikatora klienta. Aby znaleźć tę wartość, otwórz plik konfiguracyjny GoogleService-Info.plist i odszukaj klucz REVERSED_CLIENT_ID. Skopiuj wartość tego klucza i wklej ją w polu Schematy URL na stronie konfiguracji. Pozostałe pola pozostaw puste.
- Po zakończeniu konfiguracja powinna wyglądać mniej więcej tak (ale z wartościami specyficznymi dla aplikacji):
Ustawianie identyfikatora klienta na potrzeby Logowania przez Google
Po skonfigurowaniu Firebase możemy użyć identyfikatora klienta, aby skonfigurować logowanie w Google w ramach metody „didFinishLaunchingWithOptions:”.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
Dodawanie obsługi logowania
Po pomyślnym zalogowaniu się w Google użyj tego konta do uwierzytelnienia się w Firebase.
AppDelegate.swift
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
if let error = error {
print("Error \(error)")
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error {
print("Error \(error)")
return
}
}
}
automatycznie logować użytkownika; Następnie dodaj do Firebase Auth odsłuchiwanie, aby umożliwić użytkownikowi dostęp do aplikacji po zalogowaniu. Usuń też listenera na końcu.
SignInViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signInSilently()
handle = Auth.auth().addStateDidChangeListener() { (auth, user) in
if user != nil {
MeasurementHelper.sendLoginEvent()
self.performSegue(withIdentifier: Constants.Segues.SignInToFp, sender: nil)
}
}
}
deinit {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
}
Wyloguj się
Dodawanie metody wylogowywania
FCViewController.swift
@IBAction func signOut(_ sender: UIButton) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
dismiss(animated: true, completion: nil)
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError.localizedDescription)")
}
}
Przetestuj czytanie wiadomości jako zalogowany użytkownik
- Kliknij przycisk Uruchom.
- Powinien natychmiast wyświetlić się ekran logowania. Kliknij przycisk logowania w Google.
- Jeśli wszystko zadziała, powinieneś/powinnaś zobaczyć ekran wiadomości.
6. Aktywuj bazę danych czasu rzeczywistego
Importowanie wiadomości
W konsoli Firebase wybierz element Baza danych na pasku nawigacyjnym po lewej stronie. W menu rozszerzonym bazy danych wybierz Importuj plik JSON. Przejdź do pliku initial_messages.json
w katalogu friendchat, wybierz go i kliknij przycisk Importuj. Spowoduje to zastąpienie wszystkich danych znajdujących się obecnie w bazie danych. Możesz też bezpośrednio edytować bazę danych, używając zielonych + i czerwonych X do dodawania i usuwania elementów.
Po zaimportowaniu bazy danych Twoja baza danych powinna wyglądać tak:
Potwierdź zależność bazy danych Firebase
W bloku zależności w pliku Podfile
sprawdź, czy jest uwzględniony element Firebase/Database
.
Podfile
pod 'Firebase/Database'
Synchronizowanie istniejących wiadomości
Dodaj kod, który zsynchronizuje nowo dodane wiadomości z interfejsem aplikacji.
Kod, który dodasz w tej sekcji:
- Inicjalizacja bazy danych Firebase i dodanie do niej odsłuchiwacza, który będzie obsługiwać zmiany w bazie danych.
- Zaktualizuj aplikację
DataSnapshot
, aby wyświetlić nowe wiadomości.
Zmodyfikuj metody „deinit”, „configureDatabase” i „tableView:cellForRow indexPath:” kontrolera FCViewController. Zastąp je kodem zdefiniowanym poniżej:
FCViewController.swift
deinit {
if let refHandle = _refHandle {
self.ref.child("messages").removeObserver(withHandle: _refHandle)
}
}
func configureDatabase() {
ref = Database.database().reference()
// Listen for new messages in the Firebase database
_refHandle = self.ref.child("messages").observe(.childAdded, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
strongSelf.messages.append(snapshot)
strongSelf.clientTable.insertRows(at: [IndexPath(row: strongSelf.messages.count-1, section: 0)], with: .automatic)
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
let cell = self.clientTable.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
// Unpack message from Firebase DataSnapshot
let messageSnapshot = self.messages[indexPath.row]
guard let message = messageSnapshot.value as? [String: String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
let text = message[Constants.MessageFields.text] ?? ""
cell.textLabel?.text = name + ": " + text
cell.imageView?.image = UIImage(named: "ic_account_circle")
if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage(data: data)
}
return cell
}
Testowanie synchronizacji wiadomości
- Kliknij przycisk Uruchom.
- Kliknij przycisk Zaloguj się, aby rozpocząć, aby otworzyć okno wiadomości.
- Dodaj nowe wiadomości bezpośrednio w konsoli Firebase, klikając zielony symbol + obok pozycji „wiadomości” i dodając obiekt o takiej postaci:
- Sprawdź, czy pojawiają się w interfejsie przyjaznego czatu.
7. Wysyłanie wiadomości
Implementowanie funkcji Wyślij wiadomość
Przesyłanie wartości do bazy danych. Gdy dodasz dane do Bazy danych czasu rzeczywistego Firebase za pomocą metody push, zostanie dodany automatyczny identyfikator. Identyfikatory są generowane automatycznie, dzięki czemu nowe wiadomości są dodawane w odpowiedniej kolejności.
Zmodyfikuj metodę „sendMessage:” klasy FCViewController, zastępując ją kodem zdefiniowanym poniżej:
FCViewController.swift
func sendMessage(withData data: [String: String]) {
var mdata = data
mdata[Constants.MessageFields.name] = Auth.auth().currentUser?.displayName
if let photoURL = Auth.auth().currentUser?.photoURL {
mdata[Constants.MessageFields.photoURL] = photoURL.absoluteString
}
// Push data to Firebase Database
self.ref.child("messages").childByAutoId().setValue(mdata)
}
Testowanie wysyłania wiadomości
- Kliknij przycisk Uruchom.
- Kliknij Zaloguj się, aby przejść do okna wiadomości.
- Wpisz wiadomość i kliknij Wyślij. Nowa wiadomość powinna być widoczna w UI aplikacji i w konsoli Firebase.
8. Przechowywanie i odbieranie obrazów
Potwierdź zależność miejsca na dane w Firebase
Sprawdź, czy w bloku zależności Podfile
znajduje się Firebase/Storage
.
Plik Podfile
pod 'Firebase/Storage'
Konfigurowanie Cloud Storage dla Firebase
Aby skonfigurować Cloud Storage dla Firebase w projekcie Firebase:
- W panelu po lewej stronie konsoli Firebase rozwiń Kompilację i wybierz Miejsce na dane.
- Kliknij Rozpocznij.
- Wybierz lokalizację domyślnego zasobnika Storage.
Zasobniki w regionachUS-WEST1
,US-CENTRAL1
iUS-EAST1
mogą korzystać z poziomu Always Free w Google Cloud Storage. Zasobniki w innych lokalizacjach podlegają cennikom i zasadom korzystania z Google Cloud Storage. - Kliknij Rozpocznij w trybie testowym. Przeczytaj wyłączenie odpowiedzialności dotyczące reguł zabezpieczeń.
W dalszej części tego ćwiczenia z programowania dodasz reguły zabezpieczeń, aby zabezpieczyć swoje dane. Nie rozpowszechniaj ani nie udostępniaj aplikacji publicznie bez dodawania reguł zabezpieczeń do zasobnika na dane. - Kliknij Utwórz.
Skonfiguruj FirebaseStorage
FCViewController.swift,
func configureStorage() {
storageRef = Storage.storage().reference()
}
Otrzymywanie obrazów w dotychczasowych wiadomościach
Dodaj kod, który pobiera obrazy z Pamięci Firebase.
Zmodyfikuj metodę „tableView: cellForRowAt indexPath:” klasy FCViewController. Zastąp ją kodem zdefiniowanym poniżej:
FCViewController.swift,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
let cell = self.clientTable .dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
// Unpack message from Firebase DataSnapshot
let messageSnapshot: DataSnapshot! = self.messages[indexPath.row]
guard let message = messageSnapshot.value as? [String:String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
if let imageURL = message[Constants.MessageFields.imageURL] {
if imageURL.hasPrefix("gs://") {
Storage.storage().reference(forURL: imageURL).getData(maxSize: INT64_MAX) {(data, error) in
if let error = error {
print("Error downloading: \(error)")
return
}
DispatchQueue.main.async {
cell.imageView?.image = UIImage.init(data: data!)
cell.setNeedsLayout()
}
}
} else if let URL = URL(string: imageURL), let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage.init(data: data)
}
cell.textLabel?.text = "sent by: \(name)"
} else {
let text = message[Constants.MessageFields.text] ?? ""
cell.textLabel?.text = name + ": " + text
cell.imageView?.image = UIImage(named: "ic_account_circle")
if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage(data: data)
}
}
return cell
}
9. Wysyłanie wiadomości z obrazem
Wdróż przechowywanie i wysyłanie obrazów
Prześlij obraz od użytkownika, a potem zsynchronizuj adres URL jego magazynu z bazą danych, aby obraz był wysyłany w ramach wiadomości.
Zmodyfikuj metodę „imagePickerController: didFinishPickingMediaWithInfo:” w klasie FCViewController, zastępując ją kodem zdefiniowanym poniżej:
FCViewController.swift,
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
picker.dismiss(animated: true, completion:nil)
guard let uid = Auth.auth().currentUser?.uid else { return }
// if it's a photo from the library, not an image from the camera
if #available(iOS 8.0, *), let referenceURL = info[UIImagePickerControllerReferenceURL] as? URL {
let assets = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)
let asset = assets.firstObject
asset?.requestContentEditingInput(with: nil, completionHandler: { [weak self] (contentEditingInput, info) in
let imageFile = contentEditingInput?.fullSizeImageURL
let filePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\((referenceURL as AnyObject).lastPathComponent!)"
guard let strongSelf = self else { return }
strongSelf.storageRef.child(filePath)
.putFile(from: imageFile!, metadata: nil) { (metadata, error) in
if let error = error {
let nsError = error as NSError
print("Error uploading: \(nsError.localizedDescription)")
return
}
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
})
} else {
guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else { return }
let imageData = UIImageJPEGRepresentation(image, 0.8)
let imagePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
self.storageRef.child(imagePath)
.putData(imageData!, metadata: metadata) { [weak self] (metadata, error) in
if let error = error {
print("Error uploading: \(error)")
return
}
guard let strongSelf = self else { return }
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
}
}
Testowanie wysyłania i odbierania wiadomości z obrazem
- Kliknij przycisk Uruchom.
- Kliknij Zaloguj się, aby otworzyć okno wiadomości.
- Kliknij ikonę „dodaj zdjęcie”, aby wybrać zdjęcie. Nowa wiadomość ze zdjęciem powinna być widoczna w interfejsie aplikacji i konsoli Firebase.
10. Gratulacje!
Użyj Firebase do łatwego tworzenia aplikacji do czatu w czasie rzeczywistym.
Omówione zagadnienia
- Baza danych czasu rzeczywistego
- Logowanie sfederowane
- Pamięć