AutoLayout
storyboard ID๋ฅผ ์ค์ ํด๋ id๋ฅผ ์ฐพ์ ์ ์๋ค๋ ์๋ฌ๊ฐ ๋ฐ์ํจ.
Storyboard doesn't contain a view controller with identifier "ChattingViewController"
์ํฉ ์ค๋ช
Main View Controller์์ Sign In ๋ฒํผ์ ํด๋ฆญํ๋ฉด, ChattingViewController๊ฐ ๋์์ผํจ.
ChattingViewController ์ค์
@IBAction func didTappedSignInButton(_ sender: UIButton) {
guard let email = emailTextField.text,
let password = passwordTextField.text else { return }
Auth.auth().signIn(withEmail: email, password: password) { [weak self] result, error in
guard let strongSelf = self else { return }
if let error { return }
guard let viewController = self?.storyboard?.instantiateViewController(withIdentifier: "ChattingViewController") as? ChattingViewController else { return }
self?.present(viewController, animated: true, completion: nil)
}
}
์์ธ๊ณผ ํด๊ฒฐ๊ณผ์
Main.storyboard์์ storyboard๋ฅผ ๊ด๋ฆฌํ์ง ์๊ณ , storyboard reference๋ฅผ ์ฌ์ฉํ๊ณ ์์๋ค.
๊ทธ๋ฐ๋ฐ ํด๋น reference๋ฅผ ์ ์ธํด์ฃผ์ง ์์์ self.storyboard์์ ํด๋น ID๋ฅผ ์ฐพ์ ์ ์๋ ๊ฒ์ด์๋ค.
๊ทธ๋์ storyboard reference๋ฅผ ๋ค์ ์ ์ธํด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค. (push๋ก ๋ณด์ฌ์คฌ๋ ์ฐ๊ฒฐ์ ๋๊ณ ์ญ์ ํ์๋ค)
์ถ๊ฐ. ์๋ก์ด ๋ค๋น๊ฒ์ด์
์ผ๋ก ์ด๋ํ๋ ๋ฐฉ๋ฒ
// MARK: function
private func moveUserListViewController() {
let storyboard = UIStoryboard(name: "UserList", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "UserList") as! UserListViewController
let navigationController = UINavigationController(rootViewController: viewController)
navigationController.modalPresentationStyle = .fullScreen
self.present(navigationController, animated: true, completion: nil)
}
// MARK: IBAction
@IBAction func didTappedSignInButton(_ sender: UIButton) {
guard let email = emailTextField.text,
let password = passwordTextField.text else { return }
Auth.auth().signIn(withEmail: email, password: password) { [weak self] result, error in
guard let strongSelf = self else { return }
if let error {
let alert = UIAlertController(title: "์ค๋ฅ", message: "์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.", preferredStyle: .alert)
return
}
self?.moveUserListViewController()
}
}
Firestore
FIRInvalidArgumentException', reason: 'Unsupported type: __SwiftValue
์ฑํ
๋ฐฉ์ ์์ฑํ๊ธฐ ์ํด, ์ฐธ์ฌ์๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์ฌ ์ฑํ
๋ฐฉ์ด ์กด์ฌํ๋์ง, ์กด์ฌํ์ง ์๋์ง ํ์ธํ๊ธฐ ์ํด ์ฝ๋๋ฅผ ์์ฑํ๋ค.
๊ทธ๋์ users์ [currentUser, tableView์์ ์ ํํ user]๋ฅผ ๊ธฐ์ค์ผ๋ก chatroom์ ์กด์ฌํ๋ ๋ฐฉ์ด ์๋์ง ์ฝ๋๋ฅผ ์์ฑํ๋ค.
๊ทธ๋ฐ๋ฐ FIRInvalidArgumentException', reason: 'Unsupported type: __SwiftValue ์ด ์๋ฌ๊ฐ ๋ฐ์ํ๋ค;;
๊ฒฐ๋ก : ๋น์ด์๋ collection์ ์ ๊ทผํ๊ธฐ ๋๋ฌธ์ด๋ค.
chatrooms collection๊ณผ documents๊ฐ ๋ชจ๋ ๋น์ด์์๋ค.
๊ทธ๋์ ๋ด๊ฐ ์ ์ธํ codable๋๋ก ๋น์ด์๋ ๋ฐ์ดํฐ ํ๋๋ฅผ ๋ง๋ค์ด์ฃผ๋ ์กฐํ๊ฐ ์ ๋๋ค..... ๋ด ์ฝ์ง...
Chat์ ๋ณํ๋ง ๊ฐ์งํ ์ ์์๊น?
์ฐ์ ์ ์ฌ์ง์์ ๋ณผ ์ ์๋ฏ ๋๋ Chat Rooms๋ฅผ Collection์ผ๋ก ๋ง๋ค์๋ค. ์ฆ, ์ฑํ
์ด ๋ณํ๋ฉด, ChatRoom์ด ๋ณํ๊ฒ ๋๋ ๊ฒ์ด๊ณ , ๊ทธ๊ฒ์ ๋ํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ค ๋ค๋ ๊ฒ์ด๋ค.
์ด๋ด ๋ ์ด๋ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋๋! ๋ณ๊ฒฝ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ tableview๋ฅผ reloadํด์ฃผ๊ธฐ ๋๋ฌธ์, ์ ์ฒด ์ฑํ
์ด ์๋ก๊ณ ์นจ๋๊ฒ ๋๋ค. (๋ฐฑ๋ฌธ๋ถ์ฌ์ผ๊ฒฌ ์์์ ๋ณด์)
๋ฌธ์ ๊ฐ ๋ ์์
๋ฌธ์ ๊ฐ ๋ ์ฝ๋
func setUpDataListener(id: String, completion: @escaping ([Chat]) -> Void) {
let reference = database.collection("chatRooms").document(id)
reference.addSnapshotListener {document, error in
guard let document, document.exists,
let chatData = document.data()?["chat"] as? [[String: Any]] else {
print("invalid format")
return
}
do {
let data = try JSONSerialization.data(withJSONObject: chatData, options: [])
let chats = try JSONDecoder().decode([Chat].self, from: data)
completion(chats)
} catch { }
}
}
reference์ธ ChatRooms์ snapshot Listner๋ฅผ ๋ฌ์๋ค. ๊ทธ๋์ Chat์ด ์ถ๊ฐ๋๋ฉด, ChatRoom์ ๋ณํ๊ฐ ๊ฐ์ง๋๊ณ , ๊ทธ ๊ฒฐ๊ณผ chatData์ ["chat"]์ผ๋ก ์ค๋ ๊ฐ๋ค์ด ์ถ๊ฐ๋๊ฒ ๋๋ค ใ
ใ
ํด๊ฒฐ ๋ฐฉ๋ฒ
์ฟผ๋ฆฌ์ listener๋ฅผ ๋ฌ์๋ณด์ -> ์คํจ.. (์กฐ๊ธ ๋ ๊ณต๋ถํด์ผ๊ฒ ๋ค)
listener๋ ๊ทธ๋๋ก ๋๋, View Controller์์ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ ๋ฐฉ์์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค.
private func setupChatDataListener() {
guard let chattingRoomID = chattingRoom?.uuid else { return }
ChatManager.shared.setUpDataListener(id: chattingRoomID) { [weak self] chats in
for chat in chats {
self?.chatDatas.append(chat)
let index = IndexPath(row: (self?.chatDatas.count ?? 0) - 1, section: 0)
self?.chattingTableView.insertRows(at: [index], with: .bottom)
self?.chattingTableView.scrollToRow(at: index, at: .bottom, animated: true)
}
}
}
๊ธฐ์กด ์ฝ๋์์๋ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ๋ถ๋ฌ์ค๊ณ , ๊ธฐ์กด Chat data๋ฅผ ์๋ก์ด ๋ฐ์ดํฐ๋ก ๋ณ๊ฒฝํ๋ ๋ฐฉ์์ ์ฌ์ฉํ์๋ค. ์์ ์ ๋ชฐ๋์ง๋ง ์ด ์ฝ๋ ์ฐ๋ฉด, A, B, C๊ฐ ์์ ๋ ์๋ก์ด D๊ฐ ๋ค์ด์จ๋ค๋ฉด A, B, C, A, B, C, D ์ด๋ฐ์์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์ฑํ
๋ฐฉ์ ๋จ์์์์ ๊ฒ์ด๋ค.
private func setupChatDataListener() {
guard let chattingRoomID = chattingRoom?.uuid else { return }
ChatManager.shared.setUpDataListener(id: chattingRoomID) { [weak self] chats in
var newChat = [Chat]()
for chat in chats {
newChat.append(chat)
}
newChat = (self?.filterNewChatting(newData: newChat, oldData: self?.chatDatas))!
self?.chatDatas.append(contentsOf: newChat)
self?.chattingTableView.reloadData()
let index = IndexPath(row: (self?.chatDatas.count ?? 1) - 1, section: 0)
self?.chattingTableView.scrollToRow(at: index, at: .bottom, animated: true)
}
}
private func filterNewChatting(newData: [Chat], oldData: [Chat]?) -> [Chat] {
guard let oldData = oldData else { return [] }
var newChat = [Chat]()
for chat in newData {
if !oldData.contains(chat) {
newChat.append(chat)
}
}
return newChat
}
๊ธฐ์กด๊ณผ ๋์ผํ๊ฒ Chat Rooms์ ์๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ์ง๋ง, filterNewChatting์ด๋ผ๋ ํจ์๋ฅผ ๋ง๋ค์ด์ ๊ธฐ์กด ์ฑํ
์ ์๋ ์ฑํ
๋ง ๊ฐ์ ธ์ค๋๋ก ์์ ํ๋ค.
์ด๋ฅผ ์ํด์๋ Chat ๋ชจ๋ธ์ Hashable ํ๋กํ ์ฝ์ ์ฌ์ฉํด์ผํ๋ค. (contains๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์)