Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 레포지토리 리펙토링 #175

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
32a280d
fix: 온보딩에서 로비뷰로 이동할 경우 로컬 데이터를 가져와서 push를 2번하는 이슈 해결
Sonny-Kor Dec 1, 2024
41a6054
refactor: Entity Custom StringConvertible 추가
Sonny-Kor Dec 1, 2024
84e91f4
refactor: Main Repository에서 Room 하나의 스트림만 가지도록 변경
Sonny-Kor Dec 1, 2024
5e7383b
feat: Network Helper 만드는 중
Sonny-Kor Dec 1, 2024
57be0b6
fix: 임시 network helper 삭제
Sonny-Kor Dec 2, 2024
e0e715c
merge: dev 와 머지
Sonny-Kor Dec 2, 2024
7ede47c
chore: myID 수정
Sonny-Kor Dec 2, 2024
82d36d6
feat: ASMusicKit에 randomSong() 함수 추가, search @MainActor 삭제
moral-life Dec 2, 2024
90036b9
feat: SelectMusic 화면에서, 타임 아웃 될 때까지 아무 노래도 고르지 않으면 랜덤 곡 제출 구현
moral-life Dec 2, 2024
37d7f55
fix: 치명적인 로직 수정
moral-life Dec 2, 2024
815e159
feat: onboarding 화면 키보드 오류 해결
moral-life Dec 2, 2024
a78b909
chore: UIViewController+Keyboard 없애고 OnboardingViewController extensi…
moral-life Dec 2, 2024
0cfda73
chore: 단순 print Log 삭제
moral-life Dec 3, 2024
e33ec15
fix: 노래 선택할 때 중지가 아니라 정지 버튼으로 수정
Sonny-Kor Dec 2, 2024
6219078
fix: 노래 선택할 때 선택한 노래가 없으면 재생버튼 막기
Sonny-Kor Dec 2, 2024
de592a0
fix: 노래 검색 중일때만 progress 동작
Sonny-Kor Dec 2, 2024
d169840
fix: 노래 선택, 키보드 안내려가는 문제 해결
Sonny-Kor Dec 2, 2024
4f1d357
chore: 완료버튼 없어져서 다시 수정
Sonny-Kor Dec 2, 2024
8b3098a
chore: 네비게이션 Toolbar topBarTrailing으로 수정
Sonny-Kor Dec 3, 2024
2275d32
feat: Network Helper 만드는 중
Sonny-Kor Dec 1, 2024
a53414e
rebase: dev
Sonny-Kor Dec 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions alsongDalsong/ASEntity/ASEntity/Answer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,12 @@ extension Answer {
music: Music.musicStub4,
playlist: Playlist())
}

extension Answer: CustomStringConvertible {
public var description: String {
return """
player: \(player?.description ?? "nil")
music: \(music?.description ?? "nil")
"""
}
}
14 changes: 11 additions & 3 deletions alsongDalsong/ASEntity/ASEntity/GameState.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
public struct GameState {
public struct GameState: Equatable {
public let mode: Mode?
public let recordOrder: UInt8?
public let status: Status?
Expand Down Expand Up @@ -150,9 +150,17 @@ public enum GameViewType {
case .submitMusic:
(systemName: "music.note.list", color: "508DFD")
case .humming:
(systemName: "microphone", color: "FD5050")
if #available(iOS 18.0, *) {
(systemName: "microphone", color: "FD5050")
} else {
(systemName: "mic", color: "FD5050")
}
Comment on lines +153 to +157
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버전이 바뀌면서 Symbol 명이 바뀌었나 보군요 ㄷㄷㄷ

case .rehumming:
(systemName: "microphone", color: "FD5050")
if #available(iOS 18.0, *) {
(systemName: "microphone", color: "FD5050")
} else {
(systemName: "mic", color: "FD5050")
}
case .submitAnswer:
(systemName: "music.note.list", color: "508DFD")
case .result:
Expand Down
6 changes: 6 additions & 0 deletions alsongDalsong/ASEntity/ASEntity/Music.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@ extension Music {
public static let musicStub3 = Music(title: "으아~", artist: "김흥국")
public static let musicStub4 = Music(title: "이브, 프시케 그리고 푸른 수염의 아내", artist: "르세라핌")
}

extension Music: CustomStringConvertible {
public var description: String {
return "\(title ?? "Unknown") - \(artist ?? "Unknown")"
}
}
6 changes: 6 additions & 0 deletions alsongDalsong/ASEntity/ASEntity/Player.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ extension Player {
public static let playerStub3: Player = Player(id: "2", avatarUrl: nil, nickname: "Moral-life", score: nil, order: 2)
public static let playerStub4: Player = Player(id: "3", avatarUrl: nil, nickname: "Sang₩", score: nil, order: 3)
}

extension Player: CustomStringConvertible {
public var description: String {
return "\(nickname ?? "Unknown")"
}
}
9 changes: 9 additions & 0 deletions alsongDalsong/ASEntity/ASEntity/Record.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,12 @@ extension Record {
public static let recordStub4_2 = Record(player: Player.playerStub4, recordOrder: 1, fileUrl: stubm4aData)
public static let recordStub4_3 = Record(player: Player.playerStub4, recordOrder: 2, fileUrl: stubm4aData)
}

extension Record: CustomStringConvertible {
public var description: String {
return """
player: \(player?.description ?? "nil")
recordOrder: \(String(describing: recordOrder))
"""
}
}
19 changes: 19 additions & 0 deletions alsongDalsong/ASEntity/ASEntity/Room.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,22 @@ public struct Room: Codable {
self.submits = submits
}
}

extension Room: CustomStringConvertible {
public var description: String {
return """
number: \(number ?? "nil")
host: \(host?.description ?? "nil")
players: \(players?.description ?? "nil")
mode: \(mode?.title ?? "nil")
round: \(round ?? 0)
status: \(status?.description ?? "nil")
recordOrder: \(recordOrder ?? 0)
records: \(records?.description ?? "nil")
answers: \(answers?.description ?? "nil")
dueTime: \(dueTime?.description ?? "nil")
selectedRecords: \(selectedRecords?.description ?? "nil")
submits: \(submits?.description ?? "nil")
"""
}
}
17 changes: 17 additions & 0 deletions alsongDalsong/ASEntity/ASEntity/Status.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,20 @@ public enum Status: String, Codable {
case hint
case result
}

extension Status: CustomStringConvertible {
public var description: String {
switch self {
case .humming:
return "humming"
case .rehumming:
return "rehumming"
case .waiting:
return "waiting"
case .hint:
return "hint"
case .result:
return "result"
}
}
}
38 changes: 37 additions & 1 deletion alsongDalsong/ASMusicKit/ASMusicKit/ASMusicAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import MusicKit

public struct ASMusicAPI {
public init() {}

/// MusicKit을 통해 Apple Music의 노래를 검색합니다.
/// - Parameters:
/// - text: 검색 요청을 보낼 검색어
/// - maxCount: 검색해서 찾아올 음악의 갯수 기본값 설정은 25
/// - Returns: Music의 배열
@MainActor
public func search(for text: String, _ maxCount: Int = 10, _ offset: Int = 0) async throws -> [Music] {
let status = await MusicAuthorization.request()
switch status {
Expand Down Expand Up @@ -61,18 +61,54 @@ public struct ASMusicAPI {
throw ASMusicError.notAuthorized
}
}

public func randomSong(from playlistId: String) async throws -> ASEntity.Music {
let status = await MusicAuthorization.request()
switch status {
case .authorized:
do {
let request = MusicCatalogResourceRequest<MusicKit.Playlist>(matching: \.id, equalTo: MusicItemID(rawValue: playlistId))
let playlistResponse = try await request.response()
let playlist = playlistResponse.items.first!

let playlistWithTrack = try await playlist.with([.tracks])
guard let tracks = playlistWithTrack.tracks else {
throw ASMusicError.playListHasNoSongs
}

if let song = tracks.randomElement() {
return ASEntity.Music(
id: song.id.rawValue,
title: song.title,
artist: song.artistName,
artworkUrl: song.artwork?.url(width: 300, height: 300),
previewUrl: song.previewAssets?.first?.url,
artworkBackgroundColor: song.artwork?.backgroundColor?.toHex()
)
}
} catch {
throw ASMusicError.playListHasNoSongs
}
default:
throw ASMusicError.notAuthorized
}
return ASEntity.Music(id: "nil", title: nil, artist: nil, artworkUrl: nil, previewUrl: nil, artworkBackgroundColor: nil)
}
}

public enum ASMusicError: Error, LocalizedError {
case notAuthorized
case searchError
case playListHasNoSongs

public var errorDescription: String? {
switch self {
case .notAuthorized:
"애플 뮤직에 접근하는 권한이 없습니다."
case .searchError:
"노래 검색 중 오류가 발생했습니다."
case .playListHasNoSongs:
"플레이리스트에 노래가 없습니다."
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import ASEntity
import ASLogKit
import Combine
@preconcurrency internal import FirebaseFirestore

public final class ASFirebaseDatabase: ASFirebaseDatabaseProtocol {
private let firestoreRef = Firestore.firestore()
private var roomListeners: ListenerRegistration?
private var roomPublisher = PassthroughSubject<Room, Error>()
private var roomPublisher = CurrentValueSubject<Room?, Error>(nil)

public func addRoomListener(roomNumber: String) -> AnyPublisher<Room, Error> {
let roomRef = firestoreRef.collection("rooms").document(roomNumber)
Expand All @@ -18,19 +19,28 @@ public final class ASFirebaseDatabase: ASFirebaseDatabaseProtocol {
return self.roomPublisher.send(completion: .failure(ASNetworkErrors.FirebaseListenerError))
}

if document.metadata.isFromCache {
Logger.debug("로컬 캐시에서 데이터를 가져온 경우")
return
}

do {
let room = try document.data(as: Room.self)
Logger.debug("방 정보를 가져왔습니다.\n\(room)")
return self.roomPublisher.send(room)
} catch {
return self.roomPublisher.send(completion: .failure(ASNetworkErrors.FirebaseListenerError))
}
}

roomListeners = listener
return roomPublisher.eraseToAnyPublisher()
return roomPublisher
.compactMap { $0 }
.eraseToAnyPublisher()
}

public func removeRoomListener() {
roomPublisher.send(nil)
roomListeners?.remove()
}
}
15 changes: 13 additions & 2 deletions alsongDalsong/ASNetworkKit/ASNetworkKit/FirebaseEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public struct FirebaseEndpoint: Endpoint, Equatable {
public init(path: Path, method: HTTPMethod) {
self.path = path
self.method = method
headers = [:]
self.headers = [:]
}

// TODO: - firebase api/cloud func에 맞는 path 넣기
Expand All @@ -29,7 +29,7 @@ public struct FirebaseEndpoint: Endpoint, Equatable {
case submitMusic
case submitAnswer
case resetGame

public var description: String {
switch self {
case .auth:
Expand Down Expand Up @@ -69,4 +69,15 @@ public extension FirebaseEndpoint {
Self(path: .auth, method: .get)
.update(\.queryItems, with: [.init(name: "listAvatarUrls", value: "true")])
}

func withCommonQueryItems(roomNumber: String?, userID: String?) -> Self {
var items = [URLQueryItem]()
if let userID {
items.append(URLQueryItem(name: "userId", value: userID))
}
if let roomNumber {
items.append(URLQueryItem(name: "roomNumber", value: roomNumber))
}
return self.update(\.queryItems, with: items)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import Foundation

public protocol MainRepositoryProtocol {
var myId: String? { get }
var number: CurrentValueSubject<String?, Never> { get }
var host: CurrentValueSubject<Player?, Never> { get }
var players: CurrentValueSubject<[Player]?, Never> { get }
var mode: CurrentValueSubject<Mode?, Never> { get }
var round: CurrentValueSubject<UInt8?, Never> { get }
var status: CurrentValueSubject<Status?, Never> { get }
var recordOrder: CurrentValueSubject<UInt8?, Never> { get }
var records: CurrentValueSubject<[ASEntity.Record]?, Never> { get }
var answers: CurrentValueSubject<[Answer]?, Never> { get }
var dueTime: CurrentValueSubject<Date?, Never> { get }
var selectedRecords: CurrentValueSubject<[UInt8]?, Never> { get }
var submits: CurrentValueSubject<[Answer]?, Never> { get }
var room: CurrentValueSubject<Room?, Never> { get }

func connectRoom(roomNumber: String)
func disconnectRoom()


func createRoom(nickname: String, avatar: URL) async throws -> String
func joinRoom(nickname: String, avatar: URL, roomNumber: String) async throws -> Bool
func leaveRoom() async throws -> Bool
func startGame() async throws -> Bool
func changeMode(mode: Mode) async throws -> Bool
func changeRecordOrder() async throws -> Bool
func resetGame() async throws -> Bool
func submitAnswer(answer: Music) async throws -> Bool
func getAvatarUrls() async throws -> [URL]
func getResource(url: URL) async throws -> Data

func submitMusic(answer: ASEntity.Music) async throws -> Bool
func postRecording(_ record: Data) async throws -> Bool
func postResetGame() async throws -> Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ public protocol RoomActionRepositoryProtocol: Sendable {
func createRoom(nickname: String, avatar: URL) async throws -> String
func joinRoom(nickname: String, avatar: URL, roomNumber: String) async throws -> Bool
func leaveRoom() async throws -> Bool
func startGame(roomNumber: String) async throws -> Bool
func changeMode(roomNumber: String, mode: Mode) async throws -> Bool
func changeRecordOrder(roomNumber: String) async throws -> Bool
func startGame() async throws -> Bool
func changeMode(mode: Mode) async throws -> Bool
func changeRecordOrder() async throws -> Bool
func resetGame() async throws -> Bool
}

public protocol MusicRepositoryProtocol {
func getMusicData(url: URL) async -> Data?
func getMusicData(url: URL) async throws -> Data?
}

public protocol GameStateRepositoryProtocol {
Expand All @@ -71,5 +71,5 @@ public protocol GameStateRepositoryProtocol {

public protocol HummingResultRepositoryProtocol {
func getResult() -> AnyPublisher<[(answer: Answer, records: [ASEntity.Record], submit: Answer, recordOrder: UInt8)], Never>
func getRecordData(url: URL) -> Future<Data?, Error>
func getRecordData(url: URL) async throws -> Data
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
import ASDecoder
import ASEncoder
import ASEntity
import ASNetworkKit
import ASRepositoryProtocol
import Combine
import Foundation
import ASRepositoryProtocol

public final class AnswersRepository: AnswersRepositoryProtocol {
private var mainRepository: MainRepositoryProtocol
private var networkManager: ASNetworkManagerProtocol
public init(mainRepository: MainRepositoryProtocol, networkManager: ASNetworkManagerProtocol) {

public init(mainRepository: MainRepositoryProtocol) {
self.mainRepository = mainRepository
self.networkManager = networkManager
}

public func getAnswers() -> AnyPublisher<[Answer], Never> {
mainRepository.answers
mainRepository.room
.receive(on: DispatchQueue.main)
.compactMap { $0 }
.compactMap { $0?.answers }
.removeDuplicates()
.eraseToAnyPublisher()
}

public func getAnswersCount() -> AnyPublisher<Int, Never> {
mainRepository.answers
mainRepository.room
.receive(on: DispatchQueue.main)
.compactMap { $0 }
.map { $0.count }
.compactMap { $0?.answers }
.map(\.count)
.removeDuplicates()
.eraseToAnyPublisher()
}

Expand All @@ -34,9 +32,10 @@ public final class AnswersRepository: AnswersRepositoryProtocol {
return Just(nil).eraseToAnyPublisher()
}

return mainRepository.answers
return mainRepository.room
.receive(on: DispatchQueue.main)
.compactMap(\.self)
.compactMap { $0?.answers }
.removeDuplicates()
.flatMap { answers in
Just(answers.first { $0.player?.id == myId })
.eraseToAnyPublisher()
Expand All @@ -45,14 +44,6 @@ public final class AnswersRepository: AnswersRepositoryProtocol {
}

public func submitMusic(answer: ASEntity.Music) async throws -> Bool {
let queryItems = [URLQueryItem(name: "userId", value: ASFirebaseAuth.myID),
URLQueryItem(name: "roomNumber", value: mainRepository.number.value)]
let endPoint = FirebaseEndpoint(path: .submitMusic, method: .post)
.update(\.queryItems, with: queryItems)

let body = try ASEncoder.encode(answer)
let response = try await networkManager.sendRequest(to: endPoint, type: .json, body: body, option: .none)
let responseDict = try ASDecoder.decode([String: String].self, from: response)
return !responseDict.isEmpty
try await mainRepository.submitMusic(answer: answer)
}
}
Loading