저번 글에서는 Authentication 서비스를 이용해서 로그인, 회원가입, 로그아웃을 구현해보았습니다!!!
이번 글에서는 Cloud FireStore 서비스를 이용해서 채팅을 구현해보도록 할게요. Firebase FireStore는 NoSQL 클라우드 데이터베이스를 사용해 클라이언트 및 서버 측 개발에 사용되는 데이터를 저장하고 동기화해줍니다!!! 여기서 중요한 것은 동기화예요! 채팅에서 가장 중요한 것은 실시간으로 업데이트 되는 것입니다. Firebase는 데이터저장소에 Listener를 달아주어 업데이트가 되는 즉시, 연결되어 있는 모든 기기에 동기화를 시켜줍니다! 더 자세한 내용은 Cloud Firestore를 참고하실 수 있습니다.
채팅 UI 만들기 및 데이터 모델 만들기 (사전 준비)
저는 xib파일을 이용해서 만들어주었습니다. 위와 같이 xib를 구성해주었는데 sender가 "나"일 경우 왼쪽 그림을 숨기고, "상대방"일 경우 오른쪽 그림을 숨깁니다.
그리고 ChatViewController 파일에 xib를 등록해주었습니다.
messageTableView.register(UINib(nibName: "MessageCell", bundle: nil), forCellReuseIdentifier: "ReusableCell")
또한, TableView에 담아줄 Data Model을 만들어줍니다.
메세지를 보내는 사람 sender와 메세지를 담아줄 body를 변수로 선언해줍니다.
import Foundation
struct Message {
let sender: String
let body: String
}
그 후, TableViewDelegate를 지정해준 후 아래의 코드를 작성해 줍니다.
extension ChatViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let messageCell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! MessageCell
messageCell.label.text = messages[indexPath.row].body
return messageCell
}
}
이제 사전 준비는 마쳤습니다!
Firebase에 데이터 저장하기
이제 메세지 보내는 버튼을 누르면 Firebase 데이터베이스에 저장이 되게 해주는 작업을 해보려고 합니다.
let db = Firestore.firestore()
var messages: [Message] = []
파이어베이스의 DB를 가져오고, 데이터를 담아줄 배열을 만들었습니다!
@IBAction func sendMessage(_ sender: UIButton) {
if let messageBody = messageTextField.text, let messageSender = Auth.auth().currentUser?.email {
db.collection("messages").addDocument(data: [
"sender": messageSender,
"body": messageBody,
"date": Date().timeIntervalSince1970
]) { (error) in
if let e = error {
print(e.localizedDescription)
} else {
print("Success save data ")
DispatchQueue.main.async {
self.messageTextField.text = ""
}
}
}
}
}
버튼을 누르는 IBAction을 만든 후, 위의 코드를 작성해줍니다. Auth.auth().currentUser?.email
은 현재 로그인 되어 있는 user의 이메일을 가져옵니다. db.collection("messages")
는 messages 라는 이름을 가진 collection을 찾은 후, 그 안에 데이터를 배열 형식으로 저장한다는 의미입니다. 클로저 내에서는 error가 발생했을 때의 분기처리를 해주었습니다. date도 함께 저장해주는 이유는 다시 불러올 때 시간 순서대로 정렬시키기 위해서입니다!!
Firebase에 데이터 불러오기
데이터를 저장은 했으니 이제 불러와야 합니다! 어떤 상황에 메세지가 업데이트되야할까 생각해보면, 처음 채팅화면에 들어왔을 때와 메세지가 업데이트 될 때입니다! 그래서 전 당연히 함수 하나를 생성한 후에 두군데에 그 함수를 호출해야 한다고 생각했습니다. 하지만 파이어베이스의 작동방법은 제가 생각한 것과 달랐습니다! 불러오는 함수를 만들 때, 실시간 업데이트를 할 수 있는 메소드가 있었습니다. 바로 addSnapshotListner()
메소드로 문서를 수신 대기 상태로 만들 수 있었습니다.
private func loadMessages() {
db.collection("messages")
.order(by: "date")
.addSnapshotListener { (querySnapshot, error) in
self.messages = []
if let e = error {
print(e.localizedDescription)
} else {
if let snapshotDocuments = querySnapshot?.documents {
snapshotDocuments.forEach { (doc) in
let data = doc.data()
if let sender = data["sender"] as? String, let body = data["body"] as? String {
self.messages.append(Message(sender: sender, body: body))
DispatchQueue.main.async {
self.messageTableView.reloadData()
self.messageTableView.scrollToRow(at: IndexPath(row: self.messages.count-1, section: 0), at: .top, animated: false)
}
}
}
}
}
}
}
위 함수를 viewDidLoad()에서 한번만 호출하면 됩니다.
Sender에 따른 채팅 분리
메세지까지 읽어오면 messages 배열에는 파이어베이스 데이터베이스의 데이터들이 담겨져있습니다. 이 데이터들을 이제 분리시켜서 테이블뷰에 뿌려주면 됩니다.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let message = messages[indexPath.row]
let messageCell = tableView.dequeueReusableCell(withIdentifier: "ReusableCell", for: indexPath) as! MessageCell
if message.sender == Auth.auth().currentUser?.email {
messageCell.leftImageView.isHidden = true
messageCell.rightImageView.isHidden = false
messageCell.messageView.backgroundColor = UIColor.lightGray
messageCell.label.textColor = UIColor.black
} else {
messageCell.leftImageView.isHidden = false
messageCell.rightImageView.isHidden = true
messageCell.messageView.backgroundColor = UIColor.black
messageCell.label.textColor = UIColor.white
}
messageCell.label.text = message.body
return messageCell
}
결과
실시간으로 업데이트가 되네요 ㅎㅎ
'iOS' 카테고리의 다른 글
[iOS] Thread란? 그리고 DispatchQueue란? (0) | 2020.11.21 |
---|---|
[Swift] 메모리 관리하기 (1) | 2020.11.08 |
[iOS] Firebase를 이용해 채팅앱 만들기 프로젝트 (1) - Authentication 서비스 (2) | 2020.10.16 |
[iOS] 폰트 크기에 따라 망가지는 Auto Layout 잡기 (2) | 2020.10.10 |
[Swift] Self와 self의 차이 (6) | 2020.10.03 |