Skip to content

Commit

Permalink
Merge pull request #102 from bengHak/feature/고병학/firebase-chat
Browse files Browse the repository at this point in the history
[네트워킹] 파이어베이스 RealtimeDatabase를 활용해서 채팅 기능 구현
  • Loading branch information
bengHak authored Nov 23, 2022
2 parents d07ca81 + fe400d8 commit 400dfc8
Show file tree
Hide file tree
Showing 35 changed files with 751 additions and 144 deletions.
52 changes: 47 additions & 5 deletions NearTalk/NearTalk.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ final class AppCoordinator: Coordinator {
extension AppCoordinator: LaunchScreenCoordinatorDependency {
func showMainViewController() {
self.navigationController?.popViewController(animated: false)
let rootTabBarCoordinator: RootTabBarCoordinator = RootTabBarDIContainer()
.makeTabBarCoordinator(navigationController: self.navigationController)
guard let rootTabBarCoordinator: RootTabBarCoordinator = RootTabBarDIContainer()
.makeTabBarCoordinator(navigationController: self.navigationController) else {
return
}
rootTabBarCoordinator.start()
}

func showLoginViewController() {
print(Self.self, #function)
self.navigationController?.viewControllers.insert(LoginViewController(), at: 0)
self.navigationController?.popViewController(animated: false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ final class ChatRoomListDIContainer {

// MARK: - Dependencies
struct Dependencies {
let apiDataTransferService: DefaultStorageService
let imageDataTransferService: DefaultStorageService
let apiDataTransferService: StorageService
let imageDataTransferService: StorageService
}

private let dependencies: Dependencies
Expand All @@ -42,6 +42,25 @@ final class ChatRoomListDIContainer {
}

// MARK: - Services
func makeFirestoreService() -> FirestoreService {
return DefaultFirestoreService()
}

func makeDatabaseService() -> RealTimeDatabaseService {
return DefaultRealTimeDatabaseService()
}

func makeAuthService() -> AuthService {
return DefaultFirebaseAuthService()
}

// MARK: - Repository
func makeProfileRepository() -> ProfileRepository {
return DefaultProfileRepository(
firestoreService: makeFirestoreService(),
firebaseAuthService: makeAuthService()
)
}

// MARK: - UseCases
func makeChatRoomListUseCase() -> FetchChatRoomUseCase {
Expand All @@ -50,7 +69,12 @@ final class ChatRoomListDIContainer {

// MARK: - Repositories
func makeRepository() -> ChatRoomListRepository {
return DefaultChatRoomListRepository(dataTransferService: dependencies.apiDataTransferService)
return DefaultChatRoomListRepository(
dataTransferService: dependencies.apiDataTransferService,
profileRepository: makeProfileRepository(),
databaseService: makeDatabaseService(),
firestoreService: makeFirestoreService()
)
}

// ExampleMVVM에서는 보여줄수 있는 Scene의 뷰컨트롤러와 뷰모델이 존재
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ final class FriendListDIContainer {
// MARK: - Dependencies
struct Dependencies {
let firestoreService: FirestoreService
let firebaseAuthService: FirebaseAuthService
let firebaseAuthService: AuthService
}

private let dependencies: Dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ final class LaunchScreenDIContainer {
// MARK: - Dependencies

// MARK: - Services
func makeAuthService() -> FirebaseAuthService {
func makeAuthService() -> AuthService {
return DefaultFirebaseAuthService()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// Created by Preston Kim on 2022/11/15.
//

import Foundation
import UIKit

final class DefaultOnboardingDIContainer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@
// Created by 고병학 on 2022/11/18.
//

import Foundation
import UIKit

final class RootTabBarDIContainer {
// MARK: - Dependencies

// MARK: - Services
func makeStorageService() -> StorageService {
return DefaultStorageService()
}

func makeFirebaseAuthService() -> AuthService {
return DefaultFirebaseAuthService()
}

func makeFirestoreService() -> FirestoreService {
return DefaultFirestoreService()
}

// MARK: - UseCases
func makeTabBarUseCase() -> TabBarUseCase {
Expand All @@ -27,30 +37,34 @@ final class RootTabBarDIContainer {
func makeViewModel() -> RootTabBarViewModel {
return DefaultRootTabBarViewModel()
}

#warning("mapViewController DI Container 필요")
#warning("chatRoomListViewController DI Container 필요")
#warning("friendListViewController DI Container 필요")
#warning("myProfileViewController DI Container 필요")

// MARK: - Create viewController
func createTabBarController() -> RootTabBarController {
let chatRoomListRepository = DefaultChatRoomListRepository(dataTransferService: DefaultStorageService())
let chatRoomListUseCase: FetchChatRoomUseCase = DefaultFetchChatRoomUseCase(chatRoomListRepository: chatRoomListRepository)

let myProfileDIContainer: MyProfileDIContainer = .init()
let myProfileVC: MyProfileViewController = .init(coordinator: myProfileDIContainer.makeMyProfileCoordinator(), viewModel: myProfileDIContainer.makeViewModel())

let dependency: RootTabBarControllerDependency = .init(
mapViewController: MainMapViewController(),
chatRoomListViewController: ChatRoomListViewController.create(with: DefaultChatRoomListViewModel(useCase: chatRoomListUseCase)),
friendListViewController: FriendListViewController(),
myProfileViewController: myProfileVC
)
func createTabBarController(dependency: RootTabBarControllerDependency) -> RootTabBarController {
return RootTabBarController(viewModel: makeViewModel(), dependency: dependency)
}

// MARK: - Coordinator
func makeTabBarCoordinator(navigationController: UINavigationController?) -> RootTabBarCoordinator {
return RootTabBarCoordinator(navigationController: navigationController)
func makeTabBarCoordinator(navigationController: UINavigationController?) -> RootTabBarCoordinator? {
let chatRoomListDIContainerDependency: ChatRoomListDIContainer.Dependencies = .init(
apiDataTransferService: self.makeStorageService(),
imageDataTransferService: self.makeStorageService()
)
let chatRoomListDIContainer: ChatRoomListDIContainer = .init(dependencies: chatRoomListDIContainerDependency)
let chatRoomListCoordinator: ChatRoomListCoordinator = chatRoomListDIContainer.makeChatRoomListCoordinator(navigationController: .init())

let friendListDIContainerDependencies: FriendListDIContainer.Dependencies = .init(
firestoreService: self.makeFirestoreService(),
firebaseAuthService: self.makeFirebaseAuthService()
)
let friendListDIContainer: FriendListDIContainer = .init(dependencies: friendListDIContainerDependencies)
let friendListCoordinator: FriendListCoordinator = friendListDIContainer.makeFriendListCoordinator(navigationController: .init())

let dependency: RootTabBarCoordinatorDependency = .init(
mainMapCoordinator: MainMapCoordinator(),
chatRoomListCoordinator: chatRoomListCoordinator,
friendListCoordinator: friendListCoordinator,
myProfileCoordinator: MyProfileCoordinator(navigationController: .init())
)
return RootTabBarCoordinator(navigationController: navigationController, dependency: dependency)
}
}
12 changes: 8 additions & 4 deletions NearTalk/NearTalk/Data/Repositories/ChatRoomListRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ protocol ChatRoomListRepository {
// fireBase 채팅방 목록 가져오기
func fetchChatRoomList() -> Observable<[ChatRoom]>
func fetchUserChatRoomModel() -> Observable<[UserChatRoomModel]>

// coreData 채팅방 목록 가져오기
// func fetchCoreDataChatRoomList() -> Observable<[ChatRoom]>


func createChatRoom(_ chatRoom: ChatRoom) -> Completable
func fetchChatRoomListWithCoordinates(southWest: NCLocation, northEast: NCLocation) -> Single<[ChatRoom]>
func fetchUserChatRoomUUIDList() -> Single<[String]>
func fetchChatRoomInfo(_ chatRoomID: String) -> Single<ChatRoom>
func observeChatRoomInfo(_ chatRoomID: String) -> Observable<ChatRoom>
func fetchUserChatRoomTickets() -> Single<[UserChatRoomTicket]>
func updateUserChatRoomTicket(_ ticket: UserChatRoomTicket) -> Completable
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import RxSwift

final class DefaultAuthRepository: AuthRepository {
private let authService: any FirebaseAuthService
private let authService: any AuthService

init(authService: any FirebaseAuthService) {
init(authService: any AuthService) {
self.authService = authService
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// DefaultChatMessageRepository.swift
// NearTalk
//
// Created by 고병학 on 2022/11/23.
//

import Foundation
import RxSwift

final class DefaultChatMessageRepository: ChatMessageRepository {
private let databaseService: RealTimeDatabaseService

init(databaseService: RealTimeDatabaseService) {
self.databaseService = databaseService
}

func sendMessage(_ message: ChatMessage) -> Completable {
self.databaseService.sendMessage(message)
}

func fetchMessage(page: Int, skip: Int, count: Int, roomID: String) -> Single<[ChatMessage]> {
self.databaseService.fetchMessages(page: page, skip: skip, pageCount: count, roomID: roomID)
}

func observeChatRoomMessages(roomID: String) -> Observable<ChatMessage> {
self.databaseService.observeNewMessage(roomID)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,81 @@ import RxSwift

final class DefaultChatRoomListRepository {

private let dataTransferService: StorageService
private let dataTransferService: any StorageService
private let databaseService: any RealTimeDatabaseService
private let firestoreService: any FirestoreService
private let profileRepository: any ProfileRepository
private(set) var dummyData: ChatRoomDummyData = ChatRoomDummyData()

init(dataTransferService: StorageService) {
init(
dataTransferService: any StorageService,
profileRepository: any ProfileRepository,
databaseService: any RealTimeDatabaseService,
firestoreService: any FirestoreService
) {
self.dataTransferService = dataTransferService
self.profileRepository = profileRepository
self.databaseService = databaseService
self.firestoreService = firestoreService
}
}

extension DefaultChatRoomListRepository: ChatRoomListRepository {
func createChatRoom(_ chatRoom: ChatRoom) -> Completable {
Single.zip(
self.firestoreService.create(data: chatRoom, dataKey: .chatRoom),
self.databaseService.createChatRoom(chatRoom)
).asCompletable()
}

func fetchChatRoomListWithCoordinates(southWest: NCLocation, northEast: NCLocation) -> Single<[ChatRoom]> {
let queryList: [FirebaseQueryDTO] = [
.init(key: "latitude", value: southWest.latitude, queryKey: .isGreaterThan),
.init(key: "latitude", value: northEast.latitude, queryKey: .isLessThan),
.init(key: "longitude", value: southWest.latitude, queryKey: .isGreaterThan),
.init(key: "longitude", value: northEast.latitude, queryKey: .isLessThan)
]
return self.firestoreService.fetchList(dataKey: .chatRoom, queryList: queryList)
}

func fetchUserChatRoomUUIDList() -> Single<[String]> {
self.profileRepository
.fetchMyProfile()
.flatMap { [weak self] (profile: UserProfile) in
guard let self,
let uuid: String = profile.uuid else {
throw ChatRoomListRepositoryError.failedToFetch
}
return self.databaseService.fetchUserChatRoomTicketList(uuid)
}
.asObservable()
.map { (tickets: [UserChatRoomTicket]) in
return tickets.compactMap({ $0.roomID })
}.asSingle()
}

func fetchChatRoomInfo(_ chatRoomID: String) -> Single<ChatRoom> {
self.databaseService.fetchChatRoomInfo(chatRoomID)
}

func observeChatRoomInfo(_ chatRoomID: String) -> Observable<ChatRoom> {
self.databaseService.observeChatRoomInfo(chatRoomID)
}

func fetchUserChatRoomTickets() -> Single<[UserChatRoomTicket]> {
self.profileRepository.fetchMyProfile()
.flatMap { [weak self] (profile: UserProfile) in
guard let self,
let uuid: String = profile.uuid else {
throw ChatRoomListRepositoryError.failedToFetch
}
return self.databaseService.fetchUserChatRoomTicketList(uuid)
}
}

func updateUserChatRoomTicket(_ ticket: UserChatRoomTicket) -> Completable {
self.databaseService.updateUserChatRoomTicket(ticket)
}

func fetchChatRoomList() -> Observable<[ChatRoom]> {
return Observable<[ChatRoom]>.create { observer in
Expand Down Expand Up @@ -60,3 +126,8 @@ struct ChatRoomDummyData {
userChatRoomData.append(UserChatRoomModel(userID: "s001", chatRoomID: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]))
}
}

enum ChatRoomListRepositoryError: Error {
case failedToFetch
case failedToCrate
}
25 changes: 25 additions & 0 deletions NearTalk/NearTalk/Data/Repositories/DefaultMediaRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// DefaultMediaRepository.swift
// NearTalk
//
// Created by 고병학 on 2022/11/23.
//

import Foundation
import RxSwift

final class DefaultMediaRepository: MediaRepository {
private let storageService: StorageService

init(storageService: StorageService) {
self.storageService = storageService
}

func uploadImage(_ imageData: Data) -> RxSwift.Single<String> {
self.storageService.uploadData(data: imageData, fileName: "\(UUID().uuidString).jpg", dataType: .images)
}

func uploadVideo(_ videoData: Data) -> RxSwift.Single<String> {
self.storageService.uploadData(data: videoData, fileName: "\(UUID().uuidString).mov", dataType: .videos)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import RxSwift

final class DefaultProfileRepository: ProfileRepository {
private let firestoreService: FirestoreService
private let firebaseAuthService: FirebaseAuthService
private let firebaseAuthService: AuthService

init(firestoreService: FirestoreService, firebaseAuthService: FirebaseAuthService) {
init(firestoreService: FirestoreService, firebaseAuthService: AuthService) {
self.firestoreService = firestoreService
self.firebaseAuthService = firebaseAuthService
}
Expand Down
1 change: 1 addition & 0 deletions NearTalk/NearTalk/Domain/Entities/ChatRoom.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ struct ChatRoom: BaseEntity {
var accessibleRadius: Double?
var recentMessageID: String?
var maxNumberOfParticipants: Int?
var messageCount: Int?
}
16 changes: 16 additions & 0 deletions NearTalk/NearTalk/Domain/Entities/UserChatRoomTicket.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// UserChatRoomTicket.swift
// NearTalk
//
// Created by 고병학 on 2022/11/23.
//

import Foundation

struct UserChatRoomTicket: BaseEntity {
var uuid: String?
var userID: String?
var roomID: String?
var lastReadMessageID: String?
var lastRoomMessageCount: Int?
}
Loading

0 comments on commit 400dfc8

Please sign in to comment.