프로젝트/ChatApp ver.1 (w/Firebase)

04. Firebase에 쓰기

걔랑계란 2022. 10. 15. 00:39

메서드 구현


import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift

class MessagesManager: ObservableObject {
	@Published private(set) var messages: [Message] = []
    
	let db = Firestore.firestore()
	
	init() {
		getMessages()
	}
	
	func getMessages() {
		db.collection("messages").addSnapshotListener { querySnapshot, error in
			guard let documents = querySnapshot?.documents else {
				print("Error fetching documents: \(String(describing: error))")
				return
			}
			
			self.messages = documents.compactMap { document -> Message? in
				do {
					return try document.data(as: Message.self)
				} catch {
					print("Error decoding document into Message: \(error)")
					return nil
				}
			}
			
			self.messages.sort { $0.timestamp < $1.timestamp }
		}
	}
	
	func sendMessage(text: String) {
		do {
			let newMessage = Message(id: "\(UUID())", text: text, received: false, timestamp: Date())
			
			try db.collection("messages").document().setData(from: newMessage)
		} catch {
			print("Error adding message to Firestore: \(error)")
		}
	}
}

MessageManager에 새로운 메서드 sendMessage를 추가했다.

	func sendMessage(text: String) {
		do {
			let newMessage = Message(id: "\(UUID())", text: text, received: false, timestamp: Date())
			
			try db.collection("messages").document().setData(from: newMessage)
		} catch {
			print("Error adding message to Firestore: \(error)")
		}
	}

메서드는 getMessages 메서드와 반대로 Firebase에 데이터를 저장하는 역할을 한다.

TextField에서 입력된 내용을 파라미터로 받아 새로운 Message 객체를 생성하고, 이를 Firebase에 저장한다.
id를 생성할 때 사용한 UUID는 Swift에서 흔하게 사용하는 무작위 변수를 생성하는 메서드이다.

 

Apple Developer Documentation

 

developer.apple.com

해당 메서드에 관한 내용은 위의 링크에서 확인할 수 있다.

 

메서드 호출하기


import SwiftUI

struct MessageField: View {
	@EnvironmentObject var messagesManager: MessagesManager
	@State private var message = ""
	
    var body: some View {
		HStack {
			CustomTextField(placeholder: Text("Type your message."), text: $message)
			
			Button{
				messagesManager.sendMessage(text: message)
				message = ""
			} label: {
				Image(systemName: "paperplane.fill")
					.foregroundColor(.white)
					.padding(10)
					.background(Color("Yellow"))
					.cornerRadius(50)
			}
		}
		.padding(.horizontal)
		.padding(.vertical, 10)
		.background(Color("Gray"))
		.cornerRadius(50)
		.padding()
    }
}

struct MessageField_Previews: PreviewProvider {
    static var previews: some View {
        MessageField()
			.environmentObject(MessagesManager())
    }
}

struct CustomTextField: View {
	var placeholder: Text
	@Binding var text: String
	var editingChanged: (Bool) -> () = {_ in}
	var commit: () -> () = {}
	
	var body: some View {
		ZStack(alignment: .leading) {
			if text.isEmpty {
				placeholder
					.opacity((0.5))
			}
			
			TextField("", text: $text, onEditingChanged: editingChanged, onCommit: commit)
		}
	}
}

실제로 메서드를 호출하는 곳은 TextField의 Button이다.
따라서 해당 View에서 EnvironmentObject 변수로 MessageManager 형식의 인스턴스를 하나 생성한다.
EnvironmentObject를 추가했기 때문에 Preview에도 이를 적용해야 함을 잊지 말자.

Button{
				messagesManager.sendMessage(text: message)
				message = ""
			} label: {
				Image(systemName: "paperplane.fill")
					.foregroundColor(.white)
					.padding(10)
					.background(Color("Yellow"))
					.cornerRadius(50)
			}

버튼은 이제 sendMessage 메서드를 호출하기만 하면 된다.

import SwiftUI

struct ContentView: View {
	@StateObject var messagesManager = MessagesManager()
	
    var body: some View {
		VStack {
			VStack {
				TitleRow()
				
				ScrollViewReader { proxy in
					ScrollView {
						ForEach(messagesManager.messages, id: \.id) { message in
							MessageBubble(message: message)
						}
					}
					.padding(.top, 10)
					.background(.white)
					.cornerRadius(30, corners: [.topLeft, .topRight])
					.onChange(of: messagesManager.lastMessageId) { id in
						withAnimation {
							proxy.scrollTo(id, anchor: .bottom)
						}
					}
				}
			}
			.background(Color("Yellow"))
			
			MessageField()
				.environmentObject(MessagesManager())
		}
		.onTapGesture {
			hideKeyboard()
		}
    }
}

#if canImport(UIKit)
extension View {
	func hideKeyboard() {
		UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
	}
}
#endif

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

MessageField에 생성된 MessagesManager 변수는 MessageField를 호출하는 ContentView에서 전달하게 된다.
11번 라인의 messageManager를 그대로 전달하면 된다.

전송한 메시지가 서버에 즉시 저장되는 걸 확인할 수 있다.

'프로젝트 > ChatApp ver.1 (w/Firebase)' 카테고리의 다른 글

05. 더 나아가기  (0) 2022.10.15
03. Firebase 초기화 및 Swift에서 사용하기  (0) 2022.10.13
02. Firebase 연결하기  (0) 2022.10.13
01. 인터페이스 디자인  (0) 2022.10.11
00. 시작하며  (0) 2022.10.11