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

task #273 채널 생성 및 삭제 시점 변경 #275

Merged
merged 9 commits into from
Nov 30, 2024
9 changes: 3 additions & 6 deletions Projects/App/Sources/Splash/SplashViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,28 +48,25 @@ public final class SplashViewController: BaseViewController<EmptyViewModel> {
extension SplashViewController {
private func moveToMainView() {
let fetchChannelListUsecase = DIContainer.shared.resolve(FetchChannelListUsecase.self)
let createChannelUsecase = DIContainer.shared.resolve(CreateChannelUsecase.self)
let deleteChannelUsecase = DIContainer.shared.resolve(DeleteChannelUsecase.self)
let fetchChannelInfoUsecase = DIContainer.shared.resolve(FetchChannelInfoUsecase.self)
let makeBroadcastUsecase = DIContainer.shared.resolve(MakeBroadcastUsecase.self)
let fetchAllBroadcastUsecase = DIContainer.shared.resolve(FetchAllBroadcastUsecase.self)
let deleteBroadCastUsecase = DIContainer.shared.resolve(DeleteBroadcastUsecase.self)
let factory = DIContainer.shared.resolve(LiveStreamViewControllerFactory.self)
let viewModel = BroadcastCollectionViewModel(
fetchChannelListUsecase: fetchChannelListUsecase,
createChannelUsecase: createChannelUsecase,
deleteChannelUsecase: deleteChannelUsecase,
Comment on lines -60 to -61
Copy link
Collaborator

Choose a reason for hiding this comment

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

usecase 2개가 덜어졌네요!! 👀🥹👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👍🏻

fetchChannelInfoUsecase: fetchChannelInfoUsecase,
makeBroadcastUsecase: makeBroadcastUsecase,
fetchAllBroadcastUsecase: fetchAllBroadcastUsecase,
deleteBroadCastUsecase: deleteBroadCastUsecase
)
let viewController = BroadcastCollectionViewController(viewModel: viewModel, factory: factory)
let navigationController = UINavigationController(rootViewController: viewController)

let createChannelUsecase = DIContainer.shared.resolve(CreateChannelUsecase.self)

/// 유저의 이름이 저장되어 있지 않으면 유저 등록 뷰를 Navigation에 올림
if UserDefaults.standard.string(forKey: "USER_NAME") == nil {
let singUpViewModel = SignUpViewModel()
let singUpViewModel = SignUpViewModel(createChannelUsecase: createChannelUsecase)
navigationController.viewControllers.append(SignUpViewController(viewModel: singUpViewModel))
}

Expand Down
3 changes: 2 additions & 1 deletion Projects/Features/AuthFeature/Demo/Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let viewModel = SignUpViewModel()
let mockCreateChannelUsecase = MockCreateChannelUsecaseImpl()
let viewModel = SignUpViewModel(createChannelUsecase: mockCreateChannelUsecase)
let viewController = SignUpViewController(viewModel: viewModel)
window?.rootViewController = viewController
window?.makeKeyAndVisible()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Combine

import LiveStationDomainInterface

final class MockCreateChannelUsecaseImpl: CreateChannelUsecase {
func execute(name: String) -> AnyPublisher<ChannelEntity, any Error> {
Future { promise in
promise(.success(ChannelEntity(id: "", name: name)))
}.eraseToAnyPublisher()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class SignUpViewController: BaseViewController<SignUpViewModel> {

private let confettiAnimationView = LottieAnimationView(name: "confetti", bundle: Bundle(for: DesignSystemResources.self))
private let shookAnimationView = LottieAnimationView(name: "shook", bundle: Bundle(for: DesignSystemResources.self))
private lazy var loadingView = SHLoadingView(message: "아이디 등록 중")
private lazy var loadingView = SHLoadingView(message: "닉네임 등록 중")

public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Expand Down Expand Up @@ -62,6 +62,7 @@ public class SignUpViewController: BaseViewController<SignUpViewModel> {
.store(in: &cancellables)

output.isSaved
.receive(on: DispatchQueue.main)
.sink { [weak self] isSaved in
if isSaved {
self?.textField.resignFirstResponder()
Expand Down
18 changes: 16 additions & 2 deletions Projects/Features/AuthFeature/Sources/SignUpViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Combine
import Foundation

import BaseFeatureInterface
import LiveStationDomainInterface

public class SignUpViewModel: ViewModel {
public struct Input {
Expand All @@ -16,6 +17,8 @@ public class SignUpViewModel: ViewModel {
private let output = Output()
private var cancellables = Set<AnyCancellable>()

private let createChannelUsecase: any CreateChannelUsecase

public func transform(input: Input) -> Output {
input.didWriteUserName
.sink { [weak self] name in
Expand All @@ -35,7 +38,9 @@ public class SignUpViewModel: ViewModel {
return output
}

public init() { }
public init(createChannelUsecase: CreateChannelUsecase) {
self.createChannelUsecase = createChannelUsecase
}

private func validate(with name: String?) -> Bool {
guard let name else { return false }
Expand All @@ -47,6 +52,15 @@ public class SignUpViewModel: ViewModel {
UserDefaults.standard.set(name, forKey: "USER_NAME")

let savedName = UserDefaults.standard.string(forKey: "USER_NAME")
output.isSaved.send(savedName == name)

createChannelUsecase.execute(name: "Guest")
.sink { _ in
} receiveValue: { [weak self] channelEntity in
UserDefaults.standard.set(channelEntity.id, forKey: "CHANNEL_ID")
let savedID = UserDefaults.standard.string(forKey: "CHANNEL_ID")

self?.output.isSaved.send(savedName == name && savedID != nil)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
self?.output.isSaved.send(savedName == name && savedID != nil)
self?.output.isSaved.send(savedName == name && savedID == channelEntity.id)

nil 값 확인이 아닌 channelEntity.id와 비교하는 것도 좋을 것 같아요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

그방법도 좋은 것 같습니다! 다음에 수정해서 반영해두겠습니다!

}
.store(in: &cancellables)
}
}
6 changes: 0 additions & 6 deletions Projects/Features/MainFeature/Demo/Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,12 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let mockFetchChannelListUsecase = MockFetchChannelListUsecaseImpl()
let mockCreateChannelUsecase = MockCreateChannelUsecaseImpl()
let mockDeleteChannelUsecase = MockDeleteChannelUsecaseImpl()
let mockFetchChannelInfoUsecase = MockFetchChannelInfoUsecaseImpl()
let mockmakeBroadcastUsecase = MockMakeBroadcastUsecaseImpl()
let mockFetchAllBroadcastUsecase = MockFetchAllBroadcastUsecaseImpl()
let mockDeleteBroadcastUsecase = MockDeleteBroadcastUsecaseImpl()
let viewModel = BroadcastCollectionViewModel(
fetchChannelListUsecase: mockFetchChannelListUsecase,
createChannelUsecase: mockCreateChannelUsecase,
deleteChannelUsecase: mockDeleteChannelUsecase,
fetchChannelInfoUsecase: mockFetchChannelInfoUsecase,
makeBroadcastUsecase: mockmakeBroadcastUsecase,
fetchAllBroadcastUsecase: mockFetchAllBroadcastUsecase,
Expand All @@ -37,5 +33,3 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ public final class BroadcastUIViewController: BaseViewController<BroadcastCollec
}

private func didFinishBroadCast() {
dismiss(animated: false)
viewModelInput.didTapFinishStreamingButton.send()
dismiss(animated: false) { [weak self] in
self?.viewModelInput.didTapFinishStreamingButton.send()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public final class SettingUIViewController: BaseViewController<BroadcastCollecti
private let streamingDescriptionCell = SettingTableViewCell(style: .default, reuseIdentifier: nil)
private let streamingNameCell = SettingTableViewCell(style: .default, reuseIdentifier: nil)
private let placeholderStringOfCells = ["어떤 방송인지 알려주세요!", "방송 내용을 알려주세요!"]
private lazy var loadingView = SHLoadingView(message: "채널 생성 중")
private lazy var loadingView = SHLoadingView(message: "방송 생성 중")

private var broadcastPicker = RPSystemBroadcastPickerView()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ public class BroadcastCollectionViewModel: ViewModel {
private let output = Output()

private let fetchChannelListUsecase: any FetchChannelListUsecase
private let createChannelUsecase: any CreateChannelUsecase
private let deleteChannelUsecase: any DeleteChannelUsecase
private let fetchChannelInfoUsecase: any FetchChannelInfoUsecase
private let makeBroadcastUsecase: any MakeBroadcastUsecase
private let fetchAllBroadcastUsecase: any FetchAllBroadcastUsecase
Expand All @@ -68,22 +66,18 @@ public class BroadcastCollectionViewModel: ViewModel {
let extensionBundleID = "kr.codesquad.boostcamp9.Shook.BroadcastUploadExtension"

private let userName = UserDefaults.standard.string(forKey: "USER_NAME") ?? ""
private var channelName: String = ""
private var broadcastName: String = ""
private var channelDescription: String = ""
private var channel: ChannelEntity?
private var channelID = UserDefaults.standard.string(forKey: "CHANNEL_ID")
Copy link
Collaborator

Choose a reason for hiding this comment

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

이렇게 하면 init 될때만 가져오기때문에 당장은 문제가 없지만 추후 채널 아이디가 변경될 경우 (새로운 채널을 생성했을때?) 고려해야할 부분 같습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

오, 그 부분도 생각을 해야될 것 같습니다! 지금 유저입장에서는 닉네임이 자기 채널처럼 보이기 때문에 아마 닉네임쪽이랑 같이 변경되어야할 것 같긴합니다!


public init(
fetchChannelListUsecase: FetchChannelListUsecase,
createChannelUsecase: CreateChannelUsecase,
deleteChannelUsecase: DeleteChannelUsecase,
fetchChannelInfoUsecase: FetchChannelInfoUsecase,
makeBroadcastUsecase: MakeBroadcastUsecase,
fetchAllBroadcastUsecase: FetchAllBroadcastUsecase,
deleteBroadCastUsecase: DeleteBroadcastUsecase
) {
self.fetchChannelListUsecase = fetchChannelListUsecase
self.createChannelUsecase = createChannelUsecase
self.deleteChannelUsecase = deleteChannelUsecase
self.fetchChannelInfoUsecase = fetchChannelInfoUsecase
self.makeBroadcastUsecase = makeBroadcastUsecase
self.fetchAllBroadcastUsecase = fetchAllBroadcastUsecase
Expand All @@ -104,7 +98,7 @@ public class BroadcastCollectionViewModel: ViewModel {
self.output.streamingStartButtonIsActive.send(validness.isValid)
self.output.errorMessage.send(validness.errorMessage)
if validness.isValid {
channelName = name
broadcastName = name
}
}
.store(in: &cancellables)
Expand All @@ -123,9 +117,9 @@ public class BroadcastCollectionViewModel: ViewModel {

input.didTapFinishStreamingButton
.flatMap { [weak self] _ in
guard let self, let channel else { return Empty<(Void, Void), Error>().eraseToAnyPublisher() }
return deleteChannelUsecase.execute(channelID: channel.id)
.zip(deleteBroadCastUsecase.execute(id: channel.id))
guard let self,
let channelID else { return Empty<Void, Error>().eraseToAnyPublisher() }
return deleteBroadCastUsecase.execute(id: channelID)
.eraseToAnyPublisher()
}
.sink { _ in
Expand All @@ -135,16 +129,12 @@ public class BroadcastCollectionViewModel: ViewModel {
.store(in: &cancellables)

input.didTapStartBroadcastButton
.flatMap { [weak self] _ in
guard let self else { return Empty<ChannelEntity, Error>().eraseToAnyPublisher() }
output.isReadyToStream.send(false)
return createChannelUsecase.execute(name: channelName)
}
.flatMap { [weak self] in
guard let self else { return Empty<ChannelInfoEntity, Error>().eraseToAnyPublisher() }
channel = $0
return fetchChannelInfoUsecase.execute(channelID: $0.id)
.zip(makeBroadcastUsecase.execute(id: $0.id, title: $0.name, owner: userName, description: channelDescription))
guard let self,
let channelID else { return Empty<ChannelInfoEntity, Error>().eraseToAnyPublisher() }
output.isReadyToStream.send(false)
return fetchChannelInfoUsecase.execute(channelID: channelID)
.zip(makeBroadcastUsecase.execute(id: channelID, title: broadcastName, owner: userName, description: channelDescription))
.map { channelInfo, _ in channelInfo }
.eraseToAnyPublisher()
}
Expand All @@ -168,7 +158,7 @@ public class BroadcastCollectionViewModel: ViewModel {
let broadcast = broadcastInfoEntities.first { $0.id == channelEntity.id }
return Channel(
id: channelEntity.id,
title: channelEntity.name,
title: broadcast?.title ?? "Unknown",
thumbnailImageURLString: channelEntity.imageURLString,
owner: broadcast?.owner ?? "Unknown",
description: broadcast?.description ?? ""
Expand Down Expand Up @@ -210,15 +200,10 @@ public class BroadcastCollectionViewModel: ViewModel {
/// - Parameter _: 방송 이름
/// - Returns: (Bool, String?) - 유효 여부와 에러 메시지
private func valid(_ value: String) -> (isValid: Bool, errorMessage: String?) {
let isLengthValid = 3...20 ~= value.count
let isCharactersValid = value.allSatisfy { $0.isLetter || $0.isNumber || $0 == "_" }
let trimmedValue = value.trimmingCharacters(in: .whitespaces)

if !isLengthValid && !isCharactersValid {
return (false, "3글자 이상,20글자 이하로 입력해 주세요. 특수문자는 언더바(_)만 가능합니다.")
} else if !isLengthValid {
return (false, "최소 3글자 이상, 최대 20글자 이하로 입력해 주세요.")
} else if !isCharactersValid {
return (false, "특수문자는 언더바(_)만 가능합니다.")
if trimmedValue.isEmpty {
return (false, "공백을 제외하고 최소 1글자 이상 입력해주세요.")
} else {
return (true, nil)
}
Expand Down
Loading