1. 개요
친근한 채팅 Codelab에 오신 것을 환영합니다. 이 Codelab에서는 Firebase 플랫폼을 사용하여 iOS 애플리케이션을 만드는 방법을 알아봅니다. Firebase를 사용하여 채팅 클라이언트를 구현하고 성능을 모니터링합니다.
무엇을 배울 것인가
- 사용자가 로그인할 수 있도록 허용합니다.
- Firebase 실시간 데이터베이스를 사용하여 데이터를 동기화합니다.
- Firebase 저장소에 바이너리 파일을 저장합니다.
필요한 것
- Xcode
- 코코아 포드
- iOS 8.0+ 또는 시뮬레이터가 설치된 테스트 장치
이 튜토리얼을 어떻게 활용하시겠습니까?
iOS 앱 구축 경험을 어떻게 평가하시나요?
2. 샘플 코드 받기
명령줄에서 GitHub 저장소를 복제합니다.
$ git clone https://github.com/firebase/codelab-friendlychat-ios
3. 시작 앱 구축
시작 앱을 빌드하려면 다음 안내를 따르세요.
- 터미널 창에서
샘플 코드 다운로드의
ios-starter/swift-starter
디렉토리 -
pod install --repo-update
실행 - FriendlyChatSwift.xcworkspace 파일을 열어 Xcode에서 프로젝트를 엽니다.
- 다음을 클릭하세요.
실행 버튼.
몇 초 후에 친근한 채팅 홈 화면이 나타나는 것을 볼 수 있습니다. UI가 나타나야 합니다. 그러나 이 시점에서는 로그인하거나 메시지를 보내거나 받을 수 없습니다. 다음 단계를 완료할 때까지 앱은 예외와 함께 중단됩니다.
4. Firebase 콘솔 프로젝트 생성
프로젝트 생성
Firebase 콘솔 에서 프로젝트 추가를 선택합니다.
FriendlyChat
프로젝트를 호출한 다음 Create Project를 클릭하세요.
iOS 앱 연결
- 새 프로젝트의 프로젝트 개요 화면에서 iOS 앱에 Firebase 추가를 클릭합니다.
- 번들 ID를 "
com.google.firebase.codelab.FriendlyChatSwift
"로 입력합니다. - App Store ID를 "
123456
"으로 입력하세요. - 앱 등록 을 클릭합니다.
앱에 GoogleService-Info.plist 파일 추가
두 번째 화면에서 GoogleService-Info.plist 다운로드를 클릭하여 앱에 필요한 모든 Firebase 메타데이터가 포함된 구성 파일을 다운로드합니다. 해당 파일을 애플리케이션에 복사하고 FriendlyChatSwift 대상에 추가하세요.
이제 팝업의 오른쪽 상단에 있는 "x"를 클릭하여 팝업을 닫을 수 있습니다. 여기에서 해당 단계를 수행할 것이므로 3단계와 4단계를 건너뛰십시오.
Firebase 모듈 가져오기
먼저 Firebase
모듈을 가져왔는지 확인하세요.
AppDelegate.swift , FCViewController.swift
import Firebase
AppDelegate에서 Firebase 구성
.plist 파일에서 기본 Firebase 서비스를 구성하려면 application:didFinishLaunchingWithOptions 함수 내 FirebaseApp의 "configure" 메소드를 사용하세요.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().delegate = self
return true
}
5. 사용자 식별
규칙을 사용하여 인증된 사용자로 제한
이제 메시지를 읽거나 쓰기 전에 인증을 요구하는 규칙을 추가하겠습니다. 이를 위해 메시지 데이터 개체에 다음 규칙을 추가합니다. Firebase 콘솔 의 데이터베이스 섹션에서 실시간 데이터베이스를 선택한 다음 규칙 탭을 클릭하세요. 그런 다음 규칙을 업데이트하여 다음과 같이 만듭니다.
{
"rules": {
"messages": {
".read": "auth != null",
".write": "auth != null"
}
}
}
작동 방식에 대한 자세한 내용("auth" 변수에 대한 문서 포함)은 Firebase 보안 문서를 참조하세요.
인증 API 구성
애플리케이션이 사용자를 대신하여 Firebase 인증 API에 액세스할 수 있으려면 이를 활성화해야 합니다.
- Firebase 콘솔 로 이동하여 프로젝트를 선택하세요.
- 인증 선택
- 로그인 방법 탭을 선택하세요.
- Google 스위치를 활성화(파란색)로 전환합니다.
- 결과 대화 상자에서 저장을 누릅니다.
이 Codelab 후반부에 'CONFIGURATION_NOT_FOUND' 메시지와 함께 오류가 발생하면 이 단계로 돌아와 작업 내용을 다시 확인하세요.
Firebase 인증 종속성 확인
Podfile
파일에 Firebase 인증 종속 항목이 있는지 확인하세요.
포드파일
pod 'Firebase/Auth'
Google 로그인을 위해 Info.plist를 설정하세요.
XCode 프로젝트에 사용자 정의 URL 구성표를 추가해야 합니다.
- 프로젝트 구성을 엽니다. 왼쪽 트리 보기에서 프로젝트 이름을 두 번 클릭합니다. 대상 섹션에서 앱을 선택한 다음 정보 탭을 선택하고 URL 유형 섹션을 확장합니다.
- + 버튼을 클릭하고 반전된 클라이언트 ID에 대한 URL 구성표를 추가하세요. 이 값을 찾으려면 GoogleService-Info.plist 구성 파일을 열고 REVERSED_CLIENT_ID 키를 찾으세요. 해당 키 값을 복사하여 구성 페이지의 URL 체계 상자에 붙여넣습니다. 다른 필드는 비워 두세요.
- 완료되면 구성은 다음과 비슷해 보일 것입니다(단, 애플리케이션별 값은 포함).
Google 로그인을 위한 clientID 설정
Firebase가 구성된 후 clientID를 사용하여 "didFinishLaunchingWithOptions:" 메소드 내에서 Google 로그인을 설정할 수 있습니다.
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
}
로그인 핸들러 추가
Google 로그인 결과가 성공하면 해당 계정을 사용하여 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
}
}
}
사용자가 자동으로 로그인됩니다. 그런 다음 사용자가 성공적으로 로그인한 후 앱에 로그인할 수 있도록 Firebase 인증에 리스너를 추가합니다. 그리고 deinit에서 리스너를 제거합니다.
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)
}
}
로그아웃
로그아웃 방법 추가
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)")
}
}
로그인한 사용자로 메시지 읽기 테스트
- 다음을 클릭하세요.
실행 버튼.
- 즉시 로그인 화면으로 이동해야 합니다. Google 로그인 버튼을 탭하세요.
- 모든 것이 제대로 작동하면 메시지 화면으로 이동됩니다.
6. 실시간 데이터베이스 활성화
메시지 가져오기
Firebase 콘솔 의 프로젝트에서 왼쪽 탐색 모음에 있는 데이터베이스 항목을 선택합니다. 데이터베이스의 오버플로 메뉴에서 JSON 가져오기를 선택합니다. Friendlychat 디렉터리에서 initial_messages.json
파일을 찾아 선택한 다음 가져오기 버튼을 클릭하세요. 그러면 현재 데이터베이스에 있는 모든 데이터가 대체됩니다. 녹색 + 및 빨간색 x를 사용하여 항목을 추가하고 제거하여 데이터베이스를 직접 편집할 수도 있습니다.
가져온 후 데이터베이스는 다음과 같아야 합니다.
Firebase 데이터베이스 종속성 확인
Podfile
파일의 종속성 블록에서 Firebase/Database
포함되어 있는지 확인하세요.
포드파일
pod 'Firebase/Database'
기존 메시지 동기화
새로 추가된 메시지를 앱 UI에 동기화하는 코드를 추가합니다.
이 섹션에 추가하는 코드는 다음과 같습니다.
- Firebase 데이터베이스를 초기화하고 리스너를 추가하여 데이터베이스 변경 사항을 처리합니다.
- 새 메시지가 표시되도록
DataSnapshot
을 업데이트하세요.
FCViewController의 "deinit", "configureDatabase" 및 "tableView:cellForRow indexPath:" 메소드를 수정하십시오. 아래에 정의된 코드로 바꾸세요.
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
}
테스트 메시지 동기화
- 다음을 클릭하세요.
실행 버튼.
- 시작하려면 로그인 버튼을 클릭하여 메시지 창으로 이동하세요.
- '메시지' 항목 옆에 있는 녹색 + 기호를 클릭하고 다음과 같은 개체를 추가하여 Firebase 콘솔에서 직접 새 메시지를 추가하세요.
- 친선 채팅 UI에 표시되는지 확인하세요.
7. 메시지 보내기
메시지 보내기 구현
데이터베이스에 값을 푸시합니다. 푸시 메소드를 사용하여 Firebase 실시간 데이터베이스에 데이터를 추가하면 자동 ID가 추가됩니다. 자동 생성된 ID는 순차적이므로 새 메시지가 올바른 순서로 추가됩니다.
FCViewController의 "sendMessage:" 메소드를 수정하세요. 아래에 정의된 코드로 바꾸세요.
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)
}
메시지 전송 테스트
- 다음을 클릭하세요.
실행 버튼.
- 로그인을 클릭하여 메시지 창으로 이동합니다.
- 메시지를 입력하고 보내기를 누르세요. 새 메시지는 앱 UI와 Firebase 콘솔에 표시되어야 합니다.
8. 이미지 저장 및 수신
Firebase 저장소 종속성 확인
Podfile
의 종속성 블록에서 Firebase/Storage
포함되어 있는지 확인하세요.
포드파일
pod 'Firebase/Storage'
대시보드에서 Firebase 저장소 활성화
Firebase 콘솔로 이동하여 Storage가 "gs://PROJECTID.appspot.com" 도메인으로 활성화되었는지 확인하세요.
대신 활성화 창이 표시되면 "시작하기"를 클릭하여 기본 규칙으로 활성화하세요.
Firebase 저장소 구성
FCViewController.swift
func configureStorage() {
storageRef = Storage.storage().reference()
}
기존 메시지에서 이미지 수신
Firebase 저장소에서 이미지를 다운로드하는 코드를 추가합니다.
FCViewController의 "tableView: cellForRowAt indexPath:" 메소드를 수정하세요. 아래에 정의된 코드로 바꾸세요.
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. 이미지 메시지 보내기
이미지 저장 및 전송 구현
사용자로부터 이미지를 업로드한 다음 이 이미지의 저장 URL을 데이터베이스에 동기화하여 이 이미지가 메시지 내에 전송되도록 합니다.
FCViewController의 "imagePickerController: didFinishPickingMediaWithInfo:" 메소드를 수정하세요. 아래에 정의된 코드로 바꾸세요.
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])
}
}
}
이미지 메시지 보내기 및 받기 테스트
- 다음을 클릭하세요.
실행 버튼.
- 로그인을 클릭하여 메시지 창으로 이동합니다.
- 사진을 선택하려면 "사진 추가" 아이콘을 클릭하세요. 사진이 포함된 새 메시지가 앱 UI와 Firebase 콘솔에 표시되어야 합니다.
10. 축하합니다!
Firebase를 사용하여 실시간 채팅 애플리케이션을 쉽게 구축했습니다.
우리가 다룬 내용
- 실시간 데이터베이스
- 연합 로그인
- 저장