diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2a3ecc0e5..1776ef85b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -76,7 +76,7 @@ jobs: test: name: ๐Ÿงช Test - runs-on: macos-14 + runs-on: macos-15 needs: prepare-ci steps: diff --git a/.gitignore b/.gitignore index fa1024f89..22750e19e 100644 --- a/.gitignore +++ b/.gitignore @@ -113,4 +113,4 @@ amplifytools.xcconfig GoogleService-Info.plist ### Needle -Projects/App/Sources/Application/NeedleGenerated.swift \ No newline at end of file +# Projects/App/Sources/Application/NeedleGenerated.swift diff --git a/.mise.toml b/.mise.toml index 58d10e97a..540136578 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,2 +1,2 @@ [tools] -tuist = "4.12.1" +tuist = "4.33.0" diff --git a/Package.resolved b/Package.resolved index c78bfb278..8344d4e3c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -194,8 +194,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/naver/naveridlogin-sdk-ios.git", "state" : { - "branch" : "master", - "revision" : "d8d2128b8bcef8cd93068a058ea898d6dbb7d486" + "revision" : "0d89660344926a625824387a130f7e38e0fad2d7", + "version" : "4.2.3" } }, { diff --git a/Package.swift b/Package.swift index abbaa67c8..3c5a3ad6d 100644 --- a/Package.swift +++ b/Package.swift @@ -34,7 +34,7 @@ let package = Package( .package(url: "https://github.com/RxSwiftCommunity/RxDataSources.git", from: "5.0.2"), .package(url: "https://github.com/RxSwiftCommunity/RxKeyboard.git", from: "2.0.1"), .package(url: "https://github.com/huri000/SwiftEntryKit", from: "2.0.0"), - .package(url: "https://github.com/naver/naveridlogin-sdk-ios.git", branch: "master"), + .package(url: "https://github.com/naver/naveridlogin-sdk-ios.git", from: "4.2.3"), .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "1.8.2"), .package(url: "https://github.com/cbpowell/MarqueeLabel.git", from: "4.5.0"), .package(url: "https://github.com/firebase/firebase-ios-sdk.git", from: "10.25.0"), diff --git a/Plugin/EnvironmentPlugin/ProjectDescriptionHelpers/ProjectEnvironment.swift b/Plugin/EnvironmentPlugin/ProjectDescriptionHelpers/ProjectEnvironment.swift index 8767036f4..eaff19446 100644 --- a/Plugin/EnvironmentPlugin/ProjectDescriptionHelpers/ProjectEnvironment.swift +++ b/Plugin/EnvironmentPlugin/ProjectDescriptionHelpers/ProjectEnvironment.swift @@ -24,5 +24,7 @@ public let env = ProjectEnvironment( .debugInformationFormat(DebugInformationFormat.dwarfWithDsym) .otherLinkerFlags(["-ObjC"]) .bitcodeEnabled(false) + .swiftVersion("6.0") + .merging(["SWIFT_STRICT_CONCURRENCY": .string("complete")]) ) diff --git a/Projects/App/Sources/Application/AppComponent+App.swift b/Projects/App/Sources/Application/AppComponent+App.swift index ac03e8760..3485ea3a1 100644 --- a/Projects/App/Sources/Application/AppComponent+App.swift +++ b/Projects/App/Sources/Application/AppComponent+App.swift @@ -1,5 +1,6 @@ import AppDomain import AppDomainInterface +@preconcurrency import NeedleFoundation // MARK: ๋ณ€์ˆ˜๋ช… ์ฃผ์˜ // AppComponent ๋‚ด ๋ณ€์ˆ˜ == Dependency ๋‚ด ๋ณ€์ˆ˜ ์ด๋ฆ„ ๊ฐ™์•„์•ผํ•จ diff --git a/Projects/App/Sources/Application/AppComponent+Artist.swift b/Projects/App/Sources/Application/AppComponent+Artist.swift index 1c7c96532..31b3fc370 100644 --- a/Projects/App/Sources/Application/AppComponent+Artist.swift +++ b/Projects/App/Sources/Application/AppComponent+Artist.swift @@ -2,6 +2,7 @@ import ArtistDomain import ArtistDomainInterface import ArtistFeature import ArtistFeatureInterface +@preconcurrency import NeedleFoundation public extension AppComponent { // MARK: Artist diff --git a/Projects/App/Sources/Application/AppComponent+Auth.swift b/Projects/App/Sources/Application/AppComponent+Auth.swift index f731fde47..40cc21d1f 100644 --- a/Projects/App/Sources/Application/AppComponent+Auth.swift +++ b/Projects/App/Sources/Application/AppComponent+Auth.swift @@ -3,6 +3,7 @@ import AuthDomainInterface import BaseFeature import MyInfoFeature import MyInfoFeatureInterface +@preconcurrency import NeedleFoundation import SignInFeature import SignInFeatureInterface import StorageFeature diff --git a/Projects/App/Sources/Application/AppComponent+Base.swift b/Projects/App/Sources/Application/AppComponent+Base.swift index 5387df404..124a301ec 100644 --- a/Projects/App/Sources/Application/AppComponent+Base.swift +++ b/Projects/App/Sources/Application/AppComponent+Base.swift @@ -1,6 +1,7 @@ import BaseFeature import BaseFeatureInterface import Foundation +@preconcurrency import NeedleFoundation public extension AppComponent { var multiPurposePopupFactory: any MultiPurposePopupFactory { diff --git a/Projects/App/Sources/Application/AppComponent+Chart.swift b/Projects/App/Sources/Application/AppComponent+Chart.swift index 83a90a6af..f6e1a8d84 100644 --- a/Projects/App/Sources/Application/AppComponent+Chart.swift +++ b/Projects/App/Sources/Application/AppComponent+Chart.swift @@ -3,6 +3,7 @@ import ChartDomain import ChartDomainInterface import ChartFeature import ChartFeatureInterface +@preconcurrency import NeedleFoundation public extension AppComponent { var chartFactory: any ChartFactory { diff --git a/Projects/App/Sources/Application/AppComponent+Credit.swift b/Projects/App/Sources/Application/AppComponent+Credit.swift index c59d3f88c..46f2b94a0 100644 --- a/Projects/App/Sources/Application/AppComponent+Credit.swift +++ b/Projects/App/Sources/Application/AppComponent+Credit.swift @@ -2,6 +2,7 @@ import CreditDomain import CreditDomainInterface import CreditSongListFeature import CreditSongListFeatureInterface +@preconcurrency import NeedleFoundation import SongCreditFeature import SongCreditFeatureInterface diff --git a/Projects/App/Sources/Application/AppComponent+Faq.swift b/Projects/App/Sources/Application/AppComponent+Faq.swift index 12bb97865..21d589cc2 100644 --- a/Projects/App/Sources/Application/AppComponent+Faq.swift +++ b/Projects/App/Sources/Application/AppComponent+Faq.swift @@ -1,6 +1,7 @@ import BaseFeature import FaqDomain import FaqDomainInterface +@preconcurrency import NeedleFoundation import SignInFeature // MARK: ๋ณ€์ˆ˜๋ช… ์ฃผ์˜ diff --git a/Projects/App/Sources/Application/AppComponent+Fruit.swift b/Projects/App/Sources/Application/AppComponent+Fruit.swift index 538722956..1110f25eb 100644 --- a/Projects/App/Sources/Application/AppComponent+Fruit.swift +++ b/Projects/App/Sources/Application/AppComponent+Fruit.swift @@ -1,6 +1,7 @@ import Foundation import FruitDrawFeature import FruitDrawFeatureInterface +@preconcurrency import NeedleFoundation extension AppComponent { var fruitDrawFactory: any FruitDrawFactory { diff --git a/Projects/App/Sources/Application/AppComponent+Image.swift b/Projects/App/Sources/Application/AppComponent+Image.swift index 13a531cbb..28f680fe0 100644 --- a/Projects/App/Sources/Application/AppComponent+Image.swift +++ b/Projects/App/Sources/Application/AppComponent+Image.swift @@ -1,5 +1,6 @@ import ImageDomain import ImageDomainInterface +@preconcurrency import NeedleFoundation // MARK: ๋ณ€์ˆ˜๋ช… ์ฃผ์˜ // AppComponent ๋‚ด ๋ณ€์ˆ˜ == Dependency ๋‚ด ๋ณ€์ˆ˜ ์ด๋ฆ„ ๊ฐ™์•„์•ผํ•จ diff --git a/Projects/App/Sources/Application/AppComponent+Like.swift b/Projects/App/Sources/Application/AppComponent+Like.swift index 680caa3db..40eafdeae 100644 --- a/Projects/App/Sources/Application/AppComponent+Like.swift +++ b/Projects/App/Sources/Application/AppComponent+Like.swift @@ -1,6 +1,7 @@ import BaseFeature import LikeDomain import LikeDomainInterface +@preconcurrency import NeedleFoundation import SignInFeature import StorageFeature diff --git a/Projects/App/Sources/Application/AppComponent+MyInfo.swift b/Projects/App/Sources/Application/AppComponent+MyInfo.swift index 9535416f7..0bc77a1f4 100644 --- a/Projects/App/Sources/Application/AppComponent+MyInfo.swift +++ b/Projects/App/Sources/Application/AppComponent+MyInfo.swift @@ -1,6 +1,7 @@ import Foundation import MyInfoFeature import MyInfoFeatureInterface +@preconcurrency import NeedleFoundation extension AppComponent { var myInfoFactory: any MyInfoFactory { diff --git a/Projects/App/Sources/Application/AppComponent+Notice.swift b/Projects/App/Sources/Application/AppComponent+Notice.swift index 3a4b64c28..7ab44a03e 100644 --- a/Projects/App/Sources/Application/AppComponent+Notice.swift +++ b/Projects/App/Sources/Application/AppComponent+Notice.swift @@ -2,6 +2,7 @@ import BaseFeature import MainTabFeature import MyInfoFeature import MyInfoFeatureInterface +@preconcurrency import NeedleFoundation import NoticeDomain import NoticeDomainInterface import StorageFeature diff --git a/Projects/App/Sources/Application/AppComponent+Notification.swift b/Projects/App/Sources/Application/AppComponent+Notification.swift index d3bcd78c9..d569ee84a 100644 --- a/Projects/App/Sources/Application/AppComponent+Notification.swift +++ b/Projects/App/Sources/Application/AppComponent+Notification.swift @@ -1,3 +1,4 @@ +@preconcurrency import NeedleFoundation import NotificationDomain import NotificationDomainInterface diff --git a/Projects/App/Sources/Application/AppComponent+Playlist.swift b/Projects/App/Sources/Application/AppComponent+Playlist.swift index cf39453fb..45928cc3b 100644 --- a/Projects/App/Sources/Application/AppComponent+Playlist.swift +++ b/Projects/App/Sources/Application/AppComponent+Playlist.swift @@ -2,6 +2,7 @@ import BaseFeature import BaseFeatureInterface import ImageDomain import ImageDomainInterface +@preconcurrency import NeedleFoundation import PlaylistDomain import PlaylistDomainInterface import PlaylistFeature diff --git a/Projects/App/Sources/Application/AppComponent+Price.swift b/Projects/App/Sources/Application/AppComponent+Price.swift index 954208ac9..cf111f5b8 100644 --- a/Projects/App/Sources/Application/AppComponent+Price.swift +++ b/Projects/App/Sources/Application/AppComponent+Price.swift @@ -1,4 +1,5 @@ import Foundation +@preconcurrency import NeedleFoundation import PriceDomain import PriceDomainInterface diff --git a/Projects/App/Sources/Application/AppComponent+Search.swift b/Projects/App/Sources/Application/AppComponent+Search.swift index 08e8f8c20..24ba0339d 100644 --- a/Projects/App/Sources/Application/AppComponent+Search.swift +++ b/Projects/App/Sources/Application/AppComponent+Search.swift @@ -1,4 +1,5 @@ import Foundation +@preconcurrency import NeedleFoundation import SearchDomain import SearchDomainInterface import SearchFeature diff --git a/Projects/App/Sources/Application/AppComponent+Songs.swift b/Projects/App/Sources/Application/AppComponent+Songs.swift index b0f65f952..efbf43daa 100644 --- a/Projects/App/Sources/Application/AppComponent+Songs.swift +++ b/Projects/App/Sources/Application/AppComponent+Songs.swift @@ -5,6 +5,7 @@ import LyricHighlightingFeature import LyricHighlightingFeatureInterface import MusicDetailFeature import MusicDetailFeatureInterface +@preconcurrency import NeedleFoundation import SongsDomain import SongsDomainInterface diff --git a/Projects/App/Sources/Application/AppComponent+Storage.swift b/Projects/App/Sources/Application/AppComponent+Storage.swift index c753e5741..a37f045bf 100644 --- a/Projects/App/Sources/Application/AppComponent+Storage.swift +++ b/Projects/App/Sources/Application/AppComponent+Storage.swift @@ -1,5 +1,6 @@ import BaseFeature import BaseFeatureInterface +@preconcurrency import NeedleFoundation import StorageFeature import StorageFeatureInterface diff --git a/Projects/App/Sources/Application/AppComponent+Team.swift b/Projects/App/Sources/Application/AppComponent+Team.swift index c5c05fd94..0ffd48627 100644 --- a/Projects/App/Sources/Application/AppComponent+Team.swift +++ b/Projects/App/Sources/Application/AppComponent+Team.swift @@ -1,3 +1,4 @@ +@preconcurrency import NeedleFoundation import TeamDomain import TeamDomainInterface import TeamFeature diff --git a/Projects/App/Sources/Application/AppComponent+User.swift b/Projects/App/Sources/Application/AppComponent+User.swift index cdf8a4b02..c441eeeb8 100644 --- a/Projects/App/Sources/Application/AppComponent+User.swift +++ b/Projects/App/Sources/Application/AppComponent+User.swift @@ -1,4 +1,5 @@ import BaseFeature +@preconcurrency import NeedleFoundation import SignInFeature import StorageFeature import UserDomain diff --git a/Projects/App/Sources/Application/AppComponent.swift b/Projects/App/Sources/Application/AppComponent.swift index d0434b55b..676e6a772 100644 --- a/Projects/App/Sources/Application/AppComponent.swift +++ b/Projects/App/Sources/Application/AppComponent.swift @@ -3,16 +3,12 @@ import Foundation import KeychainModule import MainTabFeature import MyInfoFeature -import NeedleFoundation +@preconcurrency import NeedleFoundation import RootFeature import StorageFeature import UIKit public final class AppComponent: BootstrapComponent { - public func makeRootView() -> IntroViewController { - rootComponent.makeView() - } - public var keychain: any Keychain { shared { KeychainImpl() diff --git a/Projects/App/Sources/Application/AppDelegate.swift b/Projects/App/Sources/Application/AppDelegate.swift index 162350f4b..1934f640e 100644 --- a/Projects/App/Sources/Application/AppDelegate.swift +++ b/Projects/App/Sources/Application/AppDelegate.swift @@ -19,7 +19,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { ) -> Bool { // Use Firebase library to configure APIs FirebaseApp.configure() - if let userInfo = PreferenceManager.userInfo { + if let userInfo = PreferenceManager.shared.userInfo { LogManager.setUserID(userID: userInfo.decryptedID) } else { LogManager.setUserID(userID: nil) @@ -105,19 +105,19 @@ extension AppDelegate { } private func initializeUserProperty() { - if let loginPlatform = PreferenceManager.userInfo?.platform { + if let loginPlatform = PreferenceManager.shared.userInfo?.platform { LogManager.setUserProperty(property: .loginPlatform(platform: loginPlatform)) } else { LogManager.clearUserProperty(property: .loginPlatform(platform: "")) } - if let fruitTotal = PreferenceManager.userInfo?.itemCount { + if let fruitTotal = PreferenceManager.shared.userInfo?.itemCount { LogManager.setUserProperty(property: .fruitTotal(count: fruitTotal)) } else { LogManager.clearUserProperty(property: .fruitTotal(count: -1)) } - if let playPlatform = PreferenceManager.songPlayPlatformType { + if let playPlatform = PreferenceManager.shared.songPlayPlatformType { LogManager.setUserProperty(property: .songPlayPlatform(platform: playPlatform.display)) } diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift new file mode 100644 index 000000000..5a27e7e15 --- /dev/null +++ b/Projects/App/Sources/Application/NeedleGenerated.swift @@ -0,0 +1,2031 @@ + + +import AppDomain +import AppDomainInterface +import ArtistDomain +import ArtistDomainInterface +import ArtistFeature +import ArtistFeatureInterface +import AuthDomain +import AuthDomainInterface +import BaseDomainInterface +import BaseFeature +import BaseFeatureInterface +import ChartDomain +import ChartDomainInterface +import ChartFeature +import ChartFeatureInterface +import CreditDomain +import CreditDomainInterface +import CreditSongListFeature +import CreditSongListFeatureInterface +import FaqDomain +import FaqDomainInterface +import Foundation +import FruitDrawFeature +import FruitDrawFeatureInterface +import HomeFeature +import HomeFeatureInterface +import ImageDomain +import ImageDomainInterface +import KeychainModule +import LikeDomain +import LikeDomainInterface +import LyricHighlightingFeature +import LyricHighlightingFeatureInterface +import MainTabFeature +import MusicDetailFeature +import MusicDetailFeatureInterface +import MyInfoFeature +import MyInfoFeatureInterface +@preconcurrency import NeedleFoundation +import NoticeDomain +import NoticeDomainInterface +import NotificationDomain +import NotificationDomainInterface +import PlaylistDomain +import PlaylistDomainInterface +import PlaylistFeature +import PlaylistFeatureInterface +import PriceDomain +import PriceDomainInterface +import RootFeature +import SearchDomain +import SearchDomainInterface +import SearchFeature +import SearchFeatureInterface +import SignInFeature +import SignInFeatureInterface +import SongCreditFeature +import SongCreditFeatureInterface +import SongsDomain +import SongsDomainInterface +import StorageFeature +import StorageFeatureInterface +import TeamDomain +import TeamDomainInterface +import TeamFeature +import TeamFeatureInterface +import UIKit +import UserDomain +import UserDomainInterface + +// swiftlint:disable unused_declaration +private let needleDependenciesHash : String? = nil + +// MARK: - Traversal Helpers + +private func parent1(_ component: NeedleFoundation.Scope) -> NeedleFoundation.Scope { + return component.parent +} + +// MARK: - Providers + +#if !NEEDLE_DYNAMIC + +@MainActor private class ArtistDependency132a213bf62ad60c622cProvider: @preconcurrency ArtistDependency { + var fetchArtistListUseCase: any FetchArtistListUseCase { + return appComponent.fetchArtistListUseCase + } + var artistDetailFactory: any ArtistDetailFactory { + return appComponent.artistDetailFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ArtistComponent +@MainActor private func factorye0c5444f5894148bdd93f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ArtistDependency132a213bf62ad60c622cProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ArtistDetailDependencyee413dcf7a70e89df6d9Provider: @preconcurrency ArtistDetailDependency { + var artistMusicComponent: ArtistMusicComponent { + return appComponent.artistMusicComponent + } + var fetchArtistDetailUseCase: any FetchArtistDetailUseCase { + return appComponent.fetchArtistDetailUseCase + } + var fetchArtistSubscriptionStatusUseCase: any FetchArtistSubscriptionStatusUseCase { + return appComponent.fetchArtistSubscriptionStatusUseCase + } + var subscriptionArtistUseCase: any SubscriptionArtistUseCase { + return appComponent.subscriptionArtistUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ArtistDetailComponent +@MainActor private func factory35314797fadaf164ece6f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ArtistDetailDependencyee413dcf7a70e89df6d9Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ArtistMusicContentDependency1615ac8469e54ec51921Provider: @preconcurrency ArtistMusicContentDependency { + var fetchArtistSongListUseCase: any FetchArtistSongListUseCase { + return appComponent.fetchArtistSongListUseCase + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ArtistMusicContentComponent +@MainActor private func factory8b6ffa46033e2529b5daf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ArtistMusicContentDependency1615ac8469e54ec51921Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ArtistMusicDependencya0f5073287829dfbc260Provider: @preconcurrency ArtistMusicDependency { + var artistMusicContentComponent: ArtistMusicContentComponent { + return appComponent.artistMusicContentComponent + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ArtistMusicComponent +@MainActor private func factory382e7f8466df35a3f1d9f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ArtistMusicDependencya0f5073287829dfbc260Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class LyricHighlightingDependency47c68b56cdde819901d2Provider: @preconcurrency LyricHighlightingDependency { + var fetchLyricsUseCase: any FetchLyricsUseCase { + return appComponent.fetchLyricsUseCase + } + var lyricDecoratingComponent: LyricDecoratingComponent { + return appComponent.lyricDecoratingComponent + } + var lyricHighlightingFactory: any LyricHighlightingFactory { + return appComponent.lyricHighlightingFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->LyricHighlightingComponent +@MainActor private func factory57ee59e468bef412b173f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return LyricHighlightingDependency47c68b56cdde819901d2Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class LyricDecoratingDependencya7e8bf6f2f4ae447ba4eProvider: @preconcurrency LyricDecoratingDependency { + var fetchLyricDecoratingBackgroundUseCase: any FetchLyricDecoratingBackgroundUseCase { + return appComponent.fetchLyricDecoratingBackgroundUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->LyricDecoratingComponent +@MainActor private func factory5d05db9eb4337d682097f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return LyricDecoratingDependencya7e8bf6f2f4ae447ba4eProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class MainTabBarDependencycd05b79389a6a7a6c20fProvider: @preconcurrency MainTabBarDependency { + var fetchNoticePopupUseCase: any FetchNoticePopupUseCase { + return appComponent.fetchNoticePopupUseCase + } + var fetchNoticeIDListUseCase: any FetchNoticeIDListUseCase { + return appComponent.fetchNoticeIDListUseCase + } + var updateNotificationTokenUseCase: any UpdateNotificationTokenUseCase { + return appComponent.updateNotificationTokenUseCase + } + var fetchSongUseCase: any FetchSongUseCase { + return appComponent.fetchSongUseCase + } + var appEntryState: any AppEntryStateHandleable { + return appComponent.appEntryState + } + var homeFactory: any HomeFactory { + return appComponent.homeFactory + } + var searchFactory: any SearchFactory { + return appComponent.searchFactory + } + var artistFactory: any ArtistFactory { + return appComponent.artistFactory + } + var storageFactory: any StorageFactory { + return appComponent.storageFactory + } + var myInfoFactory: any MyInfoFactory { + return appComponent.myInfoFactory + } + var noticePopupComponent: NoticePopupComponent { + return appComponent.noticePopupComponent + } + var noticeDetailFactory: any NoticeDetailFactory { + return appComponent.noticeDetailFactory + } + var playlistDetailFactory: any PlaylistDetailFactory { + return appComponent.playlistDetailFactory + } + var musicDetailFactory: any MusicDetailFactory { + return appComponent.musicDetailFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->MainTabBarComponent +@MainActor private func factorye547a52b3fce5887c8c7f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return MainTabBarDependencycd05b79389a6a7a6c20fProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class BottomTabBarDependency237c2bd1c7be62020295Provider: @preconcurrency BottomTabBarDependency { + + + init() { + + } +} +/// ^->AppComponent->BottomTabBarComponent +@MainActor private func factoryd34fa9e493604a6295bde3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return BottomTabBarDependency237c2bd1c7be62020295Provider() +} +@MainActor private class MainContainerDependencyd9d908a1d0cf8937bbadProvider: @preconcurrency MainContainerDependency { + var bottomTabBarComponent: BottomTabBarComponent { + return appComponent.bottomTabBarComponent + } + var mainTabBarComponent: MainTabBarComponent { + return appComponent.mainTabBarComponent + } + var playlistFactory: any PlaylistFactory { + return appComponent.playlistFactory + } + var playlistPresenterGlobalState: any PlayListPresenterGlobalStateProtocol { + return appComponent.playlistPresenterGlobalState + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->MainContainerComponent +@MainActor private func factory8e19f48d5d573d3ea539f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return MainContainerDependencyd9d908a1d0cf8937bbadProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class NoticePopupDependency579e3504f53119c2eef1Provider: @preconcurrency NoticePopupDependency { + + + init() { + + } +} +/// ^->AppComponent->NoticePopupComponent +@MainActor private func factorycd081aacb61d6a707ca7e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return NoticePopupDependency579e3504f53119c2eef1Provider() +} +@MainActor private class PlaylistCoverOptionPopupDependencydda632c1d493aaca2ef1Provider: @preconcurrency PlaylistCoverOptionPopupDependency { + var fetchPlaylistImagePriceUseCase: any FetchPlaylistImagePriceUseCase { + return appComponent.fetchPlaylistImagePriceUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->PlaylistCoverOptionPopupComponent +@MainActor private func factory487946b77daee32980aff47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return PlaylistCoverOptionPopupDependencydda632c1d493aaca2ef1Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class PlaylistDetailFactoryDependencyad39fd621b86af45813fProvider: @preconcurrency PlaylistDetailFactoryDependency { + var myPlaylistDetailFactory: any MyPlaylistDetailFactory { + return appComponent.myPlaylistDetailFactory + } + var unknownPlaylistDetailFactory: any UnknownPlaylistDetailFactory { + return appComponent.unknownPlaylistDetailFactory + } + var wakmusicPlaylistDetailFactory: any WakmusicPlaylistDetailFactory { + return appComponent.wakmusicPlaylistDetailFactory + } + var requestPlaylistOwnerIDUsecase: any RequestPlaylistOwnerIDUsecase { + return appComponent.requestPlaylistOwnerIDUsecase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->PlaylistDetailComponent +@MainActor private func factory6595408565b754d9f0f7f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return PlaylistDetailFactoryDependencyad39fd621b86af45813fProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class WakmusicPlaylistDetailDependencyaf7bde55d9c36b36ed3cProvider: @preconcurrency WakmusicPlaylistDetailDependency { + var fetchWMPlaylistDetailUseCase: any FetchWMPlaylistDetailUseCase { + return appComponent.fetchWMPlaylistDetailUseCase + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->WakmusicPlaylistDetailComponent +@MainActor private func factorye3e053cabf65749566c8f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return WakmusicPlaylistDetailDependencyaf7bde55d9c36b36ed3cProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class MyPlaylistDetailDependency69ff6bac42b1db843b34Provider: @preconcurrency MyPlaylistDetailDependency { + var fetchPlaylistDetailUseCase: any FetchPlaylistDetailUseCase { + return appComponent.fetchPlaylistDetailUseCase + } + var updatePlaylistUseCase: any UpdatePlaylistUseCase { + return appComponent.updatePlaylistUseCase + } + var updateTitleAndPrivateUseCase: any UpdateTitleAndPrivateUseCase { + return appComponent.updateTitleAndPrivateUseCase + } + var removeSongsUseCase: any RemoveSongsUseCase { + return appComponent.removeSongsUseCase + } + var uploadDefaultPlaylistImageUseCase: any UploadDefaultPlaylistImageUseCase { + return appComponent.uploadDefaultPlaylistImageUseCase + } + var logoutUseCase: any LogoutUseCase { + return appComponent.logoutUseCase + } + var multiPurposePopupFactory: any MultiPurposePopupFactory { + return appComponent.multiPurposePopupFactory + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var playlistCoverOptionPopupFactory: any PlaylistCoverOptionPopupFactory { + return appComponent.playlistCoverOptionPopupFactory + } + var checkPlaylistCoverFactory: any CheckPlaylistCoverFactory { + return appComponent.checkPlaylistCoverFactory + } + var defaultPlaylistCoverFactory: any DefaultPlaylistCoverFactory { + return appComponent.defaultPlaylistCoverFactory + } + var requestCustomImageURLUseCase: any RequestCustomImageURLUseCase { + return appComponent.requestCustomImageURLUseCase + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->MyPlaylistDetailComponent +@MainActor private func factoryc6efd92ea498eaae7ff8f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return MyPlaylistDetailDependency69ff6bac42b1db843b34Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class DefaultPlaylistCoverDependency47d3a4d69f35b248b60dProvider: @preconcurrency DefaultPlaylistCoverDependency { + var fetchDefaultPlaylistImageUseCase: any FetchDefaultPlaylistImageUseCase { + return appComponent.fetchDefaultPlaylistImageUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->DefaultPlaylistCoverComponent +@MainActor private func factory89371387a9e4c131e13df47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return DefaultPlaylistCoverDependency47d3a4d69f35b248b60dProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class PlaylistDependency6f376d117dc0f38671edProvider: @preconcurrency PlaylistDependency { + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->PlaylistComponent +@MainActor private func factory3a0a6eb1061d8d5a2deff47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return PlaylistDependency6f376d117dc0f38671edProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class UnknownPlaylistDetailDependency7288879231117fbf1b1bProvider: @preconcurrency UnknownPlaylistDetailDependency { + var fetchPlaylistDetailUseCase: any FetchPlaylistDetailUseCase { + return appComponent.fetchPlaylistDetailUseCase + } + var subscribePlaylistUseCase: any SubscribePlaylistUseCase { + return appComponent.subscribePlaylistUseCase + } + var checkSubscriptionUseCase: any CheckSubscriptionUseCase { + return appComponent.checkSubscriptionUseCase + } + var logoutUseCase: any LogoutUseCase { + return appComponent.logoutUseCase + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->UnknownPlaylistDetailComponent +@MainActor private func factorya6d30d5b4471815dceb2f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return UnknownPlaylistDetailDependency7288879231117fbf1b1bProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class CheckPlaylistCoverDependency8efc6117f6bae3f05ffdProvider: @preconcurrency CheckPlaylistCoverDependency { + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->CheckPlaylistCoverComponent +@MainActor private func factory025ce9f6d91409a9f719f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return CheckPlaylistCoverDependency8efc6117f6bae3f05ffdProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class FruitStorageDependencybef279261018fd1c1669Provider: @preconcurrency FruitStorageDependency { + var fetchFruitListUseCase: any FetchFruitListUseCase { + return appComponent.fetchFruitListUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->FruitStorageComponent +@MainActor private func factory070e42b0224381c8cdf4f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return FruitStorageDependencybef279261018fd1c1669Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class FruitDrawDependency55f36ae08a622ad39d3fProvider: @preconcurrency FruitDrawDependency { + var fetchFruitDrawStatusUseCase: any FetchFruitDrawStatusUseCase { + return appComponent.fetchFruitDrawStatusUseCase + } + var drawFruitUseCase: any DrawFruitUseCase { + return appComponent.drawFruitUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->FruitDrawComponent +@MainActor private func factoryc603eb682d7a111dc261f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return FruitDrawDependency55f36ae08a622ad39d3fProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class CreditSongListTabItemDependency454e93d0f00e09bca0b5Provider: @preconcurrency CreditSongListTabItemDependency { + var fetchCreditSongListUseCase: any FetchCreditSongListUseCase { + return appComponent.fetchCreditSongListUseCase + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->CreditSongListTabItemComponent +@MainActor private func factory95828465b02bfed94ec5f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return CreditSongListTabItemDependency454e93d0f00e09bca0b5Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class CreditSongListTabDependencybdb2c08847a72c255a37Provider: @preconcurrency CreditSongListTabDependency { + var creditSongListTabItemFactory: any CreditSongListTabItemFactory { + return appComponent.creditSongListTabItemFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->CreditSongListTabComponent +@MainActor private func factory85beabbf7f1f193dec41f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return CreditSongListTabDependencybdb2c08847a72c255a37Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class CreditSongListDependencye5d029d068347ee34e68Provider: @preconcurrency CreditSongListDependency { + var creditSongListTabFactory: any CreditSongListTabFactory { + return appComponent.creditSongListTabFactory + } + var fetchCreditProfileUseCase: any FetchCreditProfileUseCase { + return appComponent.fetchCreditProfileUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->CreditSongListComponent +@MainActor private func factorye0caf4db37d0954ab34ef47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return CreditSongListDependencye5d029d068347ee34e68Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ChartDependencyafd8882010751c9ef054Provider: @preconcurrency ChartDependency { + var chartContentComponent: ChartContentComponent { + return appComponent.chartContentComponent + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ChartComponent +@MainActor private func factoryeac6a4df54bbd391d31bf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ChartDependencyafd8882010751c9ef054Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ChartContentDependency3b8e41cfba060e4d16caProvider: @preconcurrency ChartContentDependency { + var fetchChartRankingUseCase: any FetchChartRankingUseCase { + return appComponent.fetchChartRankingUseCase + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ChartContentComponent +@MainActor private func factoryc9a137630ce76907f36ff47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ChartContentDependency3b8e41cfba060e4d16caProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class TeamInfoDependency94c25b4e5acfbc37741cProvider: @preconcurrency TeamInfoDependency { + var fetchTeamListUseCase: any FetchTeamListUseCase { + return appComponent.fetchTeamListUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->TeamInfoComponent +@MainActor private func factorybe60e92b5190e00abf41f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return TeamInfoDependency94c25b4e5acfbc37741cProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class StorageDependency1447167c38e97ef97427Provider: @preconcurrency StorageDependency { + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var multiPurposePopupFactory: any MultiPurposePopupFactory { + return appComponent.multiPurposePopupFactory + } + var listStorageComponent: ListStorageComponent { + return appComponent.listStorageComponent + } + var likeStorageComponent: LikeStorageComponent { + return appComponent.likeStorageComponent + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->StorageComponent +@MainActor private func factory2415399d25299b97b98bf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return StorageDependency1447167c38e97ef97427Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ListStorageDependency77e3806cde238dfa4bf2Provider: @preconcurrency ListStorageDependency { + var multiPurposePopupFactory: any MultiPurposePopupFactory { + return appComponent.multiPurposePopupFactory + } + var playlistDetailFactory: any PlaylistDetailFactory { + return appComponent.playlistDetailFactory + } + var createPlaylistUseCase: any CreatePlaylistUseCase { + return appComponent.createPlaylistUseCase + } + var editPlayListOrderUseCase: any EditPlaylistOrderUseCase { + return appComponent.editPlayListOrderUseCase + } + var fetchPlayListUseCase: any FetchPlaylistUseCase { + return appComponent.fetchPlayListUseCase + } + var deletePlayListUseCase: any DeletePlaylistUseCase { + return appComponent.deletePlayListUseCase + } + var fetchPlaylistSongsUseCase: any FetchPlaylistSongsUseCase { + return appComponent.fetchPlaylistSongsUseCase + } + var fetchPlaylistCreationPriceUseCase: any FetchPlaylistCreationPriceUseCase { + return appComponent.fetchPlaylistCreationPriceUseCase + } + var logoutUseCase: any LogoutUseCase { + return appComponent.logoutUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var fruitDrawFactory: any FruitDrawFactory { + return appComponent.fruitDrawFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ListStorageComponent +@MainActor private func factory75c66cb7534f04d45951f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ListStorageDependency77e3806cde238dfa4bf2Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class LikeStorageDependency00a252a1ff3ab6e82ceeProvider: @preconcurrency LikeStorageDependency { + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var fetchFavoriteSongsUseCase: any FetchFavoriteSongsUseCase { + return appComponent.fetchFavoriteSongsUseCase + } + var editFavoriteSongsOrderUseCase: any EditFavoriteSongsOrderUseCase { + return appComponent.editFavoriteSongsOrderUseCase + } + var deleteFavoriteListUseCase: any DeleteFavoriteListUseCase { + return appComponent.deleteFavoriteListUseCase + } + var logoutUseCase: any LogoutUseCase { + return appComponent.logoutUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->LikeStorageComponent +@MainActor private func factory9f7222d7c56236b2e993f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return LikeStorageDependency00a252a1ff3ab6e82ceeProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class RootDependency3944cc797a4a88956fb5Provider: @preconcurrency RootDependency { + var mainContainerComponent: MainContainerComponent { + return appComponent.mainContainerComponent + } + var permissionComponent: PermissionComponent { + return appComponent.permissionComponent + } + var fetchUserInfoUseCase: any FetchUserInfoUseCase { + return appComponent.fetchUserInfoUseCase + } + var fetchAppCheckUseCase: any FetchAppCheckUseCase { + return appComponent.fetchAppCheckUseCase + } + var logoutUseCase: any LogoutUseCase { + return appComponent.logoutUseCase + } + var checkIsExistAccessTokenUseCase: any CheckIsExistAccessTokenUseCase { + return appComponent.checkIsExistAccessTokenUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->RootComponent +@MainActor private func factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return RootDependency3944cc797a4a88956fb5Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class PermissionDependency517ed7598d8c08817d14Provider: @preconcurrency PermissionDependency { + + + init() { + + } +} +/// ^->AppComponent->PermissionComponent +@MainActor private func factoryc1d4d80afbccf86bf1c0e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return PermissionDependency517ed7598d8c08817d14Provider() +} +@MainActor private class SignInDependency5dda0dd015447272446cProvider: @preconcurrency SignInDependency { + var fetchTokenUseCase: any FetchTokenUseCase { + return appComponent.fetchTokenUseCase + } + var fetchUserInfoUseCase: any FetchUserInfoUseCase { + return appComponent.fetchUserInfoUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->SignInComponent +@MainActor private func factoryda2925fd76da866a652af47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SignInDependency5dda0dd015447272446cProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class NewSongsDependencyee634cc0cae21fc2a9e3Provider: @preconcurrency NewSongsDependency { + var newSongsContentComponent: NewSongsContentComponent { + return appComponent.newSongsContentComponent + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->NewSongsComponent +@MainActor private func factory379179b05dd24ff979edf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return NewSongsDependencyee634cc0cae21fc2a9e3Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class HomeDependency443c4e1871277bd8432aProvider: @preconcurrency HomeDependency { + var fetchChartRankingUseCase: any FetchChartRankingUseCase { + return appComponent.fetchChartRankingUseCase + } + var fetchNewSongsUseCase: any FetchNewSongsUseCase { + return appComponent.fetchNewSongsUseCase + } + var fetchRecommendPlaylistUseCase: any FetchRecommendPlaylistUseCase { + return appComponent.fetchRecommendPlaylistUseCase + } + var playlistDetailFactory: any PlaylistDetailFactory { + return appComponent.playlistDetailFactory + } + var chartFactory: any ChartFactory { + return appComponent.chartFactory + } + var newSongsComponent: NewSongsComponent { + return appComponent.newSongsComponent + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->HomeComponent +@MainActor private func factory67229cdf0f755562b2b1f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return HomeDependency443c4e1871277bd8432aProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class NewSongsContentDependency93a05f20fa300c5bbec3Provider: @preconcurrency NewSongsContentDependency { + var fetchNewSongsUseCase: any FetchNewSongsUseCase { + return appComponent.fetchNewSongsUseCase + } + var fetchNewSongsPlaylistUseCase: any FetchNewSongsPlaylistUseCase { + return appComponent.fetchNewSongsPlaylistUseCase + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->NewSongsContentComponent +@MainActor private func factorye130e1fbfcbc622a4c38f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return NewSongsContentDependency93a05f20fa300c5bbec3Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class WakmusicRecommendDependency7d2e1de16b5802ae90ceProvider: @preconcurrency WakmusicRecommendDependency { + var fetchRecommendPlaylistUseCase: any FetchRecommendPlaylistUseCase { + return appComponent.fetchRecommendPlaylistUseCase + } + var wakmusicPlaylistDetailFactory: any WakmusicPlaylistDetailFactory { + return appComponent.wakmusicPlaylistDetailFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->WakmusicRecommendComponent +@MainActor private func factoryaf1c3535530356714983f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return WakmusicRecommendDependency7d2e1de16b5802ae90ceProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class BeforeSearchDependencyebdecb1d478a4766488dProvider: @preconcurrency BeforeSearchDependency { + var fetchRecommendPlaylistUseCase: any FetchRecommendPlaylistUseCase { + return appComponent.fetchRecommendPlaylistUseCase + } + var fetchCurrentVideoUseCase: any FetchCurrentVideoUseCase { + return appComponent.fetchCurrentVideoUseCase + } + var wakmusicRecommendComponent: WakmusicRecommendComponent { + return appComponent.wakmusicRecommendComponent + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var playlistDetailFactory: any PlaylistDetailFactory { + return appComponent.playlistDetailFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->BeforeSearchComponent +@MainActor private func factory9bb852337d5550979293f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return BeforeSearchDependencyebdecb1d478a4766488dProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class SearchDependencya86903a2c751a4f762e8Provider: @preconcurrency SearchDependency { + var beforeSearchComponent: BeforeSearchComponent { + return appComponent.beforeSearchComponent + } + var afterSearchComponent: AfterSearchComponent { + return appComponent.afterSearchComponent + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var searchGlobalScrollState: any SearchGlobalScrollProtocol { + return appComponent.searchGlobalScrollState + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->SearchComponent +@MainActor private func factorye3d049458b2ccbbcb3b6f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SearchDependencya86903a2c751a4f762e8Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class AfterSearchDependency61822c19bc2eb46d7c52Provider: @preconcurrency AfterSearchDependency { + var songSearchResultFactory: any SongSearchResultFactory { + return appComponent.songSearchResultFactory + } + var listSearchResultFactory: any ListSearchResultFactory { + return appComponent.listSearchResultFactory + } + var searchGlobalScrollState: any SearchGlobalScrollProtocol { + return appComponent.searchGlobalScrollState + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->AfterSearchComponent +@MainActor private func factoryeb2da679e35e2c4fb9a5f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return AfterSearchDependency61822c19bc2eb46d7c52Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ListSearchResultDependencyd64afa403e14dc980d2fProvider: @preconcurrency ListSearchResultDependency { + var fetchSearchPlaylistsUseCase: any FetchSearchPlaylistsUseCase { + return appComponent.fetchSearchPlaylistsUseCase + } + var searchSortOptionComponent: SearchSortOptionComponent { + return appComponent.searchSortOptionComponent + } + var playlistDetailFactory: any PlaylistDetailFactory { + return appComponent.playlistDetailFactory + } + var searchGlobalScrollState: any SearchGlobalScrollProtocol { + return appComponent.searchGlobalScrollState + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ListSearchResultComponent +@MainActor private func factory2c8e2a50d1fcf9efc9f8f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ListSearchResultDependencyd64afa403e14dc980d2fProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class SongSearchResultDependency7224c47222c188cd3de5Provider: @preconcurrency SongSearchResultDependency { + var fetchSearchSongsUseCase: any FetchSearchSongsUseCase { + return appComponent.fetchSearchSongsUseCase + } + var searchSortOptionComponent: SearchSortOptionComponent { + return appComponent.searchSortOptionComponent + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var searchGlobalScrollState: any SearchGlobalScrollProtocol { + return appComponent.searchGlobalScrollState + } + var songDetailPresenter: any SongDetailPresentable { + return appComponent.songDetailPresenter + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->SongSearchResultComponent +@MainActor private func factory182af2382ca6172f89c1f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SongSearchResultDependency7224c47222c188cd3de5Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ContainSongsDependencydbd9ae8a072db3a22630Provider: @preconcurrency ContainSongsDependency { + var multiPurposePopupFactory: any MultiPurposePopupFactory { + return appComponent.multiPurposePopupFactory + } + var fetchPlayListUseCase: any FetchPlaylistUseCase { + return appComponent.fetchPlayListUseCase + } + var addSongIntoPlaylistUseCase: any AddSongIntoPlaylistUseCase { + return appComponent.addSongIntoPlaylistUseCase + } + var createPlaylistUseCase: any CreatePlaylistUseCase { + return appComponent.createPlaylistUseCase + } + var fetchPlaylistCreationPriceUseCase: any FetchPlaylistCreationPriceUseCase { + return appComponent.fetchPlaylistCreationPriceUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var logoutUseCase: any LogoutUseCase { + return appComponent.logoutUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ContainSongsComponent +@MainActor private func factory4d4f4455414271fee232f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ContainSongsDependencydbd9ae8a072db3a22630Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class ServiceTermsDependencyd07df8dc0771e5580b47Provider: @preconcurrency ServiceTermsDependency { + + + init() { + + } +} +/// ^->AppComponent->ServiceTermsComponent +@MainActor private func factory8014909e2d8dba4e4f20e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return ServiceTermsDependencyd07df8dc0771e5580b47Provider() +} +@MainActor private class PrivacyDependency51c6df0186843bf53e9cProvider: @preconcurrency PrivacyDependency { + + + init() { + + } +} +/// ^->AppComponent->PrivacyComponent +@MainActor private func factorye7f5d59533cfdd1614b0e3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return PrivacyDependency51c6df0186843bf53e9cProvider() +} +@MainActor private class ServiceInfoDependency17ccca17be0fc87c9a2eProvider: @preconcurrency ServiceInfoDependency { + var openSourceLicenseFactory: any OpenSourceLicenseFactory { + return appComponent.openSourceLicenseFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ServiceInfoComponent +@MainActor private func factory3afd170b9974b0dbd863f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ServiceInfoDependency17ccca17be0fc87c9a2eProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class FaqDependency899aad15f17210a3af31Provider: @preconcurrency FaqDependency { + var faqContentFactory: any FaqContentFactory { + return appComponent.faqContentFactory + } + var fetchFaqCategoriesUseCase: any FetchFaqCategoriesUseCase { + return appComponent.fetchFaqCategoriesUseCase + } + var fetchFaqUseCase: any FetchFaqUseCase { + return appComponent.fetchFaqUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->FaqComponent +@MainActor private func factory4e13cc6545633ffc2ed5f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return FaqDependency899aad15f17210a3af31Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class QuestionDependencyf7010567c2d88e76d191Provider: @preconcurrency QuestionDependency { + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->QuestionComponent +@MainActor private func factoryedad1813a36115eec11ef47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return QuestionDependencyf7010567c2d88e76d191Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class MyInfoDependency3b44bce00dab6fc2e345Provider: @preconcurrency MyInfoDependency { + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var multiPurposePopupFactory: any MultiPurposePopupFactory { + return appComponent.multiPurposePopupFactory + } + var faqFactory: any FaqFactory { + return appComponent.faqFactory + } + var noticeFactory: any NoticeFactory { + return appComponent.noticeFactory + } + var questionFactory: any QuestionFactory { + return appComponent.questionFactory + } + var teamInfoFactory: any TeamInfoFactory { + return appComponent.teamInfoFactory + } + var settingFactory: any SettingFactory { + return appComponent.settingFactory + } + var profilePopupFactory: any ProfilePopupFactory { + return appComponent.profilePopupFactory + } + var fruitDrawFactory: any FruitDrawFactory { + return appComponent.fruitDrawFactory + } + var fruitStorageFactory: any FruitStorageFactory { + return appComponent.fruitStorageFactory + } + var fetchNoticeIDListUseCase: any FetchNoticeIDListUseCase { + return appComponent.fetchNoticeIDListUseCase + } + var setUserNameUseCase: any SetUserNameUseCase { + return appComponent.setUserNameUseCase + } + var fetchUserInfoUseCase: any FetchUserInfoUseCase { + return appComponent.fetchUserInfoUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->MyInfoComponent +@MainActor private func factoryec2cede3edc2a626b35df47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return MyInfoDependency3b44bce00dab6fc2e345Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class SettingDependency792c9caceb5cb097fbecProvider: @preconcurrency SettingDependency { + var withdrawUserInfoUseCase: any WithdrawUserInfoUseCase { + return appComponent.withdrawUserInfoUseCase + } + var logoutUseCase: any LogoutUseCase { + return appComponent.logoutUseCase + } + var updateNotificationTokenUseCase: any UpdateNotificationTokenUseCase { + return appComponent.updateNotificationTokenUseCase + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var serviceTermsFactory: any ServiceTermFactory { + return appComponent.serviceTermsFactory + } + var privacyFactory: any PrivacyFactory { + return appComponent.privacyFactory + } + var openSourceLicenseFactory: any OpenSourceLicenseFactory { + return appComponent.openSourceLicenseFactory + } + var playTypeTogglePopupFactory: any PlayTypeTogglePopupFactory { + return appComponent.playTypeTogglePopupFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->SettingComponent +@MainActor private func factoryee0bbc0b920a7007e1a9f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SettingDependency792c9caceb5cb097fbecProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class NoticeDetailDependency714af3aed40eaebda420Provider: @preconcurrency NoticeDetailDependency { + + + init() { + + } +} +/// ^->AppComponent->NoticeDetailComponent +@MainActor private func factory3db143c2f80d621d5a7fe3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return NoticeDetailDependency714af3aed40eaebda420Provider() +} +@MainActor private class ProfilePopupDependency3d548bc7afc8d2b8f092Provider: @preconcurrency ProfilePopupDependency { + var fetchProfileListUseCase: any FetchProfileListUseCase { + return appComponent.fetchProfileListUseCase + } + var setProfileUseCase: any SetProfileUseCase { + return appComponent.setProfileUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->ProfilePopupComponent +@MainActor private func factory3a1ad3396729bed7200ef47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return ProfilePopupDependency3d548bc7afc8d2b8f092Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class OpenSourceLicenseDependencyb6842dcc36b26380b91aProvider: @preconcurrency OpenSourceLicenseDependency { + + + init() { + + } +} +/// ^->AppComponent->OpenSourceLicenseComponent +@MainActor private func factoryd505894818021731340ae3b0c44298fc1c149afb(_ component: NeedleFoundation.Scope) -> AnyObject { + return OpenSourceLicenseDependencyb6842dcc36b26380b91aProvider() +} +@MainActor private class NoticeDependencyaec92ef53617a421bdf3Provider: @preconcurrency NoticeDependency { + var fetchNoticeAllUseCase: any FetchNoticeAllUseCase { + return appComponent.fetchNoticeAllUseCase + } + var noticeDetailFactory: any NoticeDetailFactory { + return appComponent.noticeDetailFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->NoticeComponent +@MainActor private func factoryaf8e5665e5b9217918f5f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return NoticeDependencyaec92ef53617a421bdf3Provider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class SongCreditDependency973cc2df82285f0722fcProvider: @preconcurrency SongCreditDependency { + var fetchSongCreditsUseCase: any FetchSongCreditsUseCase { + return appComponent.fetchSongCreditsUseCase + } + var creditSongListFactory: any CreditSongListFactory { + return appComponent.creditSongListFactory + } + var artistDetailFactory: any ArtistDetailFactory { + return appComponent.artistDetailFactory + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->SongCreditComponent +@MainActor private func factoryd48a3e0e81529a27a02bf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return SongCreditDependency973cc2df82285f0722fcProvider(appComponent: parent1(component) as! AppComponent) +} +@MainActor private class MusicDetailDependencyb872e53d21248eec044dProvider: @preconcurrency MusicDetailDependency { + var fetchSongUseCase: any FetchSongUseCase { + return appComponent.fetchSongUseCase + } + var lyricHighlightingFactory: any LyricHighlightingFactory { + return appComponent.lyricHighlightingFactory + } + var songCreditFactory: any SongCreditFactory { + return appComponent.songCreditFactory + } + var signInFactory: any SignInFactory { + return appComponent.signInFactory + } + var containSongsFactory: any ContainSongsFactory { + return appComponent.containSongsFactory + } + var karaokeFactory: any KaraokeFactory { + return appComponent.karaokeFactory + } + var textPopupFactory: any TextPopupFactory { + return appComponent.textPopupFactory + } + var artistDetailFactory: any ArtistDetailFactory { + return appComponent.artistDetailFactory + } + var playlistPresenterGlobalState: any PlayListPresenterGlobalStateProtocol { + return appComponent.playlistPresenterGlobalState + } + var addLikeSongUseCase: any AddLikeSongUseCase { + return appComponent.addLikeSongUseCase + } + var cancelLikeSongUseCase: any CancelLikeSongUseCase { + return appComponent.cancelLikeSongUseCase + } + var findArtistIDUseCase: any FindArtistIDUseCase { + return appComponent.findArtistIDUseCase + } + private let appComponent: AppComponent + init(appComponent: AppComponent) { + self.appComponent = appComponent + } +} +/// ^->AppComponent->MusicDetailComponent +@MainActor private func factory84f307443e9a78802606f47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject { + return MusicDetailDependencyb872e53d21248eec044dProvider(appComponent: parent1(component) as! AppComponent) +} + +#else +@MainActor extension AppComponent: Registration { + public func registerItems() { + + localTable["keychain-any Keychain"] = { [unowned self] in self.keychain as Any } + localTable["remoteImageDataSource-any RemoteImageDataSource"] = { [unowned self] in self.remoteImageDataSource as Any } + localTable["imageRepository-any ImageRepository"] = { [unowned self] in self.imageRepository as Any } + localTable["fetchLyricDecoratingBackgroundUseCase-any FetchLyricDecoratingBackgroundUseCase"] = { [unowned self] in self.fetchLyricDecoratingBackgroundUseCase as Any } + localTable["fetchProfileListUseCase-any FetchProfileListUseCase"] = { [unowned self] in self.fetchProfileListUseCase as Any } + localTable["fetchDefaultPlaylistImageUseCase-any FetchDefaultPlaylistImageUseCase"] = { [unowned self] in self.fetchDefaultPlaylistImageUseCase as Any } + localTable["songDetailPresenter-any SongDetailPresentable"] = { [unowned self] in self.songDetailPresenter as Any } + localTable["homeFactory-any HomeFactory"] = { [unowned self] in self.homeFactory as Any } + localTable["musicDetailFactory-any MusicDetailFactory"] = { [unowned self] in self.musicDetailFactory as Any } + localTable["karaokeFactory-any KaraokeFactory"] = { [unowned self] in self.karaokeFactory as Any } + localTable["newSongsComponent-NewSongsComponent"] = { [unowned self] in self.newSongsComponent as Any } + localTable["newSongsContentComponent-NewSongsContentComponent"] = { [unowned self] in self.newSongsContentComponent as Any } + localTable["lyricHighlightingFactory-any LyricHighlightingFactory"] = { [unowned self] in self.lyricHighlightingFactory as Any } + localTable["lyricDecoratingComponent-LyricDecoratingComponent"] = { [unowned self] in self.lyricDecoratingComponent as Any } + localTable["remoteSongsDataSource-any RemoteSongsDataSource"] = { [unowned self] in self.remoteSongsDataSource as Any } + localTable["songsRepository-any SongsRepository"] = { [unowned self] in self.songsRepository as Any } + localTable["fetchSongUseCase-any FetchSongUseCase"] = { [unowned self] in self.fetchSongUseCase as Any } + localTable["fetchLyricsUseCase-any FetchLyricsUseCase"] = { [unowned self] in self.fetchLyricsUseCase as Any } + localTable["fetchNewSongsUseCase-any FetchNewSongsUseCase"] = { [unowned self] in self.fetchNewSongsUseCase as Any } + localTable["fetchNewSongsPlaylistUseCase-any FetchNewSongsPlaylistUseCase"] = { [unowned self] in self.fetchNewSongsPlaylistUseCase as Any } + localTable["fetchSongCreditsUseCase-any FetchSongCreditsUseCase"] = { [unowned self] in self.fetchSongCreditsUseCase as Any } + localTable["remoteCreditDataSource-any RemoteCreditDataSource"] = { [unowned self] in self.remoteCreditDataSource as Any } + localTable["creditRepository-any CreditRepository"] = { [unowned self] in self.creditRepository as Any } + localTable["fetchCreditSongListUseCase-any FetchCreditSongListUseCase"] = { [unowned self] in self.fetchCreditSongListUseCase as Any } + localTable["fetchCreditProfileUseCase-any FetchCreditProfileUseCase"] = { [unowned self] in self.fetchCreditProfileUseCase as Any } + localTable["songCreditFactory-any SongCreditFactory"] = { [unowned self] in self.songCreditFactory as Any } + localTable["creditSongListFactory-any CreditSongListFactory"] = { [unowned self] in self.creditSongListFactory as Any } + localTable["creditSongListTabFactory-any CreditSongListTabFactory"] = { [unowned self] in self.creditSongListTabFactory as Any } + localTable["creditSongListTabItemFactory-any CreditSongListTabItemFactory"] = { [unowned self] in self.creditSongListTabItemFactory as Any } + localTable["remoteNotificationDataSource-any RemoteNotificationDataSource"] = { [unowned self] in self.remoteNotificationDataSource as Any } + localTable["notificationRepository-any NotificationRepository"] = { [unowned self] in self.notificationRepository as Any } + localTable["updateNotificationTokenUseCase-any UpdateNotificationTokenUseCase"] = { [unowned self] in self.updateNotificationTokenUseCase as Any } + localTable["signInFactory-any SignInFactory"] = { [unowned self] in self.signInFactory as Any } + localTable["localAuthDataSource-any LocalAuthDataSource"] = { [unowned self] in self.localAuthDataSource as Any } + localTable["remoteAuthDataSource-any RemoteAuthDataSource"] = { [unowned self] in self.remoteAuthDataSource as Any } + localTable["authRepository-any AuthRepository"] = { [unowned self] in self.authRepository as Any } + localTable["fetchTokenUseCase-any FetchTokenUseCase"] = { [unowned self] in self.fetchTokenUseCase as Any } + localTable["regenerateAccessTokenUseCase-any ReGenerateAccessTokenUseCase"] = { [unowned self] in self.regenerateAccessTokenUseCase as Any } + localTable["logoutUseCase-any LogoutUseCase"] = { [unowned self] in self.logoutUseCase as Any } + localTable["checkIsExistAccessTokenUseCase-any CheckIsExistAccessTokenUseCase"] = { [unowned self] in self.checkIsExistAccessTokenUseCase as Any } + localTable["remoteLikeDataSource-any RemoteLikeDataSource"] = { [unowned self] in self.remoteLikeDataSource as Any } + localTable["likeRepository-any LikeRepository"] = { [unowned self] in self.likeRepository as Any } + localTable["addLikeSongUseCase-any AddLikeSongUseCase"] = { [unowned self] in self.addLikeSongUseCase as Any } + localTable["cancelLikeSongUseCase-any CancelLikeSongUseCase"] = { [unowned self] in self.cancelLikeSongUseCase as Any } + localTable["playlistPresenterGlobalState-any PlayListPresenterGlobalStateProtocol"] = { [unowned self] in self.playlistPresenterGlobalState as Any } + localTable["playlistDetailFactory-any PlaylistDetailFactory"] = { [unowned self] in self.playlistDetailFactory as Any } + localTable["playlistFactory-any PlaylistFactory"] = { [unowned self] in self.playlistFactory as Any } + localTable["myPlaylistDetailFactory-any MyPlaylistDetailFactory"] = { [unowned self] in self.myPlaylistDetailFactory as Any } + localTable["unknownPlaylistDetailFactory-any UnknownPlaylistDetailFactory"] = { [unowned self] in self.unknownPlaylistDetailFactory as Any } + localTable["wakmusicPlaylistDetailFactory-any WakmusicPlaylistDetailFactory"] = { [unowned self] in self.wakmusicPlaylistDetailFactory as Any } + localTable["playlistCoverOptionPopupFactory-any PlaylistCoverOptionPopupFactory"] = { [unowned self] in self.playlistCoverOptionPopupFactory as Any } + localTable["checkPlaylistCoverFactory-any CheckPlaylistCoverFactory"] = { [unowned self] in self.checkPlaylistCoverFactory as Any } + localTable["defaultPlaylistCoverFactory-any DefaultPlaylistCoverFactory"] = { [unowned self] in self.defaultPlaylistCoverFactory as Any } + localTable["remotePlaylistDataSource-any RemotePlaylistDataSource"] = { [unowned self] in self.remotePlaylistDataSource as Any } + localTable["playlistRepository-any PlaylistRepository"] = { [unowned self] in self.playlistRepository as Any } + localTable["fetchRecommendPlaylistUseCase-any FetchRecommendPlaylistUseCase"] = { [unowned self] in self.fetchRecommendPlaylistUseCase as Any } + localTable["fetchPlaylistSongsUseCase-any FetchPlaylistSongsUseCase"] = { [unowned self] in self.fetchPlaylistSongsUseCase as Any } + localTable["fetchPlaylistDetailUseCase-any FetchPlaylistDetailUseCase"] = { [unowned self] in self.fetchPlaylistDetailUseCase as Any } + localTable["fetchWMPlaylistDetailUseCase-any FetchWMPlaylistDetailUseCase"] = { [unowned self] in self.fetchWMPlaylistDetailUseCase as Any } + localTable["createPlaylistUseCase-any CreatePlaylistUseCase"] = { [unowned self] in self.createPlaylistUseCase as Any } + localTable["updatePlaylistUseCase-any UpdatePlaylistUseCase"] = { [unowned self] in self.updatePlaylistUseCase as Any } + localTable["updateTitleAndPrivateUseCase-any UpdateTitleAndPrivateUseCase"] = { [unowned self] in self.updateTitleAndPrivateUseCase as Any } + localTable["addSongIntoPlaylistUseCase-any AddSongIntoPlaylistUseCase"] = { [unowned self] in self.addSongIntoPlaylistUseCase as Any } + localTable["removeSongsUseCase-any RemoveSongsUseCase"] = { [unowned self] in self.removeSongsUseCase as Any } + localTable["subscribePlaylistUseCase-any SubscribePlaylistUseCase"] = { [unowned self] in self.subscribePlaylistUseCase as Any } + localTable["checkSubscriptionUseCase-any CheckSubscriptionUseCase"] = { [unowned self] in self.checkSubscriptionUseCase as Any } + localTable["uploadDefaultPlaylistImageUseCase-any UploadDefaultPlaylistImageUseCase"] = { [unowned self] in self.uploadDefaultPlaylistImageUseCase as Any } + localTable["requestCustomImageURLUseCase-any RequestCustomImageURLUseCase"] = { [unowned self] in self.requestCustomImageURLUseCase as Any } + localTable["requestPlaylistOwnerIDUsecase-any RequestPlaylistOwnerIDUsecase"] = { [unowned self] in self.requestPlaylistOwnerIDUsecase as Any } + localTable["artistFactory-any ArtistFactory"] = { [unowned self] in self.artistFactory as Any } + localTable["remoteArtistDataSource-RemoteArtistDataSourceImpl"] = { [unowned self] in self.remoteArtistDataSource as Any } + localTable["artistRepository-any ArtistRepository"] = { [unowned self] in self.artistRepository as Any } + localTable["fetchArtistListUseCase-any FetchArtistListUseCase"] = { [unowned self] in self.fetchArtistListUseCase as Any } + localTable["artistDetailFactory-any ArtistDetailFactory"] = { [unowned self] in self.artistDetailFactory as Any } + localTable["fetchArtistDetailUseCase-any FetchArtistDetailUseCase"] = { [unowned self] in self.fetchArtistDetailUseCase as Any } + localTable["fetchArtistSongListUseCase-any FetchArtistSongListUseCase"] = { [unowned self] in self.fetchArtistSongListUseCase as Any } + localTable["fetchArtistSubscriptionStatusUseCase-any FetchArtistSubscriptionStatusUseCase"] = { [unowned self] in self.fetchArtistSubscriptionStatusUseCase as Any } + localTable["subscriptionArtistUseCase-any SubscriptionArtistUseCase"] = { [unowned self] in self.subscriptionArtistUseCase as Any } + localTable["findArtistIDUseCase-any FindArtistIDUseCase"] = { [unowned self] in self.findArtistIDUseCase as Any } + localTable["artistMusicComponent-ArtistMusicComponent"] = { [unowned self] in self.artistMusicComponent as Any } + localTable["artistMusicContentComponent-ArtistMusicContentComponent"] = { [unowned self] in self.artistMusicContentComponent as Any } + localTable["remoteUserDataSource-any RemoteUserDataSource"] = { [unowned self] in self.remoteUserDataSource as Any } + localTable["userRepository-any UserRepository"] = { [unowned self] in self.userRepository as Any } + localTable["setProfileUseCase-any SetProfileUseCase"] = { [unowned self] in self.setProfileUseCase as Any } + localTable["setUserNameUseCase-any SetUserNameUseCase"] = { [unowned self] in self.setUserNameUseCase as Any } + localTable["fetchPlayListUseCase-any FetchPlaylistUseCase"] = { [unowned self] in self.fetchPlayListUseCase as Any } + localTable["fetchFavoriteSongsUseCase-any FetchFavoriteSongsUseCase"] = { [unowned self] in self.fetchFavoriteSongsUseCase as Any } + localTable["editFavoriteSongsOrderUseCase-any EditFavoriteSongsOrderUseCase"] = { [unowned self] in self.editFavoriteSongsOrderUseCase as Any } + localTable["editPlayListOrderUseCase-any EditPlaylistOrderUseCase"] = { [unowned self] in self.editPlayListOrderUseCase as Any } + localTable["deletePlayListUseCase-any DeletePlaylistUseCase"] = { [unowned self] in self.deletePlayListUseCase as Any } + localTable["deleteFavoriteListUseCase-any DeleteFavoriteListUseCase"] = { [unowned self] in self.deleteFavoriteListUseCase as Any } + localTable["fetchUserInfoUseCase-any FetchUserInfoUseCase"] = { [unowned self] in self.fetchUserInfoUseCase as Any } + localTable["withdrawUserInfoUseCase-any WithdrawUserInfoUseCase"] = { [unowned self] in self.withdrawUserInfoUseCase as Any } + localTable["fetchFruitListUseCase-any FetchFruitListUseCase"] = { [unowned self] in self.fetchFruitListUseCase as Any } + localTable["fetchFruitDrawStatusUseCase-any FetchFruitDrawStatusUseCase"] = { [unowned self] in self.fetchFruitDrawStatusUseCase as Any } + localTable["drawFruitUseCase-any DrawFruitUseCase"] = { [unowned self] in self.drawFruitUseCase as Any } + localTable["mainContainerComponent-MainContainerComponent"] = { [unowned self] in self.mainContainerComponent as Any } + localTable["bottomTabBarComponent-BottomTabBarComponent"] = { [unowned self] in self.bottomTabBarComponent as Any } + localTable["mainTabBarComponent-MainTabBarComponent"] = { [unowned self] in self.mainTabBarComponent as Any } + localTable["appEntryState-any AppEntryStateHandleable"] = { [unowned self] in self.appEntryState as Any } + localTable["permissionComponent-PermissionComponent"] = { [unowned self] in self.permissionComponent as Any } + localTable["teamInfoFactory-any TeamInfoFactory"] = { [unowned self] in self.teamInfoFactory as Any } + localTable["remoteTeamDataSource-any RemoteTeamDataSource"] = { [unowned self] in self.remoteTeamDataSource as Any } + localTable["teamRepository-any TeamRepository"] = { [unowned self] in self.teamRepository as Any } + localTable["fetchTeamListUseCase-any FetchTeamListUseCase"] = { [unowned self] in self.fetchTeamListUseCase as Any } + localTable["noticePopupComponent-NoticePopupComponent"] = { [unowned self] in self.noticePopupComponent as Any } + localTable["noticeFactory-any NoticeFactory"] = { [unowned self] in self.noticeFactory as Any } + localTable["noticeDetailFactory-any NoticeDetailFactory"] = { [unowned self] in self.noticeDetailFactory as Any } + localTable["remoteNoticeDataSource-any RemoteNoticeDataSource"] = { [unowned self] in self.remoteNoticeDataSource as Any } + localTable["noticeRepository-any NoticeRepository"] = { [unowned self] in self.noticeRepository as Any } + localTable["fetchNoticeAllUseCase-any FetchNoticeAllUseCase"] = { [unowned self] in self.fetchNoticeAllUseCase as Any } + localTable["fetchNoticePopupUseCase-any FetchNoticePopupUseCase"] = { [unowned self] in self.fetchNoticePopupUseCase as Any } + localTable["fetchNoticeCategoriesUseCase-any FetchNoticeCategoriesUseCase"] = { [unowned self] in self.fetchNoticeCategoriesUseCase as Any } + localTable["fetchNoticeIDListUseCase-any FetchNoticeIDListUseCase"] = { [unowned self] in self.fetchNoticeIDListUseCase as Any } + localTable["multiPurposePopupFactory-any MultiPurposePopupFactory"] = { [unowned self] in self.multiPurposePopupFactory as Any } + localTable["textPopupFactory-any TextPopupFactory"] = { [unowned self] in self.textPopupFactory as Any } + localTable["containSongsFactory-any ContainSongsFactory"] = { [unowned self] in self.containSongsFactory as Any } + localTable["privacyFactory-any PrivacyFactory"] = { [unowned self] in self.privacyFactory as Any } + localTable["serviceTermsFactory-any ServiceTermFactory"] = { [unowned self] in self.serviceTermsFactory as Any } + localTable["storageFactory-any StorageFactory"] = { [unowned self] in self.storageFactory as Any } + localTable["listStorageComponent-ListStorageComponent"] = { [unowned self] in self.listStorageComponent as Any } + localTable["likeStorageComponent-LikeStorageComponent"] = { [unowned self] in self.likeStorageComponent as Any } + localTable["remoteFaqDataSource-any RemoteFaqDataSource"] = { [unowned self] in self.remoteFaqDataSource as Any } + localTable["faqRepository-any FaqRepository"] = { [unowned self] in self.faqRepository as Any } + localTable["fetchFaqCategoriesUseCase-any FetchFaqCategoriesUseCase"] = { [unowned self] in self.fetchFaqCategoriesUseCase as Any } + localTable["fetchFaqUseCase-any FetchFaqUseCase"] = { [unowned self] in self.fetchFaqUseCase as Any } + localTable["remoteAppDataSource-any RemoteAppDataSource"] = { [unowned self] in self.remoteAppDataSource as Any } + localTable["appRepository-any AppRepository"] = { [unowned self] in self.appRepository as Any } + localTable["fetchAppCheckUseCase-any FetchAppCheckUseCase"] = { [unowned self] in self.fetchAppCheckUseCase as Any } + localTable["chartFactory-any ChartFactory"] = { [unowned self] in self.chartFactory as Any } + localTable["chartContentComponent-ChartContentComponent"] = { [unowned self] in self.chartContentComponent as Any } + localTable["remoteChartDataSource-any RemoteChartDataSource"] = { [unowned self] in self.remoteChartDataSource as Any } + localTable["chartRepository-any ChartRepository"] = { [unowned self] in self.chartRepository as Any } + localTable["fetchChartRankingUseCase-any FetchChartRankingUseCase"] = { [unowned self] in self.fetchChartRankingUseCase as Any } + localTable["fetchCurrentVideoUseCase-any FetchCurrentVideoUseCase"] = { [unowned self] in self.fetchCurrentVideoUseCase as Any } + } +} +@MainActor extension ArtistComponent: Registration { + public func registerItems() { + keyPathToName[\ArtistDependency.fetchArtistListUseCase] = "fetchArtistListUseCase-any FetchArtistListUseCase" + keyPathToName[\ArtistDependency.artistDetailFactory] = "artistDetailFactory-any ArtistDetailFactory" + } +} +@MainActor extension ArtistDetailComponent: Registration { + public func registerItems() { + keyPathToName[\ArtistDetailDependency.artistMusicComponent] = "artistMusicComponent-ArtistMusicComponent" + keyPathToName[\ArtistDetailDependency.fetchArtistDetailUseCase] = "fetchArtistDetailUseCase-any FetchArtistDetailUseCase" + keyPathToName[\ArtistDetailDependency.fetchArtistSubscriptionStatusUseCase] = "fetchArtistSubscriptionStatusUseCase-any FetchArtistSubscriptionStatusUseCase" + keyPathToName[\ArtistDetailDependency.subscriptionArtistUseCase] = "subscriptionArtistUseCase-any SubscriptionArtistUseCase" + keyPathToName[\ArtistDetailDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\ArtistDetailDependency.signInFactory] = "signInFactory-any SignInFactory" + } +} +@MainActor extension ArtistMusicContentComponent: Registration { + public func registerItems() { + keyPathToName[\ArtistMusicContentDependency.fetchArtistSongListUseCase] = "fetchArtistSongListUseCase-any FetchArtistSongListUseCase" + keyPathToName[\ArtistMusicContentDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\ArtistMusicContentDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\ArtistMusicContentDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\ArtistMusicContentDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + } +} +@MainActor extension ArtistMusicComponent: Registration { + public func registerItems() { + keyPathToName[\ArtistMusicDependency.artistMusicContentComponent] = "artistMusicContentComponent-ArtistMusicContentComponent" + } +} +@MainActor extension LyricHighlightingComponent: Registration { + public func registerItems() { + keyPathToName[\LyricHighlightingDependency.fetchLyricsUseCase] = "fetchLyricsUseCase-any FetchLyricsUseCase" + keyPathToName[\LyricHighlightingDependency.lyricDecoratingComponent] = "lyricDecoratingComponent-LyricDecoratingComponent" + keyPathToName[\LyricHighlightingDependency.lyricHighlightingFactory] = "lyricHighlightingFactory-any LyricHighlightingFactory" + } +} +@MainActor extension LyricDecoratingComponent: Registration { + public func registerItems() { + keyPathToName[\LyricDecoratingDependency.fetchLyricDecoratingBackgroundUseCase] = "fetchLyricDecoratingBackgroundUseCase-any FetchLyricDecoratingBackgroundUseCase" + keyPathToName[\LyricDecoratingDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension MainTabBarComponent: Registration { + public func registerItems() { + keyPathToName[\MainTabBarDependency.fetchNoticePopupUseCase] = "fetchNoticePopupUseCase-any FetchNoticePopupUseCase" + keyPathToName[\MainTabBarDependency.fetchNoticeIDListUseCase] = "fetchNoticeIDListUseCase-any FetchNoticeIDListUseCase" + keyPathToName[\MainTabBarDependency.updateNotificationTokenUseCase] = "updateNotificationTokenUseCase-any UpdateNotificationTokenUseCase" + keyPathToName[\MainTabBarDependency.fetchSongUseCase] = "fetchSongUseCase-any FetchSongUseCase" + keyPathToName[\MainTabBarDependency.appEntryState] = "appEntryState-any AppEntryStateHandleable" + keyPathToName[\MainTabBarDependency.homeFactory] = "homeFactory-any HomeFactory" + keyPathToName[\MainTabBarDependency.searchFactory] = "searchFactory-any SearchFactory" + keyPathToName[\MainTabBarDependency.artistFactory] = "artistFactory-any ArtistFactory" + keyPathToName[\MainTabBarDependency.storageFactory] = "storageFactory-any StorageFactory" + keyPathToName[\MainTabBarDependency.myInfoFactory] = "myInfoFactory-any MyInfoFactory" + keyPathToName[\MainTabBarDependency.noticePopupComponent] = "noticePopupComponent-NoticePopupComponent" + keyPathToName[\MainTabBarDependency.noticeDetailFactory] = "noticeDetailFactory-any NoticeDetailFactory" + keyPathToName[\MainTabBarDependency.playlistDetailFactory] = "playlistDetailFactory-any PlaylistDetailFactory" + keyPathToName[\MainTabBarDependency.musicDetailFactory] = "musicDetailFactory-any MusicDetailFactory" + keyPathToName[\MainTabBarDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + } +} +@MainActor extension BottomTabBarComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension MainContainerComponent: Registration { + public func registerItems() { + keyPathToName[\MainContainerDependency.bottomTabBarComponent] = "bottomTabBarComponent-BottomTabBarComponent" + keyPathToName[\MainContainerDependency.mainTabBarComponent] = "mainTabBarComponent-MainTabBarComponent" + keyPathToName[\MainContainerDependency.playlistFactory] = "playlistFactory-any PlaylistFactory" + keyPathToName[\MainContainerDependency.playlistPresenterGlobalState] = "playlistPresenterGlobalState-any PlayListPresenterGlobalStateProtocol" + } +} +@MainActor extension NoticePopupComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension PlaylistCoverOptionPopupComponent: Registration { + public func registerItems() { + keyPathToName[\PlaylistCoverOptionPopupDependency.fetchPlaylistImagePriceUseCase] = "fetchPlaylistImagePriceUseCase-any FetchPlaylistImagePriceUseCase" + } +} +@MainActor extension PlaylistDetailComponent: Registration { + public func registerItems() { + keyPathToName[\PlaylistDetailFactoryDependency.myPlaylistDetailFactory] = "myPlaylistDetailFactory-any MyPlaylistDetailFactory" + keyPathToName[\PlaylistDetailFactoryDependency.unknownPlaylistDetailFactory] = "unknownPlaylistDetailFactory-any UnknownPlaylistDetailFactory" + keyPathToName[\PlaylistDetailFactoryDependency.wakmusicPlaylistDetailFactory] = "wakmusicPlaylistDetailFactory-any WakmusicPlaylistDetailFactory" + keyPathToName[\PlaylistDetailFactoryDependency.requestPlaylistOwnerIDUsecase] = "requestPlaylistOwnerIDUsecase-any RequestPlaylistOwnerIDUsecase" + keyPathToName[\PlaylistDetailFactoryDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension WakmusicPlaylistDetailComponent: Registration { + public func registerItems() { + keyPathToName[\WakmusicPlaylistDetailDependency.fetchWMPlaylistDetailUseCase] = "fetchWMPlaylistDetailUseCase-any FetchWMPlaylistDetailUseCase" + keyPathToName[\WakmusicPlaylistDetailDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\WakmusicPlaylistDetailDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\WakmusicPlaylistDetailDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + keyPathToName[\WakmusicPlaylistDetailDependency.signInFactory] = "signInFactory-any SignInFactory" + } +} +@MainActor extension MyPlaylistDetailComponent: Registration { + public func registerItems() { + keyPathToName[\MyPlaylistDetailDependency.fetchPlaylistDetailUseCase] = "fetchPlaylistDetailUseCase-any FetchPlaylistDetailUseCase" + keyPathToName[\MyPlaylistDetailDependency.updatePlaylistUseCase] = "updatePlaylistUseCase-any UpdatePlaylistUseCase" + keyPathToName[\MyPlaylistDetailDependency.updateTitleAndPrivateUseCase] = "updateTitleAndPrivateUseCase-any UpdateTitleAndPrivateUseCase" + keyPathToName[\MyPlaylistDetailDependency.removeSongsUseCase] = "removeSongsUseCase-any RemoveSongsUseCase" + keyPathToName[\MyPlaylistDetailDependency.uploadDefaultPlaylistImageUseCase] = "uploadDefaultPlaylistImageUseCase-any UploadDefaultPlaylistImageUseCase" + keyPathToName[\MyPlaylistDetailDependency.logoutUseCase] = "logoutUseCase-any LogoutUseCase" + keyPathToName[\MyPlaylistDetailDependency.multiPurposePopupFactory] = "multiPurposePopupFactory-any MultiPurposePopupFactory" + keyPathToName[\MyPlaylistDetailDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\MyPlaylistDetailDependency.playlistCoverOptionPopupFactory] = "playlistCoverOptionPopupFactory-any PlaylistCoverOptionPopupFactory" + keyPathToName[\MyPlaylistDetailDependency.checkPlaylistCoverFactory] = "checkPlaylistCoverFactory-any CheckPlaylistCoverFactory" + keyPathToName[\MyPlaylistDetailDependency.defaultPlaylistCoverFactory] = "defaultPlaylistCoverFactory-any DefaultPlaylistCoverFactory" + keyPathToName[\MyPlaylistDetailDependency.requestCustomImageURLUseCase] = "requestCustomImageURLUseCase-any RequestCustomImageURLUseCase" + keyPathToName[\MyPlaylistDetailDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + keyPathToName[\MyPlaylistDetailDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension DefaultPlaylistCoverComponent: Registration { + public func registerItems() { + keyPathToName[\DefaultPlaylistCoverDependency.fetchDefaultPlaylistImageUseCase] = "fetchDefaultPlaylistImageUseCase-any FetchDefaultPlaylistImageUseCase" + } +} +@MainActor extension PlaylistComponent: Registration { + public func registerItems() { + keyPathToName[\PlaylistDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\PlaylistDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + keyPathToName[\PlaylistDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\PlaylistDependency.signInFactory] = "signInFactory-any SignInFactory" + } +} +@MainActor extension UnknownPlaylistDetailComponent: Registration { + public func registerItems() { + keyPathToName[\UnknownPlaylistDetailDependency.fetchPlaylistDetailUseCase] = "fetchPlaylistDetailUseCase-any FetchPlaylistDetailUseCase" + keyPathToName[\UnknownPlaylistDetailDependency.subscribePlaylistUseCase] = "subscribePlaylistUseCase-any SubscribePlaylistUseCase" + keyPathToName[\UnknownPlaylistDetailDependency.checkSubscriptionUseCase] = "checkSubscriptionUseCase-any CheckSubscriptionUseCase" + keyPathToName[\UnknownPlaylistDetailDependency.logoutUseCase] = "logoutUseCase-any LogoutUseCase" + keyPathToName[\UnknownPlaylistDetailDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\UnknownPlaylistDetailDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + keyPathToName[\UnknownPlaylistDetailDependency.signInFactory] = "signInFactory-any SignInFactory" + } +} +@MainActor extension CheckPlaylistCoverComponent: Registration { + public func registerItems() { + keyPathToName[\CheckPlaylistCoverDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension FruitStorageComponent: Registration { + public func registerItems() { + keyPathToName[\FruitStorageDependency.fetchFruitListUseCase] = "fetchFruitListUseCase-any FetchFruitListUseCase" + keyPathToName[\FruitStorageDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension FruitDrawComponent: Registration { + public func registerItems() { + keyPathToName[\FruitDrawDependency.fetchFruitDrawStatusUseCase] = "fetchFruitDrawStatusUseCase-any FetchFruitDrawStatusUseCase" + keyPathToName[\FruitDrawDependency.drawFruitUseCase] = "drawFruitUseCase-any DrawFruitUseCase" + keyPathToName[\FruitDrawDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension CreditSongListTabItemComponent: Registration { + public func registerItems() { + keyPathToName[\CreditSongListTabItemDependency.fetchCreditSongListUseCase] = "fetchCreditSongListUseCase-any FetchCreditSongListUseCase" + keyPathToName[\CreditSongListTabItemDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\CreditSongListTabItemDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\CreditSongListTabItemDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\CreditSongListTabItemDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + } +} +@MainActor extension CreditSongListTabComponent: Registration { + public func registerItems() { + keyPathToName[\CreditSongListTabDependency.creditSongListTabItemFactory] = "creditSongListTabItemFactory-any CreditSongListTabItemFactory" + } +} +@MainActor extension CreditSongListComponent: Registration { + public func registerItems() { + keyPathToName[\CreditSongListDependency.creditSongListTabFactory] = "creditSongListTabFactory-any CreditSongListTabFactory" + keyPathToName[\CreditSongListDependency.fetchCreditProfileUseCase] = "fetchCreditProfileUseCase-any FetchCreditProfileUseCase" + } +} +@MainActor extension ChartComponent: Registration { + public func registerItems() { + keyPathToName[\ChartDependency.chartContentComponent] = "chartContentComponent-ChartContentComponent" + } +} +@MainActor extension ChartContentComponent: Registration { + public func registerItems() { + keyPathToName[\ChartContentDependency.fetchChartRankingUseCase] = "fetchChartRankingUseCase-any FetchChartRankingUseCase" + keyPathToName[\ChartContentDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\ChartContentDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\ChartContentDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\ChartContentDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + } +} +@MainActor extension TeamInfoComponent: Registration { + public func registerItems() { + keyPathToName[\TeamInfoDependency.fetchTeamListUseCase] = "fetchTeamListUseCase-any FetchTeamListUseCase" + } +} +@MainActor extension StorageComponent: Registration { + public func registerItems() { + keyPathToName[\StorageDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\StorageDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\StorageDependency.multiPurposePopupFactory] = "multiPurposePopupFactory-any MultiPurposePopupFactory" + keyPathToName[\StorageDependency.listStorageComponent] = "listStorageComponent-ListStorageComponent" + keyPathToName[\StorageDependency.likeStorageComponent] = "likeStorageComponent-LikeStorageComponent" + } +} +@MainActor extension ListStorageComponent: Registration { + public func registerItems() { + keyPathToName[\ListStorageDependency.multiPurposePopupFactory] = "multiPurposePopupFactory-any MultiPurposePopupFactory" + keyPathToName[\ListStorageDependency.playlistDetailFactory] = "playlistDetailFactory-any PlaylistDetailFactory" + keyPathToName[\ListStorageDependency.createPlaylistUseCase] = "createPlaylistUseCase-any CreatePlaylistUseCase" + keyPathToName[\ListStorageDependency.editPlayListOrderUseCase] = "editPlayListOrderUseCase-any EditPlaylistOrderUseCase" + keyPathToName[\ListStorageDependency.fetchPlayListUseCase] = "fetchPlayListUseCase-any FetchPlaylistUseCase" + keyPathToName[\ListStorageDependency.deletePlayListUseCase] = "deletePlayListUseCase-any DeletePlaylistUseCase" + keyPathToName[\ListStorageDependency.fetchPlaylistSongsUseCase] = "fetchPlaylistSongsUseCase-any FetchPlaylistSongsUseCase" + keyPathToName[\ListStorageDependency.fetchPlaylistCreationPriceUseCase] = "fetchPlaylistCreationPriceUseCase-any FetchPlaylistCreationPriceUseCase" + keyPathToName[\ListStorageDependency.logoutUseCase] = "logoutUseCase-any LogoutUseCase" + keyPathToName[\ListStorageDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\ListStorageDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\ListStorageDependency.fruitDrawFactory] = "fruitDrawFactory-any FruitDrawFactory" + } +} +@MainActor extension LikeStorageComponent: Registration { + public func registerItems() { + keyPathToName[\LikeStorageDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\LikeStorageDependency.fetchFavoriteSongsUseCase] = "fetchFavoriteSongsUseCase-any FetchFavoriteSongsUseCase" + keyPathToName[\LikeStorageDependency.editFavoriteSongsOrderUseCase] = "editFavoriteSongsOrderUseCase-any EditFavoriteSongsOrderUseCase" + keyPathToName[\LikeStorageDependency.deleteFavoriteListUseCase] = "deleteFavoriteListUseCase-any DeleteFavoriteListUseCase" + keyPathToName[\LikeStorageDependency.logoutUseCase] = "logoutUseCase-any LogoutUseCase" + keyPathToName[\LikeStorageDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\LikeStorageDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\LikeStorageDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + } +} +@MainActor extension RootComponent: Registration { + public func registerItems() { + keyPathToName[\RootDependency.mainContainerComponent] = "mainContainerComponent-MainContainerComponent" + keyPathToName[\RootDependency.permissionComponent] = "permissionComponent-PermissionComponent" + keyPathToName[\RootDependency.fetchUserInfoUseCase] = "fetchUserInfoUseCase-any FetchUserInfoUseCase" + keyPathToName[\RootDependency.fetchAppCheckUseCase] = "fetchAppCheckUseCase-any FetchAppCheckUseCase" + keyPathToName[\RootDependency.logoutUseCase] = "logoutUseCase-any LogoutUseCase" + keyPathToName[\RootDependency.checkIsExistAccessTokenUseCase] = "checkIsExistAccessTokenUseCase-any CheckIsExistAccessTokenUseCase" + keyPathToName[\RootDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension PermissionComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension SignInComponent: Registration { + public func registerItems() { + keyPathToName[\SignInDependency.fetchTokenUseCase] = "fetchTokenUseCase-any FetchTokenUseCase" + keyPathToName[\SignInDependency.fetchUserInfoUseCase] = "fetchUserInfoUseCase-any FetchUserInfoUseCase" + } +} +@MainActor extension NewSongsComponent: Registration { + public func registerItems() { + keyPathToName[\NewSongsDependency.newSongsContentComponent] = "newSongsContentComponent-NewSongsContentComponent" + } +} +@MainActor extension HomeComponent: Registration { + public func registerItems() { + keyPathToName[\HomeDependency.fetchChartRankingUseCase] = "fetchChartRankingUseCase-any FetchChartRankingUseCase" + keyPathToName[\HomeDependency.fetchNewSongsUseCase] = "fetchNewSongsUseCase-any FetchNewSongsUseCase" + keyPathToName[\HomeDependency.fetchRecommendPlaylistUseCase] = "fetchRecommendPlaylistUseCase-any FetchRecommendPlaylistUseCase" + keyPathToName[\HomeDependency.playlistDetailFactory] = "playlistDetailFactory-any PlaylistDetailFactory" + keyPathToName[\HomeDependency.chartFactory] = "chartFactory-any ChartFactory" + keyPathToName[\HomeDependency.newSongsComponent] = "newSongsComponent-NewSongsComponent" + keyPathToName[\HomeDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + } +} +@MainActor extension NewSongsContentComponent: Registration { + public func registerItems() { + keyPathToName[\NewSongsContentDependency.fetchNewSongsUseCase] = "fetchNewSongsUseCase-any FetchNewSongsUseCase" + keyPathToName[\NewSongsContentDependency.fetchNewSongsPlaylistUseCase] = "fetchNewSongsPlaylistUseCase-any FetchNewSongsPlaylistUseCase" + keyPathToName[\NewSongsContentDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\NewSongsContentDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\NewSongsContentDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\NewSongsContentDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + } +} +@MainActor extension WakmusicRecommendComponent: Registration { + public func registerItems() { + keyPathToName[\WakmusicRecommendDependency.fetchRecommendPlaylistUseCase] = "fetchRecommendPlaylistUseCase-any FetchRecommendPlaylistUseCase" + keyPathToName[\WakmusicRecommendDependency.wakmusicPlaylistDetailFactory] = "wakmusicPlaylistDetailFactory-any WakmusicPlaylistDetailFactory" + } +} +@MainActor extension BeforeSearchComponent: Registration { + public func registerItems() { + keyPathToName[\BeforeSearchDependency.fetchRecommendPlaylistUseCase] = "fetchRecommendPlaylistUseCase-any FetchRecommendPlaylistUseCase" + keyPathToName[\BeforeSearchDependency.fetchCurrentVideoUseCase] = "fetchCurrentVideoUseCase-any FetchCurrentVideoUseCase" + keyPathToName[\BeforeSearchDependency.wakmusicRecommendComponent] = "wakmusicRecommendComponent-WakmusicRecommendComponent" + keyPathToName[\BeforeSearchDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\BeforeSearchDependency.playlistDetailFactory] = "playlistDetailFactory-any PlaylistDetailFactory" + } +} +@MainActor extension SearchComponent: Registration { + public func registerItems() { + keyPathToName[\SearchDependency.beforeSearchComponent] = "beforeSearchComponent-BeforeSearchComponent" + keyPathToName[\SearchDependency.afterSearchComponent] = "afterSearchComponent-AfterSearchComponent" + keyPathToName[\SearchDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\SearchDependency.searchGlobalScrollState] = "searchGlobalScrollState-any SearchGlobalScrollProtocol" + } +} +@MainActor extension AfterSearchComponent: Registration { + public func registerItems() { + keyPathToName[\AfterSearchDependency.songSearchResultFactory] = "songSearchResultFactory-any SongSearchResultFactory" + keyPathToName[\AfterSearchDependency.listSearchResultFactory] = "listSearchResultFactory-any ListSearchResultFactory" + keyPathToName[\AfterSearchDependency.searchGlobalScrollState] = "searchGlobalScrollState-any SearchGlobalScrollProtocol" + } +} +@MainActor extension ListSearchResultComponent: Registration { + public func registerItems() { + keyPathToName[\ListSearchResultDependency.fetchSearchPlaylistsUseCase] = "fetchSearchPlaylistsUseCase-any FetchSearchPlaylistsUseCase" + keyPathToName[\ListSearchResultDependency.searchSortOptionComponent] = "searchSortOptionComponent-SearchSortOptionComponent" + keyPathToName[\ListSearchResultDependency.playlistDetailFactory] = "playlistDetailFactory-any PlaylistDetailFactory" + keyPathToName[\ListSearchResultDependency.searchGlobalScrollState] = "searchGlobalScrollState-any SearchGlobalScrollProtocol" + } +} +@MainActor extension SongSearchResultComponent: Registration { + public func registerItems() { + keyPathToName[\SongSearchResultDependency.fetchSearchSongsUseCase] = "fetchSearchSongsUseCase-any FetchSearchSongsUseCase" + keyPathToName[\SongSearchResultDependency.searchSortOptionComponent] = "searchSortOptionComponent-SearchSortOptionComponent" + keyPathToName[\SongSearchResultDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\SongSearchResultDependency.searchGlobalScrollState] = "searchGlobalScrollState-any SearchGlobalScrollProtocol" + keyPathToName[\SongSearchResultDependency.songDetailPresenter] = "songDetailPresenter-any SongDetailPresentable" + keyPathToName[\SongSearchResultDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\SongSearchResultDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension SearchSortOptionComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension ContainSongsComponent: Registration { + public func registerItems() { + keyPathToName[\ContainSongsDependency.multiPurposePopupFactory] = "multiPurposePopupFactory-any MultiPurposePopupFactory" + keyPathToName[\ContainSongsDependency.fetchPlayListUseCase] = "fetchPlayListUseCase-any FetchPlaylistUseCase" + keyPathToName[\ContainSongsDependency.addSongIntoPlaylistUseCase] = "addSongIntoPlaylistUseCase-any AddSongIntoPlaylistUseCase" + keyPathToName[\ContainSongsDependency.createPlaylistUseCase] = "createPlaylistUseCase-any CreatePlaylistUseCase" + keyPathToName[\ContainSongsDependency.fetchPlaylistCreationPriceUseCase] = "fetchPlaylistCreationPriceUseCase-any FetchPlaylistCreationPriceUseCase" + keyPathToName[\ContainSongsDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\ContainSongsDependency.logoutUseCase] = "logoutUseCase-any LogoutUseCase" + } +} +@MainActor extension MultiPurposePopupComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension TextPopupComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension ServiceTermsComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension PrivacyComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension ServiceInfoComponent: Registration { + public func registerItems() { + keyPathToName[\ServiceInfoDependency.openSourceLicenseFactory] = "openSourceLicenseFactory-any OpenSourceLicenseFactory" + keyPathToName[\ServiceInfoDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension PlayTypeTogglePopupComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension FaqComponent: Registration { + public func registerItems() { + keyPathToName[\FaqDependency.faqContentFactory] = "faqContentFactory-any FaqContentFactory" + keyPathToName[\FaqDependency.fetchFaqCategoriesUseCase] = "fetchFaqCategoriesUseCase-any FetchFaqCategoriesUseCase" + keyPathToName[\FaqDependency.fetchFaqUseCase] = "fetchFaqUseCase-any FetchFaqUseCase" + } +} +@MainActor extension QuestionComponent: Registration { + public func registerItems() { + keyPathToName[\QuestionDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + } +} +@MainActor extension MyInfoComponent: Registration { + public func registerItems() { + keyPathToName[\MyInfoDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\MyInfoDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\MyInfoDependency.multiPurposePopupFactory] = "multiPurposePopupFactory-any MultiPurposePopupFactory" + keyPathToName[\MyInfoDependency.faqFactory] = "faqFactory-any FaqFactory" + keyPathToName[\MyInfoDependency.noticeFactory] = "noticeFactory-any NoticeFactory" + keyPathToName[\MyInfoDependency.questionFactory] = "questionFactory-any QuestionFactory" + keyPathToName[\MyInfoDependency.teamInfoFactory] = "teamInfoFactory-any TeamInfoFactory" + keyPathToName[\MyInfoDependency.settingFactory] = "settingFactory-any SettingFactory" + keyPathToName[\MyInfoDependency.profilePopupFactory] = "profilePopupFactory-any ProfilePopupFactory" + keyPathToName[\MyInfoDependency.fruitDrawFactory] = "fruitDrawFactory-any FruitDrawFactory" + keyPathToName[\MyInfoDependency.fruitStorageFactory] = "fruitStorageFactory-any FruitStorageFactory" + keyPathToName[\MyInfoDependency.fetchNoticeIDListUseCase] = "fetchNoticeIDListUseCase-any FetchNoticeIDListUseCase" + keyPathToName[\MyInfoDependency.setUserNameUseCase] = "setUserNameUseCase-any SetUserNameUseCase" + keyPathToName[\MyInfoDependency.fetchUserInfoUseCase] = "fetchUserInfoUseCase-any FetchUserInfoUseCase" + } +} +@MainActor extension SettingComponent: Registration { + public func registerItems() { + keyPathToName[\SettingDependency.withdrawUserInfoUseCase] = "withdrawUserInfoUseCase-any WithdrawUserInfoUseCase" + keyPathToName[\SettingDependency.logoutUseCase] = "logoutUseCase-any LogoutUseCase" + keyPathToName[\SettingDependency.updateNotificationTokenUseCase] = "updateNotificationTokenUseCase-any UpdateNotificationTokenUseCase" + keyPathToName[\SettingDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\SettingDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\SettingDependency.serviceTermsFactory] = "serviceTermsFactory-any ServiceTermFactory" + keyPathToName[\SettingDependency.privacyFactory] = "privacyFactory-any PrivacyFactory" + keyPathToName[\SettingDependency.openSourceLicenseFactory] = "openSourceLicenseFactory-any OpenSourceLicenseFactory" + keyPathToName[\SettingDependency.playTypeTogglePopupFactory] = "playTypeTogglePopupFactory-any PlayTypeTogglePopupFactory" + } +} +@MainActor extension NoticeDetailComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension ProfilePopupComponent: Registration { + public func registerItems() { + keyPathToName[\ProfilePopupDependency.fetchProfileListUseCase] = "fetchProfileListUseCase-any FetchProfileListUseCase" + keyPathToName[\ProfilePopupDependency.setProfileUseCase] = "setProfileUseCase-any SetProfileUseCase" + } +} +@MainActor extension OpenSourceLicenseComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension NoticeComponent: Registration { + public func registerItems() { + keyPathToName[\NoticeDependency.fetchNoticeAllUseCase] = "fetchNoticeAllUseCase-any FetchNoticeAllUseCase" + keyPathToName[\NoticeDependency.noticeDetailFactory] = "noticeDetailFactory-any NoticeDetailFactory" + } +} +@MainActor extension FaqContentComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension SongCreditComponent: Registration { + public func registerItems() { + keyPathToName[\SongCreditDependency.fetchSongCreditsUseCase] = "fetchSongCreditsUseCase-any FetchSongCreditsUseCase" + keyPathToName[\SongCreditDependency.creditSongListFactory] = "creditSongListFactory-any CreditSongListFactory" + keyPathToName[\SongCreditDependency.artistDetailFactory] = "artistDetailFactory-any ArtistDetailFactory" + } +} +@MainActor extension KaraokeComponent: Registration { + public func registerItems() { + + } +} +@MainActor extension MusicDetailComponent: Registration { + public func registerItems() { + keyPathToName[\MusicDetailDependency.fetchSongUseCase] = "fetchSongUseCase-any FetchSongUseCase" + keyPathToName[\MusicDetailDependency.lyricHighlightingFactory] = "lyricHighlightingFactory-any LyricHighlightingFactory" + keyPathToName[\MusicDetailDependency.songCreditFactory] = "songCreditFactory-any SongCreditFactory" + keyPathToName[\MusicDetailDependency.signInFactory] = "signInFactory-any SignInFactory" + keyPathToName[\MusicDetailDependency.containSongsFactory] = "containSongsFactory-any ContainSongsFactory" + keyPathToName[\MusicDetailDependency.karaokeFactory] = "karaokeFactory-any KaraokeFactory" + keyPathToName[\MusicDetailDependency.textPopupFactory] = "textPopupFactory-any TextPopupFactory" + keyPathToName[\MusicDetailDependency.artistDetailFactory] = "artistDetailFactory-any ArtistDetailFactory" + keyPathToName[\MusicDetailDependency.playlistPresenterGlobalState] = "playlistPresenterGlobalState-any PlayListPresenterGlobalStateProtocol" + keyPathToName[\MusicDetailDependency.addLikeSongUseCase] = "addLikeSongUseCase-any AddLikeSongUseCase" + keyPathToName[\MusicDetailDependency.cancelLikeSongUseCase] = "cancelLikeSongUseCase-any CancelLikeSongUseCase" + keyPathToName[\MusicDetailDependency.findArtistIDUseCase] = "findArtistIDUseCase-any FindArtistIDUseCase" + } +} + + +#endif + +@MainActor private func factoryEmptyDependencyProvider(_ component: NeedleFoundation.Scope) -> AnyObject { + return EmptyDependencyProvider(component: component) +} + +// MARK: - Registration +@MainActor +private func registerProviderFactory(_ componentPath: String, _ factory: @escaping (NeedleFoundation.Scope) -> AnyObject) { + __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: componentPath, factory) +} + +#if !NEEDLE_DYNAMIC + +@MainActor +@inline(never) private func register1() { + registerProviderFactory("^->AppComponent", factoryEmptyDependencyProvider) + registerProviderFactory("^->AppComponent->ArtistComponent", factorye0c5444f5894148bdd93f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->ArtistDetailComponent", factory35314797fadaf164ece6f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->ArtistMusicContentComponent", factory8b6ffa46033e2529b5daf47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->ArtistMusicComponent", factory382e7f8466df35a3f1d9f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->LyricHighlightingComponent", factory57ee59e468bef412b173f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->LyricDecoratingComponent", factory5d05db9eb4337d682097f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->MainTabBarComponent", factorye547a52b3fce5887c8c7f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->BottomTabBarComponent", factoryd34fa9e493604a6295bde3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->MainContainerComponent", factory8e19f48d5d573d3ea539f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->NoticePopupComponent", factorycd081aacb61d6a707ca7e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->PlaylistCoverOptionPopupComponent", factory487946b77daee32980aff47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->PlaylistDetailComponent", factory6595408565b754d9f0f7f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->WakmusicPlaylistDetailComponent", factorye3e053cabf65749566c8f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->MyPlaylistDetailComponent", factoryc6efd92ea498eaae7ff8f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->DefaultPlaylistCoverComponent", factory89371387a9e4c131e13df47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->PlaylistComponent", factory3a0a6eb1061d8d5a2deff47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->UnknownPlaylistDetailComponent", factorya6d30d5b4471815dceb2f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->CheckPlaylistCoverComponent", factory025ce9f6d91409a9f719f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->FruitStorageComponent", factory070e42b0224381c8cdf4f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->FruitDrawComponent", factoryc603eb682d7a111dc261f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->CreditSongListTabItemComponent", factory95828465b02bfed94ec5f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->CreditSongListTabComponent", factory85beabbf7f1f193dec41f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->CreditSongListComponent", factorye0caf4db37d0954ab34ef47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->ChartComponent", factoryeac6a4df54bbd391d31bf47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->ChartContentComponent", factoryc9a137630ce76907f36ff47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->TeamInfoComponent", factorybe60e92b5190e00abf41f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->StorageComponent", factory2415399d25299b97b98bf47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->ListStorageComponent", factory75c66cb7534f04d45951f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->LikeStorageComponent", factory9f7222d7c56236b2e993f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->RootComponent", factory264bfc4d4cb6b0629b40f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->PermissionComponent", factoryc1d4d80afbccf86bf1c0e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->SignInComponent", factoryda2925fd76da866a652af47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->NewSongsComponent", factory379179b05dd24ff979edf47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->HomeComponent", factory67229cdf0f755562b2b1f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->NewSongsContentComponent", factorye130e1fbfcbc622a4c38f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->WakmusicRecommendComponent", factoryaf1c3535530356714983f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->BeforeSearchComponent", factory9bb852337d5550979293f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->SearchComponent", factorye3d049458b2ccbbcb3b6f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->AfterSearchComponent", factoryeb2da679e35e2c4fb9a5f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->ListSearchResultComponent", factory2c8e2a50d1fcf9efc9f8f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->SongSearchResultComponent", factory182af2382ca6172f89c1f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->SearchSortOptionComponent", factoryEmptyDependencyProvider) + registerProviderFactory("^->AppComponent->ContainSongsComponent", factory4d4f4455414271fee232f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->MultiPurposePopupComponent", factoryEmptyDependencyProvider) + registerProviderFactory("^->AppComponent->TextPopupComponent", factoryEmptyDependencyProvider) + registerProviderFactory("^->AppComponent->ServiceTermsComponent", factory8014909e2d8dba4e4f20e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->PrivacyComponent", factorye7f5d59533cfdd1614b0e3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->ServiceInfoComponent", factory3afd170b9974b0dbd863f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->PlayTypeTogglePopupComponent", factoryEmptyDependencyProvider) +} + +@MainActor +@inline(never) private func register2() { + registerProviderFactory("^->AppComponent->FaqComponent", factory4e13cc6545633ffc2ed5f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->QuestionComponent", factoryedad1813a36115eec11ef47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->MyInfoComponent", factoryec2cede3edc2a626b35df47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->SettingComponent", factoryee0bbc0b920a7007e1a9f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->NoticeDetailComponent", factory3db143c2f80d621d5a7fe3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->ProfilePopupComponent", factory3a1ad3396729bed7200ef47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->OpenSourceLicenseComponent", factoryd505894818021731340ae3b0c44298fc1c149afb) + registerProviderFactory("^->AppComponent->NoticeComponent", factoryaf8e5665e5b9217918f5f47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->FaqContentComponent", factoryEmptyDependencyProvider) + registerProviderFactory("^->AppComponent->SongCreditComponent", factoryd48a3e0e81529a27a02bf47b58f8f304c97af4d5) + registerProviderFactory("^->AppComponent->KaraokeComponent", factoryEmptyDependencyProvider) + registerProviderFactory("^->AppComponent->MusicDetailComponent", factory84f307443e9a78802606f47b58f8f304c97af4d5) +} +#endif + +@MainActor +public func registerProviderFactories() { +#if !NEEDLE_DYNAMIC + register1() + register2() +#endif +} diff --git a/Projects/App/Sources/Application/SceneDelegate.swift b/Projects/App/Sources/Application/SceneDelegate.swift index 42cc4a2b5..6722e8805 100644 --- a/Projects/App/Sources/Application/SceneDelegate.swift +++ b/Projects/App/Sources/Application/SceneDelegate.swift @@ -19,7 +19,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { self.window = UIWindow(windowScene: scene) registerProviderFactories() self.root = AppComponent() - self.window?.rootViewController = root?.makeRootView().wrapNavigationController + self.window?.rootViewController = root?.rootComponent.makeView().wrapNavigationController self.window?.makeKeyAndVisible() // Handling App Entry:: Not Running State diff --git a/Projects/App/Support/Info.plist b/Projects/App/Support/Info.plist index be1febdff..175f4b095 100644 --- a/Projects/App/Support/Info.plist +++ b/Projects/App/Support/Info.plist @@ -55,8 +55,8 @@ LSApplicationQueriesSchemes - naversearchapp - naversearchthirdlogin + naversearchapp + naversearchthirdlogin youtube youtubemusic diff --git a/Projects/Domains/AppDomain/Interface/DataSource/RemoteAppDataSource.swift b/Projects/Domains/AppDomain/Interface/DataSource/RemoteAppDataSource.swift index 50d3cfcb7..007fba91f 100644 --- a/Projects/Domains/AppDomain/Interface/DataSource/RemoteAppDataSource.swift +++ b/Projects/Domains/AppDomain/Interface/DataSource/RemoteAppDataSource.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol RemoteAppDataSource { +public protocol RemoteAppDataSource: Sendable { func fetchAppCheck() -> Single } diff --git a/Projects/Domains/AppDomain/Interface/Repository/AppRepository.swift b/Projects/Domains/AppDomain/Interface/Repository/AppRepository.swift index c7519217f..37a5e3cac 100644 --- a/Projects/Domains/AppDomain/Interface/Repository/AppRepository.swift +++ b/Projects/Domains/AppDomain/Interface/Repository/AppRepository.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol AppRepository { +public protocol AppRepository: Sendable { func fetchAppCheck() -> Single } diff --git a/Projects/Domains/AppDomain/Interface/UseCase/FetchAppCheckUseCase.swift b/Projects/Domains/AppDomain/Interface/UseCase/FetchAppCheckUseCase.swift index 5c3bff09f..fdc9d0d2c 100644 --- a/Projects/Domains/AppDomain/Interface/UseCase/FetchAppCheckUseCase.swift +++ b/Projects/Domains/AppDomain/Interface/UseCase/FetchAppCheckUseCase.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol FetchAppCheckUseCase { +public protocol FetchAppCheckUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/ArtistDomain/Interface/DataSource/RemoteArtistDataSource.swift b/Projects/Domains/ArtistDomain/Interface/DataSource/RemoteArtistDataSource.swift index b1bb8fc5a..46e3d9837 100644 --- a/Projects/Domains/ArtistDomain/Interface/DataSource/RemoteArtistDataSource.swift +++ b/Projects/Domains/ArtistDomain/Interface/DataSource/RemoteArtistDataSource.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol RemoteArtistDataSource { +public protocol RemoteArtistDataSource: Sendable { func fetchArtistList() -> Single<[ArtistEntity]> func fetchArtistDetail(id: String) -> Single func fetchArtistSongList(id: String, sort: ArtistSongSortType, page: Int) -> Single<[ArtistSongListEntity]> diff --git a/Projects/Domains/ArtistDomain/Interface/Repository/ArtistRepository.swift b/Projects/Domains/ArtistDomain/Interface/Repository/ArtistRepository.swift index d3aca2a5c..2d6fa8130 100644 --- a/Projects/Domains/ArtistDomain/Interface/Repository/ArtistRepository.swift +++ b/Projects/Domains/ArtistDomain/Interface/Repository/ArtistRepository.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol ArtistRepository { +public protocol ArtistRepository: Sendable { func fetchArtistList() -> Single<[ArtistEntity]> func fetchArtistDetail(id: String) -> Single func fetchArtistSongList(id: String, sort: ArtistSongSortType, page: Int) -> Single<[ArtistSongListEntity]> diff --git a/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistDetailUseCase.swift b/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistDetailUseCase.swift index f953bebd1..61501401b 100644 --- a/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistDetailUseCase.swift +++ b/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistDetailUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchArtistDetailUseCase { +public protocol FetchArtistDetailUseCase: Sendable { func execute(id: String) -> Single } diff --git a/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistListUseCase.swift b/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistListUseCase.swift index 33071ee5b..59fbaf891 100644 --- a/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistListUseCase.swift +++ b/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistListUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchArtistListUseCase { +public protocol FetchArtistListUseCase: Sendable { func execute() -> Single<[ArtistEntity]> } diff --git a/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistSongListUseCase.swift b/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistSongListUseCase.swift index 8ac70f402..11cdc4418 100644 --- a/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistSongListUseCase.swift +++ b/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistSongListUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchArtistSongListUseCase { +public protocol FetchArtistSongListUseCase: Sendable { func execute(id: String, sort: ArtistSongSortType, page: Int) -> Single<[ArtistSongListEntity]> } diff --git a/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistSubscriptionStatusUseCase.swift b/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistSubscriptionStatusUseCase.swift index 27216630f..2b3b5cbe2 100644 --- a/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistSubscriptionStatusUseCase.swift +++ b/Projects/Domains/ArtistDomain/Interface/UseCase/FetchArtistSubscriptionStatusUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchArtistSubscriptionStatusUseCase { +public protocol FetchArtistSubscriptionStatusUseCase: Sendable { func execute(id: String) -> Single } diff --git a/Projects/Domains/ArtistDomain/Interface/UseCase/FindArtistIDUseCase.swift b/Projects/Domains/ArtistDomain/Interface/UseCase/FindArtistIDUseCase.swift index b2ce2a44e..7b1e01cb8 100644 --- a/Projects/Domains/ArtistDomain/Interface/UseCase/FindArtistIDUseCase.swift +++ b/Projects/Domains/ArtistDomain/Interface/UseCase/FindArtistIDUseCase.swift @@ -1,5 +1,5 @@ import RxSwift -public protocol FindArtistIDUseCase { +public protocol FindArtistIDUseCase: Sendable { func execute(name: String) -> Single } diff --git a/Projects/Domains/ArtistDomain/Interface/UseCase/SubscriptionArtistUseCase.swift b/Projects/Domains/ArtistDomain/Interface/UseCase/SubscriptionArtistUseCase.swift index e8bbab6a8..1157ca0b0 100644 --- a/Projects/Domains/ArtistDomain/Interface/UseCase/SubscriptionArtistUseCase.swift +++ b/Projects/Domains/ArtistDomain/Interface/UseCase/SubscriptionArtistUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol SubscriptionArtistUseCase { +public protocol SubscriptionArtistUseCase: Sendable { func execute(id: String, on: Bool) -> Completable } diff --git a/Projects/Domains/ArtistDomain/Testing/UseCase/FetchArtistListUseCaseSpy.swift b/Projects/Domains/ArtistDomain/Testing/UseCase/FetchArtistListUseCaseSpy.swift index 56f15cd9c..acfcb1768 100644 --- a/Projects/Domains/ArtistDomain/Testing/UseCase/FetchArtistListUseCaseSpy.swift +++ b/Projects/Domains/ArtistDomain/Testing/UseCase/FetchArtistListUseCaseSpy.swift @@ -2,7 +2,7 @@ import ArtistDomainInterface import Foundation import RxSwift -public final class FetchArtistListUseCaseSpy: FetchArtistListUseCase { +public final class FetchArtistListUseCaseSpy: FetchArtistListUseCase, @unchecked Sendable { public private(set) var callCount = 0 public var handler: (() -> Single<[ArtistEntity]>) = { .never() } public func execute() -> Single<[ArtistEntity]> { diff --git a/Projects/Domains/ArtistDomain/Testing/UseCase/FindArtistIDUseCaseSpy.swift b/Projects/Domains/ArtistDomain/Testing/UseCase/FindArtistIDUseCaseSpy.swift index 7a37e5016..d0ca0f12a 100644 --- a/Projects/Domains/ArtistDomain/Testing/UseCase/FindArtistIDUseCaseSpy.swift +++ b/Projects/Domains/ArtistDomain/Testing/UseCase/FindArtistIDUseCaseSpy.swift @@ -1,7 +1,7 @@ import ArtistDomainInterface import RxSwift -public final class FindArtistIDUseCaseSpy: FindArtistIDUseCase { +public final class FindArtistIDUseCaseSpy: FindArtistIDUseCase, @unchecked Sendable { public private(set) var callCount = 0 public var handler: ((String) -> Single) = { _ in .never() } public func execute(name: String) -> Single { diff --git a/Projects/Domains/AuthDomain/Interface/DataSource/LocalAuthDataSource.swift b/Projects/Domains/AuthDomain/Interface/DataSource/LocalAuthDataSource.swift index 524736e50..b4f4fe7ce 100644 --- a/Projects/Domains/AuthDomain/Interface/DataSource/LocalAuthDataSource.swift +++ b/Projects/Domains/AuthDomain/Interface/DataSource/LocalAuthDataSource.swift @@ -1,6 +1,6 @@ import RxSwift -public protocol LocalAuthDataSource { +public protocol LocalAuthDataSource: Sendable { func logout() func checkIsExistAccessToken() -> Bool } diff --git a/Projects/Domains/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift b/Projects/Domains/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift index 966dbf858..422f89856 100644 --- a/Projects/Domains/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift +++ b/Projects/Domains/AuthDomain/Interface/DataSource/RemoteAuthDataSource.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol RemoteAuthDataSource { +public protocol RemoteAuthDataSource: Sendable { func fetchToken(providerType: ProviderType, token: String) -> Single func reGenerateAccessToken() -> Single func logout() -> Completable diff --git a/Projects/Domains/AuthDomain/Interface/Repository/AuthRepository.swift b/Projects/Domains/AuthDomain/Interface/Repository/AuthRepository.swift index ffa21aa9b..2f64eea3b 100644 --- a/Projects/Domains/AuthDomain/Interface/Repository/AuthRepository.swift +++ b/Projects/Domains/AuthDomain/Interface/Repository/AuthRepository.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol AuthRepository { +public protocol AuthRepository: Sendable { func fetchToken(providerType: ProviderType, token: String) -> Single func reGenerateAccessToken() -> Single func logout(localOnly: Bool) -> Completable diff --git a/Projects/Domains/AuthDomain/Interface/UseCase/CheckIsExistAccessTokenUseCase.swift b/Projects/Domains/AuthDomain/Interface/UseCase/CheckIsExistAccessTokenUseCase.swift index 434275268..51a55c2ac 100644 --- a/Projects/Domains/AuthDomain/Interface/UseCase/CheckIsExistAccessTokenUseCase.swift +++ b/Projects/Domains/AuthDomain/Interface/UseCase/CheckIsExistAccessTokenUseCase.swift @@ -1,5 +1,5 @@ import RxSwift -public protocol CheckIsExistAccessTokenUseCase { +public protocol CheckIsExistAccessTokenUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/AuthDomain/Interface/UseCase/FetchTokenUseCase.swift b/Projects/Domains/AuthDomain/Interface/UseCase/FetchTokenUseCase.swift index 4a8b6cfa7..56ab76b9d 100644 --- a/Projects/Domains/AuthDomain/Interface/UseCase/FetchTokenUseCase.swift +++ b/Projects/Domains/AuthDomain/Interface/UseCase/FetchTokenUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchTokenUseCase { +public protocol FetchTokenUseCase: Sendable { func execute(providerType: ProviderType, token: String) -> Single } diff --git a/Projects/Domains/AuthDomain/Interface/UseCase/LogoutUseCase.swift b/Projects/Domains/AuthDomain/Interface/UseCase/LogoutUseCase.swift index 8c81f1735..ad7a78b04 100644 --- a/Projects/Domains/AuthDomain/Interface/UseCase/LogoutUseCase.swift +++ b/Projects/Domains/AuthDomain/Interface/UseCase/LogoutUseCase.swift @@ -1,5 +1,5 @@ import RxSwift -public protocol LogoutUseCase { +public protocol LogoutUseCase: Sendable { func execute(localOnly: Bool) -> Completable } diff --git a/Projects/Domains/AuthDomain/Interface/UseCase/ReGenerateAccessTokenUseCase.swift b/Projects/Domains/AuthDomain/Interface/UseCase/ReGenerateAccessTokenUseCase.swift index 6daa03e4f..f32999e34 100644 --- a/Projects/Domains/AuthDomain/Interface/UseCase/ReGenerateAccessTokenUseCase.swift +++ b/Projects/Domains/AuthDomain/Interface/UseCase/ReGenerateAccessTokenUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol ReGenerateAccessTokenUseCase { +public protocol ReGenerateAccessTokenUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/AuthDomain/Testing/CheckIsExistAccessTokenUseCaseStub.swift b/Projects/Domains/AuthDomain/Testing/CheckIsExistAccessTokenUseCaseStub.swift index 80bfb1c72..4eb9d0637 100644 --- a/Projects/Domains/AuthDomain/Testing/CheckIsExistAccessTokenUseCaseStub.swift +++ b/Projects/Domains/AuthDomain/Testing/CheckIsExistAccessTokenUseCaseStub.swift @@ -1,7 +1,7 @@ import AuthDomainInterface import RxSwift -public struct CheckIsExistAccessTokenUseCaseStub: CheckIsExistAccessTokenUseCase { +public struct CheckIsExistAccessTokenUseCaseStub: CheckIsExistAccessTokenUseCase, @unchecked Sendable { public func execute() -> Single { return .just(false) } diff --git a/Projects/Domains/BaseDomain/Sources/BasePlugin/BasePlugin.swift b/Projects/Domains/BaseDomain/Sources/BasePlugin/BasePlugin.swift index dc97392be..0d9b9680f 100644 --- a/Projects/Domains/BaseDomain/Sources/BasePlugin/BasePlugin.swift +++ b/Projects/Domains/BaseDomain/Sources/BasePlugin/BasePlugin.swift @@ -76,7 +76,16 @@ private extension BasePlugin { func fetchDeviceID() -> String { let deviceIDFromKeychain: String = keychain.load(type: .deviceID) - let uuidString: String? = UIDevice.current.identifierForVendor?.uuidString + let uuidString: String? + if Thread.isMainThread { + uuidString = MainActor.assumeIsolated { + UIDevice.current.identifierForVendor?.uuidString + } + } else { + uuidString = DispatchQueue.main.sync { + UIDevice.current.identifierForVendor?.uuidString + } + } if deviceIDFromKeychain.isEmpty, let uuidString { keychain.save(type: .deviceID, value: uuidString) diff --git a/Projects/Domains/BaseDomain/Sources/DataSource/BaseRemoteDataSource.swift b/Projects/Domains/BaseDomain/Sources/DataSource/BaseRemoteDataSource.swift index ad03cb542..04fe073ea 100644 --- a/Projects/Domains/BaseDomain/Sources/DataSource/BaseRemoteDataSource.swift +++ b/Projects/Domains/BaseDomain/Sources/DataSource/BaseRemoteDataSource.swift @@ -8,14 +8,14 @@ import Foundation import KeychainModule -import Moya -import RxMoya +@preconcurrency import Moya +@preconcurrency import RxMoya import RxSwift -open class BaseRemoteDataSource { +open class BaseRemoteDataSource: @unchecked Sendable { private let keychain: any Keychain private let provider: MoyaProvider - private var refreshProvider: MoyaProvider? + private let refreshProvider: MoyaProvider private let decoder = JSONDecoder() private let maxRetryCount = 2 @@ -31,11 +31,16 @@ open class BaseRemoteDataSource { BasePlugin(keychain: keychain), CustomLoggingPlugin() ]) + self.refreshProvider = MoyaProvider(plugins: [ + JwtPlugin(keychain: keychain), + CustomLoggingPlugin() + ]) #else self.provider = provider ?? MoyaProvider(plugins: [ JwtPlugin(keychain: keychain), BasePlugin(keychain: keychain) ]) + self.refreshProvider = MoyaProvider(plugins: [JwtPlugin(keychain: keychain)]) #endif } @@ -107,20 +112,7 @@ private extension BaseRemoteDataSource { } func reissueToken() -> Completable { - #if DEBUG || QA - let provider = refreshProvider ?? MoyaProvider(plugins: [ - JwtPlugin(keychain: keychain), - CustomLoggingPlugin() - ]) - #else - let provider = refreshProvider ?? MoyaProvider(plugins: [JwtPlugin(keychain: keychain)]) - #endif - - if refreshProvider == nil { - refreshProvider = provider - } - - return provider.rx.request(.refresh) + return refreshProvider.rx.request(.refresh) .asCompletable() } } diff --git a/Projects/Domains/ChartDomain/Interface/DataSource/RemoteChartDataSource.swift b/Projects/Domains/ChartDomain/Interface/DataSource/RemoteChartDataSource.swift index 521e1feff..298f7332a 100644 --- a/Projects/Domains/ChartDomain/Interface/DataSource/RemoteChartDataSource.swift +++ b/Projects/Domains/ChartDomain/Interface/DataSource/RemoteChartDataSource.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol RemoteChartDataSource { +public protocol RemoteChartDataSource: Sendable { func fetchChartRanking(type: ChartDateType) -> Single func fetchCurrentVideoUseCase() -> Single } diff --git a/Projects/Domains/ChartDomain/Interface/Entity/CurrentVideoEntity.swift b/Projects/Domains/ChartDomain/Interface/Entity/CurrentVideoEntity.swift index 26c35b0e9..7ae5d02cd 100644 --- a/Projects/Domains/ChartDomain/Interface/Entity/CurrentVideoEntity.swift +++ b/Projects/Domains/ChartDomain/Interface/Entity/CurrentVideoEntity.swift @@ -1,6 +1,6 @@ import Foundation -public struct CurrentVideoEntity: Hashable { +public struct CurrentVideoEntity: Hashable, Sendable { public let id: String public init(id: String) { diff --git a/Projects/Domains/ChartDomain/Interface/Repository/ChartRepository.swift b/Projects/Domains/ChartDomain/Interface/Repository/ChartRepository.swift index bef1aa8c6..29a67b0d6 100644 --- a/Projects/Domains/ChartDomain/Interface/Repository/ChartRepository.swift +++ b/Projects/Domains/ChartDomain/Interface/Repository/ChartRepository.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol ChartRepository { +public protocol ChartRepository: Sendable { func fetchChartRanking(type: ChartDateType) -> Single func fetchCurrentVideoUseCase() -> Single } diff --git a/Projects/Domains/ChartDomain/Interface/UseCase/FetchChartRankingUseCase.swift b/Projects/Domains/ChartDomain/Interface/UseCase/FetchChartRankingUseCase.swift index 3093ee0d1..457aadc80 100644 --- a/Projects/Domains/ChartDomain/Interface/UseCase/FetchChartRankingUseCase.swift +++ b/Projects/Domains/ChartDomain/Interface/UseCase/FetchChartRankingUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchChartRankingUseCase { +public protocol FetchChartRankingUseCase: Sendable { func execute(type: ChartDateType) -> Single } diff --git a/Projects/Domains/ChartDomain/Interface/UseCase/FetchCurrentVideoUseCase.swift b/Projects/Domains/ChartDomain/Interface/UseCase/FetchCurrentVideoUseCase.swift index f04360eb6..9fcf5e347 100644 --- a/Projects/Domains/ChartDomain/Interface/UseCase/FetchCurrentVideoUseCase.swift +++ b/Projects/Domains/ChartDomain/Interface/UseCase/FetchCurrentVideoUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchCurrentVideoUseCase { +public protocol FetchCurrentVideoUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/CreditDomain/Interface/DataSource/RemoteCreditDataSource.swift b/Projects/Domains/CreditDomain/Interface/DataSource/RemoteCreditDataSource.swift index fe8e6f884..1c7f69687 100644 --- a/Projects/Domains/CreditDomain/Interface/DataSource/RemoteCreditDataSource.swift +++ b/Projects/Domains/CreditDomain/Interface/DataSource/RemoteCreditDataSource.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol RemoteCreditDataSource { +public protocol RemoteCreditDataSource: Sendable { func fetchCreditSongList( name: String, order: CreditSongOrderType, diff --git a/Projects/Domains/CreditDomain/Interface/Repository/CreditRepository.swift b/Projects/Domains/CreditDomain/Interface/Repository/CreditRepository.swift index ef3035664..c39c96630 100644 --- a/Projects/Domains/CreditDomain/Interface/Repository/CreditRepository.swift +++ b/Projects/Domains/CreditDomain/Interface/Repository/CreditRepository.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol CreditRepository { +public protocol CreditRepository: Sendable { func fetchCreditSongList( name: String, order: CreditSongOrderType, diff --git a/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditProfileUseCase.swift b/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditProfileUseCase.swift index 5bec1b3b7..532b2bb04 100644 --- a/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditProfileUseCase.swift +++ b/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditProfileUseCase.swift @@ -1,5 +1,5 @@ import RxSwift -public protocol FetchCreditProfileUseCase { +public protocol FetchCreditProfileUseCase: Sendable { func execute(name: String) -> Single } diff --git a/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditSongListUseCase.swift b/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditSongListUseCase.swift index 3da792edd..4957ffd5b 100644 --- a/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditSongListUseCase.swift +++ b/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditSongListUseCase.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol FetchCreditSongListUseCase { +public protocol FetchCreditSongListUseCase: Sendable { func execute( name: String, order: CreditSongOrderType, diff --git a/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditProfileUseCaseSpy.swift b/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditProfileUseCaseSpy.swift index fd207404c..395207515 100644 --- a/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditProfileUseCaseSpy.swift +++ b/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditProfileUseCaseSpy.swift @@ -2,7 +2,7 @@ import CreditDomainInterface import RxSwift import SongsDomainInterface -public final class FetchCreditProfileUseCaseSpy: FetchCreditProfileUseCase { +public final class FetchCreditProfileUseCaseSpy: FetchCreditProfileUseCase, @unchecked Sendable { public var callCount = 0 public var handler: (String) -> Single = { _ in fatalError() } diff --git a/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditSongListUseCaseSpy.swift b/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditSongListUseCaseSpy.swift index 9dd5d72fe..2d08eb554 100644 --- a/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditSongListUseCaseSpy.swift +++ b/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditSongListUseCaseSpy.swift @@ -2,7 +2,7 @@ import CreditDomainInterface import RxSwift import SongsDomainInterface -public final class FetchCreditSongListUseCaseSpy: FetchCreditSongListUseCase { +public final class FetchCreditSongListUseCaseSpy: FetchCreditSongListUseCase, @unchecked Sendable { public var callCount = 0 public var handler: (String, CreditSongOrderType, Int, Int) -> Single<[SongEntity]> = { _, _, _, _ in fatalError() } diff --git a/Projects/Domains/FaqDomain/Interface/DataSource/RemoteFaqDataSource.swift b/Projects/Domains/FaqDomain/Interface/DataSource/RemoteFaqDataSource.swift index bd5ef8d31..ed5c1c32b 100644 --- a/Projects/Domains/FaqDomain/Interface/DataSource/RemoteFaqDataSource.swift +++ b/Projects/Domains/FaqDomain/Interface/DataSource/RemoteFaqDataSource.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol RemoteFaqDataSource { +public protocol RemoteFaqDataSource: Sendable { func fetchCategories() -> Single func fetchQna() -> Single<[FaqEntity]> } diff --git a/Projects/Domains/FaqDomain/Interface/Repository/FaqRepository.swift b/Projects/Domains/FaqDomain/Interface/Repository/FaqRepository.swift index d1d223ac8..2e5840ec8 100644 --- a/Projects/Domains/FaqDomain/Interface/Repository/FaqRepository.swift +++ b/Projects/Domains/FaqDomain/Interface/Repository/FaqRepository.swift @@ -9,7 +9,7 @@ import Foundation import RxSwift -public protocol FaqRepository { +public protocol FaqRepository: Sendable { func fetchQnaCategories() -> Single func fetchQna() -> Single<[FaqEntity]> } diff --git a/Projects/Domains/FaqDomain/Interface/UseCase/FetchFaqCategoriesUseCase.swift b/Projects/Domains/FaqDomain/Interface/UseCase/FetchFaqCategoriesUseCase.swift index 56b863ece..035314f13 100644 --- a/Projects/Domains/FaqDomain/Interface/UseCase/FetchFaqCategoriesUseCase.swift +++ b/Projects/Domains/FaqDomain/Interface/UseCase/FetchFaqCategoriesUseCase.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol FetchFaqCategoriesUseCase { +public protocol FetchFaqCategoriesUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/FaqDomain/Interface/UseCase/FetchFaqUseCase.swift b/Projects/Domains/FaqDomain/Interface/UseCase/FetchFaqUseCase.swift index 34ffb4b27..5fd0aebad 100644 --- a/Projects/Domains/FaqDomain/Interface/UseCase/FetchFaqUseCase.swift +++ b/Projects/Domains/FaqDomain/Interface/UseCase/FetchFaqUseCase.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol FetchFaqUseCase { +public protocol FetchFaqUseCase: Sendable { func execute() -> Single<[FaqEntity]> } diff --git a/Projects/Domains/FaqDomain/Testing/FetchFaqCategoriesUseCaseStub.swift b/Projects/Domains/FaqDomain/Testing/FetchFaqCategoriesUseCaseStub.swift index c2c4d7aae..2a2621461 100644 --- a/Projects/Domains/FaqDomain/Testing/FetchFaqCategoriesUseCaseStub.swift +++ b/Projects/Domains/FaqDomain/Testing/FetchFaqCategoriesUseCaseStub.swift @@ -2,7 +2,7 @@ import FaqDomainInterface import Foundation import RxSwift -public struct FetchFaqCategoriesUseCaseStub: FetchFaqCategoriesUseCase { +public struct FetchFaqCategoriesUseCaseStub: FetchFaqCategoriesUseCase, @unchecked Sendable { public func execute() -> Single { return .just(FaqCategoryEntity(categories: [])) } diff --git a/Projects/Domains/FaqDomain/Testing/FetchFaqUseCaseStub.swift b/Projects/Domains/FaqDomain/Testing/FetchFaqUseCaseStub.swift index 329d37e71..48ea21a4b 100644 --- a/Projects/Domains/FaqDomain/Testing/FetchFaqUseCaseStub.swift +++ b/Projects/Domains/FaqDomain/Testing/FetchFaqUseCaseStub.swift @@ -2,7 +2,7 @@ import FaqDomainInterface import Foundation import RxSwift -public struct FetchFaqUseCaseStub: FetchFaqUseCase { +public struct FetchFaqUseCaseStub: FetchFaqUseCase, @unchecked Sendable { public func execute() -> Single<[FaqEntity]> { return .just([]) } diff --git a/Projects/Domains/ImageDomain/Interface/DataSource/RemoteImageDataSource.swift b/Projects/Domains/ImageDomain/Interface/DataSource/RemoteImageDataSource.swift index ef6eeb0da..ed4da48ea 100644 --- a/Projects/Domains/ImageDomain/Interface/DataSource/RemoteImageDataSource.swift +++ b/Projects/Domains/ImageDomain/Interface/DataSource/RemoteImageDataSource.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol RemoteImageDataSource { +public protocol RemoteImageDataSource: Sendable { func fetchLyricDecoratingBackground() -> Single<[LyricDecoratingBackgroundEntity]> func fetchProfileList() -> Single<[ProfileListEntity]> func fetchDefaultPlaylistImage() -> Single<[DefaultImageEntity]> diff --git a/Projects/Domains/ImageDomain/Interface/Entity/DefaultImageEntity.swift b/Projects/Domains/ImageDomain/Interface/Entity/DefaultImageEntity.swift index 55003262b..2acdbd4e9 100644 --- a/Projects/Domains/ImageDomain/Interface/Entity/DefaultImageEntity.swift +++ b/Projects/Domains/ImageDomain/Interface/Entity/DefaultImageEntity.swift @@ -1,6 +1,6 @@ import Foundation -public struct DefaultImageEntity: Hashable, Equatable { +public struct DefaultImageEntity: Hashable, Equatable, Sendable { public let name: String public let url: String diff --git a/Projects/Domains/ImageDomain/Interface/Repository/ImageRepository.swift b/Projects/Domains/ImageDomain/Interface/Repository/ImageRepository.swift index ef3c8e41e..d7766d591 100644 --- a/Projects/Domains/ImageDomain/Interface/Repository/ImageRepository.swift +++ b/Projects/Domains/ImageDomain/Interface/Repository/ImageRepository.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol ImageRepository { +public protocol ImageRepository: Sendable { func fetchLyricDecoratingBackground() -> Single<[LyricDecoratingBackgroundEntity]> func fetchProfileList() -> Single<[ProfileListEntity]> func fetchDefaultPlaylistImage() -> Single<[DefaultImageEntity]> diff --git a/Projects/Domains/ImageDomain/Interface/UseCase/FetchDefaultPlaylistImageUseCase.swift b/Projects/Domains/ImageDomain/Interface/UseCase/FetchDefaultPlaylistImageUseCase.swift index dca426e27..cf5bb3743 100644 --- a/Projects/Domains/ImageDomain/Interface/UseCase/FetchDefaultPlaylistImageUseCase.swift +++ b/Projects/Domains/ImageDomain/Interface/UseCase/FetchDefaultPlaylistImageUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchDefaultPlaylistImageUseCase { +public protocol FetchDefaultPlaylistImageUseCase: Sendable { func execute() -> Single<[DefaultImageEntity]> } diff --git a/Projects/Domains/ImageDomain/Interface/UseCase/FetchLyricDecoratingBackgroundUseCase.swift b/Projects/Domains/ImageDomain/Interface/UseCase/FetchLyricDecoratingBackgroundUseCase.swift index 6ea0fe745..d4097094c 100644 --- a/Projects/Domains/ImageDomain/Interface/UseCase/FetchLyricDecoratingBackgroundUseCase.swift +++ b/Projects/Domains/ImageDomain/Interface/UseCase/FetchLyricDecoratingBackgroundUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchLyricDecoratingBackgroundUseCase { +public protocol FetchLyricDecoratingBackgroundUseCase: Sendable { func execute() -> Single<[LyricDecoratingBackgroundEntity]> } diff --git a/Projects/Domains/ImageDomain/Interface/UseCase/FetchProfileListUseCase.swift b/Projects/Domains/ImageDomain/Interface/UseCase/FetchProfileListUseCase.swift index b16712279..bccddac1b 100644 --- a/Projects/Domains/ImageDomain/Interface/UseCase/FetchProfileListUseCase.swift +++ b/Projects/Domains/ImageDomain/Interface/UseCase/FetchProfileListUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchProfileListUseCase { +public protocol FetchProfileListUseCase: Sendable { func execute() -> Single<[ProfileListEntity]> } diff --git a/Projects/Domains/LikeDomain/Interface/DataSource/RemoteLikeDataSource.swift b/Projects/Domains/LikeDomain/Interface/DataSource/RemoteLikeDataSource.swift index 8937ba85d..b8dc9f0b2 100644 --- a/Projects/Domains/LikeDomain/Interface/DataSource/RemoteLikeDataSource.swift +++ b/Projects/Domains/LikeDomain/Interface/DataSource/RemoteLikeDataSource.swift @@ -9,7 +9,7 @@ import Foundation import RxSwift -public protocol RemoteLikeDataSource { +public protocol RemoteLikeDataSource: Sendable { func addLikeSong(id: String) -> Single func cancelLikeSong(id: String) -> Single func checkIsLikedSong(id: String) -> Single diff --git a/Projects/Domains/LikeDomain/Interface/Repository/LikeRepository.swift b/Projects/Domains/LikeDomain/Interface/Repository/LikeRepository.swift index d633eac31..afacc9793 100644 --- a/Projects/Domains/LikeDomain/Interface/Repository/LikeRepository.swift +++ b/Projects/Domains/LikeDomain/Interface/Repository/LikeRepository.swift @@ -9,7 +9,7 @@ import Foundation import RxSwift -public protocol LikeRepository { +public protocol LikeRepository: Sendable { func addLikeSong(id: String) -> Single func cancelLikeSong(id: String) -> Single func checkIsLikedSong(id: String) -> Single diff --git a/Projects/Domains/LikeDomain/Interface/UseCase/AddLikeSongUseCase.swift b/Projects/Domains/LikeDomain/Interface/UseCase/AddLikeSongUseCase.swift index 7fabbba89..623eebf68 100644 --- a/Projects/Domains/LikeDomain/Interface/UseCase/AddLikeSongUseCase.swift +++ b/Projects/Domains/LikeDomain/Interface/UseCase/AddLikeSongUseCase.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol AddLikeSongUseCase { +public protocol AddLikeSongUseCase: Sendable { func execute(id: String) -> Single } diff --git a/Projects/Domains/LikeDomain/Interface/UseCase/CancelLikeSongUseCase.swift b/Projects/Domains/LikeDomain/Interface/UseCase/CancelLikeSongUseCase.swift index 974a3e2b3..89f8da227 100644 --- a/Projects/Domains/LikeDomain/Interface/UseCase/CancelLikeSongUseCase.swift +++ b/Projects/Domains/LikeDomain/Interface/UseCase/CancelLikeSongUseCase.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol CancelLikeSongUseCase { +public protocol CancelLikeSongUseCase: Sendable { func execute(id: String) -> Single } diff --git a/Projects/Domains/LikeDomain/Testing/UseCase/AddLikeSongUseCaseSpy.swift b/Projects/Domains/LikeDomain/Testing/UseCase/AddLikeSongUseCaseSpy.swift index ff041e1c3..bded15795 100644 --- a/Projects/Domains/LikeDomain/Testing/UseCase/AddLikeSongUseCaseSpy.swift +++ b/Projects/Domains/LikeDomain/Testing/UseCase/AddLikeSongUseCaseSpy.swift @@ -1,7 +1,7 @@ import LikeDomainInterface import RxSwift -public final class AddLikeSongUseCaseSpy: AddLikeSongUseCase { +public final class AddLikeSongUseCaseSpy: AddLikeSongUseCase, @unchecked Sendable { public private(set) var callCount = 0 public var handler: (String) -> Single = { _ in fatalError() } diff --git a/Projects/Domains/LikeDomain/Testing/UseCase/CancelLikeSongUseCaseSpy.swift b/Projects/Domains/LikeDomain/Testing/UseCase/CancelLikeSongUseCaseSpy.swift index 180422887..668144292 100644 --- a/Projects/Domains/LikeDomain/Testing/UseCase/CancelLikeSongUseCaseSpy.swift +++ b/Projects/Domains/LikeDomain/Testing/UseCase/CancelLikeSongUseCaseSpy.swift @@ -1,7 +1,7 @@ import LikeDomainInterface import RxSwift -public final class CancelLikeSongUseCaseSpy: CancelLikeSongUseCase { +public final class CancelLikeSongUseCaseSpy: CancelLikeSongUseCase, @unchecked Sendable { public private(set) var callCount = 0 public var handler: (String) -> Single = { _ in fatalError() } diff --git a/Projects/Domains/NoticeDomain/Interface/DataSource/RemoteNoticeDataSource.swift b/Projects/Domains/NoticeDomain/Interface/DataSource/RemoteNoticeDataSource.swift index 078a30721..fc4ac8e93 100644 --- a/Projects/Domains/NoticeDomain/Interface/DataSource/RemoteNoticeDataSource.swift +++ b/Projects/Domains/NoticeDomain/Interface/DataSource/RemoteNoticeDataSource.swift @@ -9,7 +9,7 @@ import Foundation import RxSwift -public protocol RemoteNoticeDataSource { +public protocol RemoteNoticeDataSource: Sendable { func fetchNoticeCategories() -> Single func fetchNoticePopup() -> Single<[FetchNoticeEntity]> func fetchNoticeAll() -> Single<[FetchNoticeEntity]> diff --git a/Projects/Domains/NoticeDomain/Interface/Repository/NoticeRepository.swift b/Projects/Domains/NoticeDomain/Interface/Repository/NoticeRepository.swift index 0755628b7..1153826e7 100644 --- a/Projects/Domains/NoticeDomain/Interface/Repository/NoticeRepository.swift +++ b/Projects/Domains/NoticeDomain/Interface/Repository/NoticeRepository.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol NoticeRepository { +public protocol NoticeRepository: Sendable { func fetchNoticeCategories() -> Single func fetchNoticePopup() -> Single<[FetchNoticeEntity]> func fetchNoticeAll() -> Single<[FetchNoticeEntity]> diff --git a/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeAllUseCase.swift b/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeAllUseCase.swift index 740d5eae9..2f8167829 100644 --- a/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeAllUseCase.swift +++ b/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeAllUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchNoticeAllUseCase { +public protocol FetchNoticeAllUseCase: Sendable { func execute() -> Single<[FetchNoticeEntity]> } diff --git a/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeCategoriesUseCase.swift b/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeCategoriesUseCase.swift index f85908f3d..e8d4f412b 100644 --- a/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeCategoriesUseCase.swift +++ b/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeCategoriesUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchNoticeCategoriesUseCase { +public protocol FetchNoticeCategoriesUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeIDListUseCase.swift b/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeIDListUseCase.swift index f89096fd3..02681a42c 100644 --- a/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeIDListUseCase.swift +++ b/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticeIDListUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchNoticeIDListUseCase { +public protocol FetchNoticeIDListUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticePopupUseCase.swift b/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticePopupUseCase.swift index 4323ef4a3..48fd0fff4 100644 --- a/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticePopupUseCase.swift +++ b/Projects/Domains/NoticeDomain/Interface/UseCase/FetchNoticePopupUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchNoticePopupUseCase { +public protocol FetchNoticePopupUseCase: Sendable { func execute() -> Single<[FetchNoticeEntity]> } diff --git a/Projects/Domains/NoticeDomain/Testing/FetchNoticeAllUseCaseStub.swift b/Projects/Domains/NoticeDomain/Testing/FetchNoticeAllUseCaseStub.swift index 80389e2f7..8191d9632 100644 --- a/Projects/Domains/NoticeDomain/Testing/FetchNoticeAllUseCaseStub.swift +++ b/Projects/Domains/NoticeDomain/Testing/FetchNoticeAllUseCaseStub.swift @@ -2,7 +2,7 @@ import Foundation import NoticeDomainInterface import RxSwift -public struct FetchNoticeAllUseCaseStub: FetchNoticeAllUseCase { +public struct FetchNoticeAllUseCaseStub: FetchNoticeAllUseCase, @unchecked Sendable { public func execute() -> Single<[FetchNoticeEntity]> { let title1 = "๊ณต์ง€์‚ฌํ•ญ ๋‘์ค„์ธ ๊ฒฝ์šฐ ๊ณต์ง€์‚ฌํ•ญ ๋‘์ค„์ธ ๊ฒฝ์šฐ ๊ณต์ง€์‚ฌํ•ญ ๋‘์ค„์ธ ๊ฒฝ์šฐ ๊ณต์ง€์‚ฌํ•ญ ๋‘์ค„์ธ ๊ฒฝ์šฐ" let title2 = "์™ํƒ€๋ฒ„์Šค ๋ฎค์ง 2.0 ์—…๋ฐ์ดํŠธ" diff --git a/Projects/Domains/NoticeDomain/Testing/FetchNoticeCategoriesUseCaseStub.swift b/Projects/Domains/NoticeDomain/Testing/FetchNoticeCategoriesUseCaseStub.swift index 27833b9d0..7884de60f 100644 --- a/Projects/Domains/NoticeDomain/Testing/FetchNoticeCategoriesUseCaseStub.swift +++ b/Projects/Domains/NoticeDomain/Testing/FetchNoticeCategoriesUseCaseStub.swift @@ -2,7 +2,7 @@ import Foundation import NoticeDomainInterface import RxSwift -public struct FetchNoticeCategoriesUseCaseStub: FetchNoticeCategoriesUseCase { +public struct FetchNoticeCategoriesUseCaseStub: FetchNoticeCategoriesUseCase, @unchecked Sendable { public func execute() -> Single { return .just(FetchNoticeCategoriesEntity(categories: [])) } diff --git a/Projects/Domains/NotificationDomain/Interface/DataSource/RemoteNotificationDataSource.swift b/Projects/Domains/NotificationDomain/Interface/DataSource/RemoteNotificationDataSource.swift index 8b1c6224c..5989c4ac8 100644 --- a/Projects/Domains/NotificationDomain/Interface/DataSource/RemoteNotificationDataSource.swift +++ b/Projects/Domains/NotificationDomain/Interface/DataSource/RemoteNotificationDataSource.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol RemoteNotificationDataSource { +public protocol RemoteNotificationDataSource: Sendable { func updateNotificationToken(type: NotificationUpdateType) -> Completable } diff --git a/Projects/Domains/NotificationDomain/Interface/Repository/NotificationRepository.swift b/Projects/Domains/NotificationDomain/Interface/Repository/NotificationRepository.swift index 9f697805a..acbe9f3f7 100644 --- a/Projects/Domains/NotificationDomain/Interface/Repository/NotificationRepository.swift +++ b/Projects/Domains/NotificationDomain/Interface/Repository/NotificationRepository.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol NotificationRepository { +public protocol NotificationRepository: Sendable { func updateNotificationToken(type: NotificationUpdateType) -> Completable } diff --git a/Projects/Domains/NotificationDomain/Interface/UseCase/UpdateNotificationTokenUseCase.swift b/Projects/Domains/NotificationDomain/Interface/UseCase/UpdateNotificationTokenUseCase.swift index fd6e268a8..04f8300a1 100644 --- a/Projects/Domains/NotificationDomain/Interface/UseCase/UpdateNotificationTokenUseCase.swift +++ b/Projects/Domains/NotificationDomain/Interface/UseCase/UpdateNotificationTokenUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol UpdateNotificationTokenUseCase { +public protocol UpdateNotificationTokenUseCase: Sendable { func execute(type: NotificationUpdateType) -> Completable } diff --git a/Projects/Domains/PlaylistDomain/Interface/DataSource/RemotePlaylistDataSource.swift b/Projects/Domains/PlaylistDomain/Interface/DataSource/RemotePlaylistDataSource.swift index b4130c423..55f3ffb56 100644 --- a/Projects/Domains/PlaylistDomain/Interface/DataSource/RemotePlaylistDataSource.swift +++ b/Projects/Domains/PlaylistDomain/Interface/DataSource/RemotePlaylistDataSource.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol RemotePlaylistDataSource { +public protocol RemotePlaylistDataSource: Sendable { func fetchRecommendPlaylist() -> Single<[RecommendPlaylistEntity]> func fetchPlaylistDetail(id: String, type: PlaylistType) -> Single func fetchWMPlaylistDetail(id: String) -> Single diff --git a/Projects/Domains/PlaylistDomain/Interface/Entity/RecommendPlaylistEntity.swift b/Projects/Domains/PlaylistDomain/Interface/Entity/RecommendPlaylistEntity.swift index 7fdcb15c4..f69474754 100644 --- a/Projects/Domains/PlaylistDomain/Interface/Entity/RecommendPlaylistEntity.swift +++ b/Projects/Domains/PlaylistDomain/Interface/Entity/RecommendPlaylistEntity.swift @@ -1,6 +1,6 @@ import Foundation -public struct RecommendPlaylistEntity: Hashable, Equatable { +public struct RecommendPlaylistEntity: Hashable, Equatable, Sendable { public init( key: String, title: String, diff --git a/Projects/Domains/PlaylistDomain/Interface/Repository/PlaylistRepository.swift b/Projects/Domains/PlaylistDomain/Interface/Repository/PlaylistRepository.swift index c8d0e9eea..2999a3074 100644 --- a/Projects/Domains/PlaylistDomain/Interface/Repository/PlaylistRepository.swift +++ b/Projects/Domains/PlaylistDomain/Interface/Repository/PlaylistRepository.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol PlaylistRepository { +public protocol PlaylistRepository: Sendable { func fetchRecommendPlaylist() -> Single<[RecommendPlaylistEntity]> func fetchPlaylistDetail(id: String, type: PlaylistType) -> Single func fetchWMPlaylistDetail(id: String) -> Single diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/AddSongIntoPlaylistUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/AddSongIntoPlaylistUseCase.swift index ef690e71c..1110eb83c 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/AddSongIntoPlaylistUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/AddSongIntoPlaylistUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol AddSongIntoPlaylistUseCase { +public protocol AddSongIntoPlaylistUseCase: Sendable { func execute(key: String, songs: [String]) -> Single } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/CheckSubscriptionUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/CheckSubscriptionUseCase.swift index 8a0c9bc3a..9070f836c 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/CheckSubscriptionUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/CheckSubscriptionUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol CheckSubscriptionUseCase { +public protocol CheckSubscriptionUseCase: Sendable { func execute(key: String) -> Single } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/CreatePlaylistUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/CreatePlaylistUseCase.swift index b7b6f146a..6489b2bab 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/CreatePlaylistUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/CreatePlaylistUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol CreatePlaylistUseCase { +public protocol CreatePlaylistUseCase: Sendable { func execute(title: String) -> Single } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchPlaylistDetailUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchPlaylistDetailUseCase.swift index eb15b13a8..d29484c72 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchPlaylistDetailUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchPlaylistDetailUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchPlaylistDetailUseCase { +public protocol FetchPlaylistDetailUseCase: Sendable { func execute(id: String, type: PlaylistType) -> Single } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchPlaylistSongsUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchPlaylistSongsUseCase.swift index fc32cc722..a0a55fd63 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchPlaylistSongsUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchPlaylistSongsUseCase.swift @@ -3,6 +3,6 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol FetchPlaylistSongsUseCase { +public protocol FetchPlaylistSongsUseCase: Sendable { func execute(key: String) -> Single<[SongEntity]> } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchRecommendPlaylistUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchRecommendPlaylistUseCase.swift index 292d15ae9..f5d761df2 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchRecommendPlaylistUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchRecommendPlaylistUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchRecommendPlaylistUseCase { +public protocol FetchRecommendPlaylistUseCase: Sendable { func execute() -> Single<[RecommendPlaylistEntity]> } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchWMPlaylistDetailUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchWMPlaylistDetailUseCase.swift index 4f3a3ad50..ce45a4244 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchWMPlaylistDetailUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/FetchWMPlaylistDetailUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchWMPlaylistDetailUseCase { +public protocol FetchWMPlaylistDetailUseCase: Sendable { func execute(id: String) -> Single } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/RemoveSongsUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/RemoveSongsUseCase.swift index 43d13f61b..700254ffb 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/RemoveSongsUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/RemoveSongsUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol RemoveSongsUseCase { +public protocol RemoveSongsUseCase: Sendable { func execute(key: String, songs: [String]) -> Completable } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/RequestCustomImageURLUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/RequestCustomImageURLUseCase.swift index df1db7d84..945397943 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/RequestCustomImageURLUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/RequestCustomImageURLUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol RequestCustomImageURLUseCase { +public protocol RequestCustomImageURLUseCase: Sendable { func execute(key: String, data: Data) -> Completable } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/RequestPlaylistOwnerIDUsecase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/RequestPlaylistOwnerIDUsecase.swift index 2226011db..90be5331c 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/RequestPlaylistOwnerIDUsecase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/RequestPlaylistOwnerIDUsecase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol RequestPlaylistOwnerIDUsecase { +public protocol RequestPlaylistOwnerIDUsecase: Sendable { func execute(key: String) -> Single } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/SubscribePlaylistUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/SubscribePlaylistUseCase.swift index 3e3707c0e..be6c88449 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/SubscribePlaylistUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/SubscribePlaylistUseCase.swift @@ -1,5 +1,5 @@ import RxSwift -public protocol SubscribePlaylistUseCase { +public protocol SubscribePlaylistUseCase: Sendable { func execute(key: String, isSubscribing: Bool) -> Completable } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/UpdatePlaylistUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/UpdatePlaylistUseCase.swift index 043199681..2984787da 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/UpdatePlaylistUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/UpdatePlaylistUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol UpdatePlaylistUseCase { +public protocol UpdatePlaylistUseCase: Sendable { func execute(key: String, songs: [String]) -> Completable } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/UpdateTitleAndPrivateUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/UpdateTitleAndPrivateUseCase.swift index d86527627..b9a2a8892 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/UpdateTitleAndPrivateUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/UpdateTitleAndPrivateUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol UpdateTitleAndPrivateUseCase { +public protocol UpdateTitleAndPrivateUseCase: Sendable { func execute(key: String, title: String?, isPrivate: Bool?) -> Completable } diff --git a/Projects/Domains/PlaylistDomain/Interface/UseCase/UploadPlaylistImageUseCase.swift b/Projects/Domains/PlaylistDomain/Interface/UseCase/UploadPlaylistImageUseCase.swift index afead3e3b..fd55eb51e 100644 --- a/Projects/Domains/PlaylistDomain/Interface/UseCase/UploadPlaylistImageUseCase.swift +++ b/Projects/Domains/PlaylistDomain/Interface/UseCase/UploadPlaylistImageUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol UploadDefaultPlaylistImageUseCase { +public protocol UploadDefaultPlaylistImageUseCase: Sendable { func execute(key: String, model: String) -> Completable } diff --git a/Projects/Domains/PlaylistDomain/Testing/FetchPlayListUseCaseStub.swift b/Projects/Domains/PlaylistDomain/Testing/FetchPlayListUseCaseStub.swift index f8a0dffa8..fbdb036fc 100644 --- a/Projects/Domains/PlaylistDomain/Testing/FetchPlayListUseCaseStub.swift +++ b/Projects/Domains/PlaylistDomain/Testing/FetchPlayListUseCaseStub.swift @@ -1,7 +1,7 @@ import PlaylistDomainInterface import RxSwift -final class FetchPlayListUseCaseStub: FetchRecommendPlaylistUseCase { +final class FetchPlayListUseCaseStub: FetchRecommendPlaylistUseCase, @unchecked Sendable { var fetchData = [ RecommendPlaylistEntity( key: "best", diff --git a/Projects/Domains/PriceDomain/Interface/DataSource/RemotePriceDataSource.swift b/Projects/Domains/PriceDomain/Interface/DataSource/RemotePriceDataSource.swift index c938b5170..77c51baa9 100644 --- a/Projects/Domains/PriceDomain/Interface/DataSource/RemotePriceDataSource.swift +++ b/Projects/Domains/PriceDomain/Interface/DataSource/RemotePriceDataSource.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol RemotePriceDataSource { +public protocol RemotePriceDataSource: Sendable { func fetchPlaylistCreationPrice() -> Single func fetchPlaylistImagePrice() -> Single } diff --git a/Projects/Domains/PriceDomain/Interface/Repository/PriceRepository.swift b/Projects/Domains/PriceDomain/Interface/Repository/PriceRepository.swift index 455ccaf04..77abe6784 100644 --- a/Projects/Domains/PriceDomain/Interface/Repository/PriceRepository.swift +++ b/Projects/Domains/PriceDomain/Interface/Repository/PriceRepository.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol PriceRepository { +public protocol PriceRepository: Sendable { func fetchPlaylistCreationPrice() -> Single func fetchPlaylistImagePrice() -> Single } diff --git a/Projects/Domains/PriceDomain/Interface/UseCase/FetchPlaylistCreationPriceUseCase.swift b/Projects/Domains/PriceDomain/Interface/UseCase/FetchPlaylistCreationPriceUseCase.swift index 3a8d94c30..ab9813a99 100644 --- a/Projects/Domains/PriceDomain/Interface/UseCase/FetchPlaylistCreationPriceUseCase.swift +++ b/Projects/Domains/PriceDomain/Interface/UseCase/FetchPlaylistCreationPriceUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchPlaylistCreationPriceUseCase { +public protocol FetchPlaylistCreationPriceUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/PriceDomain/Interface/UseCase/FetchPlaylistImagePriceUseCase.swift b/Projects/Domains/PriceDomain/Interface/UseCase/FetchPlaylistImagePriceUseCase.swift index 5fe52649a..655db9f38 100644 --- a/Projects/Domains/PriceDomain/Interface/UseCase/FetchPlaylistImagePriceUseCase.swift +++ b/Projects/Domains/PriceDomain/Interface/UseCase/FetchPlaylistImagePriceUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchPlaylistImagePriceUseCase { +public protocol FetchPlaylistImagePriceUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/SearchDomain/Interface/DataSource/RemoteSearchDataSource.swift b/Projects/Domains/SearchDomain/Interface/DataSource/RemoteSearchDataSource.swift index 23b1c6237..f4f22e93f 100644 --- a/Projects/Domains/SearchDomain/Interface/DataSource/RemoteSearchDataSource.swift +++ b/Projects/Domains/SearchDomain/Interface/DataSource/RemoteSearchDataSource.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol RemoteSearchDataSource { +public protocol RemoteSearchDataSource: Sendable { func fetchSearchSongs(order: SortType, filter: FilterType, text: String, page: Int, limit: Int) -> Single<[SongEntity]> func fetchSearchPlaylist(order: SortType, text: String, page: Int, limit: Int) -> Single<[SearchPlaylistEntity]> diff --git a/Projects/Domains/SearchDomain/Interface/Entity/SearchPlaylistEntity.swift b/Projects/Domains/SearchDomain/Interface/Entity/SearchPlaylistEntity.swift index 978a95464..98e1af630 100644 --- a/Projects/Domains/SearchDomain/Interface/Entity/SearchPlaylistEntity.swift +++ b/Projects/Domains/SearchDomain/Interface/Entity/SearchPlaylistEntity.swift @@ -1,6 +1,6 @@ import Foundation -public struct SearchPlaylistEntity: Hashable, Equatable { +public struct SearchPlaylistEntity: Hashable, Equatable, Sendable { public init( key: String, title: String, diff --git a/Projects/Domains/SearchDomain/Interface/Enum/Option.swift b/Projects/Domains/SearchDomain/Interface/Enum/Option.swift index bee7fa6ea..0e1706928 100644 --- a/Projects/Domains/SearchDomain/Interface/Enum/Option.swift +++ b/Projects/Domains/SearchDomain/Interface/Enum/Option.swift @@ -2,7 +2,7 @@ public protocol SearchOptionType { var title: String { get } } -public enum FilterType: String, Encodable, SearchOptionType { +public enum FilterType: String, Encodable, SearchOptionType, Sendable { case all case title case artist @@ -22,7 +22,7 @@ public enum FilterType: String, Encodable, SearchOptionType { } } -public enum SortType: String, Encodable, SearchOptionType { +public enum SortType: String, Encodable, SearchOptionType, Sendable { case latest case oldest case relevance diff --git a/Projects/Domains/SearchDomain/Interface/Repository/SearchRepository.swift b/Projects/Domains/SearchDomain/Interface/Repository/SearchRepository.swift index 9f14eef76..d15e97eda 100644 --- a/Projects/Domains/SearchDomain/Interface/Repository/SearchRepository.swift +++ b/Projects/Domains/SearchDomain/Interface/Repository/SearchRepository.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol SearchRepository { +public protocol SearchRepository: Sendable { func fetchSearchSongs(order: SortType, filter: FilterType, text: String, page: Int, limit: Int) -> Single<[SongEntity]> func fetchSearchPlaylist(order: SortType, text: String, page: Int, limit: Int) -> Single<[SearchPlaylistEntity]> diff --git a/Projects/Domains/SearchDomain/Interface/UseCase/FetchSearchPlaylistsUseCase.swift b/Projects/Domains/SearchDomain/Interface/UseCase/FetchSearchPlaylistsUseCase.swift index f943f8108..674e14a61 100644 --- a/Projects/Domains/SearchDomain/Interface/UseCase/FetchSearchPlaylistsUseCase.swift +++ b/Projects/Domains/SearchDomain/Interface/UseCase/FetchSearchPlaylistsUseCase.swift @@ -2,6 +2,6 @@ import Foundation import PlaylistDomainInterface import RxSwift -public protocol FetchSearchPlaylistsUseCase { +public protocol FetchSearchPlaylistsUseCase: Sendable { func execute(order: SortType, text: String, page: Int, limit: Int) -> Single<[SearchPlaylistEntity]> } diff --git a/Projects/Domains/SearchDomain/Interface/UseCase/FetchSearchSongsUseCase.swift b/Projects/Domains/SearchDomain/Interface/UseCase/FetchSearchSongsUseCase.swift index 3fb83c108..e281c0b7a 100644 --- a/Projects/Domains/SearchDomain/Interface/UseCase/FetchSearchSongsUseCase.swift +++ b/Projects/Domains/SearchDomain/Interface/UseCase/FetchSearchSongsUseCase.swift @@ -2,6 +2,6 @@ import Foundation import RxSwift import SongsDomainInterface -public protocol FetchSearchSongsUseCase { +public protocol FetchSearchSongsUseCase: Sendable { func execute(order: SortType, filter: FilterType, text: String, page: Int, limit: Int) -> Single<[SongEntity]> } diff --git a/Projects/Domains/SongsDomain/Interface/DataSource/RemoteSongsDataSource.swift b/Projects/Domains/SongsDomain/Interface/DataSource/RemoteSongsDataSource.swift index a71466d11..cc1f3c5bd 100644 --- a/Projects/Domains/SongsDomain/Interface/DataSource/RemoteSongsDataSource.swift +++ b/Projects/Domains/SongsDomain/Interface/DataSource/RemoteSongsDataSource.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol RemoteSongsDataSource { +public protocol RemoteSongsDataSource: Sendable { func fetchSong(id: String) -> Single func fetchLyrics(id: String) -> Single func fetchSongCredits(id: String) -> Single<[SongCreditsEntity]> diff --git a/Projects/Domains/SongsDomain/Interface/Entity/SongDetailEntity.swift b/Projects/Domains/SongsDomain/Interface/Entity/SongDetailEntity.swift index 04d484828..36e51143f 100644 --- a/Projects/Domains/SongsDomain/Interface/Entity/SongDetailEntity.swift +++ b/Projects/Domains/SongsDomain/Interface/Entity/SongDetailEntity.swift @@ -1,6 +1,6 @@ import Foundation -public struct SongDetailEntity: Hashable { +public struct SongDetailEntity: Hashable, Sendable { public init( id: String, title: String, @@ -28,7 +28,7 @@ public struct SongDetailEntity: Hashable { public let isLiked: Bool public let karaokeNumber: SongDetailEntity.KaraokeNumber - public struct KaraokeNumber: Hashable, Equatable { + public struct KaraokeNumber: Hashable, Equatable, Sendable { public init (tj: Int?, ky: Int?) { self.tj = tj self.ky = ky diff --git a/Projects/Domains/SongsDomain/Interface/Entity/SongEntity.swift b/Projects/Domains/SongsDomain/Interface/Entity/SongEntity.swift index b4f20862d..945567caf 100644 --- a/Projects/Domains/SongsDomain/Interface/Entity/SongEntity.swift +++ b/Projects/Domains/SongsDomain/Interface/Entity/SongEntity.swift @@ -1,6 +1,9 @@ import Foundation -public struct SongEntity: Hashable { +/** + isSelected๊ฐ€ mutable์ด๋‚˜ sendableํ•˜์ง€ ๋ชปํ•จ. + */ +public struct SongEntity: Hashable, @unchecked Sendable { public init( id: String, title: String, diff --git a/Projects/Domains/SongsDomain/Interface/Repository/SongsRepository.swift b/Projects/Domains/SongsDomain/Interface/Repository/SongsRepository.swift index ffeafca85..6f81438f2 100644 --- a/Projects/Domains/SongsDomain/Interface/Repository/SongsRepository.swift +++ b/Projects/Domains/SongsDomain/Interface/Repository/SongsRepository.swift @@ -1,7 +1,7 @@ import Foundation import RxSwift -public protocol SongsRepository { +public protocol SongsRepository: Sendable { func fetchSong(id: String) -> Single func fetchLyrics(id: String) -> Single func fetchSongCredits(id: String) -> Single<[SongCreditsEntity]> diff --git a/Projects/Domains/SongsDomain/Interface/UseCase/FetchLyricsUseCase.swift b/Projects/Domains/SongsDomain/Interface/UseCase/FetchLyricsUseCase.swift index 7cc1b8fe1..6aff8a65e 100644 --- a/Projects/Domains/SongsDomain/Interface/UseCase/FetchLyricsUseCase.swift +++ b/Projects/Domains/SongsDomain/Interface/UseCase/FetchLyricsUseCase.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol FetchLyricsUseCase { +public protocol FetchLyricsUseCase: Sendable { func execute(id: String) -> Single } diff --git a/Projects/Domains/SongsDomain/Interface/UseCase/FetchNewSongsPlaylistUseCase.swift b/Projects/Domains/SongsDomain/Interface/UseCase/FetchNewSongsPlaylistUseCase.swift index d32c9ff5c..b5ce06d0f 100644 --- a/Projects/Domains/SongsDomain/Interface/UseCase/FetchNewSongsPlaylistUseCase.swift +++ b/Projects/Domains/SongsDomain/Interface/UseCase/FetchNewSongsPlaylistUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchNewSongsPlaylistUseCase { +public protocol FetchNewSongsPlaylistUseCase: Sendable { func execute(type: NewSongGroupType) -> Single } diff --git a/Projects/Domains/SongsDomain/Interface/UseCase/FetchNewSongsUseCase.swift b/Projects/Domains/SongsDomain/Interface/UseCase/FetchNewSongsUseCase.swift index 379030e45..7e6d952f7 100644 --- a/Projects/Domains/SongsDomain/Interface/UseCase/FetchNewSongsUseCase.swift +++ b/Projects/Domains/SongsDomain/Interface/UseCase/FetchNewSongsUseCase.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol FetchNewSongsUseCase { +public protocol FetchNewSongsUseCase: Sendable { func execute(type: NewSongGroupType, page: Int, limit: Int) -> Single<[NewSongsEntity]> } diff --git a/Projects/Domains/SongsDomain/Interface/UseCase/FetchSongCreditsUseCase.swift b/Projects/Domains/SongsDomain/Interface/UseCase/FetchSongCreditsUseCase.swift index 716227c6b..7baf4cb03 100644 --- a/Projects/Domains/SongsDomain/Interface/UseCase/FetchSongCreditsUseCase.swift +++ b/Projects/Domains/SongsDomain/Interface/UseCase/FetchSongCreditsUseCase.swift @@ -9,6 +9,6 @@ import Foundation import RxSwift -public protocol FetchSongCreditsUseCase { +public protocol FetchSongCreditsUseCase: Sendable { func execute(id: String) -> Single<[SongCreditsEntity]> } diff --git a/Projects/Domains/SongsDomain/Interface/UseCase/FetchSongUseCase.swift b/Projects/Domains/SongsDomain/Interface/UseCase/FetchSongUseCase.swift index c4a2c81d2..a1b5b27b5 100644 --- a/Projects/Domains/SongsDomain/Interface/UseCase/FetchSongUseCase.swift +++ b/Projects/Domains/SongsDomain/Interface/UseCase/FetchSongUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchSongUseCase { +public protocol FetchSongUseCase: Sendable { func execute(id: String) -> Single } diff --git a/Projects/Domains/SongsDomain/Testing/UseCase/FetchSongCreditsUseCaseSpy.swift b/Projects/Domains/SongsDomain/Testing/UseCase/FetchSongCreditsUseCaseSpy.swift index 7e26eee22..bb7007207 100644 --- a/Projects/Domains/SongsDomain/Testing/UseCase/FetchSongCreditsUseCaseSpy.swift +++ b/Projects/Domains/SongsDomain/Testing/UseCase/FetchSongCreditsUseCaseSpy.swift @@ -1,7 +1,7 @@ import RxSwift import SongsDomainInterface -public final class FetchSongCreditsUseCaseSpy: FetchSongCreditsUseCase { +public final class FetchSongCreditsUseCaseSpy: FetchSongCreditsUseCase, @unchecked Sendable { public private(set) var callCount = 0 public var handler: ((String) -> Single<[SongCreditsEntity]>) = { _ in fatalError() } diff --git a/Projects/Domains/SongsDomain/Testing/UseCase/FetchSongUseCaseSpy.swift b/Projects/Domains/SongsDomain/Testing/UseCase/FetchSongUseCaseSpy.swift index 5642fb95b..e344d4849 100644 --- a/Projects/Domains/SongsDomain/Testing/UseCase/FetchSongUseCaseSpy.swift +++ b/Projects/Domains/SongsDomain/Testing/UseCase/FetchSongUseCaseSpy.swift @@ -1,7 +1,7 @@ import RxSwift import SongsDomainInterface -public final class FetchSongUseCaseSpy: FetchSongUseCase { +public final class FetchSongUseCaseSpy: FetchSongUseCase, @unchecked Sendable { public private(set) var callCount = 0 public var handler: ((String) -> Single) = { _ in fatalError() } diff --git a/Projects/Domains/TeamDomain/Interface/DataSource/RemoteTeamDataSource.swift b/Projects/Domains/TeamDomain/Interface/DataSource/RemoteTeamDataSource.swift index 83fd547f2..212c71600 100644 --- a/Projects/Domains/TeamDomain/Interface/DataSource/RemoteTeamDataSource.swift +++ b/Projects/Domains/TeamDomain/Interface/DataSource/RemoteTeamDataSource.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol RemoteTeamDataSource { +public protocol RemoteTeamDataSource: Sendable { func fetchTeamList() -> Single<[TeamListEntity]> } diff --git a/Projects/Domains/TeamDomain/Interface/Repository/TeamRepository.swift b/Projects/Domains/TeamDomain/Interface/Repository/TeamRepository.swift index 9a9d3a697..24dc4472f 100644 --- a/Projects/Domains/TeamDomain/Interface/Repository/TeamRepository.swift +++ b/Projects/Domains/TeamDomain/Interface/Repository/TeamRepository.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol TeamRepository { +public protocol TeamRepository: Sendable { func fetchTeamList() -> Single<[TeamListEntity]> } diff --git a/Projects/Domains/TeamDomain/Interface/UseCase/FetchTeamListUseCase.swift b/Projects/Domains/TeamDomain/Interface/UseCase/FetchTeamListUseCase.swift index a5e5ab653..349099bea 100644 --- a/Projects/Domains/TeamDomain/Interface/UseCase/FetchTeamListUseCase.swift +++ b/Projects/Domains/TeamDomain/Interface/UseCase/FetchTeamListUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchTeamListUseCase { +public protocol FetchTeamListUseCase: Sendable { func execute() -> Single<[TeamListEntity]> } diff --git a/Projects/Domains/TeamDomain/Sources/DataSource/RemoteTeamDataSourceImpl.swift b/Projects/Domains/TeamDomain/Sources/DataSource/RemoteTeamDataSourceImpl.swift index 2b9c348f8..57a13932b 100644 --- a/Projects/Domains/TeamDomain/Sources/DataSource/RemoteTeamDataSourceImpl.swift +++ b/Projects/Domains/TeamDomain/Sources/DataSource/RemoteTeamDataSourceImpl.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift import TeamDomainInterface -public final class RemoteTeamDataSourceImpl: BaseRemoteDataSource, RemoteTeamDataSource { +public final class RemoteTeamDataSourceImpl: BaseRemoteDataSource, RemoteTeamDataSource, @unchecked Sendable { public func fetchTeamList() -> Single<[TeamListEntity]> { request(.fetchTeamList) .map([FetchTeamListResponseDTO].self) diff --git a/Projects/Domains/UserDomain/Interface/DataSource/RemoteUserDataSource.swift b/Projects/Domains/UserDomain/Interface/DataSource/RemoteUserDataSource.swift index 4ff9bdad9..d3a3849cf 100644 --- a/Projects/Domains/UserDomain/Interface/DataSource/RemoteUserDataSource.swift +++ b/Projects/Domains/UserDomain/Interface/DataSource/RemoteUserDataSource.swift @@ -2,7 +2,7 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol RemoteUserDataSource { +public protocol RemoteUserDataSource: Sendable { func setProfile(image: String) -> Completable func setUserName(name: String) -> Completable func fetchPlaylist() -> Single<[PlaylistEntity]> diff --git a/Projects/Domains/UserDomain/Interface/Entity/FavoriteSongEntity.swift b/Projects/Domains/UserDomain/Interface/Entity/FavoriteSongEntity.swift index f75e05fcb..e87d85bd5 100644 --- a/Projects/Domains/UserDomain/Interface/Entity/FavoriteSongEntity.swift +++ b/Projects/Domains/UserDomain/Interface/Entity/FavoriteSongEntity.swift @@ -1,7 +1,8 @@ import Foundation import SongsDomainInterface -public struct FavoriteSongEntity: Equatable { +// TODO: concurrency๋ฅผ ์œ„ํ•œ Model์˜ Mutable ์ œ๊ฑฐ ํ•„์š” +public struct FavoriteSongEntity: Equatable, @unchecked Sendable { public init(songID: String, title: String, artist: String, like: Int) { self.songID = songID self.title = title diff --git a/Projects/Domains/UserDomain/Interface/Repository/UserRepository.swift b/Projects/Domains/UserDomain/Interface/Repository/UserRepository.swift index 0a61d777f..75581a314 100644 --- a/Projects/Domains/UserDomain/Interface/Repository/UserRepository.swift +++ b/Projects/Domains/UserDomain/Interface/Repository/UserRepository.swift @@ -2,7 +2,7 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol UserRepository { +public protocol UserRepository: Sendable { func setProfile(image: String) -> Completable func setUserName(name: String) -> Completable func fetchPlaylist() -> Single<[PlaylistEntity]> diff --git a/Projects/Domains/UserDomain/Interface/UseCase/DeleteFavoriteListUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/DeleteFavoriteListUseCase.swift index 0a20c0fc3..0c5659f70 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/DeleteFavoriteListUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/DeleteFavoriteListUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol DeleteFavoriteListUseCase { +public protocol DeleteFavoriteListUseCase: Sendable { func execute(ids: [String]) -> Completable } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/DeletePlaylistUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/DeletePlaylistUseCase.swift index 47790162b..bbe5b24a4 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/DeletePlaylistUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/DeletePlaylistUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol DeletePlaylistUseCase { +public protocol DeletePlaylistUseCase: Sendable { func execute(ids: [String]) -> Completable } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/DrawFruitUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/DrawFruitUseCase.swift index db3bae005..9bd75b371 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/DrawFruitUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/DrawFruitUseCase.swift @@ -1,5 +1,5 @@ import RxSwift -public protocol DrawFruitUseCase { +public protocol DrawFruitUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/EditFavoriteSongsOrderUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/EditFavoriteSongsOrderUseCase.swift index 7347aed7e..1451813ea 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/EditFavoriteSongsOrderUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/EditFavoriteSongsOrderUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol EditFavoriteSongsOrderUseCase { +public protocol EditFavoriteSongsOrderUseCase: Sendable { func execute(ids: [String]) -> Completable } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/EditPlaylistOrderUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/EditPlaylistOrderUseCase.swift index dcca28bc3..d7f6c5263 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/EditPlaylistOrderUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/EditPlaylistOrderUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol EditPlaylistOrderUseCase { +public protocol EditPlaylistOrderUseCase: Sendable { func execute(ids: [String]) -> Completable } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/FetchFavoriteSongsUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/FetchFavoriteSongsUseCase.swift index 4d872c3dd..ebad66746 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/FetchFavoriteSongsUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/FetchFavoriteSongsUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchFavoriteSongsUseCase { +public protocol FetchFavoriteSongsUseCase: Sendable { func execute() -> Single<[FavoriteSongEntity]> } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/FetchFruitDrawStatusUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/FetchFruitDrawStatusUseCase.swift index 475d1c502..626bab570 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/FetchFruitDrawStatusUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/FetchFruitDrawStatusUseCase.swift @@ -1,5 +1,5 @@ import RxSwift -public protocol FetchFruitDrawStatusUseCase { +public protocol FetchFruitDrawStatusUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/FetchFruitListUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/FetchFruitListUseCase.swift index 5d5db31d6..3302bd0b1 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/FetchFruitListUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/FetchFruitListUseCase.swift @@ -1,5 +1,5 @@ import RxSwift -public protocol FetchFruitListUseCase { +public protocol FetchFruitListUseCase: Sendable { func execute() -> Single<[FruitEntity]> } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/FetchPlaylistUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/FetchPlaylistUseCase.swift index 893de9add..ff36e4697 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/FetchPlaylistUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/FetchPlaylistUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchPlaylistUseCase { +public protocol FetchPlaylistUseCase: Sendable { func execute() -> Single<[PlaylistEntity]> } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/FetchUserInfoUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/FetchUserInfoUseCase.swift index 68311c55c..6d4214358 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/FetchUserInfoUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/FetchUserInfoUseCase.swift @@ -1,6 +1,6 @@ import Foundation import RxSwift -public protocol FetchUserInfoUseCase { +public protocol FetchUserInfoUseCase: Sendable { func execute() -> Single } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/SetProfileUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/SetProfileUseCase.swift index 484b410c7..d1a650d8a 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/SetProfileUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/SetProfileUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol SetProfileUseCase { +public protocol SetProfileUseCase: Sendable { func execute(image: String) -> Completable } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/SetUserNameUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/SetUserNameUseCase.swift index 10e04d8aa..0085c9e01 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/SetUserNameUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/SetUserNameUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol SetUserNameUseCase { +public protocol SetUserNameUseCase: Sendable { func execute(name: String) -> Completable } diff --git a/Projects/Domains/UserDomain/Interface/UseCase/WithdrawUserInfoUseCase.swift b/Projects/Domains/UserDomain/Interface/UseCase/WithdrawUserInfoUseCase.swift index 5ed6ce676..8b7d11768 100644 --- a/Projects/Domains/UserDomain/Interface/UseCase/WithdrawUserInfoUseCase.swift +++ b/Projects/Domains/UserDomain/Interface/UseCase/WithdrawUserInfoUseCase.swift @@ -2,6 +2,6 @@ import BaseDomainInterface import Foundation import RxSwift -public protocol WithdrawUserInfoUseCase { +public protocol WithdrawUserInfoUseCase: Sendable { func execute() -> Completable } diff --git a/Projects/Domains/UserDomain/Testing/FetchFavoriteSongsUseCaseStub.swift b/Projects/Domains/UserDomain/Testing/FetchFavoriteSongsUseCaseStub.swift index f37a39c63..c8219c03a 100644 --- a/Projects/Domains/UserDomain/Testing/FetchFavoriteSongsUseCaseStub.swift +++ b/Projects/Domains/UserDomain/Testing/FetchFavoriteSongsUseCaseStub.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift import UserDomainInterface -public struct FetchFavoriteSongsUseCaseStub: FetchFavoriteSongsUseCase { +public struct FetchFavoriteSongsUseCaseStub: FetchFavoriteSongsUseCase, @unchecked Sendable { let items: [FavoriteSongEntity] = [ .init( songID: "fgSXAKsq-Vo", diff --git a/Projects/Domains/UserDomain/Testing/FetchPlaylistUseCaseStub.swift b/Projects/Domains/UserDomain/Testing/FetchPlaylistUseCaseStub.swift index eb31b2137..e1dfe3a2c 100644 --- a/Projects/Domains/UserDomain/Testing/FetchPlaylistUseCaseStub.swift +++ b/Projects/Domains/UserDomain/Testing/FetchPlaylistUseCaseStub.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift import UserDomainInterface -public struct FetchPlaylistUseCaseStub: FetchPlaylistUseCase { +public struct FetchPlaylistUseCaseStub: FetchPlaylistUseCase, @unchecked Sendable { let items: [PlaylistEntity] = [ .init( key: "123", diff --git a/Projects/Features/ArtistFeature/Interface/ArtistDetailFactory.swift b/Projects/Features/ArtistFeature/Interface/ArtistDetailFactory.swift index 21751cdc3..5b9a7da11 100644 --- a/Projects/Features/ArtistFeature/Interface/ArtistDetailFactory.swift +++ b/Projects/Features/ArtistFeature/Interface/ArtistDetailFactory.swift @@ -2,6 +2,7 @@ import ArtistDomainInterface import Foundation import UIKit +@MainActor public protocol ArtistDetailFactory { func makeView(artistID: String) -> UIViewController } diff --git a/Projects/Features/ArtistFeature/Interface/ArtistFactory.swift b/Projects/Features/ArtistFeature/Interface/ArtistFactory.swift index b7e455886..8c1b1a6aa 100644 --- a/Projects/Features/ArtistFeature/Interface/ArtistFactory.swift +++ b/Projects/Features/ArtistFeature/Interface/ArtistFactory.swift @@ -1,6 +1,7 @@ import Foundation import UIKit +@MainActor public protocol ArtistFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/ArtistFeature/Sources/Components/ArtistMusicComponent.swift b/Projects/Features/ArtistFeature/Sources/Components/ArtistMusicComponent.swift index b3251233f..c6743ac84 100644 --- a/Projects/Features/ArtistFeature/Sources/Components/ArtistMusicComponent.swift +++ b/Projects/Features/ArtistFeature/Sources/Components/ArtistMusicComponent.swift @@ -14,6 +14,7 @@ public protocol ArtistMusicDependency: Dependency { var artistMusicContentComponent: ArtistMusicContentComponent { get } } +@MainActor public final class ArtistMusicComponent: Component { public func makeView(model: ArtistEntity?) -> ArtistMusicViewController { return ArtistMusicViewController.viewController( diff --git a/Projects/Features/ArtistFeature/Sources/Components/ArtistMusicContentComponent.swift b/Projects/Features/ArtistFeature/Sources/Components/ArtistMusicContentComponent.swift index 95846a0ab..03293d923 100644 --- a/Projects/Features/ArtistFeature/Sources/Components/ArtistMusicContentComponent.swift +++ b/Projects/Features/ArtistFeature/Sources/Components/ArtistMusicContentComponent.swift @@ -13,6 +13,7 @@ public protocol ArtistMusicContentDependency: Dependency { var songDetailPresenter: any SongDetailPresentable { get } } +@MainActor public final class ArtistMusicContentComponent: Component { public func makeView( type: ArtistSongSortType, diff --git a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistDetailViewController.swift b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistDetailViewController.swift index 1a0baaac7..eb91e232f 100644 --- a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistDetailViewController.swift +++ b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistDetailViewController.swift @@ -9,7 +9,8 @@ import SignInFeatureInterface import UIKit import Utility -public final class ArtistDetailViewController: UIViewController, ViewControllerFromStoryBoard, ContainerViewType { +public final class ArtistDetailViewController: UIViewController, ViewControllerFromStoryBoard, + @preconcurrency ContainerViewType { @IBOutlet weak var gradationView: UIView! @IBOutlet weak var backButton: UIButton! @IBOutlet weak var subscriptionButton: UIButton! diff --git a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicContentViewController.swift b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicContentViewController.swift index f3d57ef88..f94bc6b3f 100644 --- a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicContentViewController.swift +++ b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicContentViewController.swift @@ -13,7 +13,7 @@ import UIKit import Utility public final class ArtistMusicContentViewController: - BaseViewController, ViewControllerFromStoryBoard, SongCartViewType { + BaseViewController, ViewControllerFromStoryBoard, @preconcurrency SongCartViewType { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var activityIndidator: NVActivityIndicatorView! @IBOutlet weak var songCartOnView: UIView! @@ -238,7 +238,7 @@ extension ArtistMusicContentViewController: SongCartViewDelegate { let log = CommonAnalyticsLog.clickAddMusicsButton(location: .artist) LogManager.analytics(log) - if PreferenceManager.userInfo == nil { + if PreferenceManager.shared.userInfo == nil { output.showLogin.onNext(.addMusics) return } @@ -341,14 +341,18 @@ extension ArtistMusicContentViewController: PlayButtonGroupViewDelegate { extension Reactive where Base: ArtistMusicContentViewController { var refresh: Binder { return Binder(base) { viewController, _ in - viewController.input.pageID.accept(1) + Task { @MainActor in + viewController.input.pageID.accept(1) + } } } var loadMore: Binder { return Binder(base) { viewController, _ in - let pageID = viewController.input.pageID.value - viewController.input.pageID.accept(pageID + 1) + Task { @MainActor in + let pageID = viewController.input.pageID.value + viewController.input.pageID.accept(pageID + 1) + } } } } diff --git a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicViewController.swift b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicViewController.swift index 207a94b27..1a9e3dc40 100644 --- a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicViewController.swift +++ b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistMusicViewController.swift @@ -87,7 +87,7 @@ extension ArtistMusicViewController { } } -extension ArtistMusicViewController: PageboyViewControllerDataSource, TMBarDataSource { +extension ArtistMusicViewController: @preconcurrency PageboyViewControllerDataSource, @preconcurrency TMBarDataSource { public func barItem(for bar: TMBar, at index: Int) -> TMBarItemable { switch index { case 0: diff --git a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistViewController.swift b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistViewController.swift index 45948439e..b570081b1 100644 --- a/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistViewController.swift +++ b/Projects/Features/ArtistFeature/Sources/ViewControllers/ArtistViewController.swift @@ -15,7 +15,7 @@ public final class ArtistViewController: BaseViewController, ViewControllerFromStoryBoard, EqualHandleTappedType, - StoryboardView { + @preconcurrency StoryboardView { public typealias Reactor = ArtistReactor @IBOutlet weak var collectionView: UICollectionView! @@ -201,13 +201,15 @@ extension ArtistViewController: WaterfallLayoutDelegate { } public extension ArtistViewController { - func equalHandleTapped() { - let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 - if viewControllersCount > 1 { - self.navigationController?.popToRootViewController(animated: true) - } else { - guard reactor?.currentState.artistList.isEmpty == false else { return } - self.collectionView.setContentOffset(CGPoint(x: 0, y: -STATUS_BAR_HEGHIT()), animated: true) + nonisolated func equalHandleTapped() { + Task { @MainActor in + let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 + if viewControllersCount > 1 { + self.navigationController?.popToRootViewController(animated: true) + } else { + guard reactor?.currentState.artistList.isEmpty == false else { return } + self.collectionView.setContentOffset(CGPoint(x: 0, y: -STATUS_BAR_HEGHIT()), animated: true) + } } } } diff --git a/Projects/Features/ArtistFeature/Sources/ViewModels/ArtistDetailViewModel.swift b/Projects/Features/ArtistFeature/Sources/ViewModels/ArtistDetailViewModel.swift index 391eeac53..02d2b5661 100644 --- a/Projects/Features/ArtistFeature/Sources/ViewModels/ArtistDetailViewModel.swift +++ b/Projects/Features/ArtistFeature/Sources/ViewModels/ArtistDetailViewModel.swift @@ -59,8 +59,8 @@ public final class ArtistDetailViewModel: ViewModelType { .disposed(by: disposeBag) input.fetchArtistSubscriptionStatus - .withLatestFrom(PreferenceManager.$userInfo) - .withLatestFrom(PreferenceManager.$pushNotificationAuthorizationStatus) { ($0, $1 ?? false) } + .withLatestFrom(PreferenceManager.shared.$userInfo) + .withLatestFrom(PreferenceManager.shared.$pushNotificationAuthorizationStatus) { ($0, $1 ?? false) } .flatMap { [fetchArtistSubscriptionStatusUseCase] userInfo, granted -> Observable in if userInfo == nil || granted == false { @@ -81,7 +81,7 @@ public final class ArtistDetailViewModel: ViewModelType { ArtistAnalyticsLog.clickArtistSubscriptionButton(artist: id) ) }) - .withLatestFrom(PreferenceManager.$userInfo) + .withLatestFrom(PreferenceManager.shared.$userInfo) .filter { userInfo in if userInfo == nil { output.showLogin.onNext(.artistSubscribe) @@ -89,7 +89,7 @@ public final class ArtistDetailViewModel: ViewModelType { } return true } - .withLatestFrom(PreferenceManager.$pushNotificationAuthorizationStatus) + .withLatestFrom(PreferenceManager.shared.$pushNotificationAuthorizationStatus) .map { $0 ?? false } .filter { granted in if granted == false { @@ -118,8 +118,8 @@ public final class ArtistDetailViewModel: ViewModelType { // ๋กœ๊ทธ์ธ/์•„์›ƒ, ๊ธฐ๊ธฐ์•Œ๋ฆผ ๋” ์ƒํƒœ ๋ฐ˜์˜ Observable.combineLatest( - PreferenceManager.$userInfo.map { $0?.ID }.distinctUntilChanged(), - PreferenceManager.$pushNotificationAuthorizationStatus.distinctUntilChanged().map { $0 ?? false } + PreferenceManager.shared.$userInfo.map { $0?.ID }.distinctUntilChanged(), + PreferenceManager.shared.$pushNotificationAuthorizationStatus.distinctUntilChanged().map { $0 ?? false } ) { id, granted -> (String?, Bool) in return (id, granted) } diff --git a/Projects/Features/ArtistFeature/Sources/ViewModels/ArtistMusicContentViewModel.swift b/Projects/Features/ArtistFeature/Sources/ViewModels/ArtistMusicContentViewModel.swift index bbf1f42b2..2a8a32067 100644 --- a/Projects/Features/ArtistFeature/Sources/ViewModels/ArtistMusicContentViewModel.swift +++ b/Projects/Features/ArtistFeature/Sources/ViewModels/ArtistMusicContentViewModel.swift @@ -102,7 +102,7 @@ public final class ArtistMusicContentViewModel: ViewModelType { .bind(to: output.indexOfSelectedSongs) .disposed(by: disposeBag) - Utility.PreferenceManager.$startPage + Utility.PreferenceManager.shared.$startPage .skip(1) .map { _ in [] } .bind(to: output.indexOfSelectedSongs) diff --git a/Projects/Features/ArtistFeature/Sources/Views/ArtistMusicCell.swift b/Projects/Features/ArtistFeature/Sources/Views/ArtistMusicCell.swift index 211ff81e3..acbe9db71 100644 --- a/Projects/Features/ArtistFeature/Sources/Views/ArtistMusicCell.swift +++ b/Projects/Features/ArtistFeature/Sources/Views/ArtistMusicCell.swift @@ -6,6 +6,7 @@ import SongsDomainInterface import UIKit import Utility +@MainActor protocol ArtistMusicCellDelegate: AnyObject { func tappedThumbnail(id: String) } @@ -22,16 +23,18 @@ final class ArtistMusicCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - backgroundColor = .clear - contentView.backgroundColor = .clear + Task { @MainActor in + backgroundColor = .clear + contentView.backgroundColor = .clear - albumImageButton.layer.cornerRadius = 4 - albumImageButton.contentMode = .scaleAspectFill - albumImageButton.clipsToBounds = true + albumImageButton.layer.cornerRadius = 4 + albumImageButton.contentMode = .scaleAspectFill + albumImageButton.clipsToBounds = true - titleStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 14) - groupStringLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) - releaseDateLabel.font = DesignSystemFontFamily.SCoreDream._3Light.font(size: 12) + titleStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 14) + groupStringLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + releaseDateLabel.font = DesignSystemFontFamily.SCoreDream._3Light.font(size: 12) + } } @IBAction func thumbnailToPlayButtonAction(_ sender: Any) { diff --git a/Projects/Features/ArtistFeature/Sources/WaterfallLayout/WaterfallLayout.swift b/Projects/Features/ArtistFeature/Sources/WaterfallLayout/WaterfallLayout.swift index a0d6439bc..e02d900b8 100644 --- a/Projects/Features/ArtistFeature/Sources/WaterfallLayout/WaterfallLayout.swift +++ b/Projects/Features/ArtistFeature/Sources/WaterfallLayout/WaterfallLayout.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol WaterfallLayoutDelegate: AnyObject { // MARK: - Required func collectionViewLayout(for section: Int) -> WaterfallLayout.Layout diff --git a/Projects/Features/BaseFeature/Interface/ContainSongs/ContainSongsFactory.swift b/Projects/Features/BaseFeature/Interface/ContainSongs/ContainSongsFactory.swift index 930bfb13c..ed34086eb 100644 --- a/Projects/Features/BaseFeature/Interface/ContainSongs/ContainSongsFactory.swift +++ b/Projects/Features/BaseFeature/Interface/ContainSongs/ContainSongsFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol ContainSongsFactory { func makeView(songs: [String]) -> UIViewController } diff --git a/Projects/Features/BaseFeature/Interface/MultiPurposePopup/MultiPurposePopupFactory.swift b/Projects/Features/BaseFeature/Interface/MultiPurposePopup/MultiPurposePopupFactory.swift index bce2c594a..21857f08d 100644 --- a/Projects/Features/BaseFeature/Interface/MultiPurposePopup/MultiPurposePopupFactory.swift +++ b/Projects/Features/BaseFeature/Interface/MultiPurposePopup/MultiPurposePopupFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol MultiPurposePopupFactory { func makeView( type: PurposeType, diff --git a/Projects/Features/BaseFeature/Interface/Privacy/PrivacyFactory.swift b/Projects/Features/BaseFeature/Interface/Privacy/PrivacyFactory.swift index 75be26c6c..65113fb0d 100644 --- a/Projects/Features/BaseFeature/Interface/Privacy/PrivacyFactory.swift +++ b/Projects/Features/BaseFeature/Interface/Privacy/PrivacyFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol PrivacyFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/BaseFeature/Interface/ServiceTerm/ServiceTermFactory.swift b/Projects/Features/BaseFeature/Interface/ServiceTerm/ServiceTermFactory.swift index da3a20d2e..333e67ccb 100644 --- a/Projects/Features/BaseFeature/Interface/ServiceTerm/ServiceTermFactory.swift +++ b/Projects/Features/BaseFeature/Interface/ServiceTerm/ServiceTermFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol ServiceTermFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/BaseFeature/Interface/TextPopup/TextPopupFactory.swift b/Projects/Features/BaseFeature/Interface/TextPopup/TextPopupFactory.swift index e4abc1518..ce4078bc1 100644 --- a/Projects/Features/BaseFeature/Interface/TextPopup/TextPopupFactory.swift +++ b/Projects/Features/BaseFeature/Interface/TextPopup/TextPopupFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol TextPopupFactory { func makeView( text: String?, diff --git a/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState+Playlist.swift b/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState+Playlist.swift index 1bef4fcf0..99dbe58b0 100644 --- a/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState+Playlist.swift +++ b/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState+Playlist.swift @@ -11,7 +11,8 @@ import Foundation import SongsDomainInterface import Utility -final class Playlist { +final class Playlist: @unchecked Sendable { + private let lock = NSRecursiveLock() @Published private(set) var list: [PlaylistItem] = [] init(list: [PlaylistItem] = []) { @@ -26,27 +27,39 @@ final class Playlist { } func append(_ item: PlaylistItem) { - list.append(item) + lock.withLock { + list.append(item) + } } func append(_ items: [PlaylistItem]) { - list.append(contentsOf: items) + lock.withLock { + list.append(contentsOf: items) + } } func insert(_ newElement: PlaylistItem, at: Int) { - list.insert(newElement, at: at) + lock.withLock { + list.insert(newElement, at: at) + } } func update(contentsOf items: [PlaylistItem]) { - list = items + lock.withLock { + list = items + } } func remove(at index: Int) { + lock.lock() + defer { lock.unlock() } guard list[safe: index] != nil else { return } list.remove(at: index) } func remove(indexs: [Int]) { + lock.lock() + defer { lock.unlock() } let sortedIndexs = indexs.sorted(by: >) // ์•ž์—์„œ๋ถ€ํ„ฐ ์‚ญ์ œํ•˜๋ฉด ์ธ๋ฑ์Šค ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€œ sortedIndexs.forEach { index in remove(at: index) @@ -54,28 +67,40 @@ final class Playlist { } func remove(id: String) { + lock.lock() + defer { lock.unlock() } if let index = list.firstIndex(where: { $0.id == id }) { remove(at: index) } } func removeAll() { - list.removeAll() + lock.withLock { + list.removeAll() + } } func removeAll(where shouldBeRemoved: (PlaylistItem) -> Bool) { - list.removeAll(where: shouldBeRemoved) + lock.withLock { + list.removeAll(where: shouldBeRemoved) + } } func contains(_ item: PlaylistItem) -> Bool { - return list.contains(item) + return lock.withLock { + list.contains(item) + } } func contains(id: String) -> Bool { - return list.contains { $0.id == id } + return lock.withLock { + list.contains { $0.id == id } + } } func reorderPlaylist(from: Int, to: Int) { + lock.lock() + defer { lock.unlock() } let movedData = list[from] list.remove(at: from) list.insert(movedData, at: to) @@ -83,6 +108,8 @@ final class Playlist { /// ํ•ด๋‹น ๊ณก์ด ์ด๋ฏธ ์žฌ์ƒ๋ชฉ๋ก์— ์žˆ์œผ๋ฉด ์žฌ์ƒ๋ชฉ๋ก ์† ํ•ด๋‹น ๊ณก์˜ index, ์—†์œผ๋ฉด nil ๋ฆฌํ„ด func uniqueIndex(of item: PlaylistItem) -> Int? { - return list.firstIndex(of: item) + return lock.withLock { + list.firstIndex(of: item) + } } } diff --git a/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift b/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift index 7a844c459..bc7a1c461 100644 --- a/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift +++ b/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift @@ -13,9 +13,9 @@ import SongsDomainInterface import Utility /// ์™„์ „ํžˆ ๋„๋ฉ”์ธ ๋กœ์ง์œผ๋กœ ์ „ํ™˜ ๊ณ ๋ ค -public final class PlayState { +public final class PlayState: @unchecked Sendable { public static let shared = PlayState() - private var playlist: Playlist + private let playlist: Playlist private var subscription = Set() public var count: Int { playlist.count } public var isEmpty: Bool { playlist.isEmpty } diff --git a/Projects/Features/BaseFeature/Sources/Protocols/EditSheetViewType.swift b/Projects/Features/BaseFeature/Sources/Protocols/EditSheetViewType.swift index 66f680f61..1039a9785 100644 --- a/Projects/Features/BaseFeature/Sources/Protocols/EditSheetViewType.swift +++ b/Projects/Features/BaseFeature/Sources/Protocols/EditSheetViewType.swift @@ -11,6 +11,7 @@ import Foundation import UIKit import Utility +/// ๊ฐ•์ œ mutable ํƒ€์ž… ํ•ด๊ฒฐํ•ด์•ผํ•จ... @preconcurrency public protocol EditSheetViewType: AnyObject { var editSheetView: EditSheetView! { get set } var bottomSheetView: BottomSheetView! { get set } @@ -21,6 +22,7 @@ public enum EditSheetType { case profile // ๋ณด๊ด€ํ•จ > ํ”„๋กœํ•„ ํŽธ์ง‘ } +@MainActor public extension EditSheetViewType where Self: UIViewController { /// ํŽธ์ง‘ํ•˜๊ธฐ ํŒ์—…์„ ๋„์›๋‹ˆ๋‹ค. /// - Parameter view: ํŒ์—…์„ ๋ถ™์ผ ๋Œ€์ƒ์ด ๋˜๋Š” ๋ทฐ (ex: ์•„ํ‹ฐ์ŠคํŠธ ๋…ธ๋ž˜ ๋ฆฌ์ŠคํŠธ, viewController.view) diff --git a/Projects/Features/BaseFeature/Sources/Protocols/PlaylistEditSheetViewType.swift b/Projects/Features/BaseFeature/Sources/Protocols/PlaylistEditSheetViewType.swift index 0e1366615..378160fa6 100644 --- a/Projects/Features/BaseFeature/Sources/Protocols/PlaylistEditSheetViewType.swift +++ b/Projects/Features/BaseFeature/Sources/Protocols/PlaylistEditSheetViewType.swift @@ -3,6 +3,7 @@ import Foundation import UIKit import Utility +/// concurrency๋ฅผ ์œ„ํ•ด mutable ์ƒํƒœ ๋ฆฌํŒฉํ† ๋ง ํ•„์š” public protocol PlaylistEditSheetViewType: AnyObject { var playlisteditSheetView: PlaylistEditSheetView! { get set } var bottomSheetView: BottomSheetView! { get set } @@ -13,6 +14,7 @@ public enum PlaylistEditType { case share } +@MainActor public extension PlaylistEditSheetViewType where Self: UIViewController { /// ํŽธ์ง‘ํ•˜๊ธฐ ํŒ์—…์„ ๋„์›๋‹ˆ๋‹ค. /// - Parameter view: ํŒ์—…์„ ๋ถ™์ผ ๋Œ€์ƒ์ด ๋˜๋Š” ๋ทฐ (ex: ์•„ํ‹ฐ์ŠคํŠธ ๋…ธ๋ž˜ ๋ฆฌ์ŠคํŠธ, viewController.view) diff --git a/Projects/Features/BaseFeature/Sources/Protocols/SongCartViewType.swift b/Projects/Features/BaseFeature/Sources/Protocols/SongCartViewType.swift index 1a696743d..f76b25bec 100644 --- a/Projects/Features/BaseFeature/Sources/Protocols/SongCartViewType.swift +++ b/Projects/Features/BaseFeature/Sources/Protocols/SongCartViewType.swift @@ -11,6 +11,7 @@ import Foundation import UIKit import Utility +/// SongCartViewType ์–ด๋–ป๊ฒŒ๋“  ํ•ด๊ฒฐํ•ด์•ผํ•จ.. public protocol SongCartViewType: AnyObject { var songCartView: SongCartView! { get set } var bottomSheetView: BottomSheetView! { get set } @@ -29,6 +30,7 @@ public enum SongCartType { case creditSong // ํฌ๋ ˆ๋”ง ์ž‘์—…์ž ๋…ธ๋ž˜ ๋ฆฌ์ŠคํŠธ (๋ฐฑํŠผ) } +@MainActor public extension SongCartViewType where Self: UIViewController { /// ๋…ธ๋ž˜ ๋‹ด๊ธฐ ํŒ์—…์„ ๋„์›๋‹ˆ๋‹ค. /// - Parameter view: ํŒ์—…์„ ๋ถ™์ผ ๋Œ€์ƒ์ด ๋˜๋Š” ๋ทฐ (ex: ์•„ํ‹ฐ์ŠคํŠธ ๋…ธ๋ž˜ ๋ฆฌ์ŠคํŠธ, viewController.view) diff --git a/Projects/Features/BaseFeature/Sources/Protocols/WMBottomSheetViewType.swift b/Projects/Features/BaseFeature/Sources/Protocols/WMBottomSheetViewType.swift index 2f01b0108..fe0df015f 100644 --- a/Projects/Features/BaseFeature/Sources/Protocols/WMBottomSheetViewType.swift +++ b/Projects/Features/BaseFeature/Sources/Protocols/WMBottomSheetViewType.swift @@ -7,6 +7,7 @@ public protocol WMBottomSheetViewType: AnyObject { var bottomSheetView: BottomSheetView! { get set } } +@MainActor public extension WMBottomSheetViewType where Self: UIViewController { /// ํŽธ์ง‘ํ•˜๊ธฐ ํŒ์—…์„ ๋„์›๋‹ˆ๋‹ค. /// - Parameter view: ํŒ์—…์„ ๋ถ™์ผ ๋Œ€์ƒ์ด ๋˜๋Š” ๋ทฐ diff --git a/Projects/Features/BaseFeature/Sources/ViewControllers/BaseReactorViewController.swift b/Projects/Features/BaseFeature/Sources/ViewControllers/BaseReactorViewController.swift index 399f0bcab..e206c885f 100644 --- a/Projects/Features/BaseFeature/Sources/ViewControllers/BaseReactorViewController.swift +++ b/Projects/Features/BaseFeature/Sources/ViewControllers/BaseReactorViewController.swift @@ -6,7 +6,7 @@ import SnapKit import Then import UIKit -open class BaseReactorViewController: UIViewController, View { +open class BaseReactorViewController: UIViewController, @preconcurrency View { public var disposeBag = DisposeBag() open lazy var indicator = NVActivityIndicatorView(frame: .zero).then { $0.color = DesignSystemAsset.PrimaryColorV2.point.color diff --git a/Projects/Features/BaseFeature/Sources/ViewControllers/BaseStoryboardReactorViewController.swift b/Projects/Features/BaseFeature/Sources/ViewControllers/BaseStoryboardReactorViewController.swift index e51a38fa3..56be991c4 100644 --- a/Projects/Features/BaseFeature/Sources/ViewControllers/BaseStoryboardReactorViewController.swift +++ b/Projects/Features/BaseFeature/Sources/ViewControllers/BaseStoryboardReactorViewController.swift @@ -6,7 +6,8 @@ import Then import UIKit import Utility -open class BaseStoryboardReactorViewController: UIViewController, StoryboardView, +open class BaseStoryboardReactorViewController: UIViewController, + @preconcurrency StoryboardView, ViewControllerFromStoryBoard { public var disposeBag = DisposeBag() diff --git a/Projects/Features/BaseFeature/Sources/ViewControllers/ContainSongsViewController.swift b/Projects/Features/BaseFeature/Sources/ViewControllers/ContainSongsViewController.swift index 444f559b3..688d53537 100644 --- a/Projects/Features/BaseFeature/Sources/ViewControllers/ContainSongsViewController.swift +++ b/Projects/Features/BaseFeature/Sources/ViewControllers/ContainSongsViewController.swift @@ -153,7 +153,7 @@ extension ContainSongsViewController { .disposed(by: disposeBag) output.showPricePopup - .withLatestFrom(PreferenceManager.$userInfo) { $1 } + .withLatestFrom(PreferenceManager.shared.$userInfo) { $1 } .compactMap { $0 } .withLatestFrom(output.creationPrice) { ($0, $1) } .bind(with: self) { owner, info in diff --git a/Projects/Features/BaseFeature/Sources/ViewControllers/ContractViewController.swift b/Projects/Features/BaseFeature/Sources/ViewControllers/ContractViewController.swift index 0e8fe94d8..6b6070014 100644 --- a/Projects/Features/BaseFeature/Sources/ViewControllers/ContractViewController.swift +++ b/Projects/Features/BaseFeature/Sources/ViewControllers/ContractViewController.swift @@ -16,7 +16,7 @@ import SnapKit import UIKit import Utility -public enum ContractType { +public enum ContractType: Sendable { case privacy case service } @@ -111,13 +111,17 @@ private extension ContractViewController { private extension ContractViewController { func loadPDF() { - DispatchQueue.global(qos: .default).async { - guard let url = URL(string: self.type.url), + DispatchQueue.global(qos: .default).async { [type] in + guard let url = URL(string: type.url), let document = PDFDocument(url: url) else { - self.loadFailPDF() + Task { @MainActor in + self.loadFailPDF() + } return } - self.configurePDF(document: document) + Task { @MainActor in + self.configurePDF(document: document) + } } } diff --git a/Projects/Features/BaseFeature/Sources/ViewModels/ContainSongsViewModel.swift b/Projects/Features/BaseFeature/Sources/ViewModels/ContainSongsViewModel.swift index e8f8ca33e..34972d57a 100644 --- a/Projects/Features/BaseFeature/Sources/ViewModels/ContainSongsViewModel.swift +++ b/Projects/Features/BaseFeature/Sources/ViewModels/ContainSongsViewModel.swift @@ -104,7 +104,7 @@ public final class ContainSongsViewModel: ViewModelType { )) }) .catchAndReturn([]) - .withLatestFrom(PreferenceManager.$userInfo) { ($0, $1) } + .withLatestFrom(PreferenceManager.shared.$userInfo) { ($0, $1) } .map { playlist, userInfo in return playlist.filter { $0.userId == userInfo?.decryptedID } diff --git a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetCalculator.swift b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetCalculator.swift index f0a5d1886..2a346b067 100644 --- a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetCalculator.swift +++ b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetCalculator.swift @@ -6,6 +6,7 @@ import UIKit import Utility extension CGFloat { + @MainActor static var safeAreaBottomInset: CGFloat { return SAFEAREA_BOTTOM_HEIGHT() } @@ -18,6 +19,7 @@ enum BottomSheetCalculator { /// - contentView: the content view of the bottom sheet /// - superview: the bottom sheet container view /// - height: preferred height for the content view + @MainActor static func offset( for contentView: UIView, in superview: UIView, @@ -34,6 +36,7 @@ enum BottomSheetCalculator { return max(superview.frame.height - targetHeight, 0) } + @MainActor static func contentHeight( for contentView: UIView, in superview: UIView, @@ -59,6 +62,7 @@ enum BottomSheetCalculator { /// - currentTargetIndex: index of the current target offset of the BottomSheetView /// - superview: the bottom sheet container view /// - targetMaxHeight: flag specifying whether the last translation target should dismiss the BottomSheetView + @MainActor static func createTranslationTargets( for targetOffsets: [CGFloat], at currentTargetIndex: Int, diff --git a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetPresentationController.swift b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetPresentationController.swift index 1036612ec..0b13fe6da 100644 --- a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetPresentationController.swift +++ b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetPresentationController.swift @@ -218,7 +218,7 @@ extension BottomSheetPresentationController: UIViewControllerInteractiveTransiti // MARK: - BottomSheetViewPresenterDelegate -extension BottomSheetPresentationController: BottomSheetViewDismissalDelegate { +extension BottomSheetPresentationController: @preconcurrency BottomSheetViewDismissalDelegate { func bottomSheetView(_ view: BottomSheetView, willDismissBy action: BottomSheetView.DismissAction) { guard presentationDelegate?.bottomSheetPresentationController(self, shouldDismissBy: action) ?? true else { view.reset() diff --git a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetTransitioningDelegate.swift b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetTransitioningDelegate.swift index a1f3dcb5f..a9c0b11e1 100644 --- a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetTransitioningDelegate.swift +++ b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetTransitioningDelegate.swift @@ -4,6 +4,7 @@ import UIKit +@MainActor public final class BottomSheetTransitioningDelegate: NSObject { public private(set) var contentHeights: [CGFloat] private var startTargetIndex: Int diff --git a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetView.swift b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetView.swift index dcff08090..9c884378f 100644 --- a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetView.swift +++ b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/BottomSheetView.swift @@ -17,6 +17,7 @@ public extension CGFloat { } public extension Array where Element == CGFloat { + @MainActor static var bottomSheetDefault: [CGFloat] { let screenSize = UIScreen.main.bounds.size @@ -44,7 +45,8 @@ public protocol BottomSheetViewAnimationDelegate: AnyObject { // MARK: - View public final class BottomSheetView: UIView { - public enum HandleBackground { + @MainActor + public enum HandleBackground: Sendable { case color(UIColor) case visualEffect(UIVisualEffect) diff --git a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/SpringAnimator.swift b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/SpringAnimator.swift index 5df679f48..1b5a50aec 100644 --- a/Projects/Features/BaseFeature/Sources/Views/BottomSheet/SpringAnimator.swift +++ b/Projects/Features/BaseFeature/Sources/Views/BottomSheet/SpringAnimator.swift @@ -10,6 +10,7 @@ extension SpringAnimator { } } +@MainActor class SpringAnimator: NSObject { var fromPosition: CGPoint = .zero { didSet { diff --git a/Projects/Features/BaseFeature/Sources/Views/ContainPlaylistHeaderView.swift b/Projects/Features/BaseFeature/Sources/Views/ContainPlaylistHeaderView.swift index 6af835b79..3a40098a1 100644 --- a/Projects/Features/BaseFeature/Sources/Views/ContainPlaylistHeaderView.swift +++ b/Projects/Features/BaseFeature/Sources/Views/ContainPlaylistHeaderView.swift @@ -1,6 +1,7 @@ import DesignSystem import UIKit +@MainActor public protocol ContainPlaylistHeaderViewDelegate: AnyObject { func action() } diff --git a/Projects/Features/BaseFeature/Sources/Views/CurrentPlaylistTableViewCell.swift b/Projects/Features/BaseFeature/Sources/Views/CurrentPlaylistTableViewCell.swift index ed104346f..3db6e4cd9 100644 --- a/Projects/Features/BaseFeature/Sources/Views/CurrentPlaylistTableViewCell.swift +++ b/Projects/Features/BaseFeature/Sources/Views/CurrentPlaylistTableViewCell.swift @@ -11,15 +11,17 @@ class CurrentPlaylistTableViewCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - self.backgroundColor = DesignSystemAsset.BlueGrayColor.gray100.color - self.playlistImageView.layer.cornerRadius = 4 - self.playlistNameLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 14) - self.playlistNameLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color - self.playlistNameLabel.setTextWithAttributes(kernValue: -0.5) - self.playlistCountLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) - self.playlistCountLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color - self.playlistCountLabel.setTextWithAttributes(kernValue: -0.5) - self.lockImageView.image = DesignSystemAsset.Storage.storageClose.image + Task { @MainActor in + self.backgroundColor = DesignSystemAsset.BlueGrayColor.gray100.color + self.playlistImageView.layer.cornerRadius = 4 + self.playlistNameLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 14) + self.playlistNameLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color + self.playlistNameLabel.setTextWithAttributes(kernValue: -0.5) + self.playlistCountLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + self.playlistCountLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color + self.playlistCountLabel.setTextWithAttributes(kernValue: -0.5) + self.lockImageView.image = DesignSystemAsset.Storage.storageClose.image + } } } diff --git a/Projects/Features/BaseFeature/Sources/Views/EditSheetView.swift b/Projects/Features/BaseFeature/Sources/Views/EditSheetView.swift index bebed910f..4a4194e82 100644 --- a/Projects/Features/BaseFeature/Sources/Views/EditSheetView.swift +++ b/Projects/Features/BaseFeature/Sources/Views/EditSheetView.swift @@ -10,6 +10,7 @@ import DesignSystem import UIKit import Utility +@MainActor public protocol EditSheetViewDelegate: AnyObject { func buttonTapped(type: EditSheetSelectType) } diff --git a/Projects/Features/BaseFeature/Sources/Views/NoticeCollectionViewCell.swift b/Projects/Features/BaseFeature/Sources/Views/NoticeCollectionViewCell.swift index b08a1d9c2..0b478db15 100644 --- a/Projects/Features/BaseFeature/Sources/Views/NoticeCollectionViewCell.swift +++ b/Projects/Features/BaseFeature/Sources/Views/NoticeCollectionViewCell.swift @@ -15,8 +15,10 @@ public final class NoticeCollectionViewCell: UICollectionViewCell { override public func awakeFromNib() { super.awakeFromNib() - contentImageView.contentMode = .scaleAspectFill - contentImageView.clipsToBounds = true + Task { @MainActor in + contentImageView.contentMode = .scaleAspectFill + contentImageView.clipsToBounds = true + } } } diff --git a/Projects/Features/BaseFeature/Sources/Views/PlayButtonGroupView.swift b/Projects/Features/BaseFeature/Sources/Views/PlayButtonGroupView.swift index 58c29b7de..1e03f6353 100644 --- a/Projects/Features/BaseFeature/Sources/Views/PlayButtonGroupView.swift +++ b/Projects/Features/BaseFeature/Sources/Views/PlayButtonGroupView.swift @@ -14,6 +14,7 @@ public enum PlayEvent { case shufflePlay } +@MainActor public protocol PlayButtonGroupViewDelegate: AnyObject { func play(_ event: PlayEvent) } diff --git a/Projects/Features/BaseFeature/Sources/Views/PlaylistEditSheetView.swift b/Projects/Features/BaseFeature/Sources/Views/PlaylistEditSheetView.swift index 2f73288fa..6fab82903 100644 --- a/Projects/Features/BaseFeature/Sources/Views/PlaylistEditSheetView.swift +++ b/Projects/Features/BaseFeature/Sources/Views/PlaylistEditSheetView.swift @@ -4,6 +4,7 @@ import Then import UIKit import Utility +@MainActor public protocol PlaylistEditSheetDelegate: AnyObject { func didTap(_ type: PlaylistEditType) } diff --git a/Projects/Features/BaseFeature/Sources/Views/RecommendPlayListCell.swift b/Projects/Features/BaseFeature/Sources/Views/RecommendPlayListCell.swift index 89078765b..5bd4c7d9b 100644 --- a/Projects/Features/BaseFeature/Sources/Views/RecommendPlayListCell.swift +++ b/Projects/Features/BaseFeature/Sources/Views/RecommendPlayListCell.swift @@ -19,14 +19,16 @@ public class RecommendPlayListCell: UICollectionViewCell { override public func awakeFromNib() { super.awakeFromNib() - self.contentView.layer.cornerRadius = 8 - self.contentView.layer.borderColor = UIColor.white.cgColor - self.contentView.layer.borderWidth = 1 - self.contentView.backgroundColor = DesignSystemAsset.GrayColor.gray25.color + Task { @MainActor in + self.contentView.layer.cornerRadius = 8 + self.contentView.layer.borderColor = UIColor.white.cgColor + self.contentView.layer.borderWidth = 1 + self.contentView.backgroundColor = DesignSystemAsset.GrayColor.gray25.color - let itemWidth: CGFloat = (APP_WIDTH() - (20 + 8 + 20)) / 2.0 - let itemHeight: CGFloat = (80.0 * itemWidth) / 164.0 - self.logoImageView.layer.cornerRadius = ((48 * itemHeight) / 80.0) / 2.0 + let itemWidth: CGFloat = (APP_WIDTH() - (20 + 8 + 20)) / 2.0 + let itemHeight: CGFloat = (80.0 * itemWidth) / 164.0 + self.logoImageView.layer.cornerRadius = ((48 * itemHeight) / 80.0) / 2.0 + } } } diff --git a/Projects/Features/BaseFeature/Sources/Views/RecommendPlayListView.swift b/Projects/Features/BaseFeature/Sources/Views/RecommendPlayListView.swift index ce06dac83..23dcea7ba 100644 --- a/Projects/Features/BaseFeature/Sources/Views/RecommendPlayListView.swift +++ b/Projects/Features/BaseFeature/Sources/Views/RecommendPlayListView.swift @@ -11,6 +11,7 @@ import PlaylistDomainInterface import UIKit import Utility +@MainActor public protocol RecommendPlayListViewDelegate: AnyObject { func itemSelected(model: RecommendPlaylistEntity) } diff --git a/Projects/Features/BaseFeature/Sources/Views/SongCartView.swift b/Projects/Features/BaseFeature/Sources/Views/SongCartView.swift index 57426af90..ced915c4a 100644 --- a/Projects/Features/BaseFeature/Sources/Views/SongCartView.swift +++ b/Projects/Features/BaseFeature/Sources/Views/SongCartView.swift @@ -10,6 +10,7 @@ import DesignSystem import UIKit import Utility +@MainActor public protocol SongCartViewDelegate: AnyObject { func buttonTapped(type: SongCartSelectType) } diff --git a/Projects/Features/BaseFeature/Testing/PrivacyComponentStub.swift b/Projects/Features/BaseFeature/Testing/PrivacyComponentStub.swift index f7a3ac181..30e53b004 100644 --- a/Projects/Features/BaseFeature/Testing/PrivacyComponentStub.swift +++ b/Projects/Features/BaseFeature/Testing/PrivacyComponentStub.swift @@ -2,7 +2,7 @@ import BaseFeatureInterface import UIKit -public final class PrivacyComponentStub: PrivacyFactory { +public final class PrivacyComponentStub: PrivacyFactory, @unchecked Sendable { public func makeView() -> UIViewController { return ContractViewController.viewController(type: .privacy) } diff --git a/Projects/Features/BaseFeature/Testing/ServiceTermsComponentStub.swift b/Projects/Features/BaseFeature/Testing/ServiceTermsComponentStub.swift index 029651015..a654046dd 100644 --- a/Projects/Features/BaseFeature/Testing/ServiceTermsComponentStub.swift +++ b/Projects/Features/BaseFeature/Testing/ServiceTermsComponentStub.swift @@ -2,7 +2,7 @@ import BaseFeatureInterface import UIKit -public final class ServiceTermComponentStub: ServiceTermFactory { +public final class ServiceTermComponentStub: ServiceTermFactory, @unchecked Sendable { public func makeView() -> UIViewController { return ContractViewController.viewController(type: .service) } diff --git a/Projects/Features/BaseFeature/Testing/TextPopUpComponentStub.swift b/Projects/Features/BaseFeature/Testing/TextPopUpComponentStub.swift index 5c4324243..5acd8e830 100644 --- a/Projects/Features/BaseFeature/Testing/TextPopUpComponentStub.swift +++ b/Projects/Features/BaseFeature/Testing/TextPopUpComponentStub.swift @@ -2,7 +2,7 @@ import BaseFeatureInterface import UIKit -public final class TextPopupComponentStub: TextPopupFactory { +public final class TextPopupComponentStub: TextPopupFactory, @unchecked Sendable { public func makeView( text: String?, cancelButtonIsHidden: Bool, diff --git a/Projects/Features/ChartFeature/Interface/ChartFactory.swift b/Projects/Features/ChartFeature/Interface/ChartFactory.swift index 22aa0b352..307154465 100644 --- a/Projects/Features/ChartFeature/Interface/ChartFactory.swift +++ b/Projects/Features/ChartFeature/Interface/ChartFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol ChartFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/ChartFeature/Sources/Components/ChartContentComponent.swift b/Projects/Features/ChartFeature/Sources/Components/ChartContentComponent.swift index 7e6291d00..b3dafa9d4 100644 --- a/Projects/Features/ChartFeature/Sources/Components/ChartContentComponent.swift +++ b/Projects/Features/ChartFeature/Sources/Components/ChartContentComponent.swift @@ -13,6 +13,7 @@ public protocol ChartContentDependency: Dependency { var songDetailPresenter: any SongDetailPresentable { get } } +@MainActor public final class ChartContentComponent: Component { public func makeView(type: ChartDateType) -> ChartContentViewController { return ChartContentViewController.viewController( diff --git a/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartContentViewController.swift b/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartContentViewController.swift index a4621acd7..6255584e6 100644 --- a/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartContentViewController.swift +++ b/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartContentViewController.swift @@ -12,7 +12,8 @@ import Then import UIKit import Utility -public final class ChartContentViewController: BaseViewController, ViewControllerFromStoryBoard, SongCartViewType { +public final class ChartContentViewController: BaseViewController, ViewControllerFromStoryBoard, + @preconcurrency SongCartViewType { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var activityIncidator: NVActivityIndicatorView! @@ -279,7 +280,7 @@ extension ChartContentViewController: SongCartViewDelegate { case .addSong: let log = CommonAnalyticsLog.clickAddMusicsButton(location: .chart) LogManager.analytics(log) - if PreferenceManager.userInfo == nil { + if PreferenceManager.shared.userInfo == nil { output.showLogin.onNext(()) return } diff --git a/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartPlayPopupViewController.swift b/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartPlayPopupViewController.swift index ddbf839bb..56d72432a 100644 --- a/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartPlayPopupViewController.swift +++ b/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartPlayPopupViewController.swift @@ -4,6 +4,7 @@ import SnapKit import Then import UIKit +@MainActor public protocol ChartPlayPopupViewControllerDelegate: AnyObject { func playTapped(type: HalfPlayType) } diff --git a/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartViewController.swift b/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartViewController.swift index 1c704d919..4d85711c5 100644 --- a/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartViewController.swift +++ b/Projects/Features/ChartFeature/Sources/ViewContrillers/ChartViewController.swift @@ -110,7 +110,7 @@ private extension ChartViewController { } } -extension ChartViewController: PageboyViewControllerDataSource, TMBarDataSource { +extension ChartViewController: @preconcurrency PageboyViewControllerDataSource, @preconcurrency TMBarDataSource { public func barItem(for bar: TMBar, at index: Int) -> TMBarItemable { switch index { case 0: diff --git a/Projects/Features/ChartFeature/Sources/ViewModels/ChartContentViewModel.swift b/Projects/Features/ChartFeature/Sources/ViewModels/ChartContentViewModel.swift index 4bba3ea9f..768c4179f 100644 --- a/Projects/Features/ChartFeature/Sources/ViewModels/ChartContentViewModel.swift +++ b/Projects/Features/ChartFeature/Sources/ViewModels/ChartContentViewModel.swift @@ -86,7 +86,7 @@ public final class ChartContentViewModel: ViewModelType { .bind(to: output.indexOfSelectedSongs) .disposed(by: disposeBag) - Utility.PreferenceManager.$startPage + Utility.PreferenceManager.shared.$startPage .skip(1) .map { _ in [] } .bind(to: output.indexOfSelectedSongs) diff --git a/Projects/Features/ChartFeature/Sources/Views/ChartContentTableViewCell.swift b/Projects/Features/ChartFeature/Sources/Views/ChartContentTableViewCell.swift index f702096ae..f6d211ff8 100644 --- a/Projects/Features/ChartFeature/Sources/Views/ChartContentTableViewCell.swift +++ b/Projects/Features/ChartFeature/Sources/Views/ChartContentTableViewCell.swift @@ -8,6 +8,7 @@ import Then import UIKit import Utility +@MainActor protocol ChartContentTableViewCellDelegate: AnyObject { func tappedThumbnail(id: String) } diff --git a/Projects/Features/ChartFeature/Sources/Views/PlayButtonForChartView.swift b/Projects/Features/ChartFeature/Sources/Views/PlayButtonForChartView.swift index 9642173b7..8c72fa1da 100644 --- a/Projects/Features/ChartFeature/Sources/Views/PlayButtonForChartView.swift +++ b/Projects/Features/ChartFeature/Sources/Views/PlayButtonForChartView.swift @@ -5,11 +5,12 @@ import SnapKit import Then import UIKit -public enum PlayEvent { +public enum PlayEvent: Sendable { case allPlay case shufflePlay } +@MainActor public protocol PlayButtonForChartViewDelegate: AnyObject { func pressPlay(_ event: PlayEvent) } diff --git a/Projects/Features/CreditSongListFeature/Demo/Sources/AppDelegate.swift b/Projects/Features/CreditSongListFeature/Demo/Sources/AppDelegate.swift index 6c7849559..607653d8a 100644 --- a/Projects/Features/CreditSongListFeature/Demo/Sources/AppDelegate.swift +++ b/Projects/Features/CreditSongListFeature/Demo/Sources/AppDelegate.swift @@ -81,7 +81,7 @@ final class FakeCreditSongListTabItemFactory: CreditSongListTabItemFactory { } } -final class DummySongDetailPresenter: SongDetailPresentable { +final class DummySongDetailPresenter: SongDetailPresentable, @unchecked Sendable { var presentSongDetailObservable: RxSwift.Observable<(ids: [String], selectedID: String)> { .empty() } @@ -91,7 +91,7 @@ final class DummySongDetailPresenter: SongDetailPresentable { func present(ids: [String], selectedID: String) {} } -final class DummyContainSongsFactory: ContainSongsFactory { +final class DummyContainSongsFactory: ContainSongsFactory, @unchecked Sendable { func makeView(songs: [String]) -> UIViewController { let viewController = UIViewController() DispatchQueue.main.asyncAfter(deadline: .now() + 3) { @@ -101,13 +101,13 @@ final class DummyContainSongsFactory: ContainSongsFactory { } } -final class DummySignInFactory: SignInFactory { +final class DummySignInFactory: SignInFactory, @unchecked Sendable { func makeView() -> UIViewController { return UIViewController() } } -final class DummyTextPopupFactory: TextPopupFactory { +final class DummyTextPopupFactory: TextPopupFactory, @unchecked Sendable { func makeView( text: String?, cancelButtonIsHidden: Bool, diff --git a/Projects/Features/CreditSongListFeature/Interface/CreditSongListFactory.swift b/Projects/Features/CreditSongListFeature/Interface/CreditSongListFactory.swift index 100b3e0ad..6e9d5c51a 100644 --- a/Projects/Features/CreditSongListFeature/Interface/CreditSongListFactory.swift +++ b/Projects/Features/CreditSongListFeature/Interface/CreditSongListFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol CreditSongListFactory { func makeViewController(workerName: String) -> UIViewController } diff --git a/Projects/Features/CreditSongListFeature/Interface/CreditSongListTabFactory.swift b/Projects/Features/CreditSongListFeature/Interface/CreditSongListTabFactory.swift index ddf166b74..2eb71ca7e 100644 --- a/Projects/Features/CreditSongListFeature/Interface/CreditSongListTabFactory.swift +++ b/Projects/Features/CreditSongListFeature/Interface/CreditSongListTabFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol CreditSongListTabFactory { func makeViewController(workerName: String) -> UIViewController } diff --git a/Projects/Features/CreditSongListFeature/Interface/CreditSongListTabItemFactory.swift b/Projects/Features/CreditSongListFeature/Interface/CreditSongListTabItemFactory.swift index fde4ff1d2..93a32a50c 100644 --- a/Projects/Features/CreditSongListFeature/Interface/CreditSongListTabItemFactory.swift +++ b/Projects/Features/CreditSongListFeature/Interface/CreditSongListTabItemFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol CreditSongListTabItemFactory { func makeViewController(workerName: String, sortType: CreditSongSortType) -> UIViewController } diff --git a/Projects/Features/CreditSongListFeature/Sources/CreditSongList/View/CreditProfileView.swift b/Projects/Features/CreditSongListFeature/Sources/CreditSongList/View/CreditProfileView.swift index b7110981f..66dff77c4 100644 --- a/Projects/Features/CreditSongListFeature/Sources/CreditSongList/View/CreditProfileView.swift +++ b/Projects/Features/CreditSongListFeature/Sources/CreditSongList/View/CreditProfileView.swift @@ -5,6 +5,7 @@ import Then import UIKit import Utility +@MainActor protocol CreditProfileViewStateProtocol { func updateProfile(entity: CreditProfileEntity) } diff --git a/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabItemReactor.swift b/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabItemReactor.swift index 98a152052..a39c43f26 100644 --- a/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabItemReactor.swift +++ b/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabItemReactor.swift @@ -180,7 +180,7 @@ private extension CreditSongListTabItemReactor { } func addSongButtonDidTap() -> Observable { - guard PreferenceManager.userInfo != nil else { + guard PreferenceManager.shared.userInfo != nil else { return navigateMutation( navigateType: .textPopup( text: LocalizationStrings.needLoginWarning, diff --git a/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabItemViewController.swift b/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabItemViewController.swift index 37acd281f..038ec18f3 100644 --- a/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabItemViewController.swift +++ b/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabItemViewController.swift @@ -11,7 +11,7 @@ import Utility final class CreditSongListTabItemViewController: BaseReactorViewController, - SongCartViewType { + @preconcurrency SongCartViewType { var songCartView: SongCartView! var bottomSheetView: BottomSheetView! diff --git a/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabViewController.swift b/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabViewController.swift index f408b43fa..19b0bcf5b 100644 --- a/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabViewController.swift +++ b/Projects/Features/CreditSongListFeature/Sources/CreditSongListTab/CreditSongListTabViewController.swift @@ -92,7 +92,7 @@ private extension CreditSongListTabViewController { } } -extension CreditSongListTabViewController: PageboyViewControllerDataSource { +extension CreditSongListTabViewController: @preconcurrency PageboyViewControllerDataSource { func numberOfViewControllers(in pageboyViewController: PageboyViewController) -> Int { sortTypes.count } @@ -109,7 +109,7 @@ extension CreditSongListTabViewController: PageboyViewControllerDataSource { } } -extension CreditSongListTabViewController: TMBarDataSource { +extension CreditSongListTabViewController: @preconcurrency TMBarDataSource { func barItem(for bar: any TMBar, at index: Int) -> any TMBarItemable { guard let sortItem = sortTypes[safe: index] else { return TMBarItem(title: "") } diff --git a/Projects/Features/CreditSongListFeature/Sources/ScopedState/CreditSongListScopedState.swift b/Projects/Features/CreditSongListFeature/Sources/ScopedState/CreditSongListScopedState.swift index 746bebc9c..17e393802 100644 --- a/Projects/Features/CreditSongListFeature/Sources/ScopedState/CreditSongListScopedState.swift +++ b/Projects/Features/CreditSongListFeature/Sources/ScopedState/CreditSongListScopedState.swift @@ -1,9 +1,9 @@ import Foundation import RxCocoa -import RxSwift +@preconcurrency import RxSwift import UIKit -final class CreditSongListScopedState { +final class CreditSongListScopedState: Sendable { static let shared = CreditSongListScopedState() private init() {} diff --git a/Projects/Features/FruitDrawFeature/Interface/FruitDrawFactory.swift b/Projects/Features/FruitDrawFeature/Interface/FruitDrawFactory.swift index 48d843dce..45d9e4b9a 100644 --- a/Projects/Features/FruitDrawFeature/Interface/FruitDrawFactory.swift +++ b/Projects/Features/FruitDrawFeature/Interface/FruitDrawFactory.swift @@ -1,9 +1,11 @@ import UIKit +@MainActor public protocol FruitDrawViewControllerDelegate: AnyObject { func completedFruitDraw(itemCount: Int) } +@MainActor public protocol FruitDrawFactory { func makeView(delegate: FruitDrawViewControllerDelegate) -> UIViewController } diff --git a/Projects/Features/FruitDrawFeature/Interface/FruitStorageFactory.swift b/Projects/Features/FruitDrawFeature/Interface/FruitStorageFactory.swift index 4079e460b..691f88ec1 100644 --- a/Projects/Features/FruitDrawFeature/Interface/FruitStorageFactory.swift +++ b/Projects/Features/FruitDrawFeature/Interface/FruitStorageFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol FruitStorageFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/FruitDrawFeature/Sources/Views/FruitListCell.swift b/Projects/Features/FruitDrawFeature/Sources/Views/FruitListCell.swift index 15bb5cbbb..ab5b9805c 100644 --- a/Projects/Features/FruitDrawFeature/Sources/Views/FruitListCell.swift +++ b/Projects/Features/FruitDrawFeature/Sources/Views/FruitListCell.swift @@ -3,6 +3,7 @@ import UIKit import UserDomainInterface import Utility +@MainActor public protocol FruitListCellDelegate: AnyObject { func itemSelected(item: FruitEntity) } diff --git a/Projects/Features/HomeFeature/Interface/HomeFactory.swift b/Projects/Features/HomeFeature/Interface/HomeFactory.swift index 4150d1f89..82a91b1ad 100644 --- a/Projects/Features/HomeFeature/Interface/HomeFactory.swift +++ b/Projects/Features/HomeFeature/Interface/HomeFactory.swift @@ -1,6 +1,7 @@ import Foundation import UIKit +@MainActor public protocol HomeFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/HomeFeature/Sources/Components/NewSongsComponent.swift b/Projects/Features/HomeFeature/Sources/Components/NewSongsComponent.swift index 0d29afde5..f14e20f56 100644 --- a/Projects/Features/HomeFeature/Sources/Components/NewSongsComponent.swift +++ b/Projects/Features/HomeFeature/Sources/Components/NewSongsComponent.swift @@ -5,6 +5,7 @@ public protocol NewSongsDependency: Dependency { var newSongsContentComponent: NewSongsContentComponent { get } } +@MainActor public final class NewSongsComponent: Component { public func makeView() -> NewSongsViewController { return NewSongsViewController.viewController(newSongsContentComponent: dependency.newSongsContentComponent) diff --git a/Projects/Features/HomeFeature/Sources/Components/NewSongsContentComponent.swift b/Projects/Features/HomeFeature/Sources/Components/NewSongsContentComponent.swift index 530926b4c..150962d0f 100644 --- a/Projects/Features/HomeFeature/Sources/Components/NewSongsContentComponent.swift +++ b/Projects/Features/HomeFeature/Sources/Components/NewSongsContentComponent.swift @@ -15,6 +15,7 @@ public protocol NewSongsContentDependency: Dependency { var songDetailPresenter: any SongDetailPresentable { get } } +@MainActor public final class NewSongsContentComponent: Component { public func makeView(type: NewSongGroupType) -> NewSongsContentViewController { return NewSongsContentViewController.viewController( diff --git a/Projects/Features/HomeFeature/Sources/ViewControllers/HomeViewController.swift b/Projects/Features/HomeFeature/Sources/ViewControllers/HomeViewController.swift index 86ccdc97b..fda71071d 100644 --- a/Projects/Features/HomeFeature/Sources/ViewControllers/HomeViewController.swift +++ b/Projects/Features/HomeFeature/Sources/ViewControllers/HomeViewController.swift @@ -457,13 +457,15 @@ extension HomeViewController: RecommendPlayListViewDelegate { } public extension HomeViewController { - func equalHandleTapped() { - let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 - if viewControllersCount > 1 { - self.navigationController?.popToRootViewController(animated: true) - } else { - guard let scrollView = self.scrollView else { return } - scrollView.setContentOffset(CGPoint(x: 0, y: -STATUS_BAR_HEGHIT()), animated: true) + nonisolated func equalHandleTapped() { + Task { @MainActor in + let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 + if viewControllersCount > 1 { + self.navigationController?.popToRootViewController(animated: true) + } else { + guard let scrollView = self.scrollView else { return } + scrollView.setContentOffset(CGPoint(x: 0, y: -STATUS_BAR_HEGHIT()), animated: true) + } } } } diff --git a/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsContentViewController.swift b/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsContentViewController.swift index 06f15d263..b26521b2f 100644 --- a/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsContentViewController.swift +++ b/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsContentViewController.swift @@ -11,7 +11,8 @@ import SongsDomainInterface import UIKit import Utility -public class NewSongsContentViewController: UIViewController, ViewControllerFromStoryBoard, SongCartViewType { +public class NewSongsContentViewController: UIViewController, ViewControllerFromStoryBoard, + @preconcurrency SongCartViewType { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var activityIncidator: NVActivityIndicatorView! @@ -285,7 +286,7 @@ extension NewSongsContentViewController: SongCartViewDelegate { let log = CommonAnalyticsLog.clickAddMusicsButton(location: .recentMusic) LogManager.analytics(log) - if PreferenceManager.userInfo == nil { + if PreferenceManager.shared.userInfo == nil { output.showLogin.onNext(()) return } @@ -332,8 +333,10 @@ extension NewSongsContentViewController: SongCartViewDelegate { extension Reactive where Base: NewSongsContentViewController { var loadMore: Binder { return Binder(base) { viewController, _ in - let pageID = viewController.input.pageID.value - viewController.input.pageID.accept(pageID + 1) + Task { @MainActor in + let pageID = viewController.input.pageID.value + viewController.input.pageID.accept(pageID + 1) + } } } } diff --git a/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsViewController.swift b/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsViewController.swift index 770c84f49..ef19febd2 100644 --- a/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsViewController.swift +++ b/Projects/Features/HomeFeature/Sources/ViewControllers/NewSongsViewController.swift @@ -112,7 +112,7 @@ extension NewSongsViewController { } } -extension NewSongsViewController: PageboyViewControllerDataSource, TMBarDataSource { +extension NewSongsViewController: @preconcurrency PageboyViewControllerDataSource, @preconcurrency TMBarDataSource { public func barItem(for bar: TMBar, at index: Int) -> TMBarItemable { switch index { case 0: diff --git a/Projects/Features/HomeFeature/Sources/ViewModels/NewSongsContentViewModel.swift b/Projects/Features/HomeFeature/Sources/ViewModels/NewSongsContentViewModel.swift index 26b00ae4f..afac493aa 100644 --- a/Projects/Features/HomeFeature/Sources/ViewModels/NewSongsContentViewModel.swift +++ b/Projects/Features/HomeFeature/Sources/ViewModels/NewSongsContentViewModel.swift @@ -126,7 +126,7 @@ public final class NewSongsContentViewModel: ViewModelType { .bind(to: output.indexOfSelectedSongs) .disposed(by: disposeBag) - Utility.PreferenceManager.$startPage + Utility.PreferenceManager.shared.$startPage .skip(1) .map { _ in [] } .bind(to: output.indexOfSelectedSongs) diff --git a/Projects/Features/HomeFeature/Sources/Views/HomeChartCell.swift b/Projects/Features/HomeFeature/Sources/Views/HomeChartCell.swift index c44c2c4d8..80765557c 100644 --- a/Projects/Features/HomeFeature/Sources/Views/HomeChartCell.swift +++ b/Projects/Features/HomeFeature/Sources/Views/HomeChartCell.swift @@ -15,6 +15,7 @@ import SnapKit import UIKit import Utility +@MainActor protocol HomeChartCellDelegate: AnyObject { func thumbnailDidTap(model: ChartRankingEntity) func playButtonDidTap(model: ChartRankingEntity) @@ -35,10 +36,12 @@ class HomeChartCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - albumImageView.layer.cornerRadius = 4 - albumImageView.contentMode = .scaleAspectFill - playImageView.image = DesignSystemAsset.Home.playSmall.image - bind() + Task { @MainActor in + albumImageView.layer.cornerRadius = 4 + albumImageView.contentMode = .scaleAspectFill + playImageView.image = DesignSystemAsset.Home.playSmall.image + bind() + } } } diff --git a/Projects/Features/HomeFeature/Sources/Views/HomeNewSongCell.swift b/Projects/Features/HomeFeature/Sources/Views/HomeNewSongCell.swift index 0b290477d..93645e83a 100644 --- a/Projects/Features/HomeFeature/Sources/Views/HomeNewSongCell.swift +++ b/Projects/Features/HomeFeature/Sources/Views/HomeNewSongCell.swift @@ -14,6 +14,7 @@ import SongsDomainInterface import UIKit import Utility +@MainActor protocol HomeNewSongCellDelegate: AnyObject { func thumbnailDidTap(model: NewSongsEntity) func playButtonDidTap(model: NewSongsEntity) @@ -32,14 +33,16 @@ final class HomeNewSongCell: UICollectionViewCell { override func awakeFromNib() { super.awakeFromNib() - self.backgroundColor = .clear - self.contentView.backgroundColor = .clear + Task { @MainActor in + self.backgroundColor = .clear + self.contentView.backgroundColor = .clear - albumImageView.layer.cornerRadius = 8 - albumImageView.clipsToBounds = true - albumImageView.contentMode = .scaleAspectFill - playImageView.image = DesignSystemAsset.Home.playSmall.image - bind() + albumImageView.layer.cornerRadius = 8 + albumImageView.clipsToBounds = true + albumImageView.contentMode = .scaleAspectFill + playImageView.image = DesignSystemAsset.Home.playSmall.image + bind() + } } } diff --git a/Projects/Features/HomeFeature/Sources/Views/NewSongsCell.swift b/Projects/Features/HomeFeature/Sources/Views/NewSongsCell.swift index 72e8fb9aa..5c1d4a307 100644 --- a/Projects/Features/HomeFeature/Sources/Views/NewSongsCell.swift +++ b/Projects/Features/HomeFeature/Sources/Views/NewSongsCell.swift @@ -4,6 +4,7 @@ import SongsDomainInterface import UIKit import Utility +@MainActor protocol NewSongsCellDelegate: AnyObject { func tappedThumbnail(id: String) } @@ -20,10 +21,11 @@ class NewSongsCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - contentView.backgroundColor = .clear - albumImageView.clipsToBounds = true - albumImageView.layer.cornerRadius = 4 - albumImageView.contentMode = .scaleAspectFill + Task { @MainActor in + albumImageView.layer.cornerRadius = 4 + albumImageView.clipsToBounds = true + albumImageView.contentMode = .scaleAspectFill + } } @IBAction func playButtonAction(_ sender: Any) { diff --git a/Projects/Features/LyricHighlightingFeature/Interface/LyricHighlightingFactory.swift b/Projects/Features/LyricHighlightingFeature/Interface/LyricHighlightingFactory.swift index 70ad81de4..28583f29c 100644 --- a/Projects/Features/LyricHighlightingFeature/Interface/LyricHighlightingFactory.swift +++ b/Projects/Features/LyricHighlightingFeature/Interface/LyricHighlightingFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol LyricHighlightingFactory { func makeView(model: LyricHighlightingRequiredModel) -> UIViewController } diff --git a/Projects/Features/LyricHighlightingFeature/Sources/Components/LyricDecoratingComponent.swift b/Projects/Features/LyricHighlightingFeature/Sources/Components/LyricDecoratingComponent.swift index 3bd9f12aa..ccc993927 100644 --- a/Projects/Features/LyricHighlightingFeature/Sources/Components/LyricDecoratingComponent.swift +++ b/Projects/Features/LyricHighlightingFeature/Sources/Components/LyricDecoratingComponent.swift @@ -9,6 +9,7 @@ public protocol LyricDecoratingDependency: Dependency { var textPopupFactory: any TextPopupFactory { get } } +@MainActor public final class LyricDecoratingComponent: Component { public func makeView(model: LyricHighlightingRequiredModel) -> LyricDecoratingViewController { let viewModel = LyricDecoratingViewModel( diff --git a/Projects/Features/LyricHighlightingFeature/Sources/ViewControllers/LyricDecoratingViewController.swift b/Projects/Features/LyricHighlightingFeature/Sources/ViewControllers/LyricDecoratingViewController.swift index 59522ffc4..37ed6be56 100644 --- a/Projects/Features/LyricHighlightingFeature/Sources/ViewControllers/LyricDecoratingViewController.swift +++ b/Projects/Features/LyricHighlightingFeature/Sources/ViewControllers/LyricDecoratingViewController.swift @@ -317,19 +317,21 @@ private extension LyricDecoratingViewController { } public extension LyricDecoratingViewController { - func showPhotoLibrary() { - let image = decorateShareContentView.asImage(size: .init(width: 960, height: 960)) - PHPhotoLibrary.shared().performChanges { - PHAssetChangeRequest.creationRequestForAsset(from: image) - } completionHandler: { _, error in - var message: String = "" - if let error = error { - message = error.localizedDescription - } else { - message = "ํ•ด๋‹น ์ด๋ฏธ์ง€๊ฐ€ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค." - } - DispatchQueue.main.async { - self.showToast(text: message, options: [.tabBar]) + nonisolated func showPhotoLibrary() { + Task { @MainActor in + let image = decorateShareContentView.asImage(size: .init(width: 960, height: 960)) + PHPhotoLibrary.shared().performChanges { + PHAssetChangeRequest.creationRequestForAsset(from: image) + } completionHandler: { _, error in + var message: String = "" + if let error = error { + message = error.localizedDescription + } else { + message = "ํ•ด๋‹น ์ด๋ฏธ์ง€๊ฐ€ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค." + } + DispatchQueue.main.async { + self.showToast(text: message, options: [.tabBar]) + } } } } diff --git a/Projects/Features/MainTabFeature/Sources/AppEntryState/AppEntryState.swift b/Projects/Features/MainTabFeature/Sources/AppEntryState/AppEntryState.swift index 8db55cd2d..0228dc37d 100644 --- a/Projects/Features/MainTabFeature/Sources/AppEntryState/AppEntryState.swift +++ b/Projects/Features/MainTabFeature/Sources/AppEntryState/AppEntryState.swift @@ -1,12 +1,12 @@ import Foundation import RxRelay -public protocol AppEntryStateHandleable { +public protocol AppEntryStateHandleable: Sendable { var moveSceneObservable: BehaviorRelay<[String: Any]> { get } func moveScene(params: [String: Any]) } -public final class AppEntryState: AppEntryStateHandleable { +public final class AppEntryState: AppEntryStateHandleable, @unchecked Sendable { private let moveSceneRelay: BehaviorRelay<[String: Any]> = .init(value: [:]) public var moveSceneObservable: BehaviorRelay<[String: Any]> { diff --git a/Projects/Features/MainTabFeature/Sources/Components/BottomTabBarComponent.swift b/Projects/Features/MainTabFeature/Sources/Components/BottomTabBarComponent.swift index df7336485..461ed6486 100644 --- a/Projects/Features/MainTabFeature/Sources/Components/BottomTabBarComponent.swift +++ b/Projects/Features/MainTabFeature/Sources/Components/BottomTabBarComponent.swift @@ -3,6 +3,7 @@ import NeedleFoundation public protocol BottomTabBarDependency: Dependency {} +@MainActor public final class BottomTabBarComponent: Component { public func makeView() -> BottomTabBarViewController { return BottomTabBarViewController.viewController() diff --git a/Projects/Features/MainTabFeature/Sources/Components/MainContainerComponent.swift b/Projects/Features/MainTabFeature/Sources/Components/MainContainerComponent.swift index b74fd80d2..578ab7729 100644 --- a/Projects/Features/MainTabFeature/Sources/Components/MainContainerComponent.swift +++ b/Projects/Features/MainTabFeature/Sources/Components/MainContainerComponent.swift @@ -11,6 +11,7 @@ public protocol MainContainerDependency: Dependency { var playlistPresenterGlobalState: any PlayListPresenterGlobalStateProtocol { get } } +@MainActor public final class MainContainerComponent: Component { public func makeView() -> MainContainerViewController { return MainContainerViewController diff --git a/Projects/Features/MainTabFeature/Sources/Components/MainTabBarComponent.swift b/Projects/Features/MainTabFeature/Sources/Components/MainTabBarComponent.swift index 8b50be05e..d9874b32c 100644 --- a/Projects/Features/MainTabFeature/Sources/Components/MainTabBarComponent.swift +++ b/Projects/Features/MainTabFeature/Sources/Components/MainTabBarComponent.swift @@ -30,6 +30,7 @@ public protocol MainTabBarDependency: Dependency { var songDetailPresenter: any SongDetailPresentable { get } } +@MainActor public final class MainTabBarComponent: Component { public func makeView() -> MainTabBarViewController { return MainTabBarViewController.viewController( diff --git a/Projects/Features/MainTabFeature/Sources/Components/NoticePopupComponent.swift b/Projects/Features/MainTabFeature/Sources/Components/NoticePopupComponent.swift index 5835ccc4b..08bae538d 100644 --- a/Projects/Features/MainTabFeature/Sources/Components/NoticePopupComponent.swift +++ b/Projects/Features/MainTabFeature/Sources/Components/NoticePopupComponent.swift @@ -12,6 +12,7 @@ import NoticeDomainInterface public protocol NoticePopupDependency: Dependency {} +@MainActor public final class NoticePopupComponent: Component { public func makeView(model: [FetchNoticeEntity]) -> NoticePopupViewController { return NoticePopupViewController.viewController( diff --git a/Projects/Features/MainTabFeature/Sources/ViewControllers/BottomTabBarViewController.swift b/Projects/Features/MainTabFeature/Sources/ViewControllers/BottomTabBarViewController.swift index afc97c8fa..224e754ed 100644 --- a/Projects/Features/MainTabFeature/Sources/ViewControllers/BottomTabBarViewController.swift +++ b/Projects/Features/MainTabFeature/Sources/ViewControllers/BottomTabBarViewController.swift @@ -4,6 +4,7 @@ import RxSwift import UIKit import Utility +@MainActor protocol BottomTabBarViewDelegate: AnyObject { func handleTapped(index previous: Int, current: Int) func equalHandleTapped(index current: Int) @@ -12,7 +13,7 @@ protocol BottomTabBarViewDelegate: AnyObject { public final class BottomTabBarViewController: UIViewController, ViewControllerFromStoryBoard { @IBOutlet weak var stackView: UIStackView! - private var currentIndex = Utility.PreferenceManager.startPage ?? 0 + private var currentIndex = Utility.PreferenceManager.shared.startPage ?? 0 weak var delegate: BottomTabBarViewDelegate? private lazy var tabs: [TabItemView] = { @@ -78,7 +79,7 @@ public final class BottomTabBarViewController: UIViewController, ViewControllerF private extension BottomTabBarViewController { func configureUI() { - let startPage: Int = Utility.PreferenceManager.startPage ?? 0 + let startPage: Int = Utility.PreferenceManager.shared.startPage ?? 0 LogManager.printDebug("startPage: \(startPage)") for (index, model) in self.tabItems.enumerated() { diff --git a/Projects/Features/MainTabFeature/Sources/ViewControllers/MainContainerViewController.swift b/Projects/Features/MainTabFeature/Sources/ViewControllers/MainContainerViewController.swift index 11a0b654f..4b81ad395 100644 --- a/Projects/Features/MainTabFeature/Sources/ViewControllers/MainContainerViewController.swift +++ b/Projects/Features/MainTabFeature/Sources/ViewControllers/MainContainerViewController.swift @@ -55,7 +55,7 @@ private extension MainContainerViewController { func setLayout() { view.addSubview(playlistFloatingActionButton) - let startPage: Int = PreferenceManager.startPage ?? 0 + let startPage: Int = PreferenceManager.shared.startPage ?? 0 let bottomOffset: CGFloat = startPage == 3 ? PlaylistFloatingButtonPosition.top.bottomOffset : PlaylistFloatingButtonPosition.default.bottomOffset @@ -174,7 +174,7 @@ private extension MainContainerViewController { .disposed(by: disposeBag) Observable.combineLatest( - PreferenceManager.$startPage.map { $0 ?? 0 }, + PreferenceManager.shared.$startPage.map { $0 ?? 0 }, NotificationCenter.default.rx .notification(.shouldMovePlaylistFloatingButton) .map { $0.object as? PlaylistFloatingButtonPosition ?? .default } diff --git a/Projects/Features/MainTabFeature/Sources/ViewControllers/MainTabBarViewController.swift b/Projects/Features/MainTabFeature/Sources/ViewControllers/MainTabBarViewController.swift index 84ddb57c1..c5d12cb0a 100644 --- a/Projects/Features/MainTabFeature/Sources/ViewControllers/MainTabBarViewController.swift +++ b/Projects/Features/MainTabFeature/Sources/ViewControllers/MainTabBarViewController.swift @@ -18,11 +18,13 @@ import StorageFeatureInterface import UIKit import Utility -public final class MainTabBarViewController: BaseViewController, ViewControllerFromStoryBoard, ContainerViewType { +public final class MainTabBarViewController: BaseViewController, + ViewControllerFromStoryBoard, + @preconcurrency ContainerViewType { @IBOutlet public weak var contentView: UIView! private var previousIndex: Int? - private var selectedIndex: Int = Utility.PreferenceManager.startPage ?? 0 + private var selectedIndex: Int = Utility.PreferenceManager.shared.startPage ?? 0 private lazy var viewControllers: [UIViewController] = { return [ homeFactory.makeView().wrapNavigationController, @@ -208,35 +210,31 @@ private extension MainTabBarViewController { private extension MainTabBarViewController { func requestNotificationAuthorization() { - Messaging.messaging().delegate = self - UNUserNotificationCenter.current().delegate = self - - let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] - UNUserNotificationCenter.current().requestAuthorization( - options: authOptions, - completionHandler: { granted, _ in - if granted { - DispatchQueue.main.async { - UIApplication.shared.registerForRemoteNotifications() - } - LogManager.printDebug("๐Ÿ””:: Notification authorized: \(Messaging.messaging().fcmToken ?? "")") - } else { - LogManager.printDebug("๐Ÿ””:: Notification denied") - } - PreferenceManager.pushNotificationAuthorizationStatus = granted + Task { @MainActor in + Messaging.messaging().delegate = self + UNUserNotificationCenter.current().delegate = self + + let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] + let granted = try await UNUserNotificationCenter.current().requestAuthorization(options: authOptions) + if granted { + UIApplication.shared.registerForRemoteNotifications() + LogManager.printDebug("๐Ÿ””:: Notification authorized: \(Messaging.messaging().fcmToken ?? "")") + } else { + LogManager.printDebug("๐Ÿ””:: Notification denied") } - ) + PreferenceManager.shared.pushNotificationAuthorizationStatus = granted + } } func configureUI() { - let startPage: Int = Utility.PreferenceManager.startPage ?? 0 + let startPage: Int = Utility.PreferenceManager.shared.startPage ?? 0 add(asChildViewController: viewControllers[startPage]) } } extension MainTabBarViewController { func updateContent(previous: Int, current: Int) { - Utility.PreferenceManager.startPage = current + Utility.PreferenceManager.shared.startPage = current remove(asChildViewController: viewControllers[previous]) add(asChildViewController: viewControllers[current]) @@ -268,7 +266,7 @@ extension MainTabBarViewController: NoticePopupViewControllerDelegate { } extension MainTabBarViewController: UNUserNotificationCenterDelegate { - public func userNotificationCenter( + public nonisolated func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification ) async -> UNNotificationPresentationOptions { @@ -284,12 +282,12 @@ extension MainTabBarViewController: UNUserNotificationCenterDelegate { return [[.list, .banner, .sound]] } - public func userNotificationCenter( + public nonisolated func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse ) async { let userInfo = response.notification.request.content.userInfo - appEntryState.moveScene(params: userInfo.parseNotificationInfo) + await appEntryState.moveScene(params: userInfo.parseNotificationInfo) // With swizzling disabled you must let Messaging know about the message, for Analytics Messaging.messaging().appDidReceiveMessage(userInfo) @@ -301,10 +299,12 @@ extension MainTabBarViewController: UNUserNotificationCenterDelegate { extension MainTabBarViewController: MessagingDelegate { /// [START refresh_token] - public func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { + public nonisolated func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { LogManager.printDebug("๐Ÿ””:: Firebase registration token: \(String(describing: fcmToken ?? "-"))") // If necessary send token to application server. - input.detectedRefreshPushToken.onNext(()) + Task { @MainActor in + input.detectedRefreshPushToken.onNext(()) + } // Note: This callback is fired at each app startup and whenever a new token is generated. } // [END refresh_token] diff --git a/Projects/Features/MainTabFeature/Sources/ViewControllers/NoticePopupViewController.swift b/Projects/Features/MainTabFeature/Sources/ViewControllers/NoticePopupViewController.swift index 4b4285800..b60c91489 100644 --- a/Projects/Features/MainTabFeature/Sources/ViewControllers/NoticePopupViewController.swift +++ b/Projects/Features/MainTabFeature/Sources/ViewControllers/NoticePopupViewController.swift @@ -17,6 +17,7 @@ import Then import UIKit import Utility +@MainActor public protocol NoticePopupViewControllerDelegate: AnyObject { func noticeTapped(model: FetchNoticeEntity) } @@ -59,13 +60,13 @@ public class NoticePopupViewController: UIViewController, ViewControllerFromStor } @IBAction func ignoreButtonAction(_ sender: Any) { - let savedIgoredNoticeIds: [Int] = Utility.PreferenceManager.ignoredPopupIDs ?? [] + let savedIgoredNoticeIds: [Int] = Utility.PreferenceManager.shared.ignoredPopupIDs ?? [] let currentNoticeIds: [Int] = output.originDataSource.value.map { $0.id } if savedIgoredNoticeIds.isEmpty { - Utility.PreferenceManager.ignoredPopupIDs = currentNoticeIds + Utility.PreferenceManager.shared.ignoredPopupIDs = currentNoticeIds } else { - Utility.PreferenceManager.ignoredPopupIDs = savedIgoredNoticeIds + currentNoticeIds + Utility.PreferenceManager.shared.ignoredPopupIDs = savedIgoredNoticeIds + currentNoticeIds } dismiss(animated: true) } diff --git a/Projects/Features/MainTabFeature/Sources/ViewModels/MainTabBarViewModel.swift b/Projects/Features/MainTabFeature/Sources/ViewModels/MainTabBarViewModel.swift index 87722e9ee..b9e7984d2 100644 --- a/Projects/Features/MainTabFeature/Sources/ViewModels/MainTabBarViewModel.swift +++ b/Projects/Features/MainTabFeature/Sources/ViewModels/MainTabBarViewModel.swift @@ -4,7 +4,7 @@ import LogManager import NoticeDomainInterface import NotificationDomainInterface import RxRelay -import RxSwift +@preconcurrency import RxSwift import SongsDomainInterface import Utility @@ -14,7 +14,7 @@ private typealias Observer = ( grantedNotificationAuthorization: Bool ) -public final class MainTabBarViewModel { +public final class MainTabBarViewModel: Sendable { private let fetchNoticePopupUseCase: FetchNoticePopupUseCase private let fetchNoticeIDListUseCase: FetchNoticeIDListUseCase private let updateNotificationTokenUseCase: UpdateNotificationTokenUseCase @@ -45,7 +45,7 @@ public final class MainTabBarViewModel { public func transform(from input: Input) -> Output { let output = Output() - let ignoredPopupIDs: [Int] = Utility.PreferenceManager.ignoredPopupIDs ?? [] + let ignoredPopupIDs: [Int] = Utility.PreferenceManager.shared.ignoredPopupIDs ?? [] DEBUG_LOG("ignoredPopupIDs: \(ignoredPopupIDs)") input.fetchNoticePopup @@ -64,7 +64,7 @@ public final class MainTabBarViewModel { .disposed(by: disposeBag) input.fetchNoticeIDList - .withLatestFrom(PreferenceManager.$readNoticeIDs) + .withLatestFrom(PreferenceManager.shared.$readNoticeIDs) .filter { ($0 ?? []).isEmpty } .flatMap { [fetchNoticeIDListUseCase] _ -> Single in return fetchNoticeIDListUseCase.execute() @@ -72,15 +72,15 @@ public final class MainTabBarViewModel { } .map { $0.data } .bind { allNoticeIDs in - PreferenceManager.readNoticeIDs = allNoticeIDs + PreferenceManager.shared.readNoticeIDs = allNoticeIDs } .disposed(by: disposeBag) // ํ˜ธ์ถœ ์กฐ๊ฑด: ์•ฑ ์‹คํ–‰ ์‹œ 1ํšŒ, ๋ฆฌํ”„๋ ˆ์‰ฌ ํ† ํฐ ๊ฐ์ง€, ๊ธฐ๊ธฐ์•Œ๋ฆผ on/off Observable.combineLatest( input.detectedRefreshPushToken, - PreferenceManager.$userInfo.map { $0?.ID }.distinctUntilChanged(), - PreferenceManager.$pushNotificationAuthorizationStatus.distinctUntilChanged().map { $0 ?? false } + PreferenceManager.shared.$userInfo.map { $0?.ID }.distinctUntilChanged(), + PreferenceManager.shared.$pushNotificationAuthorizationStatus.distinctUntilChanged().map { $0 ?? false } ) { detected, id, granted -> Observer in return Observer( detectedRefreshPushToken: detected, diff --git a/Projects/Features/MainTabFeature/Sources/Views/TabItemView.swift b/Projects/Features/MainTabFeature/Sources/Views/TabItemView.swift index a1a165e31..50e9aa6f0 100644 --- a/Projects/Features/MainTabFeature/Sources/Views/TabItemView.swift +++ b/Projects/Features/MainTabFeature/Sources/Views/TabItemView.swift @@ -13,6 +13,7 @@ import SnapKit import UIKit import Utility +@MainActor protocol TabItemViewDelegate: AnyObject { func handleTap(view: TabItemView) } @@ -48,7 +49,9 @@ final class TabItemView: UIView { override func awakeFromNib() { super.awakeFromNib() - self.addTapGesture() + Task { @MainActor in + self.addTapGesture() + } } } diff --git a/Projects/Features/MusicDetailFeature/Demo/Sources/AppDelegate.swift b/Projects/Features/MusicDetailFeature/Demo/Sources/AppDelegate.swift index ebbcf1b78..a349c0521 100644 --- a/Projects/Features/MusicDetailFeature/Demo/Sources/AppDelegate.swift +++ b/Projects/Features/MusicDetailFeature/Demo/Sources/AppDelegate.swift @@ -90,31 +90,31 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { } } -final class DummyLyricHighlightingFactory: LyricHighlightingFactory { +final class DummyLyricHighlightingFactory: LyricHighlightingFactory, @unchecked Sendable { func makeView(model: LyricHighlightingRequiredModel) -> UIViewController { return UIViewController() } } -final class DummyContainSongsFactory: ContainSongsFactory { +final class DummyContainSongsFactory: ContainSongsFactory, @unchecked Sendable { func makeView(songs: [String]) -> UIViewController { return UIViewController() } } -final class DummySongCreditFactory: SongCreditFactory { +final class DummySongCreditFactory: SongCreditFactory, @unchecked Sendable { func makeViewController(songID: String) -> UIViewController { return UIViewController() } } -final class DummySignInFactory: SignInFactory { +final class DummySignInFactory: SignInFactory, @unchecked Sendable { func makeView() -> UIViewController { return UIViewController() } } -final class DummyTextPopupFactory: TextPopupFactory { +final class DummyTextPopupFactory: TextPopupFactory, @unchecked Sendable { func makeView( text: String?, cancelButtonIsHidden: Bool, @@ -127,19 +127,19 @@ final class DummyTextPopupFactory: TextPopupFactory { } } -final class DummyPlaylistPresenterGlobalState: PlayListPresenterGlobalStateProtocol { +final class DummyPlaylistPresenterGlobalState: PlayListPresenterGlobalStateProtocol, @unchecked Sendable { var presentPlayListObservable: RxSwift.Observable { .empty() } func presentPlayList(currentSongID: String?) {} func presentPlayList() {} } -final class DummyKaraokeFactory: KaraokeFactory { +final class DummyKaraokeFactory: KaraokeFactory, @unchecked Sendable { func makeViewController(ky: Int?, tj: Int?) -> UIViewController { UIViewController() } } -final class DummyArtistDetailFactory: ArtistDetailFactory { +final class DummyArtistDetailFactory: ArtistDetailFactory, @unchecked Sendable { func makeView(artistID: String) -> UIViewController { UIViewController() } diff --git a/Projects/Features/MusicDetailFeature/Interface/KaraokeFactory.swift b/Projects/Features/MusicDetailFeature/Interface/KaraokeFactory.swift index 1ce7c0045..307978779 100644 --- a/Projects/Features/MusicDetailFeature/Interface/KaraokeFactory.swift +++ b/Projects/Features/MusicDetailFeature/Interface/KaraokeFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol KaraokeFactory { func makeViewController(ky: Int?, tj: Int?) -> UIViewController } diff --git a/Projects/Features/MusicDetailFeature/Interface/MusicDetailFactory.swift b/Projects/Features/MusicDetailFeature/Interface/MusicDetailFactory.swift index fe384d845..147032f34 100644 --- a/Projects/Features/MusicDetailFeature/Interface/MusicDetailFactory.swift +++ b/Projects/Features/MusicDetailFeature/Interface/MusicDetailFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol MusicDetailFactory { func makeViewController(songIDs: [String], selectedID: String) -> UIViewController } diff --git a/Projects/Features/MusicDetailFeature/Sources/Karaoke/Views/KaraokeContentView.swift b/Projects/Features/MusicDetailFeature/Sources/Karaoke/Views/KaraokeContentView.swift index 978d200e2..1535c26d6 100644 --- a/Projects/Features/MusicDetailFeature/Sources/Karaoke/Views/KaraokeContentView.swift +++ b/Projects/Features/MusicDetailFeature/Sources/Karaoke/Views/KaraokeContentView.swift @@ -3,6 +3,7 @@ import SnapKit import Then import UIKit +@MainActor private protocol KaraokeContentStateProtocol { func update(number: Int?, kind: KaraokeKind) } diff --git a/Projects/Features/MusicDetailFeature/Sources/Karaoke/Views/KaraokeInfoView.swift b/Projects/Features/MusicDetailFeature/Sources/Karaoke/Views/KaraokeInfoView.swift index 370ab439a..f3f871fc3 100644 --- a/Projects/Features/MusicDetailFeature/Sources/Karaoke/Views/KaraokeInfoView.swift +++ b/Projects/Features/MusicDetailFeature/Sources/Karaoke/Views/KaraokeInfoView.swift @@ -3,6 +3,7 @@ import SnapKit import Then import UIKit +@MainActor private protocol KaraokeStateProtocol { func update(ky: Int?, tj: Int?) } diff --git a/Projects/Features/MusicDetailFeature/Sources/MusicDetail/MusicDetailReactor.swift b/Projects/Features/MusicDetailFeature/Sources/MusicDetail/MusicDetailReactor.swift index 8b3f9bc6e..a52e00bea 100644 --- a/Projects/Features/MusicDetailFeature/Sources/MusicDetail/MusicDetailReactor.swift +++ b/Projects/Features/MusicDetailFeature/Sources/MusicDetail/MusicDetailReactor.swift @@ -355,7 +355,7 @@ private extension MusicDetailReactor { } func likeButtonDidTap() -> Observable { - guard PreferenceManager.userInfo != nil else { + guard PreferenceManager.shared.userInfo != nil else { return navigateMutation( navigate: NavigateType.textPopup( text: LocalizationStrings.needLoginWarning, @@ -401,7 +401,7 @@ private extension MusicDetailReactor { let log = CommonAnalyticsLog.clickAddMusicsButton(location: .songDetail) LogManager.analytics(log) - guard PreferenceManager.userInfo != nil else { + guard PreferenceManager.shared.userInfo != nil else { return navigateMutation( navigate: NavigateType.textPopup( text: LocalizationStrings.needLoginWarning, diff --git a/Projects/Features/MusicDetailFeature/Sources/MusicDetail/MusicDetailView.swift b/Projects/Features/MusicDetailFeature/Sources/MusicDetail/MusicDetailView.swift index 1441a4af1..7c3f9e598 100644 --- a/Projects/Features/MusicDetailFeature/Sources/MusicDetail/MusicDetailView.swift +++ b/Projects/Features/MusicDetailFeature/Sources/MusicDetail/MusicDetailView.swift @@ -7,6 +7,7 @@ import Then import UIKit import Utility +@MainActor private protocol MusicDetailStateProtocol { func updateTitle(title: String) func updateArtist(artist: String) @@ -246,14 +247,17 @@ extension MusicDetailView: MusicDetailStateProtocol { } } -extension Reactive: MusicDetailActionProtocol where Base: MusicDetailView { +extension Reactive: @preconcurrency MusicDetailActionProtocol where Base: MusicDetailView { var prevMusicButtonDidTap: Observable { base.musicControlView.rx.prevMusicButtonDidTap } var playMusicButtonDidTap: Observable { base.musicControlView.rx.playMusicButtonDidTap } var nextMusicButtonDidTap: Observable { base.musicControlView.rx.nextMusicButtonDidTap } var singingRoomButtonDidTap: Observable { base.musicControlView.rx.singingRoomButtonDidTap } var lyricsButtonDidTap: Observable { base.musicControlView.rx.lyricsButtonDidTap } + @MainActor var likeButtonDidTap: Observable { base.musicToolbarView.rx.likeButtonDidTap } + @MainActor var musicPickButtonDidTap: Observable { base.musicToolbarView.rx.musicPickButtonDidTap } + @MainActor var playlistButtonDidTap: Observable { base.musicToolbarView.rx.playlistButtonDidTap } var creditButtonDidTap: Observable { base.creditButton.rx.tap.asObservable() } var dismissButtonDidTap: Observable { base.dismissButton.rx.tap.asObservable() } diff --git a/Projects/Features/MusicDetailFeature/Sources/MusicDetail/Views/MusicControlView/MusicControlView.swift b/Projects/Features/MusicDetailFeature/Sources/MusicDetail/Views/MusicControlView/MusicControlView.swift index ff3789c61..7c4f441f4 100644 --- a/Projects/Features/MusicDetailFeature/Sources/MusicDetail/Views/MusicControlView/MusicControlView.swift +++ b/Projects/Features/MusicDetailFeature/Sources/MusicDetail/Views/MusicControlView/MusicControlView.swift @@ -7,6 +7,7 @@ import Then import UIKit import Utility +@MainActor private protocol MusicControlStateProtool { func updateTitle(title: String) func updateArtist(artist: String) diff --git a/Projects/Features/MusicDetailFeature/Sources/MusicDetail/Views/MusicToolbarView.swift b/Projects/Features/MusicDetailFeature/Sources/MusicDetail/Views/MusicToolbarView.swift index a7636ad4b..d2724b13d 100644 --- a/Projects/Features/MusicDetailFeature/Sources/MusicDetail/Views/MusicToolbarView.swift +++ b/Projects/Features/MusicDetailFeature/Sources/MusicDetail/Views/MusicToolbarView.swift @@ -5,6 +5,7 @@ import Then import UIKit import Utility +@MainActor private protocol MusicToolbarStateProtocol { func updateViews(views: Int) func updateIsLike(likes: Int, isLike: Bool) @@ -70,15 +71,18 @@ extension MusicToolbarView: MusicToolbarStateProtocol { } } -extension Reactive: MusicToolbarActionProtocol where Base: MusicToolbarView { +extension Reactive: @preconcurrency MusicToolbarActionProtocol where Base: MusicToolbarView { + @MainActor var likeButtonDidTap: Observable { base.heartButton.rx.tap.asObservable() } + @MainActor var musicPickButtonDidTap: Observable { base.musicPickButton.rx.tap.asObservable() } + @MainActor var playlistButtonDidTap: Observable { base.playlistButton.rx.tap.asObservable() } diff --git a/Projects/Features/MyInfoFeature/Interface/Protocol/Factory.swift b/Projects/Features/MyInfoFeature/Interface/Protocol/Factory.swift index 9b27cabab..afd281757 100644 --- a/Projects/Features/MyInfoFeature/Interface/Protocol/Factory.swift +++ b/Projects/Features/MyInfoFeature/Interface/Protocol/Factory.swift @@ -2,42 +2,52 @@ import FaqDomainInterface import NoticeDomainInterface import UIKit +@MainActor public protocol MyInfoFactory { func makeView() -> UIViewController } +@MainActor public protocol SettingFactory { func makeView() -> UIViewController } +@MainActor public protocol OpenSourceLicenseFactory { func makeView() -> UIViewController } +@MainActor public protocol FaqFactory { func makeView() -> UIViewController } +@MainActor public protocol FaqContentFactory { func makeView(dataSource: [FaqEntity]) -> UIViewController } +@MainActor public protocol NoticeFactory { func makeView() -> UIViewController } +@MainActor public protocol NoticeDetailFactory { func makeView(model: FetchNoticeEntity) -> UIViewController } +@MainActor public protocol QuestionFactory { func makeView() -> UIViewController } +@MainActor public protocol ServiceInfoFactory { func makeView() -> UIViewController } +@MainActor public protocol ProfilePopupFactory { func makeView(completion: (() -> Void)?) -> UIViewController } diff --git a/Projects/Features/MyInfoFeature/Interface/Protocol/PlayTypeTogglePopupFactory.swift b/Projects/Features/MyInfoFeature/Interface/Protocol/PlayTypeTogglePopupFactory.swift index 11ada2cbb..eee9fcfc3 100644 --- a/Projects/Features/MyInfoFeature/Interface/Protocol/PlayTypeTogglePopupFactory.swift +++ b/Projects/Features/MyInfoFeature/Interface/Protocol/PlayTypeTogglePopupFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol PlayTypeTogglePopupFactory { func makeView( completion: ((_ selectedItemString: String) -> Void)?, diff --git a/Projects/Features/MyInfoFeature/Sources/Reactors/MyInfoReactor.swift b/Projects/Features/MyInfoFeature/Sources/Reactors/MyInfoReactor.swift index 06f465b98..bcba6cebb 100644 --- a/Projects/Features/MyInfoFeature/Sources/Reactors/MyInfoReactor.swift +++ b/Projects/Features/MyInfoFeature/Sources/Reactors/MyInfoReactor.swift @@ -199,7 +199,7 @@ final class MyInfoReactor: Reactor { private extension MyInfoReactor { func viewDidLoad() -> Observable { - guard PreferenceManager.userInfo != nil else { return .empty() } + guard PreferenceManager.shared.userInfo != nil else { return .empty() } return mutateFetchUserInfo() } diff --git a/Projects/Features/MyInfoFeature/Sources/Reactors/SettingReactor.swift b/Projects/Features/MyInfoFeature/Sources/Reactors/SettingReactor.swift index 785ecf21b..225be12f6 100644 --- a/Projects/Features/MyInfoFeature/Sources/Reactors/SettingReactor.swift +++ b/Projects/Features/MyInfoFeature/Sources/Reactors/SettingReactor.swift @@ -68,7 +68,6 @@ final class SettingReactor: Reactor { private let withDrawUserInfoUseCase: any WithdrawUserInfoUseCase private let logoutUseCase: any LogoutUseCase private let updateNotificationTokenUseCase: any UpdateNotificationTokenUseCase - private let naverLoginInstance = NaverThirdPartyLoginConnection.getSharedInstance() init( withDrawUserInfoUseCase: WithdrawUserInfoUseCase, @@ -76,10 +75,10 @@ final class SettingReactor: Reactor { updateNotificationTokenUseCase: UpdateNotificationTokenUseCase ) { self.initialState = .init( - userInfo: Utility.PreferenceManager.userInfo, + userInfo: Utility.PreferenceManager.shared.userInfo, isHiddenLogoutButton: true, isHiddenWithDrawButton: true, - notificationAuthorizationStatus: PreferenceManager.pushNotificationAuthorizationStatus ?? false, + notificationAuthorizationStatus: PreferenceManager.shared.pushNotificationAuthorizationStatus ?? false, isShowActivityIndicator: false ) self.withDrawUserInfoUseCase = withDrawUserInfoUseCase @@ -146,7 +145,7 @@ final class SettingReactor: Reactor { } func transform(mutation: Observable) -> Observable { - let updateIsLoggedInMutation = PreferenceManager.$userInfo.map { $0?.ID } + let updateIsLoggedInMutation = PreferenceManager.shared.$userInfo.map { $0?.ID } .distinctUntilChanged() .map { $0 != nil } .withUnretained(self) @@ -157,7 +156,8 @@ final class SettingReactor: Reactor { ) } - let updatepushNotificationAuthorizationStatusMutation = PreferenceManager.$pushNotificationAuthorizationStatus + let updatepushNotificationAuthorizationStatusMutation = PreferenceManager.shared + .$pushNotificationAuthorizationStatus .skip(1) .distinctUntilChanged() .map { $0 ?? false } @@ -165,7 +165,7 @@ final class SettingReactor: Reactor { return .just(.changedNotificationAuthorizationStatus(granted)) } - let updatePlayTypeMutation = PreferenceManager.$songPlayPlatformType + let updatePlayTypeMutation = PreferenceManager.shared.$songPlayPlatformType .distinctUntilChanged() .map { $0 ?? .youtube } .flatMap { playType -> Observable in @@ -197,7 +197,7 @@ private extension SettingReactor { } func confirmLogoutButtonDidTap() -> Observable { - let notificationGranted = PreferenceManager.pushNotificationAuthorizationStatus ?? false + let notificationGranted = PreferenceManager.shared.pushNotificationAuthorizationStatus ?? false let logoutUseCase = logoutUseCase.execute(localOnly: false) .andThen( .concat( @@ -328,9 +328,11 @@ private extension SettingReactor { private extension SettingReactor { func handleThirdPartyWithDraw() -> Observable { - let platform = Utility.PreferenceManager.userInfo?.platform + let platform = Utility.PreferenceManager.shared.userInfo?.platform if platform == "naver" { - naverLoginInstance?.requestDeleteToken() + Task { @MainActor in + NaverThirdPartyLoginConnection.getSharedInstance().requestDeleteToken() + } } return .empty() } diff --git a/Projects/Features/MyInfoFeature/Sources/Service/MyInfoCommonService.swift b/Projects/Features/MyInfoFeature/Sources/Service/MyInfoCommonService.swift index bf251b766..a4651ac5a 100644 --- a/Projects/Features/MyInfoFeature/Sources/Service/MyInfoCommonService.swift +++ b/Projects/Features/MyInfoFeature/Sources/Service/MyInfoCommonService.swift @@ -13,12 +13,12 @@ final class DefaultMyInfoCommonService: MyInfoCommonService { let didChangedUserInfoEvent: Observable let didChangedReadNoticeIDsEvent: Observable<[Int]?> - static let shared = DefaultMyInfoCommonService() + nonisolated(unsafe) static let shared = DefaultMyInfoCommonService() init() { let notificationCenter = NotificationCenter.default willRefreshUserInfoEvent = notificationCenter.rx.notification(.willRefreshUserInfo) - didChangedUserInfoEvent = PreferenceManager.$userInfo - didChangedReadNoticeIDsEvent = PreferenceManager.$readNoticeIDs + didChangedUserInfoEvent = PreferenceManager.shared.$userInfo + didChangedReadNoticeIDsEvent = PreferenceManager.shared.$readNoticeIDs } } diff --git a/Projects/Features/MyInfoFeature/Sources/ViewControllers/FAQ/FaqViewController.swift b/Projects/Features/MyInfoFeature/Sources/ViewControllers/FAQ/FaqViewController.swift index 6c395ef6a..74ffb3ad9 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewControllers/FAQ/FaqViewController.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewControllers/FAQ/FaqViewController.swift @@ -138,7 +138,7 @@ extension FaqViewController { } } -extension FaqViewController: PageboyViewControllerDataSource, TMBarDataSource { +extension FaqViewController: @preconcurrency PageboyViewControllerDataSource, @preconcurrency TMBarDataSource { public func numberOfViewControllers(in pageboyViewController: Pageboy.PageboyViewController) -> Int { LogManager.printDebug(self.viewControllers.count) return self.viewControllers.count diff --git a/Projects/Features/MyInfoFeature/Sources/ViewControllers/MyInfo/MyInfoViewController.swift b/Projects/Features/MyInfoFeature/Sources/ViewControllers/MyInfo/MyInfoViewController.swift index 60cb00400..fdbd64cbd 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewControllers/MyInfo/MyInfoViewController.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewControllers/MyInfo/MyInfoViewController.swift @@ -14,7 +14,7 @@ import Then import UIKit import Utility -final class MyInfoViewController: BaseReactorViewController, EditSheetViewType { +final class MyInfoViewController: BaseReactorViewController, @preconcurrency EditSheetViewType { let myInfoView = MyInfoView() private var profilePopupFactory: ProfilePopupFactory! private var textPopupFactory: TextPopupFactory! @@ -339,16 +339,20 @@ extension MyInfoViewController: EditSheetViewDelegate { } extension MyInfoViewController: EqualHandleTappedType { - func equalHandleTapped() { - let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 - if viewControllersCount > 1 { - self.navigationController?.popToRootViewController(animated: true) + nonisolated func equalHandleTapped() { + Task { @MainActor in + let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 + if viewControllersCount > 1 { + self.navigationController?.popToRootViewController(animated: true) + } } } } extension MyInfoViewController: FruitDrawViewControllerDelegate { - func completedFruitDraw(itemCount: Int) { - reactor?.action.onNext(.completedFruitDraw) + nonisolated func completedFruitDraw(itemCount: Int) { + Task { @MainActor in + reactor?.action.onNext(.completedFruitDraw) + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/ViewControllers/OpenSourceLicense/OpenSourceLicenseViewController.swift b/Projects/Features/MyInfoFeature/Sources/ViewControllers/OpenSourceLicense/OpenSourceLicenseViewController.swift index fc7a58cd5..a8964ea40 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewControllers/OpenSourceLicense/OpenSourceLicenseViewController.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewControllers/OpenSourceLicense/OpenSourceLicenseViewController.swift @@ -44,6 +44,8 @@ public class OpenSourceLicenseViewController: UIViewController, ViewControllerFr extension OpenSourceLicenseViewController { private func inputBind() { + viewModel.input.viewDidLoad.onNext(()) + tableView.rx .setDelegate(self) .disposed(by: disposeBag) diff --git a/Projects/Features/MyInfoFeature/Sources/ViewControllers/PlayTypeTogglePopup/PlayTypeTogglePopupViewController.swift b/Projects/Features/MyInfoFeature/Sources/ViewControllers/PlayTypeTogglePopup/PlayTypeTogglePopupViewController.swift index 865c21107..98c8e5443 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewControllers/PlayTypeTogglePopup/PlayTypeTogglePopupViewController.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewControllers/PlayTypeTogglePopup/PlayTypeTogglePopupViewController.swift @@ -232,7 +232,7 @@ private extension PlayTypeTogglePopupViewController { self.view.backgroundColor = .clear contentView.clipsToBounds = true - let playType = PreferenceManager.songPlayPlatformType ?? .youtube + let playType = PreferenceManager.shared.songPlayPlatformType ?? .youtube self.selectedItemString = playType.display firstItemButton.setTitleWithOption(title: YoutubePlayType.youtube.display) diff --git a/Projects/Features/MyInfoFeature/Sources/ViewControllers/Question/QuestionViewController.swift b/Projects/Features/MyInfoFeature/Sources/ViewControllers/Question/QuestionViewController.swift index b8a02d189..e036f739f 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewControllers/Question/QuestionViewController.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewControllers/Question/QuestionViewController.swift @@ -282,7 +282,7 @@ extension QuestionViewController { } } -extension QuestionViewController: MFMailComposeViewControllerDelegate { +extension QuestionViewController: @preconcurrency MFMailComposeViewControllerDelegate { public func mailComposeController( _ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, diff --git a/Projects/Features/MyInfoFeature/Sources/ViewControllers/Setting/SettingViewController.swift b/Projects/Features/MyInfoFeature/Sources/ViewControllers/Setting/SettingViewController.swift index 8b1583d9a..d8f619719 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewControllers/Setting/SettingViewController.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewControllers/Setting/SettingViewController.swift @@ -261,7 +261,7 @@ extension SettingViewController: UITableViewDelegate { completion: { selectedItemString in switch selectedItemString { case YoutubePlayType.youtube.display: - PreferenceManager.songPlayPlatformType = .youtube + PreferenceManager.shared.songPlayPlatformType = .youtube LogManager.analytics( SettingAnalyticsLog.completeSelectSongPlayPlatform(platform: YoutubePlayType.youtube.display) ) @@ -269,7 +269,7 @@ extension SettingViewController: UITableViewDelegate { property: .songPlayPlatform(platform: YoutubePlayType.youtube.display) ) case YoutubePlayType.youtubeMusic.display: - PreferenceManager.songPlayPlatformType = .youtubeMusic + PreferenceManager.shared.songPlayPlatformType = .youtubeMusic LogManager.analytics( SettingAnalyticsLog.completeSelectSongPlayPlatform( platform: YoutubePlayType.youtubeMusic.display diff --git a/Projects/Features/MyInfoFeature/Sources/ViewModels/NoticeDetailViewModel.swift b/Projects/Features/MyInfoFeature/Sources/ViewModels/NoticeDetailViewModel.swift index c3bd4df47..fc54ffb46 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewModels/NoticeDetailViewModel.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewModels/NoticeDetailViewModel.swift @@ -65,12 +65,12 @@ public final class NoticeDetailViewModel { private extension NoticeDetailViewModel { func readNotice(ID: Int) { - var newReadNoticeIDs = PreferenceManager.readNoticeIDs ?? [] + var newReadNoticeIDs = PreferenceManager.shared.readNoticeIDs ?? [] guard newReadNoticeIDs.contains(ID) == false else { return } newReadNoticeIDs.append(ID) - PreferenceManager.readNoticeIDs = newReadNoticeIDs + PreferenceManager.shared.readNoticeIDs = newReadNoticeIDs } func downloadImageSize(url: URL) -> Observable { diff --git a/Projects/Features/MyInfoFeature/Sources/ViewModels/NoticeViewModel.swift b/Projects/Features/MyInfoFeature/Sources/ViewModels/NoticeViewModel.swift index 689269e4e..8d32c43f3 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewModels/NoticeViewModel.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewModels/NoticeViewModel.swift @@ -35,7 +35,7 @@ public final class NoticeViewModel { .catchAndReturn([]) } .map { notices in - let readIDs = Set(PreferenceManager.readNoticeIDs ?? []) + let readIDs = Set(PreferenceManager.shared.readNoticeIDs ?? []) return notices.map { notice in var notice = notice notice.isRead = readIDs.contains(notice.id) diff --git a/Projects/Features/MyInfoFeature/Sources/ViewModels/OpenSourceLicenseViewModel.swift b/Projects/Features/MyInfoFeature/Sources/ViewModels/OpenSourceLicenseViewModel.swift index a70d96583..811fe60dc 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewModels/OpenSourceLicenseViewModel.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewModels/OpenSourceLicenseViewModel.swift @@ -7,185 +7,179 @@ // import Foundation -import RxCocoa -import RxSwift +@preconcurrency import RxCocoa +@preconcurrency import RxSwift import Utility -public final class OpenSourceLicenseViewModel { +public final class OpenSourceLicenseViewModel: Sendable { let input = Input() let output = Output() - var disposeBag = DisposeBag() + let disposeBag = DisposeBag() - public struct Input {} - public struct Output { - var dataSource: BehaviorRelay<[OpenSourceLicense]> = BehaviorRelay(value: []) + public struct Input: Sendable { + let viewDidLoad: PublishSubject = PublishSubject() } - init() { - var dataSource: [OpenSourceLicense] = [ - OpenSourceLicense( - title: "Alamofire", - description: "The MIT License\nCopyright (c) 2014-2018 Alamofire Software Foundation", - link: "https://github.com/Alamofire/Alamofire" - ), - OpenSourceLicense( - title: "Moya", - description: "The MIT License\nCopyright (c) 2014-present Artsy, Ash Furrow", - link: "https://github.com/Moya/Moya.git" - ), - OpenSourceLicense( - title: "Kingfisher", - description: "The MIT License\nCopyright (c) 2018 Wei Wang", - link: "https://github.com/onevcat/Kingfisher.git" - ), - OpenSourceLicense( - title: "RxSwift", - description: "The MIT License\nCopyright ยฉ 2015 Krunoslav Zaher All rights reserved.", - link: "https://github.com/ReactiveX/RxSwift.git" - ), - OpenSourceLicense( - title: "RxGesture", - description: "The MIT License\nCopyright ยฉ 2016 RxSwiftCommunity.", - link: "https://github.com/RxSwiftCommunity/RxGesture.git" - ), - OpenSourceLicense( - title: "RxDataSources", - description: "The MIT License\nCopyright (c) 2017 RxSwift Community", - link: "https://github.com/RxSwiftCommunity/RxDataSources.git" - ), - OpenSourceLicense( - title: "RxKeyboard", - description: "The MIT License\nCopyright (c) 2016 Suyeol Jeon (xoul.kr)", - link: "https://github.com/RxSwiftCommunity/RxKeyboard.git" - ), - OpenSourceLicense( - title: "FittedSheets", - description: "The MIT License\nCopyright (c) 2018 Gordon Tucker", - link: "https://github.com/gordontucker/FittedSheets.git" - ), - OpenSourceLicense( - title: "Then", - description: "The MIT License\nCopyright (c) 2015 Suyeol Jeon (xoul.kr)", - link: "https://github.com/devxoul/Then" - ), - OpenSourceLicense( - title: "SnapKit", - description: "The MIT License\nCopyright (c) 2011-Present SnapKit Team", - link: "https://github.com/SnapKit/SnapKit.git" - ), - OpenSourceLicense( - title: "Reachability", - description: "The MIT License\nCopyright (c) 2016 Ashley Mills", - link: "https://github.com/ashleymills/Reachability.swift" - ), - OpenSourceLicense( - title: "Lottie-ios", - description: "Apache License 2.0\nCopyright 2018 Airbnb, Inc.", - link: "https://github.com/airbnb/lottie-ios.git" - ), - OpenSourceLicense( - title: "Needle", - description: "uber/needle is licensed under the Apache License 2.0", - link: "https://github.com/uber/needle.git" - ), - OpenSourceLicense( - title: "Tabman", - description: "The MIT License\nCopyright (c) 2022 UI At Six", - link: "https://github.com/uias/Tabman.git" - ), - OpenSourceLicense( - title: "SwiftEntryKit", - description: "The MIT License\nCopyright (c) 2018 Daniel Huri", - link: "https://github.com/huri000/SwiftEntryKit" - ), - OpenSourceLicense( - title: "Naveridlogin-sdk-ios", - description: "naver/naveridlogin-sdk-ios is licensed under the Apache License 2.0", - link: "https://github.com/naver/naveridlogin-sdk-ios" - ), - OpenSourceLicense( - title: "CryptoSwift", - description: "The MIT License\nCopyright (C) 2014-3099 Marcin Krzyลผanowski", - link: "https://github.com/krzyzanowskim/CryptoSwift.git" - ), - OpenSourceLicense( - title: "MarqueeLabel", - description: "The MIT License\nCopyright (c) 2011-2017 Charles Powell", - link: "https://github.com/cbpowell/MarqueeLabel.git" - ), - OpenSourceLicense( - title: "Firebase-ios-sdk", - description: "firebase/firebase-ios-sdk is licensed under the Apache License 2.0", - link: "https://github.com/firebase/firebase-ios-sdk.git" - ), - OpenSourceLicense( - title: "NVActivityIndicatorView", - description: "The MIT License\nCopyright (c) 2016 Vinh Nguyen", - link: "https://github.com/ninjaprox/NVActivityIndicatorView.git" - ), - OpenSourceLicense( - title: "RealmSwift", - description: "realm/realm-swift is licensed under the Apache License 2.0", - link: "https://github.com/realm/realm-swift" - ) - ].sorted { $0.title < $1.title } + public struct Output: Sendable { + let dataSource: BehaviorRelay<[OpenSourceLicense]> = BehaviorRelay(value: []) + } + + init() {} + + func bind() { + input.viewDidLoad.bind { [weak self] in + self?.loadLicense() + } + .disposed(by: disposeBag) + } + + private func loadLicense() { + Task { + var dataSource: [OpenSourceLicense] = [ + OpenSourceLicense( + title: "Alamofire", + description: "The MIT License\nCopyright (c) 2014-2018 Alamofire Software Foundation", + link: "https://github.com/Alamofire/Alamofire" + ), + OpenSourceLicense( + title: "Moya", + description: "The MIT License\nCopyright (c) 2014-present Artsy, Ash Furrow", + link: "https://github.com/Moya/Moya.git" + ), + OpenSourceLicense( + title: "Kingfisher", + description: "The MIT License\nCopyright (c) 2018 Wei Wang", + link: "https://github.com/onevcat/Kingfisher.git" + ), + OpenSourceLicense( + title: "RxSwift", + description: "The MIT License\nCopyright ยฉ 2015 Krunoslav Zaher All rights reserved.", + link: "https://github.com/ReactiveX/RxSwift.git" + ), + OpenSourceLicense( + title: "RxGesture", + description: "The MIT License\nCopyright ยฉ 2016 RxSwiftCommunity.", + link: "https://github.com/RxSwiftCommunity/RxGesture.git" + ), + OpenSourceLicense( + title: "RxDataSources", + description: "The MIT License\nCopyright (c) 2017 RxSwift Community", + link: "https://github.com/RxSwiftCommunity/RxDataSources.git" + ), + OpenSourceLicense( + title: "RxKeyboard", + description: "The MIT License\nCopyright (c) 2016 Suyeol Jeon (xoul.kr)", + link: "https://github.com/RxSwiftCommunity/RxKeyboard.git" + ), + OpenSourceLicense( + title: "FittedSheets", + description: "The MIT License\nCopyright (c) 2018 Gordon Tucker", + link: "https://github.com/gordontucker/FittedSheets.git" + ), + OpenSourceLicense( + title: "Then", + description: "The MIT License\nCopyright (c) 2015 Suyeol Jeon (xoul.kr)", + link: "https://github.com/devxoul/Then" + ), + OpenSourceLicense( + title: "SnapKit", + description: "The MIT License\nCopyright (c) 2011-Present SnapKit Team", + link: "https://github.com/SnapKit/SnapKit.git" + ), + OpenSourceLicense( + title: "Reachability", + description: "The MIT License\nCopyright (c) 2016 Ashley Mills", + link: "https://github.com/ashleymills/Reachability.swift" + ), + OpenSourceLicense( + title: "Lottie-ios", + description: "Apache License 2.0\nCopyright 2018 Airbnb, Inc.", + link: "https://github.com/airbnb/lottie-ios.git" + ), + OpenSourceLicense( + title: "Needle", + description: "uber/needle is licensed under the Apache License 2.0", + link: "https://github.com/uber/needle.git" + ), + OpenSourceLicense( + title: "Tabman", + description: "The MIT License\nCopyright (c) 2022 UI At Six", + link: "https://github.com/uias/Tabman.git" + ), + OpenSourceLicense( + title: "SwiftEntryKit", + description: "The MIT License\nCopyright (c) 2018 Daniel Huri", + link: "https://github.com/huri000/SwiftEntryKit" + ), + OpenSourceLicense( + title: "Naveridlogin-sdk-ios", + description: "naver/naveridlogin-sdk-ios is licensed under the Apache License 2.0", + link: "https://github.com/naver/naveridlogin-sdk-ios" + ), + OpenSourceLicense( + title: "CryptoSwift", + description: "The MIT License\nCopyright (C) 2014-3099 Marcin Krzyลผanowski", + link: "https://github.com/krzyzanowskim/CryptoSwift.git" + ), + OpenSourceLicense( + title: "MarqueeLabel", + description: "The MIT License\nCopyright (c) 2011-2017 Charles Powell", + link: "https://github.com/cbpowell/MarqueeLabel.git" + ), + OpenSourceLicense( + title: "Firebase-ios-sdk", + description: "firebase/firebase-ios-sdk is licensed under the Apache License 2.0", + link: "https://github.com/firebase/firebase-ios-sdk.git" + ), + OpenSourceLicense( + title: "NVActivityIndicatorView", + description: "The MIT License\nCopyright (c) 2016 Vinh Nguyen", + link: "https://github.com/ninjaprox/NVActivityIndicatorView.git" + ), + OpenSourceLicense( + title: "RealmSwift", + description: "realm/realm-swift is licensed under the Apache License 2.0", + link: "https://github.com/realm/realm-swift" + ) + ].sorted { $0.title < $1.title } - self.loadTextFileFromBundle(fileName: "ApacheLicense") { [weak self] result in - guard let self else { return } - switch result { - case let .success(contents): - let apacheLicense = OpenSourceLicense( + async let apacheLicenseContent = loadTextFileFromBundle(fileName: "ApacheLicense") + async let mitLicenseContent = loadTextFileFromBundle(fileName: "MITLicense") + + let ( + apacheLicense, + mitLicense + ) = try await ( + OpenSourceLicense( type: .license, title: "Apache License 2.0", - description: contents, + description: apacheLicenseContent, + link: "" + ), + OpenSourceLicense( + type: .license, + title: "MIT License (MIT)", + description: mitLicenseContent, link: "" ) - dataSource.append(apacheLicense) - - self.loadTextFileFromBundle(fileName: "MITLicense") { [weak self] result in - guard let self else { return } - switch result { - case let .success(contents): - let mitLicense = OpenSourceLicense( - type: .license, - title: "MIT License (MIT)", - description: contents, - link: "" - ) - dataSource.append(mitLicense) - self.output.dataSource.accept(dataSource) + ) - case let .failure(error): - DEBUG_LOG("ํŒŒ์ผ ๋กœ๋“œ ์—๋Ÿฌ: \(error)") - } - } - case let .failure(error): - DEBUG_LOG("ํŒŒ์ผ ๋กœ๋“œ ์—๋Ÿฌ: \(error)") - } + dataSource.append(apacheLicense) + dataSource.append(mitLicense) + self.output.dataSource.accept(dataSource) } - } -} -extension OpenSourceLicenseViewModel { - private func loadTextFileFromBundle( - fileName: String, - completionHandler: @escaping (Result) -> Void - ) { - DispatchQueue.global().async { + func loadTextFileFromBundle(fileName: String) async throws -> String { if let fileURL = MyInfoFeatureResources.bundle.url(forResource: fileName, withExtension: "txt") { - do { - let contents = try String(contentsOf: fileURL, encoding: .utf8) - completionHandler(.success(contents)) - } catch { - completionHandler(.failure(error)) - } + let contents = try String(contentsOf: fileURL, encoding: .utf8) + return contents } else { let error = NSError( - domain: "com.example.app", + domain: "yongbeomkwak.Billboardoo", code: 0, userInfo: [NSLocalizedDescriptionKey: "ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."] ) - completionHandler(.failure(error)) + throw error } } } diff --git a/Projects/Features/MyInfoFeature/Sources/ViewModels/QuestionViewModel.swift b/Projects/Features/MyInfoFeature/Sources/ViewModels/QuestionViewModel.swift index 8ed3fff35..56cd6c584 100644 --- a/Projects/Features/MyInfoFeature/Sources/ViewModels/QuestionViewModel.swift +++ b/Projects/Features/MyInfoFeature/Sources/ViewModels/QuestionViewModel.swift @@ -127,6 +127,7 @@ extension InquiryType { } } + @MainActor var suffix: String { switch self { default: @@ -135,7 +136,7 @@ extension InquiryType { * ์ž๋™์œผ๋กœ ์ž‘์„ฑ๋œ ์‹œ์Šคํ…œ ์ •๋ณด์ž…๋‹ˆ๋‹ค. ์›ํ™œํ•œ ๋ฌธ์˜๋ฅผ ์œ„ํ•ด์„œ ์‚ญ์ œํ•˜์ง€ ๋ง์•„ ์ฃผ์„ธ์š”.\n \(APP_NAME()) v\(APP_VERSION()) \(Device().modelName) / \(OS_NAME()) \(OS_VERSION()) - ๋‹‰๋„ค์ž„: \(Utility.PreferenceManager.userInfo?.decryptedName ?? "") + ๋‹‰๋„ค์ž„: \(Utility.PreferenceManager.shared.userInfo?.decryptedName ?? "") """ } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/AnswerTableViewCell.swift b/Projects/Features/MyInfoFeature/Sources/Views/AnswerTableViewCell.swift index 20392cb6b..b34bf264d 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/AnswerTableViewCell.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/AnswerTableViewCell.swift @@ -7,8 +7,10 @@ class AnswerTableViewCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - answerLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 14) - answerLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 6) + Task { @MainActor in + answerLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 14) + answerLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 6) + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/FruitDrawButtonView.swift b/Projects/Features/MyInfoFeature/Sources/Views/FruitDrawButtonView.swift index 83002867f..42f57aae5 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/FruitDrawButtonView.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/FruitDrawButtonView.swift @@ -5,6 +5,7 @@ import SnapKit import Then import UIKit +@MainActor private protocol FruitDrawStateProtocol { func updateFruitCount(count: Int) } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/MyInfoView.swift b/Projects/Features/MyInfoFeature/Sources/Views/MyInfoView.swift index 894c8874d..128abc0b1 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/MyInfoView.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/MyInfoView.swift @@ -8,6 +8,7 @@ import UIKit import UserDomainInterface import Utility +@MainActor private protocol MyInfoStateProtocol { func updateIsHiddenLoginWarningView(isLoggedIn: Bool) func updateFruitCount(count: Int) @@ -30,6 +31,7 @@ private protocol MyInfoActionProtocol { final class MyInfoView: UIView { let scrollView = UIScrollView() let contentView = UIView() + fileprivate let tapGestureRecognizer = UITapGestureRecognizer() let loginWarningView = LoginWarningView(text: "๋กœ๊ทธ์ธ์„ ํ•ด์ฃผ์„ธ์š”.") @@ -90,6 +92,8 @@ final class MyInfoView: UIView { super.init(frame: .zero) addView() setLayout() + scrollView.addGestureRecognizer(tapGestureRecognizer) + scrollView.isUserInteractionEnabled = true self.backgroundColor = DesignSystemAsset.BlueGrayColor.blueGray100.color } @@ -195,15 +199,13 @@ extension MyInfoView: MyInfoStateProtocol { } } -extension Reactive: MyInfoActionProtocol where Base: MyInfoView { +extension Reactive: @preconcurrency MyInfoActionProtocol where Base: MyInfoView { var scrollViewDidTap: Observable { - let tapGestureRecognizer = UITapGestureRecognizer() - base.scrollView.addGestureRecognizer(tapGestureRecognizer) - base.scrollView.isUserInteractionEnabled = true - return tapGestureRecognizer.rx.event.map { _ in }.asObservable() + return base.tapGestureRecognizer.rx.event.map { _ in }.asObservable() } var loginButtonDidTap: Observable { base.loginWarningView.rx.loginButtonDidTap } + @MainActor var profileImageDidTap: Observable { base.profileView.rx.profileImageDidTap } var fruitStorageButtonDidTap: Observable { base.fruitDrawButtonView.rx.fruitStorageButtonDidTap } var drawButtonDidTap: Observable { base.fruitDrawButtonView.rx.drawButtonDidTap } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/NoticeDetailHeaderView.swift b/Projects/Features/MyInfoFeature/Sources/Views/NoticeDetailHeaderView.swift index a9fd653d9..3b533180c 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/NoticeDetailHeaderView.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/NoticeDetailHeaderView.swift @@ -12,21 +12,23 @@ class NoticeDetailHeaderView: UICollectionReusableView { override func awakeFromNib() { super.awakeFromNib() - titleStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 18) - titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color - titleStringLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 1.26) + Task { @MainActor in + titleStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 18) + titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color + titleStringLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 1.26) - dateLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) - dateLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color - dateLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 0) + dateLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + dateLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color + dateLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 0) - timeLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) - timeLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color - timeLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 0) + timeLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + timeLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color + timeLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 0) - contentStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 14) - contentStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color - contentStringLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 1.26) + contentStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 14) + contentStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color + contentStringLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 1.26) + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/NoticeListCell.swift b/Projects/Features/MyInfoFeature/Sources/Views/NoticeListCell.swift index 719cf798b..7491dd960 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/NoticeListCell.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/NoticeListCell.swift @@ -10,20 +10,22 @@ class NoticeListCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - self.backgroundColor = .clear - self.contentView.backgroundColor = .clear + Task { @MainActor in + self.backgroundColor = .clear + self.contentView.backgroundColor = .clear - titleStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 16) - titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.blueGray900.color - titleStringLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 1.26) + titleStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 16) + titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.blueGray900.color + titleStringLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 1.26) - dayLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) - dayLabel.textColor = DesignSystemAsset.BlueGrayColor.blueGray500.color - dayLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 0) + dayLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + dayLabel.textColor = DesignSystemAsset.BlueGrayColor.blueGray500.color + dayLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 0) - timeLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) - timeLabel.textColor = DesignSystemAsset.BlueGrayColor.blueGray500.color - timeLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 0) + timeLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + timeLabel.textColor = DesignSystemAsset.BlueGrayColor.blueGray500.color + timeLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 0, lineHeightMultiple: 0) + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/OpenSourceLibraryCell.swift b/Projects/Features/MyInfoFeature/Sources/Views/OpenSourceLibraryCell.swift index 70187daf1..25eb8b90c 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/OpenSourceLibraryCell.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/OpenSourceLibraryCell.swift @@ -16,17 +16,19 @@ public class OpenSourceLibraryCell: UITableViewCell { override public func awakeFromNib() { super.awakeFromNib() - self.backgroundColor = .clear - self.contentView.backgroundColor = .clear - titleStringLabel.font = DesignSystemFontFamily.Pretendard.bold.font(size: 15) - titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color - titleStringLabel.setTextWithAttributes(kernValue: -0.5) - titleStringLabel.numberOfLines = 0 - descriptionLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 13) - descriptionLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color - descriptionLabel.setTextWithAttributes(kernValue: -0.5) - descriptionLabel.lineBreakMode = .byWordWrapping - descriptionLabel.numberOfLines = 0 + Task { @MainActor in + self.backgroundColor = .clear + self.contentView.backgroundColor = .clear + titleStringLabel.font = DesignSystemFontFamily.Pretendard.bold.font(size: 15) + titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color + titleStringLabel.setTextWithAttributes(kernValue: -0.5) + titleStringLabel.numberOfLines = 0 + descriptionLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 13) + descriptionLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color + descriptionLabel.setTextWithAttributes(kernValue: -0.5) + descriptionLabel.lineBreakMode = .byWordWrapping + descriptionLabel.numberOfLines = 0 + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/OpenSourceLicenseCell.swift b/Projects/Features/MyInfoFeature/Sources/Views/OpenSourceLicenseCell.swift index d1f15254b..bef5d51d4 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/OpenSourceLicenseCell.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/OpenSourceLicenseCell.swift @@ -16,17 +16,19 @@ class OpenSourceLicenseCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - self.backgroundColor = .clear - self.contentView.backgroundColor = .clear - titleStringLabel.font = DesignSystemFontFamily.Pretendard.bold.font(size: 18) - titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color - titleStringLabel.setTextWithAttributes(kernValue: -0.5) - titleStringLabel.numberOfLines = 0 - descriptionLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 13) - descriptionLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color - descriptionLabel.setTextWithAttributes(kernValue: -0.5) - descriptionLabel.lineBreakMode = .byWordWrapping - descriptionLabel.numberOfLines = 0 + Task { @MainActor in + self.backgroundColor = .clear + self.contentView.backgroundColor = .clear + titleStringLabel.font = DesignSystemFontFamily.Pretendard.bold.font(size: 18) + titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color + titleStringLabel.setTextWithAttributes(kernValue: -0.5) + titleStringLabel.numberOfLines = 0 + descriptionLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 13) + descriptionLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color + descriptionLabel.setTextWithAttributes(kernValue: -0.5) + descriptionLabel.lineBreakMode = .byWordWrapping + descriptionLabel.numberOfLines = 0 + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/PlayTypeTogglePopupItemButton.swift b/Projects/Features/MyInfoFeature/Sources/Views/PlayTypeTogglePopupItemButton.swift index 02423ee13..706799cbd 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/PlayTypeTogglePopupItemButton.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/PlayTypeTogglePopupItemButton.swift @@ -3,6 +3,7 @@ import SnapKit import Then import UIKit +@MainActor protocol PlayTypeTogglePopupItemButtonViewDelegate: AnyObject { func tappedButtonAction(title: String) } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/ProfileCollectionViewCell.swift b/Projects/Features/MyInfoFeature/Sources/Views/ProfileCollectionViewCell.swift index 121019677..e14514623 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/ProfileCollectionViewCell.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/ProfileCollectionViewCell.swift @@ -9,8 +9,10 @@ public class ProfileCollectionViewCell: UICollectionViewCell { override public func awakeFromNib() { super.awakeFromNib() - clipsToBounds = false - contentView.clipsToBounds = false + Task { @MainActor in + clipsToBounds = false + contentView.clipsToBounds = false + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/ProfileView.swift b/Projects/Features/MyInfoFeature/Sources/Views/ProfileView.swift index 8a27a11af..aa3b1e593 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/ProfileView.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/ProfileView.swift @@ -8,6 +8,7 @@ import Then import UIKit import Utility +@MainActor private protocol ProfileStateProtocol { func updateProfileImage(image: String) func updateNickName(nickname: String) @@ -170,7 +171,8 @@ extension ProfileView: ProfileStateProtocol { } } -extension Reactive: ProfileActionProtocol where Base: ProfileView { +extension Reactive: @preconcurrency ProfileActionProtocol where Base: ProfileView { + @MainActor var profileImageDidTap: Observable { return base.didTapProfileImageSubject.asObserver() .throttle(.milliseconds(500), latest: false, scheduler: MainScheduler.asyncInstance) diff --git a/Projects/Features/MyInfoFeature/Sources/Views/QuestionTableViewCell.swift b/Projects/Features/MyInfoFeature/Sources/Views/QuestionTableViewCell.swift index ce19cce34..55535f824 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/QuestionTableViewCell.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/QuestionTableViewCell.swift @@ -9,10 +9,12 @@ class QuestionTableViewCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - categoryLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) - categoryLabel.setTextWithAttributes(kernValue: -0.5) - titleLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 16) - titleLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 5) + Task { @MainActor in + categoryLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + categoryLabel.setTextWithAttributes(kernValue: -0.5) + titleLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 16) + titleLabel.setTextWithAttributes(kernValue: -0.5, lineSpacing: 5) + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/ServiceInfoCell.swift b/Projects/Features/MyInfoFeature/Sources/Views/ServiceInfoCell.swift index 241988d67..ada2fdc06 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/ServiceInfoCell.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/ServiceInfoCell.swift @@ -18,15 +18,17 @@ class ServiceInfoCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() - arrowImageView.image = DesignSystemAsset.Navigation.serviceInfoArrowRight.image - self.backgroundColor = .clear - self.contentView.backgroundColor = .clear - titleStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 16) - titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color - titleStringLabel.setTextWithAttributes(kernValue: -0.5) - subTitleStringLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) - subTitleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color - subTitleStringLabel.setTextWithAttributes(kernValue: -0.5) + Task { @MainActor in + arrowImageView.image = DesignSystemAsset.Navigation.serviceInfoArrowRight.image + self.backgroundColor = .clear + self.contentView.backgroundColor = .clear + titleStringLabel.font = DesignSystemFontFamily.Pretendard.medium.font(size: 16) + titleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray900.color + titleStringLabel.setTextWithAttributes(kernValue: -0.5) + subTitleStringLabel.font = DesignSystemFontFamily.Pretendard.light.font(size: 12) + subTitleStringLabel.textColor = DesignSystemAsset.BlueGrayColor.gray500.color + subTitleStringLabel.setTextWithAttributes(kernValue: -0.5) + } } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/SettingItemTableViewCell.swift b/Projects/Features/MyInfoFeature/Sources/Views/SettingItemTableViewCell.swift index d35b6a63b..47f91230d 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/SettingItemTableViewCell.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/SettingItemTableViewCell.swift @@ -71,8 +71,9 @@ private extension SettingItemTableViewCell { func configureSubTitle(type: SettingItemType) { switch type { case let .navigate(category): - let pushNotificationAuthorizationStatus = PreferenceManager.pushNotificationAuthorizationStatus ?? false - let playType = PreferenceManager.songPlayPlatformType ?? .youtube + let pushNotificationAuthorizationStatus = PreferenceManager.shared + .pushNotificationAuthorizationStatus ?? false + let playType = PreferenceManager.shared.songPlayPlatformType ?? .youtube switch category { case .appPush: self.subTitleLabel.text = pushNotificationAuthorizationStatus ? "์ผœ์ง" : "๊บผ์ง" diff --git a/Projects/Features/MyInfoFeature/Sources/Views/SettingView.swift b/Projects/Features/MyInfoFeature/Sources/Views/SettingView.swift index 59ae8108c..2c37d005e 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/SettingView.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/SettingView.swift @@ -9,6 +9,7 @@ import UIKit import UserDomainInterface import Utility +@MainActor private protocol SettingStateProtocol { func updateIsHiddenWithDrawButton(isHidden: Bool) func updateIsHiddenLogoutButton(isHidden: Bool) @@ -152,7 +153,8 @@ extension SettingView: SettingStateProtocol { } } -extension Reactive: SettingActionProtocol where Base: SettingView { +extension Reactive: @preconcurrency SettingActionProtocol where Base: SettingView { + @MainActor var withDrawButtonDidTap: Observable { base.withDrawLabel.rx.didTap } var dismissButtonDidTap: Observable { base.dismissButton.rx.tap.asObservable() } } diff --git a/Projects/Features/MyInfoFeature/Sources/Views/WithDrawLabel.swift b/Projects/Features/MyInfoFeature/Sources/Views/WithDrawLabel.swift index 1f07a648b..e72efe56e 100644 --- a/Projects/Features/MyInfoFeature/Sources/Views/WithDrawLabel.swift +++ b/Projects/Features/MyInfoFeature/Sources/Views/WithDrawLabel.swift @@ -74,6 +74,7 @@ final class WithDrawLabel: UILabel { } extension Reactive where Base: WithDrawLabel { + @MainActor var didTap: Observable { return base.didTapSubject.asObservable() } diff --git a/Projects/Features/MyInfoFeature/Testing/FaqComponentStub.swift b/Projects/Features/MyInfoFeature/Testing/FaqComponentStub.swift index caa833a07..d41edc124 100644 --- a/Projects/Features/MyInfoFeature/Testing/FaqComponentStub.swift +++ b/Projects/Features/MyInfoFeature/Testing/FaqComponentStub.swift @@ -5,7 +5,7 @@ import Foundation import MyInfoFeatureInterface import UIKit -public final class FaqComponentStub: FaqFactory { +public final class FaqComponentStub: FaqFactory, @unchecked Sendable { public func makeView() -> UIViewController { return FaqViewController.viewController( viewModel: .init( diff --git a/Projects/Features/MyInfoFeature/Testing/FaqContentComponentStub.swift b/Projects/Features/MyInfoFeature/Testing/FaqContentComponentStub.swift index 9b1ec61e7..f43708149 100644 --- a/Projects/Features/MyInfoFeature/Testing/FaqContentComponentStub.swift +++ b/Projects/Features/MyInfoFeature/Testing/FaqContentComponentStub.swift @@ -4,7 +4,7 @@ import Foundation import MyInfoFeatureInterface import UIKit -public final class FaqContentComponentStub: FaqContentFactory { +public final class FaqContentComponentStub: FaqContentFactory, @unchecked Sendable { public func makeView(dataSource: [FaqEntity]) -> UIViewController { return FaqContentViewController.viewController(viewModel: .init(dataSource: dataSource)) } diff --git a/Projects/Features/MyInfoFeature/Testing/MyInfoComponentStub.swift b/Projects/Features/MyInfoFeature/Testing/MyInfoComponentStub.swift index a58859682..f07388835 100644 --- a/Projects/Features/MyInfoFeature/Testing/MyInfoComponentStub.swift +++ b/Projects/Features/MyInfoFeature/Testing/MyInfoComponentStub.swift @@ -6,7 +6,7 @@ import SignInFeatureInterface @testable import SignInFeatureTesting import UIKit -public final class MyInfoComponentStub: MyInfoFactory { +public final class MyInfoComponentStub: MyInfoFactory, @unchecked Sendable { public func makeView() -> UIViewController { return UIViewController() } diff --git a/Projects/Features/MyInfoFeature/Testing/NoticeComponentStub.swift b/Projects/Features/MyInfoFeature/Testing/NoticeComponentStub.swift index 8ce8e626f..92bdf2ab9 100644 --- a/Projects/Features/MyInfoFeature/Testing/NoticeComponentStub.swift +++ b/Projects/Features/MyInfoFeature/Testing/NoticeComponentStub.swift @@ -6,7 +6,7 @@ import NoticeDomainInterface @testable import NoticeDomainTesting import UIKit -public final class NoticeComponentStub: NoticeFactory { +public final class NoticeComponentStub: NoticeFactory, @unchecked Sendable { public func makeView() -> UIViewController { return NoticeViewController.viewController( viewModel: .init( diff --git a/Projects/Features/MyInfoFeature/Testing/NoticeDetailComponentStub.swift b/Projects/Features/MyInfoFeature/Testing/NoticeDetailComponentStub.swift index a549b6196..e30b244a0 100644 --- a/Projects/Features/MyInfoFeature/Testing/NoticeDetailComponentStub.swift +++ b/Projects/Features/MyInfoFeature/Testing/NoticeDetailComponentStub.swift @@ -4,7 +4,7 @@ import MyInfoFeatureInterface import NoticeDomainInterface import UIKit -public final class NoticeDetailComponentStub: NoticeDetailFactory { +public final class NoticeDetailComponentStub: NoticeDetailFactory, @unchecked Sendable { public func makeView(model: FetchNoticeEntity) -> UIViewController { return NoticeDetailViewController.viewController( viewModel: .init( diff --git a/Projects/Features/MyInfoFeature/Testing/OpenSourceLicenseComponentStub.swift b/Projects/Features/MyInfoFeature/Testing/OpenSourceLicenseComponentStub.swift index 6f9d484b9..9694a60eb 100644 --- a/Projects/Features/MyInfoFeature/Testing/OpenSourceLicenseComponentStub.swift +++ b/Projects/Features/MyInfoFeature/Testing/OpenSourceLicenseComponentStub.swift @@ -3,7 +3,7 @@ import Foundation import MyInfoFeatureInterface import UIKit -public final class OpenSourceLicenseComponentStub: OpenSourceLicenseFactory { +public final class OpenSourceLicenseComponentStub: OpenSourceLicenseFactory, @unchecked Sendable { public func makeView() -> UIViewController { return OpenSourceLicenseViewController.viewController( viewModel: OpenSourceLicenseViewModel() diff --git a/Projects/Features/MyInfoFeature/Testing/QuestionComponentStub.swift b/Projects/Features/MyInfoFeature/Testing/QuestionComponentStub.swift index 808b849d2..bb9062dac 100644 --- a/Projects/Features/MyInfoFeature/Testing/QuestionComponentStub.swift +++ b/Projects/Features/MyInfoFeature/Testing/QuestionComponentStub.swift @@ -4,7 +4,7 @@ import BaseFeatureInterface import MyInfoFeatureInterface import UIKit -public final class QuestionComponentStub: QuestionFactory { +public final class QuestionComponentStub: QuestionFactory, @unchecked Sendable { public func makeView() -> UIViewController { return QuestionViewController.viewController( viewModel: .init(), diff --git a/Projects/Features/MyInfoFeature/Testing/SettingComponentStub.swift b/Projects/Features/MyInfoFeature/Testing/SettingComponentStub.swift index ea6cd31d8..9db711481 100644 --- a/Projects/Features/MyInfoFeature/Testing/SettingComponentStub.swift +++ b/Projects/Features/MyInfoFeature/Testing/SettingComponentStub.swift @@ -11,7 +11,7 @@ import UIKit import UserDomainInterface @testable import UserDomainTesting -public final class SettingComponentStub: SettingFactory { +public final class SettingComponentStub: SettingFactory, @unchecked Sendable { public func makeView() -> UIViewController { return SettingViewController.viewController( reactor: SettingReactor( @@ -29,7 +29,7 @@ public final class SettingComponentStub: SettingFactory { } } -final class PlayTypeTogglePopupComponentStub: PlayTypeTogglePopupFactory { +final class PlayTypeTogglePopupComponentStub: PlayTypeTogglePopupFactory, @unchecked Sendable { public func makeView( completion: ((_ selectedItemString: String) -> Void)? = nil, cancelCompletion: (() -> Void)? = nil diff --git a/Projects/Features/PlaylistFeature/Interface/CheckThumbnailFactory.swift b/Projects/Features/PlaylistFeature/Interface/CheckThumbnailFactory.swift index f25461848..8cd9fd2fc 100644 --- a/Projects/Features/PlaylistFeature/Interface/CheckThumbnailFactory.swift +++ b/Projects/Features/PlaylistFeature/Interface/CheckThumbnailFactory.swift @@ -4,6 +4,7 @@ public protocol CheckPlaylistCoverDelegate: AnyObject { func receive(_ imageData: Data) } +@MainActor public protocol CheckPlaylistCoverFactory { func makeView(delegate: any CheckPlaylistCoverDelegate, imageData: Data) -> UIViewController } diff --git a/Projects/Features/PlaylistFeature/Interface/DefaultPlaylistImageFactory.swift b/Projects/Features/PlaylistFeature/Interface/DefaultPlaylistImageFactory.swift index c4b38e682..90694bff1 100644 --- a/Projects/Features/PlaylistFeature/Interface/DefaultPlaylistImageFactory.swift +++ b/Projects/Features/PlaylistFeature/Interface/DefaultPlaylistImageFactory.swift @@ -4,6 +4,7 @@ public protocol DefaultPlaylistCoverDelegate: AnyObject { func receive(url: String, imageName: String) } +@MainActor public protocol DefaultPlaylistCoverFactory { func makeView(_ delegate: any DefaultPlaylistCoverDelegate) -> UIViewController } diff --git a/Projects/Features/PlaylistFeature/Interface/MyPlaylistDetailFactory.swift b/Projects/Features/PlaylistFeature/Interface/MyPlaylistDetailFactory.swift index 27478f33a..96f63b023 100644 --- a/Projects/Features/PlaylistFeature/Interface/MyPlaylistDetailFactory.swift +++ b/Projects/Features/PlaylistFeature/Interface/MyPlaylistDetailFactory.swift @@ -1,6 +1,7 @@ import BaseFeatureInterface import UIKit +@MainActor public protocol MyPlaylistDetailFactory { func makeView(key: String) -> UIViewController } diff --git a/Projects/Features/PlaylistFeature/Interface/PlaylistCoverOptionPopupFactory.swift b/Projects/Features/PlaylistFeature/Interface/PlaylistCoverOptionPopupFactory.swift index 71ee1f231..e0e66be2d 100644 --- a/Projects/Features/PlaylistFeature/Interface/PlaylistCoverOptionPopupFactory.swift +++ b/Projects/Features/PlaylistFeature/Interface/PlaylistCoverOptionPopupFactory.swift @@ -1,9 +1,11 @@ import UIKit +@MainActor public protocol PlaylistCoverOptionPopupDelegate: AnyObject { func didTap(_ index: Int, _ price: Int) } +@MainActor public protocol PlaylistCoverOptionPopupFactory { func makeView(delegate: any PlaylistCoverOptionPopupDelegate) -> UIViewController } diff --git a/Projects/Features/PlaylistFeature/Interface/PlaylistDetailFactory.swift b/Projects/Features/PlaylistFeature/Interface/PlaylistDetailFactory.swift index 0eb7e4541..4ac90222c 100644 --- a/Projects/Features/PlaylistFeature/Interface/PlaylistDetailFactory.swift +++ b/Projects/Features/PlaylistFeature/Interface/PlaylistDetailFactory.swift @@ -1,12 +1,13 @@ import BaseFeatureInterface import UIKit -public enum PlaylistDetailKind { +public enum PlaylistDetailKind: Sendable { case wakmu case my case unknown } +@MainActor public protocol PlaylistDetailFactory { func makeView(key: String) -> UIViewController func makeWmView(key: String) -> UIViewController diff --git a/Projects/Features/PlaylistFeature/Interface/PlaylistDetailNavigator.swift b/Projects/Features/PlaylistFeature/Interface/PlaylistDetailNavigator.swift index 6e50ad01e..221ddcd81 100644 --- a/Projects/Features/PlaylistFeature/Interface/PlaylistDetailNavigator.swift +++ b/Projects/Features/PlaylistFeature/Interface/PlaylistDetailNavigator.swift @@ -1,6 +1,7 @@ import Foundation import UIKit +@MainActor public protocol PlaylistDetailNavigator { var playlistDetailFactory: any PlaylistDetailFactory { get } @@ -10,12 +11,14 @@ public protocol PlaylistDetailNavigator { } public extension PlaylistDetailNavigator where Self: UIViewController { + @MainActor func navigateWMPlaylistDetail(key: String) { let dest = playlistDetailFactory.makeWmView(key: key) self.navigationController?.pushViewController(dest, animated: true) } + @MainActor func navigatePlaylistDetail(key: String) { let dest = playlistDetailFactory.makeView(key: key) diff --git a/Projects/Features/PlaylistFeature/Interface/PlaylistFactory.swift b/Projects/Features/PlaylistFeature/Interface/PlaylistFactory.swift index d6f220472..bdadb2c70 100644 --- a/Projects/Features/PlaylistFeature/Interface/PlaylistFactory.swift +++ b/Projects/Features/PlaylistFeature/Interface/PlaylistFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol PlaylistFactory { func makeViewController() -> UIViewController func makeViewController(currentSongID: String) -> UIViewController diff --git a/Projects/Features/PlaylistFeature/Interface/UnknownPlaylistDetailFactory.swift b/Projects/Features/PlaylistFeature/Interface/UnknownPlaylistDetailFactory.swift index b47d91a29..ba6ff5e07 100644 --- a/Projects/Features/PlaylistFeature/Interface/UnknownPlaylistDetailFactory.swift +++ b/Projects/Features/PlaylistFeature/Interface/UnknownPlaylistDetailFactory.swift @@ -1,6 +1,7 @@ import BaseFeatureInterface import UIKit +@MainActor public protocol UnknownPlaylistDetailFactory { func makeView(key: String) -> UIViewController } diff --git a/Projects/Features/PlaylistFeature/Interface/WakmusicPlaylistDetailFactory.swift b/Projects/Features/PlaylistFeature/Interface/WakmusicPlaylistDetailFactory.swift index 671ac6e51..80d332acd 100644 --- a/Projects/Features/PlaylistFeature/Interface/WakmusicPlaylistDetailFactory.swift +++ b/Projects/Features/PlaylistFeature/Interface/WakmusicPlaylistDetailFactory.swift @@ -1,6 +1,7 @@ import BaseFeatureInterface import UIKit +@MainActor public protocol WakmusicPlaylistDetailFactory { func makeView(key: String) -> UIViewController } diff --git a/Projects/Features/PlaylistFeature/Sources/DataSource/UnkwonPlaylistDetailDataSource.swift b/Projects/Features/PlaylistFeature/Sources/DataSource/UnkwonPlaylistDetailDataSource.swift index b8fda3c41..310e0d849 100644 --- a/Projects/Features/PlaylistFeature/Sources/DataSource/UnkwonPlaylistDetailDataSource.swift +++ b/Projects/Features/PlaylistFeature/Sources/DataSource/UnkwonPlaylistDetailDataSource.swift @@ -1,4 +1,4 @@ -import SongsDomainInterface +@preconcurrency import SongsDomainInterface import UIKit final class UnknownPlaylistDetailDataSource: UITableViewDiffableDataSource { diff --git a/Projects/Features/PlaylistFeature/Sources/Reactors/UnknownPlaylistDetailReactor.swift b/Projects/Features/PlaylistFeature/Sources/Reactors/UnknownPlaylistDetailReactor.swift index 82e552dc4..dbfa0140f 100644 --- a/Projects/Features/PlaylistFeature/Sources/Reactors/UnknownPlaylistDetailReactor.swift +++ b/Projects/Features/PlaylistFeature/Sources/Reactors/UnknownPlaylistDetailReactor.swift @@ -185,7 +185,7 @@ private extension UnknownPlaylistDetailReactor { Observable.just( Mutation.updateDataSource(Array(songs)) ), - PreferenceManager.userInfo == nil ? .just(.updateSubscribeState(false)) : owner + PreferenceManager.shared.userInfo == nil ? .just(.updateSubscribeState(false)) : owner .checkSubscription() ]) } @@ -271,7 +271,7 @@ private extension UnknownPlaylistDetailReactor { let prev = currentState.isSubscribing - if PreferenceManager.userInfo == nil { + if PreferenceManager.shared.userInfo == nil { return .just(.updateLoginPopupState((true, .playlistSubscribe))) } else { return subscribePlaylistUseCase.execute(key: key, isSubscribing: prev) diff --git a/Projects/Features/PlaylistFeature/Sources/Service/PlaylistCommonService.swift b/Projects/Features/PlaylistFeature/Sources/Service/PlaylistCommonService.swift index c712251dc..f75b8d27b 100644 --- a/Projects/Features/PlaylistFeature/Sources/Service/PlaylistCommonService.swift +++ b/Projects/Features/PlaylistFeature/Sources/Service/PlaylistCommonService.swift @@ -1,12 +1,12 @@ import Foundation -import RxSwift +@preconcurrency import RxSwift import Utility protocol PlaylistCommonService { var removeSubscriptionPlaylistEvent: Observable { get } } -final class DefaultPlaylistCommonService: PlaylistCommonService { +final class DefaultPlaylistCommonService: PlaylistCommonService, Sendable { let removeSubscriptionPlaylistEvent: Observable = NotificationCenter.default.rx .notification(.didRemovedSubscriptionPlaylist) diff --git a/Projects/Features/PlaylistFeature/Sources/ViewControllers/MyPlaylistDetailViewController.swift b/Projects/Features/PlaylistFeature/Sources/ViewControllers/MyPlaylistDetailViewController.swift index 01573ccab..b1461f749 100644 --- a/Projects/Features/PlaylistFeature/Sources/ViewControllers/MyPlaylistDetailViewController.swift +++ b/Projects/Features/PlaylistFeature/Sources/ViewControllers/MyPlaylistDetailViewController.swift @@ -14,7 +14,8 @@ import UIKit import Utility final class MyPlaylistDetailViewController: BaseReactorViewController, - PlaylistEditSheetViewType, SongCartViewType { + @preconcurrency PlaylistEditSheetViewType, + @preconcurrency SongCartViewType { private enum Limit { static let imageSizeLimitPerMB: Double = 10.0 } @@ -698,19 +699,21 @@ extension MyPlaylistDetailViewController: PlaylistEditSheetDelegate { } extension MyPlaylistDetailViewController: RequestPermissionable { - public func showPhotoLibrary() { - var configuration = PHPickerConfiguration() - configuration.filter = .any(of: [.images]) - configuration.selectionLimit = 1 // ๊ฐฏ์ˆ˜ ์ œํ•œ + public nonisolated func showPhotoLibrary() { + Task { @MainActor in + var configuration = PHPickerConfiguration() + configuration.filter = .any(of: [.images]) + configuration.selectionLimit = 1 // ๊ฐฏ์ˆ˜ ์ œํ•œ - let picker = PHPickerViewController(configuration: configuration) - picker.delegate = self + let picker = PHPickerViewController(configuration: configuration) + picker.delegate = self - let pickerToWrapNavigationController = picker.wrapNavigationController - pickerToWrapNavigationController.modalPresentationStyle = .overFullScreen - pickerToWrapNavigationController.setNavigationBarHidden(true, animated: false) + let pickerToWrapNavigationController = picker.wrapNavigationController + pickerToWrapNavigationController.modalPresentationStyle = .overFullScreen + pickerToWrapNavigationController.setNavigationBarHidden(true, animated: false) - self.present(pickerToWrapNavigationController, animated: true) + self.present(pickerToWrapNavigationController, animated: true) + } } } @@ -732,19 +735,19 @@ extension MyPlaylistDetailViewController: PHPickerViewControllerDelegate { DEBUG_LOG("error: \(error)") } else { - DispatchQueue.main.async { - guard let image = image as? UIImage, - let resizeImage = image.customizeForPlaylistCover( - targetSize: CGSize(width: 500, height: 500) - ), - var imageData = resizeImage.jpegData(compressionQuality: 1.0) - else { return } // 80% ์••์ถ• - - let sizeMB: Double = Double(imageData.count).megabytes - - if sizeMB > Limit.imageSizeLimitPerMB { - imageData = image.jpegData(compressionQuality: 0.8) ?? imageData - } + guard let image = image as? UIImage, + let resizeImage = image.customizeForPlaylistCover( + targetSize: CGSize(width: 500, height: 500) + ), + var imageData = resizeImage.jpegData(compressionQuality: 1.0) + else { return } // 80% ์••์ถ• + + let sizeMB: Double = Double(imageData.count).megabytes + + if sizeMB > Limit.imageSizeLimitPerMB { + imageData = image.jpegData(compressionQuality: 0.8) ?? imageData + } + Task { @MainActor in self.navigateToCheckPlaylistCover(imageData: imageData) } } @@ -774,7 +777,7 @@ extension MyPlaylistDetailViewController: PlaylistCoverOptionPopupDelegate { PlaylistAnalyticsLog.clickPlaylistImageButton(type: "custom") ) - guard let user = PreferenceManager.userInfo else { + guard let user = PreferenceManager.shared.userInfo else { return } @@ -790,15 +793,19 @@ extension MyPlaylistDetailViewController: PlaylistCoverOptionPopupDelegate { } extension MyPlaylistDetailViewController: CheckPlaylistCoverDelegate { - func receive(_ imageData: Data) { - reactor?.action.onNext(.changeImageData(.custom(data: imageData))) - headerView.updateThumbnailFromGallery(imageData) + nonisolated func receive(_ imageData: Data) { + Task { @MainActor in + reactor?.action.onNext(.changeImageData(.custom(data: imageData))) + headerView.updateThumbnailFromGallery(imageData) + } } } extension MyPlaylistDetailViewController: DefaultPlaylistCoverDelegate { - func receive(url: String, imageName: String) { - reactor?.action.onNext(.changeImageData(.default(imageName: imageName))) - headerView.updateThumbnailByDefault(url) + nonisolated func receive(url: String, imageName: String) { + Task { @MainActor in + reactor?.action.onNext(.changeImageData(.default(imageName: imageName))) + headerView.updateThumbnailByDefault(url) + } } } diff --git a/Projects/Features/PlaylistFeature/Sources/ViewControllers/PlaylistDetailContainerViewController.swift b/Projects/Features/PlaylistFeature/Sources/ViewControllers/PlaylistDetailContainerViewController.swift index adb200e4e..8cd909d33 100644 --- a/Projects/Features/PlaylistFeature/Sources/ViewControllers/PlaylistDetailContainerViewController.swift +++ b/Projects/Features/PlaylistFeature/Sources/ViewControllers/PlaylistDetailContainerViewController.swift @@ -9,7 +9,7 @@ import UIKit import Utility final class PlaylistDetailContainerViewController: BaseReactorViewController, - ContainerViewType { + @preconcurrency ContainerViewType { var contentView: UIView! = UIView().then { $0.backgroundColor = DesignSystemAsset.BlueGrayColor.gray100.color } @@ -74,7 +74,7 @@ final class PlaylistDetailContainerViewController: BaseReactorViewController, - SongCartViewType { + @preconcurrency SongCartViewType { var songCartView: SongCartView! var bottomSheetView: BottomSheetView! @@ -422,7 +422,7 @@ extension UnknownPlaylistDetailViewController: SongCartViewDelegate { let log = CommonAnalyticsLog.clickAddMusicsButton(location: .playlistDetail) LogManager.analytics(log) - if PreferenceManager.userInfo == nil { + if PreferenceManager.shared.userInfo == nil { reactor.action.onNext(.requestLoginRequiredAction(source: .addMusics)) return } diff --git a/Projects/Features/PlaylistFeature/Sources/ViewControllers/WakmusicPlaylistDetailViewController.swift b/Projects/Features/PlaylistFeature/Sources/ViewControllers/WakmusicPlaylistDetailViewController.swift index 649588138..1ddf002fb 100644 --- a/Projects/Features/PlaylistFeature/Sources/ViewControllers/WakmusicPlaylistDetailViewController.swift +++ b/Projects/Features/PlaylistFeature/Sources/ViewControllers/WakmusicPlaylistDetailViewController.swift @@ -7,13 +7,13 @@ import PhotosUI import ReactorKit import SignInFeatureInterface import SnapKit -import SongsDomainInterface +@preconcurrency import SongsDomainInterface import Then import UIKit import Utility final class WakmusicPlaylistDetailViewController: BaseReactorViewController, - SongCartViewType { + @preconcurrency SongCartViewType { var songCartView: SongCartView! var bottomSheetView: BottomSheetView! @@ -367,7 +367,7 @@ extension WakmusicPlaylistDetailViewController: SongCartViewDelegate { return } - if PreferenceManager.userInfo == nil { + if PreferenceManager.shared.userInfo == nil { reactor.action.onNext(.requestLoginRequiredAction) return } diff --git a/Projects/Features/PlaylistFeature/Sources/Views/MyPlaylistHeaderView.swift b/Projects/Features/PlaylistFeature/Sources/Views/MyPlaylistHeaderView.swift index 8b3a73cf5..b6fb17c8e 100644 --- a/Projects/Features/PlaylistFeature/Sources/Views/MyPlaylistHeaderView.swift +++ b/Projects/Features/PlaylistFeature/Sources/Views/MyPlaylistHeaderView.swift @@ -6,6 +6,7 @@ import SnapKit import Then import UIKit +@MainActor private protocol MyPlaylistHeaderStateProtocol { func updateEditState(_ isEditing: Bool) func updateData(_ model: PlaylistDetailHeaderModel) diff --git a/Projects/Features/PlaylistFeature/Sources/Views/PlaylistDateTableViewCell.swift b/Projects/Features/PlaylistFeature/Sources/Views/PlaylistDateTableViewCell.swift index 89412273d..b4a9423f4 100644 --- a/Projects/Features/PlaylistFeature/Sources/Views/PlaylistDateTableViewCell.swift +++ b/Projects/Features/PlaylistFeature/Sources/Views/PlaylistDateTableViewCell.swift @@ -7,6 +7,7 @@ import Then import UIKit import Utility +@MainActor internal protocol PlaylistDateTableViewCellDelegate: AnyObject { func thumbnailDidTap(key: String) } diff --git a/Projects/Features/PlaylistFeature/Sources/Views/PlaylistTableViewCell.swift b/Projects/Features/PlaylistFeature/Sources/Views/PlaylistTableViewCell.swift index c45f85336..7e840f665 100644 --- a/Projects/Features/PlaylistFeature/Sources/Views/PlaylistTableViewCell.swift +++ b/Projects/Features/PlaylistFeature/Sources/Views/PlaylistTableViewCell.swift @@ -9,6 +9,7 @@ import Then import UIKit import Utility +@MainActor internal protocol PlaylistTableViewCellDelegate: AnyObject { func playButtonDidTap(model: PlaylistItemModel) } diff --git a/Projects/Features/PlaylistFeature/Sources/Views/UnknownPlaylistHeaderView.swift b/Projects/Features/PlaylistFeature/Sources/Views/UnknownPlaylistHeaderView.swift index d1d1eb8db..a29f72ec5 100644 --- a/Projects/Features/PlaylistFeature/Sources/Views/UnknownPlaylistHeaderView.swift +++ b/Projects/Features/PlaylistFeature/Sources/Views/UnknownPlaylistHeaderView.swift @@ -6,6 +6,7 @@ import SnapKit import Then import UIKit +@MainActor private protocol UnknownPlaylistHeaderStateProtocol { func updateData(_ model: PlaylistDetailHeaderModel) } diff --git a/Projects/Features/PlaylistFeature/Sources/Views/WakmusicPlaylistHeaderView.swift b/Projects/Features/PlaylistFeature/Sources/Views/WakmusicPlaylistHeaderView.swift index 1b0ac6feb..16fc30bff 100644 --- a/Projects/Features/PlaylistFeature/Sources/Views/WakmusicPlaylistHeaderView.swift +++ b/Projects/Features/PlaylistFeature/Sources/Views/WakmusicPlaylistHeaderView.swift @@ -6,6 +6,7 @@ import SnapKit import Then import UIKit +@MainActor private protocol WakmusicPlaylistHeaderStateProtocol { func updateData(_ model: PlaylistDetailHeaderModel) } diff --git a/Projects/Features/PlaylistFeature/Testing/PlaylistDetailFactoryStub.swift b/Projects/Features/PlaylistFeature/Testing/PlaylistDetailFactoryStub.swift index 6c46eb06d..d303f3aa1 100644 --- a/Projects/Features/PlaylistFeature/Testing/PlaylistDetailFactoryStub.swift +++ b/Projects/Features/PlaylistFeature/Testing/PlaylistDetailFactoryStub.swift @@ -1,7 +1,7 @@ import PlaylistFeatureInterface import UIKit -public final class PlaylistDetailFactoryStub: PlaylistDetailFactory { +public final class PlaylistDetailFactoryStub: PlaylistDetailFactory, @unchecked Sendable { public func makeView(key: String) -> UIViewController { return UIViewController() } diff --git a/Projects/Features/RootFeature/Sources/Components/PermissionComponent.swift b/Projects/Features/RootFeature/Sources/Components/PermissionComponent.swift index a1f6fe60e..d9538a614 100644 --- a/Projects/Features/RootFeature/Sources/Components/PermissionComponent.swift +++ b/Projects/Features/RootFeature/Sources/Components/PermissionComponent.swift @@ -11,6 +11,7 @@ import UIKit public protocol PermissionDependency: Dependency {} +@MainActor public final class PermissionComponent: Component { public func makeView() -> PermissionViewController { return PermissionViewController.viewController() diff --git a/Projects/Features/RootFeature/Sources/Components/RootComponent.swift b/Projects/Features/RootFeature/Sources/Components/RootComponent.swift index f53e81416..d39b1276a 100644 --- a/Projects/Features/RootFeature/Sources/Components/RootComponent.swift +++ b/Projects/Features/RootFeature/Sources/Components/RootComponent.swift @@ -16,6 +16,7 @@ public protocol RootDependency: Dependency { var textPopupFactory: any TextPopupFactory { get } } +@MainActor public final class RootComponent: Component { public func makeView() -> IntroViewController { return IntroViewController.viewController( diff --git a/Projects/Features/RootFeature/Sources/ViewControllers/PermissionViewController.swift b/Projects/Features/RootFeature/Sources/ViewControllers/PermissionViewController.swift index 97cd991e8..b87bf9066 100644 --- a/Projects/Features/RootFeature/Sources/ViewControllers/PermissionViewController.swift +++ b/Projects/Features/RootFeature/Sources/ViewControllers/PermissionViewController.swift @@ -46,7 +46,7 @@ public class PermissionViewController: UIViewController, ViewControllerFromStory @IBAction func confirmButtonAction(_ sender: Any) { dismiss(animated: true, completion: { - Utility.PreferenceManager.appPermissionChecked = true + Utility.PreferenceManager.shared.appPermissionChecked = true }) } } diff --git a/Projects/Features/RootFeature/Sources/ViewModels/IntroViewModel.swift b/Projects/Features/RootFeature/Sources/ViewModels/IntroViewModel.swift index cc66b2dbe..65894192a 100644 --- a/Projects/Features/RootFeature/Sources/ViewModels/IntroViewModel.swift +++ b/Projects/Features/RootFeature/Sources/ViewModels/IntroViewModel.swift @@ -54,7 +54,7 @@ public final class IntroViewModel: ViewModelType { Observable.combineLatest( input.fetchPermissionCheck, - Utility.PreferenceManager.$appPermissionChecked + Utility.PreferenceManager.shared.$appPermissionChecked ) { _, permission -> Bool? in return permission } @@ -97,7 +97,7 @@ public final class IntroViewModel: ViewModelType { .disposed(by: disposeBag) input.checkUserInfoPreference - .withLatestFrom(PreferenceManager.$userInfo) + .withLatestFrom(PreferenceManager.shared.$userInfo) .flatMap { [logoutUseCase, checkIsExistAccessTokenUseCase] userInfo -> Observable in // ๋น„๋กœ๊ทธ์ธ ์ƒํƒœ์ธ๋ฐ, ํ‚ค์ฒด์ธ์— ์ €์žฅ๋œ ์—‘์„ธ์Šค ํ† ํฐ์ด ์‚ด์•„์žˆ๋‹ค๋Š”๊ฑด ๋กœ๊ทธ์ธ ์ƒํƒœ๋กœ ์•ฑ์„ ์‚ญ์ œํ•œ ์œ ์ €์ž„ guard userInfo == nil else { diff --git a/Projects/Features/SearchFeature/Interface/Factory/ListSearchResultFactory.swift b/Projects/Features/SearchFeature/Interface/Factory/ListSearchResultFactory.swift index d37dfa03d..5464c312f 100644 --- a/Projects/Features/SearchFeature/Interface/Factory/ListSearchResultFactory.swift +++ b/Projects/Features/SearchFeature/Interface/Factory/ListSearchResultFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol ListSearchResultFactory { func makeView(_ text: String) -> UIViewController } diff --git a/Projects/Features/SearchFeature/Interface/Factory/SearchFactory.swift b/Projects/Features/SearchFeature/Interface/Factory/SearchFactory.swift index 880e54544..96ec425d3 100644 --- a/Projects/Features/SearchFeature/Interface/Factory/SearchFactory.swift +++ b/Projects/Features/SearchFeature/Interface/Factory/SearchFactory.swift @@ -1,6 +1,7 @@ import BaseFeatureInterface import UIKit +@MainActor public protocol SearchFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/SearchFeature/Interface/Factory/SongSearchResultFactory.swift b/Projects/Features/SearchFeature/Interface/Factory/SongSearchResultFactory.swift index a309e21db..38f99c184 100644 --- a/Projects/Features/SearchFeature/Interface/Factory/SongSearchResultFactory.swift +++ b/Projects/Features/SearchFeature/Interface/Factory/SongSearchResultFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol SongSearchResultFactory { func makeView(_ text: String) -> UIViewController } diff --git a/Projects/Features/SearchFeature/Sources/After/Components/AfterSearchComponent.swift b/Projects/Features/SearchFeature/Sources/After/Components/AfterSearchComponent.swift index 0fafabecd..782fb5fd9 100644 --- a/Projects/Features/SearchFeature/Sources/After/Components/AfterSearchComponent.swift +++ b/Projects/Features/SearchFeature/Sources/After/Components/AfterSearchComponent.swift @@ -11,6 +11,7 @@ public protocol AfterSearchDependency: Dependency { var searchGlobalScrollState: any SearchGlobalScrollProtocol { get } } +@MainActor public final class AfterSearchComponent: Component { public func makeView(text: String) -> AfterSearchViewController { return AfterSearchViewController.viewController( diff --git a/Projects/Features/SearchFeature/Sources/After/Components/SearchOptionComponent.swift b/Projects/Features/SearchFeature/Sources/After/Components/SearchOptionComponent.swift index 830c99eb0..95e91542e 100644 --- a/Projects/Features/SearchFeature/Sources/After/Components/SearchOptionComponent.swift +++ b/Projects/Features/SearchFeature/Sources/After/Components/SearchOptionComponent.swift @@ -3,6 +3,7 @@ import NeedleFoundation import SearchDomainInterface import UIKit +@MainActor public final class SearchOptionComponent: Component { public func makeView(_ sortType: SortType) -> UIViewController { return SearchSortOptionViewController(selectedModel: sortType) diff --git a/Projects/Features/SearchFeature/Sources/After/Components/SearchSortOptionComponent.swift b/Projects/Features/SearchFeature/Sources/After/Components/SearchSortOptionComponent.swift index 5a039ba75..62e854127 100644 --- a/Projects/Features/SearchFeature/Sources/After/Components/SearchSortOptionComponent.swift +++ b/Projects/Features/SearchFeature/Sources/After/Components/SearchSortOptionComponent.swift @@ -2,6 +2,7 @@ import NeedleFoundation import SearchDomainInterface import UIKit +@MainActor public final class SearchSortOptionComponent: Component { public func makeView(_ selectedModel: SortType) -> UIViewController { return SearchSortOptionViewController(selectedModel: selectedModel) diff --git a/Projects/Features/SearchFeature/Sources/After/View/OptionButton.swift b/Projects/Features/SearchFeature/Sources/After/View/OptionButton.swift index ec6ea7fde..48bf5e564 100644 --- a/Projects/Features/SearchFeature/Sources/After/View/OptionButton.swift +++ b/Projects/Features/SearchFeature/Sources/After/View/OptionButton.swift @@ -6,6 +6,7 @@ import SnapKit import Then import UIKit +@MainActor private protocol OptionButtonStateProtocol { func updateSortrState(_ filterType: SortType) } diff --git a/Projects/Features/SearchFeature/Sources/After/View/SearchOptionHeaderView.swift b/Projects/Features/SearchFeature/Sources/After/View/SearchOptionHeaderView.swift index 5dcf98ad5..59a164ee8 100644 --- a/Projects/Features/SearchFeature/Sources/After/View/SearchOptionHeaderView.swift +++ b/Projects/Features/SearchFeature/Sources/After/View/SearchOptionHeaderView.swift @@ -6,6 +6,7 @@ import Then import UIKit import Utility +@MainActor private protocol SearchOptionHeaderStateProtocol { func updateSortState(_ sortType: SortType) } @@ -133,7 +134,8 @@ extension SearchOptionHeaderView: SearchOptionHeaderStateProtocol { } } -extension Reactive: SearchOptionHeaderActionProtocol where Base: SearchOptionHeaderView { +extension Reactive: @preconcurrency SearchOptionHeaderActionProtocol where Base: SearchOptionHeaderView { + @MainActor var selectedFilterItem: Observable { base.collectionView.rx.itemSelected .map { base.dataSource[$0.row] } diff --git a/Projects/Features/SearchFeature/Sources/After/View/SongResultCell.swift b/Projects/Features/SearchFeature/Sources/After/View/SongResultCell.swift index c89ea9d82..62ead6c1e 100644 --- a/Projects/Features/SearchFeature/Sources/After/View/SongResultCell.swift +++ b/Projects/Features/SearchFeature/Sources/After/View/SongResultCell.swift @@ -5,6 +5,7 @@ import SongsDomainInterface import UIKit import Utility +@MainActor public protocol SongResultCellDelegate: AnyObject { func thumbnailDidTap(key: String) } diff --git a/Projects/Features/SearchFeature/Sources/After/ViewControllers/AfterSearchViewController.swift b/Projects/Features/SearchFeature/Sources/After/ViewControllers/AfterSearchViewController.swift index e7ea91d74..af2ef6d3a 100644 --- a/Projects/Features/SearchFeature/Sources/After/ViewControllers/AfterSearchViewController.swift +++ b/Projects/Features/SearchFeature/Sources/After/ViewControllers/AfterSearchViewController.swift @@ -12,7 +12,8 @@ import Tabman import UIKit import Utility -public final class AfterSearchViewController: TabmanViewController, ViewControllerFromStoryBoard, StoryboardView { +public final class AfterSearchViewController: TabmanViewController, ViewControllerFromStoryBoard, + @preconcurrency StoryboardView { @IBOutlet weak var tabBarView: UIView! @IBOutlet weak var fakeView: UIView! @IBOutlet weak var indicator: NVActivityIndicatorView! @@ -122,7 +123,7 @@ extension AfterSearchViewController { } } -extension AfterSearchViewController: PageboyViewControllerDataSource, TMBarDataSource { +extension AfterSearchViewController: @preconcurrency PageboyViewControllerDataSource, @preconcurrency TMBarDataSource { public func numberOfViewControllers(in pageboyViewController: Pageboy.PageboyViewController) -> Int { 2 } diff --git a/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift b/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift index 42c4e97c1..269a84482 100644 --- a/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift +++ b/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift @@ -217,10 +217,12 @@ extension ListSearchResultViewController { } extension ListSearchResultViewController: SearchSortOptionDelegate { - func updateSortType(_ type: SortType) { - LogManager.analytics(SearchAnalyticsLog.selectSearchSort(option: type.rawValue, category: "list")) - if reactor?.currentState.sortType != type { - reactor?.action.onNext(.changeSortType(type)) + nonisolated func updateSortType(_ type: SortType) { + Task { @MainActor in + LogManager.analytics(SearchAnalyticsLog.selectSearchSort(option: type.rawValue, category: "list")) + if reactor?.currentState.sortType != type { + reactor?.action.onNext(.changeSortType(type)) + } } } } diff --git a/Projects/Features/SearchFeature/Sources/After/ViewControllers/SongSearchResultViewController.swift b/Projects/Features/SearchFeature/Sources/After/ViewControllers/SongSearchResultViewController.swift index 1f5ebe891..567c18b83 100644 --- a/Projects/Features/SearchFeature/Sources/After/ViewControllers/SongSearchResultViewController.swift +++ b/Projects/Features/SearchFeature/Sources/After/ViewControllers/SongSearchResultViewController.swift @@ -8,12 +8,13 @@ import RxSwift import SearchDomainInterface import SignInFeatureInterface import SnapKit -import SongsDomainInterface +@preconcurrency import SongsDomainInterface import Then import UIKit import Utility -final class SongSearchResultViewController: BaseReactorViewController, SongCartViewType { +final class SongSearchResultViewController: BaseReactorViewController, + @preconcurrency SongCartViewType { var songCartView: SongCartView! var bottomSheetView: BottomSheetView! @@ -308,10 +309,12 @@ extension SongSearchResultViewController: UICollectionViewDelegate { } extension SongSearchResultViewController: SearchSortOptionDelegate { - func updateSortType(_ type: SortType) { - LogManager.analytics(SearchAnalyticsLog.selectSearchSort(option: type.rawValue, category: "song")) - if reactor?.currentState.sortType != type { - reactor?.action.onNext(.changeSortType(type)) + nonisolated func updateSortType(_ type: SortType) { + Task { @MainActor in + LogManager.analytics(SearchAnalyticsLog.selectSearchSort(option: type.rawValue, category: "song")) + if reactor?.currentState.sortType != type { + reactor?.action.onNext(.changeSortType(type)) + } } } } @@ -353,7 +356,7 @@ extension SongSearchResultViewController: SongCartViewDelegate { return } - if PreferenceManager.userInfo == nil { + if PreferenceManager.shared.userInfo == nil { let vc = self.textPopupFactory.makeView( text: LocalizationStrings.needLoginWarning, cancelButtonIsHidden: false, diff --git a/Projects/Features/SearchFeature/Sources/Before/Components/BeforeSearchComponent.swift b/Projects/Features/SearchFeature/Sources/Before/Components/BeforeSearchComponent.swift index 505f41217..31ea643e8 100644 --- a/Projects/Features/SearchFeature/Sources/Before/Components/BeforeSearchComponent.swift +++ b/Projects/Features/SearchFeature/Sources/Before/Components/BeforeSearchComponent.swift @@ -15,6 +15,7 @@ public protocol BeforeSearchDependency: Dependency { var playlistDetailFactory: any PlaylistDetailFactory { get } } +@MainActor public final class BeforeSearchComponent: Component { public func makeView() -> UIViewController { return BeforeSearchContentViewController( diff --git a/Projects/Features/SearchFeature/Sources/Before/Components/WakmusicRecommendComponent.swift b/Projects/Features/SearchFeature/Sources/Before/Components/WakmusicRecommendComponent.swift index 19ff30e17..0934305ee 100644 --- a/Projects/Features/SearchFeature/Sources/Before/Components/WakmusicRecommendComponent.swift +++ b/Projects/Features/SearchFeature/Sources/Before/Components/WakmusicRecommendComponent.swift @@ -9,6 +9,7 @@ public protocol WakmusicRecommendDependency: Dependency { var wakmusicPlaylistDetailFactory: any WakmusicPlaylistDetailFactory { get } } +@MainActor public final class WakmusicRecommendComponent: Component { public func makeView() -> UIViewController { let reactor = WakmusicRecommendReactor( diff --git a/Projects/Features/SearchFeature/Sources/Before/CompositionalLayout/Enum/DataSource/BeforeVcDataSoruce.swift b/Projects/Features/SearchFeature/Sources/Before/CompositionalLayout/Enum/DataSource/BeforeVcDataSoruce.swift index 1be895d53..cd87f2620 100644 --- a/Projects/Features/SearchFeature/Sources/Before/CompositionalLayout/Enum/DataSource/BeforeVcDataSoruce.swift +++ b/Projects/Features/SearchFeature/Sources/Before/CompositionalLayout/Enum/DataSource/BeforeVcDataSoruce.swift @@ -4,7 +4,7 @@ import PlaylistDomainInterface #warning("์‹ค์ œ ๋ฐ์ดํ„ฐ entity๋กœ ๋ฐ”๊พธ๊ธฐ") -enum BeforeVcDataSoruce: Hashable { +enum BeforeVcDataSoruce: Hashable, Sendable { case youtube(model: CurrentVideoEntity) case recommend(model: RecommendPlaylistEntity) // case popularList(model: Model) diff --git a/Projects/Features/SearchFeature/Sources/Before/ViewControllers/BeforeSearchContentViewController.swift b/Projects/Features/SearchFeature/Sources/Before/ViewControllers/BeforeSearchContentViewController.swift index 5e3127969..d2d13dc1c 100644 --- a/Projects/Features/SearchFeature/Sources/Before/ViewControllers/BeforeSearchContentViewController.swift +++ b/Projects/Features/SearchFeature/Sources/Before/ViewControllers/BeforeSearchContentViewController.swift @@ -131,7 +131,7 @@ final class BeforeSearchContentViewController: BaseReactorViewController, ContainerViewType, +final class SearchViewController: BaseStoryboardReactorViewController, @preconcurrency ContainerViewType, EqualHandleTappedType { private enum Font { static let headerFontSize: CGFloat = 16 @@ -312,15 +312,17 @@ extension SearchViewController { } extension SearchViewController { - public func equalHandleTapped() { - let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 - if viewControllersCount > 1 { - self.navigationController?.popToRootViewController(animated: true) - } else { - if let before = children.first as? BeforeSearchContentViewController { - before.scrollToTop() - } else if let after = children.first as? AfterSearchViewController { - after.scrollToTop() + public nonisolated func equalHandleTapped() { + Task { @MainActor in + let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 + if viewControllersCount > 1 { + self.navigationController?.popToRootViewController(animated: true) + } else { + if let before = children.first as? BeforeSearchContentViewController { + before.scrollToTop() + } else if let after = children.first as? AfterSearchViewController { + after.scrollToTop() + } } } } diff --git a/Projects/Features/SearchFeature/Sources/Service/SearchCommonService.swift b/Projects/Features/SearchFeature/Sources/Service/SearchCommonService.swift index 5308ac617..bd4003908 100644 --- a/Projects/Features/SearchFeature/Sources/Service/SearchCommonService.swift +++ b/Projects/Features/SearchFeature/Sources/Service/SearchCommonService.swift @@ -1,5 +1,5 @@ import Foundation -import RxSwift +@preconcurrency import RxSwift import SearchFeatureInterface protocol SearchCommonService { @@ -7,7 +7,7 @@ protocol SearchCommonService { var recentText: PublishSubject { get } } -final class DefaultSearchCommonService: SearchCommonService { +final class DefaultSearchCommonService: SearchCommonService, Sendable { let typingStatus: BehaviorSubject = .init(value: .before) let recentText: PublishSubject = .init() diff --git a/Projects/Features/SignInFeature/Interface/SignInFactory.swift b/Projects/Features/SignInFeature/Interface/SignInFactory.swift index 3fa1aaf51..d2b333990 100644 --- a/Projects/Features/SignInFeature/Interface/SignInFactory.swift +++ b/Projects/Features/SignInFeature/Interface/SignInFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol SignInFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/SignInFeature/Sources/ViewModels/LoginViewModel.swift b/Projects/Features/SignInFeature/Sources/ViewModels/LoginViewModel.swift index c6281dcf9..655f1d327 100644 --- a/Projects/Features/SignInFeature/Sources/ViewModels/LoginViewModel.swift +++ b/Projects/Features/SignInFeature/Sources/ViewModels/LoginViewModel.swift @@ -3,13 +3,13 @@ import AuthenticationServices import BaseFeature import Localization import LogManager -import NaverThirdPartyLogin -import RxRelay +@preconcurrency import NaverThirdPartyLogin +@preconcurrency import RxRelay import RxSwift import UserDomainInterface import Utility -public final class LoginViewModel: NSObject { // ๋„ค์ด๋ฒ„ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋ฅผ ๋ฐ›๊ธฐ์œ„ํ•œ NSObject ์ƒ์† +public final class LoginViewModel: NSObject, @unchecked Sendable { // ๋„ค์ด๋ฒ„ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋ฅผ ๋ฐ›๊ธฐ์œ„ํ•œ NSObject ์ƒ์† private let fetchTokenUseCase: FetchTokenUseCase private let fetchUserInfoUseCase: FetchUserInfoUseCase let input: Input = Input() @@ -40,7 +40,9 @@ public final class LoginViewModel: NSObject { // ๋„ค์ด๋ฒ„ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋ฅผ self.fetchUserInfoUseCase = fetchUserInfoUseCase super.init() GoogleLoginManager.shared.googleOAuthLoginDelegate = self - NaverThirdPartyLoginConnection.getSharedInstance()?.delegate = self + Task { @MainActor in + NaverThirdPartyLoginConnection.getSharedInstance().delegate = self + } bind() } } @@ -49,8 +51,10 @@ private extension LoginViewModel { func bind() { input.didTapNaverLoginButton .bind(with: self, onNext: { owner, _ in - NaverThirdPartyLoginConnection.getSharedInstance()?.delegate = owner - NaverThirdPartyLoginConnection.getSharedInstance()?.requestThirdPartyLogin() + Task { @MainActor in + NaverThirdPartyLoginConnection.getSharedInstance()?.delegate = owner + NaverThirdPartyLoginConnection.getSharedInstance()?.requestThirdPartyLogin() + } }) .disposed(by: disposeBag) @@ -103,32 +107,37 @@ private extension LoginViewModel { extension LoginViewModel: GoogleOAuthLoginDelegate { public func requestGoogleAccessToken(_ code: String) { - Task { + let arrivedTokenFromThirdParty = input.arrivedTokenFromThirdParty + Task { @MainActor in let id = try await GoogleLoginManager.shared.getGoogleOAuthToken(code) let log = SigninAnalyticsLog.completeSocialLogin(type: .google) LogManager.analytics(log) - input.arrivedTokenFromThirdParty.accept((.google, id)) + arrivedTokenFromThirdParty.accept((.google, id)) } } } extension LoginViewModel: NaverThirdPartyLoginConnectionDelegate { public func oauth20ConnectionDidFinishRequestACTokenWithAuthCode() { - let shared = NaverThirdPartyLoginConnection.getSharedInstance() - guard let accessToken = shared?.accessToken else { return } - let log = SigninAnalyticsLog.completeSocialLogin(type: .naver) - LogManager.analytics(log) + let arrivedTokenFromThirdParty = input.arrivedTokenFromThirdParty + Task { @MainActor in + guard let accessToken = NaverThirdPartyLoginConnection.getSharedInstance().accessToken else { return } + let log = SigninAnalyticsLog.completeSocialLogin(type: .naver) + LogManager.analytics(log) - input.arrivedTokenFromThirdParty.accept((.naver, accessToken)) + arrivedTokenFromThirdParty.accept((.naver, accessToken)) + } } public func oauth20ConnectionDidFinishRequestACTokenWithRefreshToken() { - let shared = NaverThirdPartyLoginConnection.getSharedInstance() - guard let accessToken = shared?.accessToken else { return } - let log = SigninAnalyticsLog.completeSocialLogin(type: .naver) - LogManager.analytics(log) + let arrivedTokenFromThirdParty = input.arrivedTokenFromThirdParty + Task { @MainActor in + guard let accessToken = NaverThirdPartyLoginConnection.getSharedInstance().accessToken else { return } + let log = SigninAnalyticsLog.completeSocialLogin(type: .naver) + LogManager.analytics(log) - input.arrivedTokenFromThirdParty.accept((.naver, accessToken)) + arrivedTokenFromThirdParty.accept((.naver, accessToken)) + } } public func oauth20ConnectionDidFinishDeleteToken() { diff --git a/Projects/Features/SignInFeature/Testing/SignInComponentStub.swift b/Projects/Features/SignInFeature/Testing/SignInComponentStub.swift index 6296866ce..6761dfd27 100644 --- a/Projects/Features/SignInFeature/Testing/SignInComponentStub.swift +++ b/Projects/Features/SignInFeature/Testing/SignInComponentStub.swift @@ -7,7 +7,7 @@ import UIKit import UserDomainInterface @testable import UserDomainTesting -public final class SignInComponentStub: SignInFactory { +public final class SignInComponentStub: SignInFactory, @unchecked Sendable { public func makeView() -> UIViewController { return LoginViewController.viewController( viewModel: .init( diff --git a/Projects/Features/SongCreditFeature/Demo/Sources/AppDelegate.swift b/Projects/Features/SongCreditFeature/Demo/Sources/AppDelegate.swift index 04ffc7ed7..c8febd366 100644 --- a/Projects/Features/SongCreditFeature/Demo/Sources/AppDelegate.swift +++ b/Projects/Features/SongCreditFeature/Demo/Sources/AppDelegate.swift @@ -45,14 +45,14 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { } } -final class DummyCreditSongListFactory: CreditSongListFactory { +final class DummyCreditSongListFactory: CreditSongListFactory, @unchecked Sendable { func makeViewController(workerName: String) -> UIViewController { let viewController = UIViewController() return viewController } } -final class DummyArtistDetailFactory: ArtistDetailFactory { +final class DummyArtistDetailFactory: ArtistDetailFactory, @unchecked Sendable { func makeView(artistID: String) -> UIViewController { return UIViewController() } diff --git a/Projects/Features/SongCreditFeature/Interface/SongCreditFactory.swift b/Projects/Features/SongCreditFeature/Interface/SongCreditFactory.swift index 456c542ff..59b9fd729 100644 --- a/Projects/Features/SongCreditFeature/Interface/SongCreditFactory.swift +++ b/Projects/Features/SongCreditFeature/Interface/SongCreditFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol SongCreditFactory { func makeViewController(songID: String) -> UIViewController } diff --git a/Projects/Features/StorageFeature/Sources/Components/LikeStorageComponent.swift b/Projects/Features/StorageFeature/Sources/Components/LikeStorageComponent.swift index 11f9c53f5..436352cce 100644 --- a/Projects/Features/StorageFeature/Sources/Components/LikeStorageComponent.swift +++ b/Projects/Features/StorageFeature/Sources/Components/LikeStorageComponent.swift @@ -19,6 +19,7 @@ public protocol LikeStorageDependency: Dependency { var songDetailPresenter: any SongDetailPresentable { get } } +@MainActor public final class LikeStorageComponent: Component { public func makeView() -> UIViewController { return LikeStorageViewController.viewController( diff --git a/Projects/Features/StorageFeature/Sources/Components/ListStorageComponent.swift b/Projects/Features/StorageFeature/Sources/Components/ListStorageComponent.swift index 311867607..17d389c2f 100644 --- a/Projects/Features/StorageFeature/Sources/Components/ListStorageComponent.swift +++ b/Projects/Features/StorageFeature/Sources/Components/ListStorageComponent.swift @@ -26,6 +26,7 @@ public protocol ListStorageDependency: Dependency { var fruitDrawFactory: any FruitDrawFactory { get } } +@MainActor public final class ListStorageComponent: Component { public func makeView() -> UIViewController { return ListStorageViewController( diff --git a/Projects/Features/StorageFeature/Sources/Reactors/LikeStorageReactor.swift b/Projects/Features/StorageFeature/Sources/Reactors/LikeStorageReactor.swift index e2b810f06..a6dbab580 100644 --- a/Projects/Features/StorageFeature/Sources/Reactors/LikeStorageReactor.swift +++ b/Projects/Features/StorageFeature/Sources/Reactors/LikeStorageReactor.swift @@ -248,7 +248,7 @@ final class LikeStorageReactor: Reactor { extension LikeStorageReactor { func viewDidLoad() -> Observable { - let isLoggedIn = PreferenceManager.userInfo != nil + let isLoggedIn = PreferenceManager.shared.userInfo != nil if !isLoggedIn { return .empty() } return .concat( updateIsLoggedIn(isLoggedIn), @@ -332,10 +332,12 @@ extension LikeStorageReactor { func playWithAddToCurrentPlaylist(song: FavoriteSongEntity) -> Observable { let appendingPlaylisItem = PlaylistItem(id: song.songID, title: song.title, artist: song.artist) PlayState.shared.append(item: appendingPlaylisItem) - WakmusicYoutubePlayer( - id: song.songID, - playPlatform: song.title.isContainShortsTagTitle ? .youtube : .automatic - ).play() + Task { @MainActor in + WakmusicYoutubePlayer( + id: song.songID, + playPlatform: song.title.isContainShortsTagTitle ? .youtube : .automatic + ).play() + } return .empty() } diff --git a/Projects/Features/StorageFeature/Sources/Reactors/ListStorageReactor.swift b/Projects/Features/StorageFeature/Sources/Reactors/ListStorageReactor.swift index 8bf963e4f..da5d94d5d 100644 --- a/Projects/Features/StorageFeature/Sources/Reactors/ListStorageReactor.swift +++ b/Projects/Features/StorageFeature/Sources/Reactors/ListStorageReactor.swift @@ -7,7 +7,7 @@ import PriceDomainInterface import ReactorKit import RxCocoa import RxSwift -import SongsDomainInterface +@preconcurrency import SongsDomainInterface import UserDomainInterface import Utility @@ -280,7 +280,7 @@ final class ListStorageReactor: Reactor { extension ListStorageReactor { func viewDidLoad() -> Observable { - let isLoggedIn = PreferenceManager.userInfo != nil + let isLoggedIn = PreferenceManager.shared.userInfo != nil if !isLoggedIn { return .empty() } return .concat( updateIsLoggedIn(isLoggedIn), @@ -366,7 +366,7 @@ extension ListStorageReactor { } func addToCurrentPlaylist() -> Observable { - let limit = 50 + _ = 50 let selectedPlaylists = currentState.dataSource .flatMap { $0.items.filter { $0.isSelected == true } } @@ -424,12 +424,15 @@ extension ListStorageReactor { .asObservable() .do(onNext: { [weak self] appendingPlaylistItems in PlayState.shared.appendSongsToPlaylist(appendingPlaylistItems) - let ids = appendingPlaylistItems.map { $0.id } - .prefix(50) - if appendingPlaylistItems.allSatisfy({ $0.title.isContainShortsTagTitle }) { - WakmusicYoutubePlayer(ids: Array(ids), title: "์™ํƒ€๋ฒ„์Šค ๋ฎค์ง", playPlatform: .youtube).play() - } else { - WakmusicYoutubePlayer(ids: Array(ids), title: "์™ํƒ€๋ฒ„์Šค ๋ฎค์ง").play() + Task { @MainActor in + let ids = appendingPlaylistItems.map { $0.id } + .prefix(50) + + if appendingPlaylistItems.allSatisfy({ $0.title.isContainShortsTagTitle }) { + WakmusicYoutubePlayer(ids: Array(ids), title: "์™ํƒ€๋ฒ„์Šค ๋ฎค์ง", playPlatform: .youtube).play() + } else { + WakmusicYoutubePlayer(ids: Array(ids), title: "์™ํƒ€๋ฒ„์Šค ๋ฎค์ง").play() + } } self?.storageCommonService.isEditingState.onNext(false) }) @@ -525,7 +528,7 @@ private extension ListStorageReactor { func mutateDeletePlaylist(_ playlists: [PlaylistEntity]) -> Observable { let noti = NotificationCenter.default - let subscribedPlaylistKeys = playlists.filter { $0.userId != PreferenceManager.userInfo?.decryptedID } + let subscribedPlaylistKeys = playlists.filter { $0.userId != PreferenceManager.shared.userInfo?.decryptedID } .map { $0.key } let ids = playlists.map { $0.key } return deletePlayListUseCase.execute(ids: ids) @@ -572,7 +575,7 @@ private extension ListStorageReactor { .asObservable() .map { $0.price } .flatMap { price -> Observable in - guard let userItemCount = PreferenceManager.userInfo?.itemCount else { + guard let userItemCount = PreferenceManager.shared.userInfo?.itemCount else { return .just(.showToast(LocalizationStrings.unknownErrorWarning)) } if userItemCount < price { diff --git a/Projects/Features/StorageFeature/Sources/Reactors/StorageReactor.swift b/Projects/Features/StorageFeature/Sources/Reactors/StorageReactor.swift index 9dd6afd12..230875d7d 100644 --- a/Projects/Features/StorageFeature/Sources/Reactors/StorageReactor.swift +++ b/Projects/Features/StorageFeature/Sources/Reactors/StorageReactor.swift @@ -97,7 +97,7 @@ final class StorageReactor: Reactor { private extension StorageReactor { func viewDidLoad() -> Observable { - let isLoggedIn = PreferenceManager.userInfo != nil + let isLoggedIn = PreferenceManager.shared.userInfo != nil return .just(.updateIsLoggedIn(isLoggedIn)) } diff --git a/Projects/Features/StorageFeature/Sources/Service/StorageCommonService.swift b/Projects/Features/StorageFeature/Sources/Service/StorageCommonService.swift index 13fd28f04..cf2ef16a9 100644 --- a/Projects/Features/StorageFeature/Sources/Service/StorageCommonService.swift +++ b/Projects/Features/StorageFeature/Sources/Service/StorageCommonService.swift @@ -1,5 +1,5 @@ import Foundation -import RxSwift +@preconcurrency import RxSwift import Utility protocol StorageCommonService { @@ -9,7 +9,7 @@ protocol StorageCommonService { var likeListRefreshEvent: Observable { get } } -final class DefaultStorageCommonService: StorageCommonService { +final class DefaultStorageCommonService: StorageCommonService, Sendable { static let shared = DefaultStorageCommonService() let isEditingState: BehaviorSubject @@ -20,7 +20,7 @@ final class DefaultStorageCommonService: StorageCommonService { init() { let notificationCenter = NotificationCenter.default isEditingState = .init(value: false) - loginStateDidChangedEvent = PreferenceManager.$userInfo.map(\.?.ID).distinctUntilChanged().skip(1) + loginStateDidChangedEvent = PreferenceManager.shared.$userInfo.map(\.?.ID).distinctUntilChanged().skip(1) playlistRefreshEvent = notificationCenter.rx.notification(.shouldRefreshPlaylist) likeListRefreshEvent = notificationCenter.rx.notification(.shouldRefreshLikeList).map { _ in () } } diff --git a/Projects/Features/StorageFeature/Sources/ViewControllers/LikeStorageViewController.swift b/Projects/Features/StorageFeature/Sources/ViewControllers/LikeStorageViewController.swift index 77d25a7d3..b6f2fffd6 100644 --- a/Projects/Features/StorageFeature/Sources/ViewControllers/LikeStorageViewController.swift +++ b/Projects/Features/StorageFeature/Sources/ViewControllers/LikeStorageViewController.swift @@ -17,7 +17,7 @@ import Utility typealias LikeSectionModel = SectionModel -final class LikeStorageViewController: BaseReactorViewController, SongCartViewType { +final class LikeStorageViewController: BaseReactorViewController, @preconcurrency SongCartViewType { let likeStorageView = LikeStorageView() var containSongsFactory: ContainSongsFactory! @@ -151,7 +151,7 @@ final class LikeStorageViewController: BaseReactorViewController -final class ListStorageViewController: BaseReactorViewController, SongCartViewType, +final class ListStorageViewController: BaseReactorViewController, @preconcurrency SongCartViewType, PlaylistDetailNavigator { private let createListButton = CreateListButtonView( padding: .init( @@ -206,7 +206,7 @@ final class ListStorageViewController: BaseReactorViewController Int { self.viewControllers.count } @@ -244,12 +244,14 @@ extension StorageViewController: EqualHandleTappedType { } } - public func equalHandleTapped() { - let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 - if viewControllersCount > 1 { - self.navigationController?.popToRootViewController(animated: true) - } else { - scrollToTop() + public nonisolated func equalHandleTapped() { + Task { @MainActor in + let viewControllersCount: Int = self.navigationController?.viewControllers.count ?? 0 + if viewControllersCount > 1 { + self.navigationController?.popToRootViewController(animated: true) + } else { + scrollToTop() + } } } } diff --git a/Projects/Features/StorageFeature/Sources/Views/LikeStorageTableViewCell.swift b/Projects/Features/StorageFeature/Sources/Views/LikeStorageTableViewCell.swift index 098a7a9a6..5827b23dd 100644 --- a/Projects/Features/StorageFeature/Sources/Views/LikeStorageTableViewCell.swift +++ b/Projects/Features/StorageFeature/Sources/Views/LikeStorageTableViewCell.swift @@ -7,6 +7,7 @@ import UIKit import UserDomainInterface import Utility +@MainActor public protocol LikeStorageTableViewCellDelegate: AnyObject { func buttonTapped(type: LikeStorageTableViewCellDelegateConstant) } diff --git a/Projects/Features/StorageFeature/Sources/Views/LikeStorageView.swift b/Projects/Features/StorageFeature/Sources/Views/LikeStorageView.swift index 05948a730..6c9ec2e3e 100644 --- a/Projects/Features/StorageFeature/Sources/Views/LikeStorageView.swift +++ b/Projects/Features/StorageFeature/Sources/Views/LikeStorageView.swift @@ -14,6 +14,7 @@ import UIKit import UserDomainInterface import Utility +@MainActor private protocol LikeStorageStateProtocol { func updateActivityIndicatorState(isPlaying: Bool) func updateRefreshControlState(isPlaying: Bool) @@ -134,7 +135,8 @@ extension LikeStorageView: LikeStorageStateProtocol { } } -extension Reactive: LikeStorageActionProtocol where Base: LikeStorageView { +extension Reactive: @preconcurrency LikeStorageActionProtocol where Base: LikeStorageView { + @MainActor var loginButtonDidTap: Observable { base.loginWarningView.loginButtonDidTapSubject.asObservable() } diff --git a/Projects/Features/StorageFeature/Sources/Views/ListStorageTableViewCell.swift b/Projects/Features/StorageFeature/Sources/Views/ListStorageTableViewCell.swift index 64f2c330b..f7e8925c6 100644 --- a/Projects/Features/StorageFeature/Sources/Views/ListStorageTableViewCell.swift +++ b/Projects/Features/StorageFeature/Sources/Views/ListStorageTableViewCell.swift @@ -8,6 +8,7 @@ import UIKit import UserDomainInterface import Utility +@MainActor public protocol ListStorageTableViewCellDelegate: AnyObject { func buttonTapped(type: ListStorageTableViewCellDelegateConstant) } diff --git a/Projects/Features/StorageFeature/Sources/Views/ListStorageView.swift b/Projects/Features/StorageFeature/Sources/Views/ListStorageView.swift index 080cf64a2..a32ad3728 100644 --- a/Projects/Features/StorageFeature/Sources/Views/ListStorageView.swift +++ b/Projects/Features/StorageFeature/Sources/Views/ListStorageView.swift @@ -14,6 +14,7 @@ import UIKit import UserDomainInterface import Utility +@MainActor private protocol ListStorageStateProtocol { func updateActivityIndicatorState(isPlaying: Bool) func updateRefreshControlState(isPlaying: Bool) @@ -190,7 +191,8 @@ extension ListStorageView: ListStorageStateProtocol { } } -extension Reactive: ListStorageActionProtocol where Base: ListStorageView { +extension Reactive: @preconcurrency ListStorageActionProtocol where Base: ListStorageView { + @MainActor var loginButtonDidTap: Observable { base.loginWarningView.loginButtonDidTapSubject.asObservable() } diff --git a/Projects/Features/StorageFeature/Sources/Views/ParticleAnimationView.swift b/Projects/Features/StorageFeature/Sources/Views/ParticleAnimationView.swift index c406a7b67..301347c24 100644 --- a/Projects/Features/StorageFeature/Sources/Views/ParticleAnimationView.swift +++ b/Projects/Features/StorageFeature/Sources/Views/ParticleAnimationView.swift @@ -5,6 +5,7 @@ import Then import UIKit import Utility +@MainActor private protocol ParticleAnimationStateProtocol { func startAnimation() } diff --git a/Projects/Features/StorageFeature/Sources/Views/StorageView.swift b/Projects/Features/StorageFeature/Sources/Views/StorageView.swift index 7f17c65b1..dcb74cc17 100644 --- a/Projects/Features/StorageFeature/Sources/Views/StorageView.swift +++ b/Projects/Features/StorageFeature/Sources/Views/StorageView.swift @@ -6,6 +6,7 @@ import Then import UIKit import Utility +@MainActor private protocol StorageStateProtocol { func updateIsHiddenEditButton(isHidden: Bool) } @@ -16,6 +17,7 @@ private protocol StorageActionProtocol { } private enum ButtonAttributed { + @MainActor static let edit = NSMutableAttributedString( string: "ํŽธ์ง‘", attributes: [ @@ -23,6 +25,7 @@ private enum ButtonAttributed { .foregroundColor: DesignSystemAsset.BlueGrayColor.blueGray400.color ] ) + @MainActor static let save = NSMutableAttributedString( string: "์™„๋ฃŒ", attributes: [ diff --git a/Projects/Features/StorageFeature/interface/StorageFactory.swift b/Projects/Features/StorageFeature/interface/StorageFactory.swift index f478f0d2f..07ffd977a 100644 --- a/Projects/Features/StorageFeature/interface/StorageFactory.swift +++ b/Projects/Features/StorageFeature/interface/StorageFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol StorageFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/TeamFeature/Interface/TeamInfoFactory.swift b/Projects/Features/TeamFeature/Interface/TeamInfoFactory.swift index 82a3b2b09..84f145422 100644 --- a/Projects/Features/TeamFeature/Interface/TeamInfoFactory.swift +++ b/Projects/Features/TeamFeature/Interface/TeamInfoFactory.swift @@ -1,5 +1,6 @@ import UIKit +@MainActor public protocol TeamInfoFactory { func makeView() -> UIViewController } diff --git a/Projects/Features/TeamFeature/Sources/ViewControllers/TeamInfoViewController.swift b/Projects/Features/TeamFeature/Sources/ViewControllers/TeamInfoViewController.swift index b24112176..f6adfafca 100644 --- a/Projects/Features/TeamFeature/Sources/ViewControllers/TeamInfoViewController.swift +++ b/Projects/Features/TeamFeature/Sources/ViewControllers/TeamInfoViewController.swift @@ -170,7 +170,7 @@ private extension TeamInfoViewController { } } -extension TeamInfoViewController: PageboyViewControllerDataSource, TMBarDataSource { +extension TeamInfoViewController: @preconcurrency PageboyViewControllerDataSource, @preconcurrency TMBarDataSource { public func barItem(for bar: TMBar, at index: Int) -> TMBarItemable { return TMBarItem(title: output.teams.value[index]) } diff --git a/Projects/Features/TeamFeature/Sources/Views/TeamInfoSectionView.swift b/Projects/Features/TeamFeature/Sources/Views/TeamInfoSectionView.swift index 181a37bc1..1aab07f2f 100644 --- a/Projects/Features/TeamFeature/Sources/Views/TeamInfoSectionView.swift +++ b/Projects/Features/TeamFeature/Sources/Views/TeamInfoSectionView.swift @@ -2,6 +2,7 @@ import DesignSystem import UIKit import Utility +@MainActor protocol TeamInfoSectionViewDelegate: AnyObject { func toggleSection(header: TeamInfoSectionView, section: Int) } diff --git a/Projects/Modules/KeychainModule/Sources/Keychain.swift b/Projects/Modules/KeychainModule/Sources/Keychain.swift index 1d016e50f..3134385d3 100644 --- a/Projects/Modules/KeychainModule/Sources/Keychain.swift +++ b/Projects/Modules/KeychainModule/Sources/Keychain.swift @@ -1,11 +1,11 @@ -public enum KeychainType: String { +public enum KeychainType: String, Sendable { case accessToken = "ACCESS-TOKEN" case refreshToken = "REFRESH-TOKEN" case accessExpiresIn = "ACCESS-EXPIRES-IN" // ํ† ํฐ์ด ๋งŒ๋ฃŒ๋  ์‹œ๊ฐ„์„ Miliseconds(timestamp)๋กœ ์ €์žฅํ•ด์š” case deviceID = "DEVICE-ID" } -public protocol Keychain { +public protocol Keychain: Sendable { func save(type: KeychainType, value: String) func load(type: KeychainType) -> String func delete(type: KeychainType) diff --git a/Projects/Modules/KeychainModule/Sources/KeychainFake.swift b/Projects/Modules/KeychainModule/Sources/KeychainFake.swift index cda52e1f3..ec8c21d1d 100644 --- a/Projects/Modules/KeychainModule/Sources/KeychainFake.swift +++ b/Projects/Modules/KeychainModule/Sources/KeychainFake.swift @@ -1,6 +1,6 @@ import Foundation -final class KeychainFake: Keychain { +final class KeychainFake: Keychain, @unchecked Sendable { var store: [String: String] = [:] func save(type: KeychainType, value: String) { diff --git a/Projects/Modules/LogManager/Sources/AnalyticsLogType.swift b/Projects/Modules/LogManager/Sources/AnalyticsLogType.swift index 2ec2917be..f46488cba 100644 --- a/Projects/Modules/LogManager/Sources/AnalyticsLogType.swift +++ b/Projects/Modules/LogManager/Sources/AnalyticsLogType.swift @@ -1,6 +1,6 @@ import Foundation -public protocol AnalyticsLogType { +public protocol AnalyticsLogType: Sendable { var name: String { get } var params: [String: Any] { get } } diff --git a/Projects/Modules/LogManager/Sources/CommonAnalyticsLog.swift b/Projects/Modules/LogManager/Sources/CommonAnalyticsLog.swift index 11ae089f0..dfd13a490 100644 --- a/Projects/Modules/LogManager/Sources/CommonAnalyticsLog.swift +++ b/Projects/Modules/LogManager/Sources/CommonAnalyticsLog.swift @@ -20,7 +20,7 @@ public enum CommonAnalyticsLog: AnalyticsLogType { } public extension CommonAnalyticsLog { - enum PageName: String, AnalyticsLogEnumParametable { + enum PageName: String, AnalyticsLogEnumParametable, Sendable { case home case musicDetail = "music_detail" case musicLyrics = "music_lyrics" @@ -48,7 +48,7 @@ public extension CommonAnalyticsLog { } } - enum PlaylistItemLocation: String, AnalyticsLogEnumParametable { + enum PlaylistItemLocation: String, AnalyticsLogEnumParametable, Sendable { case home case storage case search @@ -59,7 +59,7 @@ public extension CommonAnalyticsLog { } } - enum PlayButtonLocation: String, AnalyticsLogEnumParametable { + enum PlayButtonLocation: String, AnalyticsLogEnumParametable, Sendable { case home case search case artist @@ -73,7 +73,7 @@ public extension CommonAnalyticsLog { case creditSongList = "credit_song_list" } - enum PlayButtonType: String, AnalyticsLogEnumParametable { + enum PlayButtonType: String, AnalyticsLogEnumParametable, Sendable { case single case multiple case all @@ -83,7 +83,7 @@ public extension CommonAnalyticsLog { case playlist } - enum AddMusicLocation: String, AnalyticsLogEnumParametable { + enum AddMusicLocation: String, AnalyticsLogEnumParametable, Sendable { case songDetail = "song_detail" case search case chart @@ -94,14 +94,14 @@ public extension CommonAnalyticsLog { case storageLike = "storage_like" } - enum EditButtonLocation: String, AnalyticsLogEnumParametable { + enum EditButtonLocation: String, AnalyticsLogEnumParametable, Sendable { case playlistDetail = "playlist_detail" case myPlaylist = "my_playlist" case storageLike = "storage_like" case playlist } - enum LoginButtonEntry: String, AnalyticsLogEnumParametable { + enum LoginButtonEntry: String, AnalyticsLogEnumParametable, Sendable { case myPlaylist = "my_playlist" case storageLike = "storage_like" case mypage diff --git a/Projects/Modules/LogManager/Sources/LogHistory/LogHistorySectionItem.swift b/Projects/Modules/LogManager/Sources/LogHistory/LogHistorySectionItem.swift index 616e7f704..de0c08d62 100644 --- a/Projects/Modules/LogManager/Sources/LogHistory/LogHistorySectionItem.swift +++ b/Projects/Modules/LogManager/Sources/LogHistory/LogHistorySectionItem.swift @@ -1,7 +1,7 @@ #if DEBUG || QA import Foundation - struct LogHistorySectionItem: Hashable, Equatable { + struct LogHistorySectionItem: Hashable, Equatable, Sendable { let index: Int let log: any AnalyticsLogType diff --git a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift index 0b3d8f41d..003317aeb 100644 --- a/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift +++ b/Projects/Modules/LogManager/Sources/LogHistory/LogHistoryStorage.swift @@ -1,12 +1,15 @@ #if DEBUG || QA import Foundation - final class LogHistoryStorage { + final class LogHistoryStorage: @unchecked Sendable { static let shared = LogHistoryStorage() + private let lock = NSRecursiveLock() private(set) var logHistory: [any AnalyticsLogType] = [] func appendHistory(log: any AnalyticsLogType) { + lock.lock() + defer { lock.unlock() } logHistory.append(log) } } diff --git a/Projects/Modules/Utility/Sources/Enums/WMToastOptions.swift b/Projects/Modules/Utility/Sources/Enums/WMToastOptions.swift index a45ba220f..0fbb08909 100644 --- a/Projects/Modules/Utility/Sources/Enums/WMToastOptions.swift +++ b/Projects/Modules/Utility/Sources/Enums/WMToastOptions.swift @@ -1,6 +1,6 @@ import Foundation -public struct WMToastOptions: OptionSet { +public struct WMToastOptions: OptionSet, Sendable { public let rawValue: Int public static let empty = WMToastOptions(rawValue: 1 << 0) public static let tabBar = WMToastOptions(rawValue: 1 << 1) diff --git a/Projects/Modules/Utility/Sources/Enums/YoutubePlayType.swift b/Projects/Modules/Utility/Sources/Enums/YoutubePlayType.swift index 2743af89d..1d4c47c72 100644 --- a/Projects/Modules/Utility/Sources/Enums/YoutubePlayType.swift +++ b/Projects/Modules/Utility/Sources/Enums/YoutubePlayType.swift @@ -1,6 +1,6 @@ import Foundation -public enum YoutubePlayType: Codable { +public enum YoutubePlayType: Codable, Sendable { case youtube case youtubeMusic diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+AVAsset.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+AVAsset.swift deleted file mode 100644 index 8998ae1ae..000000000 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+AVAsset.swift +++ /dev/null @@ -1,20 +0,0 @@ -import AVKit -import Foundation - -public extension AVAsset { - func generateThumbnail(completion: @escaping (UIImage?) -> Void) { - DispatchQueue.global().async { - let imageGenerator = AVAssetImageGenerator(asset: self) - let time = CMTime(seconds: 0.0, preferredTimescale: 600) - let times = [NSValue(time: time)] - - imageGenerator.generateCGImagesAsynchronously(forTimes: times, completionHandler: { _, image, _, _, _ in - if let image = image { - completion(UIImage(cgImage: image)) - } else { - completion(nil) - } - }) - } - } -} diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+CALayer.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+CALayer.swift index 97b828b0e..7d37b063a 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+CALayer.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+CALayer.swift @@ -23,6 +23,7 @@ public extension CALayer { UIRectEdge.right, //์˜ค๋ฅธ์ชฝ */ + @MainActor func addBorder(_ edges: [UIRectEdge], color: UIColor, height: CGFloat) { for edge in edges { let border = CALayer() @@ -56,6 +57,7 @@ public extension CALayer { /// - y: ๊ทธ๋ฆผ์ž ์œ„์น˜ ์กฐ์ •(pt๋‹จ์œ„) ex) 0 /// - blur: ํ”ผ๊ทธ๋งˆ ๊ธฐ์ค€ blur ๊ฐ’(pt๋‹จ์œ„) ex) 20 /// - spread: ํ”ผ๊ทธ๋งˆ ๊ธฐ์ค€ spread ๊ฐ’(pt๋‹จ์œ„) ex) 20 + @MainActor func addShadow( color: UIColor, alpha: Float, diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+Double.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+Double.swift index 12ec0515f..2070795db 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+Double.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+Double.swift @@ -18,18 +18,22 @@ public extension Double { return megabytes / 1024 } + @MainActor var correctLeading: CGFloat { return self * APP_WIDTH() / 375.0 } + @MainActor var correctTrailing: CGFloat { return -self * APP_WIDTH() / 375.0 } + @MainActor var correctTop: CGFloat { return APP_HEIGHT() * (self / 812.0) } + @MainActor var correctBottom: CGFloat { return -APP_HEIGHT() * (self / 812.0) } diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+PreferenceManager.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+PreferenceManager.swift index c319761e4..84e7ffafb 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+PreferenceManager.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+PreferenceManager.swift @@ -16,7 +16,7 @@ public extension PreferenceManager { /// - Parameter word: ์ตœ๊ทผ ๊ฒ€์ƒ‰์–ด func addRecentRecords(word: String) { let maxSize: Int = 10 - var currentRecentRecords = Utility.PreferenceManager.recentRecords ?? [] + var currentRecentRecords = Utility.PreferenceManager.shared.recentRecords ?? [] if currentRecentRecords.contains(word) { if let i = currentRecentRecords.firstIndex(where: { $0 == word }) { @@ -31,19 +31,19 @@ public extension PreferenceManager { currentRecentRecords.insert(word, at: 0) } - Utility.PreferenceManager.recentRecords = currentRecentRecords + Utility.PreferenceManager.shared.recentRecords = currentRecentRecords } /// ์ตœ๊ทผ ๊ฒ€์ƒ‰์–ด๋ฅผ ์‚ญ์ œ /// - Parameter word: ์ตœ๊ทผ ๊ฒ€์ƒ‰์–ด func removeRecentRecords(word: String) { - var currentRecentRecords = Utility.PreferenceManager.recentRecords ?? [] + var currentRecentRecords = Utility.PreferenceManager.shared.recentRecords ?? [] if let i = currentRecentRecords.firstIndex(where: { $0 == word }) { currentRecentRecords.remove(at: i) } - Utility.PreferenceManager.recentRecords = currentRecentRecords + Utility.PreferenceManager.shared.recentRecords = currentRecentRecords } /// ์œ ์ € ์ •๋ณด ์ €์žฅ @@ -61,7 +61,7 @@ public extension PreferenceManager { name: AES256.encrypt(string: name), itemCount: itemCount ) - Utility.PreferenceManager.userInfo = userInfo + Utility.PreferenceManager.shared.userInfo = userInfo LogManager.setUserProperty(property: .fruitTotal(count: userInfo.itemCount)) LogManager.setUserProperty(property: .loginPlatform(platform: userInfo.platform)) } @@ -69,7 +69,7 @@ public extension PreferenceManager { static func clearUserInfo() { LogManager.setUserID(userID: nil) Crashlytics.crashlytics().setUserID(nil) - PreferenceManager.userInfo = nil + PreferenceManager.shared.userInfo = nil LogManager.clearUserProperty(property: .fruitTotal(count: -1)) LogManager.clearUserProperty(property: .loginPlatform(platform: "")) } diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+UIControl.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+UIControl.swift index 2e4411da6..b4eb6c12b 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+UIControl.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+UIControl.swift @@ -10,13 +10,15 @@ import Combine import Foundation import UIKit +@MainActor extension UIControl { func controlPublisher(for event: UIControl.Event) -> UIControl.EventPublisher { return UIControl.EventPublisher(control: self, event: event) } /// Publisher - struct EventPublisher: Publisher { + @MainActor + struct EventPublisher: @preconcurrency Publisher { typealias Output = UIControl typealias Failure = Never @@ -30,7 +32,8 @@ extension UIControl { } /// Subscription - fileprivate class EventSubscription: Subscription + @MainActor + fileprivate class EventSubscription: Subscription, Sendable where EventSubscriber.Input == UIControl, EventSubscriber.Failure == Never { let control: UIControl let event: UIControl.Event @@ -44,11 +47,20 @@ extension UIControl { control.addTarget(self, action: #selector(eventDidOccur), for: event) } - func request(_ demand: Subscribers.Demand) {} + nonisolated func request(_ demand: Subscribers.Demand) {} - func cancel() { - subscriber = nil - control.removeTarget(self, action: #selector(eventDidOccur), for: event) + nonisolated func cancel() { + if Thread.isMainThread { + MainActor.assumeIsolated { + subscriber = nil + control.removeTarget(self, action: #selector(eventDidOccur), for: event) + } + } else { + Task { @MainActor in + subscriber = nil + control.removeTarget(self, action: #selector(eventDidOccur), for: event) + } + } } @objc func eventDidOccur() { diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+UIPanGestureRecognizer.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+UIPanGestureRecognizer.swift index 0c8b8a821..719d0e95f 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+UIPanGestureRecognizer.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+UIPanGestureRecognizer.swift @@ -10,7 +10,7 @@ import Foundation import UIKit public extension UIPanGestureRecognizer { - struct PanGestureDirection: OptionSet { + struct PanGestureDirection: OptionSet, Sendable { public let rawValue: UInt8 public init(rawValue: UInt8) { diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+UIStackView.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+UIStackView.swift index b6b52debe..00f5b98e9 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+UIStackView.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+UIStackView.swift @@ -2,6 +2,6 @@ import UIKit public extension UIStackView { func addArrangedSubviews(_ views: UIView...) { - views.forEach(self.addArrangedSubview(_:)) + views.forEach { self.addArrangedSubview($0) } } } diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+UITapGesutreRecognizer.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+UITapGesutreRecognizer.swift index ebf4b6cee..f9bfaaeae 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+UITapGesutreRecognizer.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+UITapGesutreRecognizer.swift @@ -11,8 +11,8 @@ import Foundation import UIKit /// UIControl ์„ ์ƒ์†๋ฐ›์ง€ ์•Š๋Š” UIView ๋“ฑ์˜ ์ด๋ฒคํŠธ๋ฅผ Combine ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ Publisher ์ž…๋‹ˆ๋‹ค. -public extension UITapGestureRecognizer { - struct GesturePublisher: Publisher { +extension UITapGestureRecognizer { + struct GesturePublisher: @preconcurrency Publisher { public typealias Output = TapRecognizer public typealias Failure = Never @@ -24,6 +24,7 @@ public extension UITapGestureRecognizer { self.view = view } + @MainActor public func receive(subscriber: S) where S: Subscriber, Never == S.Failure, TapRecognizer == S.Input { let subscription = GestureSubscription( subscriber: subscriber, @@ -34,6 +35,7 @@ public extension UITapGestureRecognizer { } } + @MainActor final class GestureSubscription: Subscription where S.Input == TapRecognizer { private var subscriber: S? @@ -46,10 +48,18 @@ public extension UITapGestureRecognizer { view.addGestureRecognizer(recognizer) } - public func request(_ demand: Subscribers.Demand) {} + public nonisolated func request(_ demand: Subscribers.Demand) {} - public func cancel() { - subscriber = nil + public nonisolated func cancel() { + if Thread.isMainThread { + MainActor.assumeIsolated { + subscriber = nil + } + } else { + Task { @MainActor in + subscriber = nil + } + } } @objc func eventHandler() { diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+UIView.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+UIView.swift index cc153cec1..15c524d0f 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+UIView.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+UIView.swift @@ -110,7 +110,7 @@ public extension UIView { } func addSubviews(_ views: UIView...) { - views.forEach(self.addSubview(_:)) + views.forEach { self.addSubview($0) } } var asImage: UIImage { diff --git a/Projects/Modules/Utility/Sources/GoogleOAuth/GoogleLoginManager.swift b/Projects/Modules/Utility/Sources/GoogleOAuth/GoogleLoginManager.swift index 278f28367..0230ba6ad 100644 --- a/Projects/Modules/Utility/Sources/GoogleOAuth/GoogleLoginManager.swift +++ b/Projects/Modules/Utility/Sources/GoogleOAuth/GoogleLoginManager.swift @@ -25,7 +25,7 @@ public enum WMGoogleError: Error { case internalError } -public class GoogleLoginManager { +public class GoogleLoginManager: @unchecked Sendable { // MARK: - ๋ณ€์ˆ˜ ์„ ์–ธ private let googleURL = "https://accounts.google.com/o/oauth2/v2/auth" private let accessTokenGoogleURL = "https://oauth2.googleapis.com" @@ -52,10 +52,12 @@ public class GoogleLoginManager { components?.queryItems = [scope, responseType, codeChallenge, redirectURI, clientID, codeChallengeMethod] - if let url = components?.url, UIApplication.shared.canOpenURL(url) { - LogManager.printDebug(url) - let safari = SFSafariViewController(url: url) - UIApplication.topVisibleViewController()?.present(safari, animated: true) + Task { @MainActor in + if let url = components?.url, UIApplication.shared.canOpenURL(url) { + LogManager.printDebug(url) + let safari = SFSafariViewController(url: url) + UIApplication.topVisibleViewController()?.present(safari, animated: true) + } } } diff --git a/Projects/Modules/Utility/Sources/Manager/HapticManager.swift b/Projects/Modules/Utility/Sources/Manager/HapticManager.swift index bb842bd6f..d3294efa3 100644 --- a/Projects/Modules/Utility/Sources/Manager/HapticManager.swift +++ b/Projects/Modules/Utility/Sources/Manager/HapticManager.swift @@ -19,7 +19,8 @@ import UIKit // HapticManager.shared.impact(style: .rigid) // HapticManager.shared.impact(style: .soft) -public class HapticManager { +@MainActor +public class HapticManager: Sendable { public static let shared = HapticManager() public func notification(type: UINotificationFeedbackGenerator.FeedbackType) { diff --git a/Projects/Modules/Utility/Sources/Manager/PreferenceManager.swift b/Projects/Modules/Utility/Sources/Manager/PreferenceManager.swift index c3f1df5e6..d2f4a189b 100644 --- a/Projects/Modules/Utility/Sources/Manager/PreferenceManager.swift +++ b/Projects/Modules/Utility/Sources/Manager/PreferenceManager.swift @@ -10,7 +10,7 @@ import Foundation import RxSwift /// UserDefaults์— ํŽธ๋ฆฌํ•˜๊ฒŒ ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค ์ •์˜ -public final class PreferenceManager { +public final class PreferenceManager: @unchecked Sendable { public static let shared: PreferenceManager = PreferenceManager() /// UserDefaults์— ์ €์žฅ ๋œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ํ‚ค ๊ฐ’์˜ ๋‚˜์—ด. @@ -26,38 +26,57 @@ public final class PreferenceManager { } @UserDefaultWrapper(key: Constants.recentRecords.rawValue, defaultValue: nil) - public static var recentRecords: [String]? + public var recentRecords: [String]? @UserDefaultWrapper(key: Constants.startPage.rawValue, defaultValue: nil) - public static var startPage: Int? + public var startPage: Int? @UserDefaultWrapper(key: Constants.user.rawValue, defaultValue: nil) - public static var userInfo: UserInfo? + public var userInfo: UserInfo? @UserDefaultWrapper(key: Constants.appPermissionChecked.rawValue, defaultValue: nil) - public static var appPermissionChecked: Bool? + public var appPermissionChecked: Bool? @UserDefaultWrapper(key: Constants.ignoredPopupIDs.rawValue, defaultValue: nil) - public static var ignoredPopupIDs: [Int]? + public var ignoredPopupIDs: [Int]? @UserDefaultWrapper(key: Constants.readNoticeIDs.rawValue, defaultValue: nil) - public static var readNoticeIDs: [Int]? + public var readNoticeIDs: [Int]? @UserDefaultWrapper(key: Constants.pushNotificationAuthorizationStatus.rawValue, defaultValue: nil) - public static var pushNotificationAuthorizationStatus: Bool? + public var pushNotificationAuthorizationStatus: Bool? @UserDefaultWrapper(key: Constants.songPlayPlatformType.rawValue, defaultValue: YoutubePlayType.youtube) - public static var songPlayPlatformType: YoutubePlayType? + public var songPlayPlatformType: YoutubePlayType? } +/// BehaviorSubject๊ฐ€ Sendable์„ ์ฑ„ํƒํ•˜์ง€ ์•Š์•„ @unchecked @propertyWrapper -public final class UserDefaultWrapper { +public final class UserDefaultWrapper: @unchecked Sendable { + private let lock = NSLock() private let key: String private let defaultValue: T? + private let subject: BehaviorSubject init(key: String, defaultValue: T?) { self.key = key self.defaultValue = defaultValue + + let initialValue: T? + if let savedData = UserDefaults.standard.object(forKey: key) as? Data { + let decoder = JSONDecoder() + if let lodedObejct = try? decoder.decode(T.self, from: savedData) { + initialValue = lodedObejct + } else { + initialValue = nil + } + + } else if UserDefaults.standard.array(forKey: key) != nil { + initialValue = UserDefaults.standard.array(forKey: key) as? T + } else { + initialValue = defaultValue + } + self.subject = .init(value: initialValue) } public var wrappedValue: T? { @@ -82,7 +101,6 @@ public final class UserDefaultWrapper { } } - private lazy var subject = BehaviorSubject(value: wrappedValue) public var projectedValue: Observable { return subject.asObservable() } diff --git a/Projects/Modules/Utility/Sources/Manager/UserInfoManager.swift b/Projects/Modules/Utility/Sources/Manager/UserInfoManager.swift index ec3b9a44f..1b9f3561b 100644 --- a/Projects/Modules/Utility/Sources/Manager/UserInfoManager.swift +++ b/Projects/Modules/Utility/Sources/Manager/UserInfoManager.swift @@ -8,7 +8,7 @@ import Foundation -public struct UserInfo: Codable, Equatable { +public struct UserInfo: Codable, Equatable, Sendable { public let ID: String public let platform: String public let profile: String diff --git a/Projects/Modules/Utility/Sources/Player/WakmusicPlayer.swift b/Projects/Modules/Utility/Sources/Player/WakmusicPlayer.swift index 7963e92a5..331ba4caf 100644 --- a/Projects/Modules/Utility/Sources/Player/WakmusicPlayer.swift +++ b/Projects/Modules/Utility/Sources/Player/WakmusicPlayer.swift @@ -1,3 +1,4 @@ -public protocol WakmusicPlayer { +@MainActor +public protocol WakmusicPlayer: Sendable { func play() } diff --git a/Projects/Modules/Utility/Sources/Player/WakmusicYoutubePlayer.swift b/Projects/Modules/Utility/Sources/Player/WakmusicYoutubePlayer.swift index bbe42c8d2..ce2962c77 100644 --- a/Projects/Modules/Utility/Sources/Player/WakmusicYoutubePlayer.swift +++ b/Projects/Modules/Utility/Sources/Player/WakmusicYoutubePlayer.swift @@ -1,8 +1,9 @@ import Foundation -import LinkPresentation +@preconcurrency import LinkPresentation import UIKit -public struct WakmusicYoutubePlayer: WakmusicPlayer { +@MainActor +public struct WakmusicYoutubePlayer: WakmusicPlayer, Sendable { fileprivate enum OpenerPlatform { case youtube case youtubeMusic @@ -59,6 +60,7 @@ public struct WakmusicYoutubePlayer: WakmusicPlayer { self.youtubeURLGenerator = youtubeURLGenerator } + @MainActor public func play() { switch youtubeVideoType { case let .videos(ids): @@ -209,7 +211,7 @@ private extension WakmusicYoutubePlayer.PlayPlatform { switch self { case .youtube: return .youtube case .youtubeMusic: return .youtubeMusic - case .automatic: return PreferenceManager.songPlayPlatformType?.toOpnerPlatform ?? .youtube + case .automatic: return PreferenceManager.shared.songPlayPlatformType?.toOpnerPlatform ?? .youtube } } } diff --git a/Projects/Modules/Utility/Sources/Protocols/ContainerViewType.swift b/Projects/Modules/Utility/Sources/Protocols/ContainerViewType.swift index f77415e25..00a99af78 100644 --- a/Projects/Modules/Utility/Sources/Protocols/ContainerViewType.swift +++ b/Projects/Modules/Utility/Sources/Protocols/ContainerViewType.swift @@ -10,10 +10,12 @@ import Foundation import SnapKit import UIKit +// FIXME: concurrency๋ฅผ ์œ„ํ•ด mutable ํ”„๋กœํผํ‹ฐ ๋ฆฌํŒฉํ† ๋ง ํ•„์š” ใ…œใ…œใ…œ public protocol ContainerViewType { var contentView: UIView! { get set } } +@MainActor public extension ContainerViewType where Self: UIViewController { func add(asChildViewController viewController: UIViewController?) { guard let viewController = viewController else { diff --git a/Projects/Modules/Utility/Sources/Protocols/ViewControllerFromStoryBoard.swift b/Projects/Modules/Utility/Sources/Protocols/ViewControllerFromStoryBoard.swift index 0d2cc9e14..3892a9966 100644 --- a/Projects/Modules/Utility/Sources/Protocols/ViewControllerFromStoryBoard.swift +++ b/Projects/Modules/Utility/Sources/Protocols/ViewControllerFromStoryBoard.swift @@ -12,6 +12,7 @@ import UIKit public protocol ViewControllerFromStoryBoard {} public protocol ViewControllerFromStoryBoardAndNeedle {} +@MainActor public extension ViewControllerFromStoryBoard where Self: UIViewController { /// ์Šคํ† ๋ฆฌ ๋ณด๋“œ์— ์ •์˜๋œ ํ™”๋ฉด์„ ๊ฐ์ฒดํ™” ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. /// where Self: UIViewController: UIViewController์— ํ•œ์ •ํ•˜์—ฌ ์‚ฌ์šฉ diff --git a/Projects/Modules/Utility/Sources/Realm/RealmManager.swift b/Projects/Modules/Utility/Sources/Realm/RealmManager.swift index c108fd353..e659bda3a 100644 --- a/Projects/Modules/Utility/Sources/Realm/RealmManager.swift +++ b/Projects/Modules/Utility/Sources/Realm/RealmManager.swift @@ -22,9 +22,10 @@ import RealmSwift - ์žฌ์ƒ๋ชฉ๋ก ์ €์žฅ์„ ์œ„ํ•œ ์—”ํ‹ฐํ‹ฐ (๋ฆฌํŒฉํ† ๋ง ๋ฒ„์ „) - ๊ธฐ์กด PlayedList๋Š” ๋ ˆ๊ฑฐ์‹œ๋กœ ํŒ์ •ํ•˜์—ฌ migration ๊ณผ์ •์—์„œ ๋ฐ์ดํ„ฐ ๋ชจ๋‘ ์ œ๊ฑฐ */ -public class RealmManager: NSObject { +/// Realm์ด Sendable์„ ์ฑ„ํƒํ•˜์ง€ ์•Š์•„์„œ @unchecked ํ‘œ์‹œ +public class RealmManager: NSObject, @unchecked Sendable { public static let shared = RealmManager() - private var realm: Realm! + private let realm: Realm override init() { let config = Realm.Configuration( diff --git a/Projects/Modules/Utility/Sources/Utils/Utils.swift b/Projects/Modules/Utility/Sources/Utils/Utils.swift index 6c6e9890b..3ec317573 100644 --- a/Projects/Modules/Utility/Sources/Utils/Utils.swift +++ b/Projects/Modules/Utility/Sources/Utils/Utils.swift @@ -9,11 +9,13 @@ import Foundation import UIKit +@MainActor public func APP_WIDTH() -> CGFloat { let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene return windowScene?.screen.bounds.size.width ?? .zero } +@MainActor public func APP_HEIGHT() -> CGFloat { let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene return windowScene?.screen.bounds.size.height ?? .zero @@ -23,6 +25,7 @@ public func PLAYER_HEIGHT() -> CGFloat { return 56.0 } +@MainActor public func STATUS_BAR_HEGHIT() -> CGFloat { return UIApplication.shared.connectedScenes .compactMap { $0 as? UIWindowScene } @@ -33,6 +36,7 @@ public func STATUS_BAR_HEGHIT() -> CGFloat { .top ?? 0 } +@MainActor public func SAFEAREA_BOTTOM_HEIGHT() -> CGFloat { return UIApplication.shared.connectedScenes .compactMap { $0 as? UIWindowScene } @@ -51,6 +55,7 @@ public func APP_NAME() -> String { return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? "" } +@MainActor public func OS_VERSION() -> String { return UIDevice.current.systemVersion } diff --git a/Projects/Modules/Utility/Sources/Youtube/YoutubeURLGenerator.swift b/Projects/Modules/Utility/Sources/Youtube/YoutubeURLGenerator.swift index ab0ab8927..021b0f771 100644 --- a/Projects/Modules/Utility/Sources/Youtube/YoutubeURLGenerator.swift +++ b/Projects/Modules/Utility/Sources/Youtube/YoutubeURLGenerator.swift @@ -1,6 +1,6 @@ import Foundation -public protocol YoutubeURLGeneratable { +public protocol YoutubeURLGeneratable: Sendable { func generateThumbnailURL(id: String) -> String func generateHDThumbnailURL(id: String) -> String func generateYoutubeVideoAppURL(id: String) -> String @@ -16,7 +16,7 @@ public protocol YoutubeURLGeneratable { func generateYoutubeMusicPlaylistWebURL(id: String) -> String } -public struct YoutubeURLGenerator: YoutubeURLGeneratable { +public struct YoutubeURLGenerator: YoutubeURLGeneratable, Sendable { public init() {} // MARK: Youtube diff --git a/Projects/UsertInterfaces/DesignSystem/Sources/LikeButton.swift b/Projects/UsertInterfaces/DesignSystem/Sources/LikeButton.swift index cf68a824e..a62dbd079 100644 --- a/Projects/UsertInterfaces/DesignSystem/Sources/LikeButton.swift +++ b/Projects/UsertInterfaces/DesignSystem/Sources/LikeButton.swift @@ -9,11 +9,7 @@ import DesignSystem import UIKit -protocol Likeable { - var isLiked: Bool { get set } -} - -class LikeButton: VerticalImageButton, Likeable { +class LikeButton: VerticalImageButton { var isLiked: Bool = false { didSet { setColor() diff --git a/Projects/UsertInterfaces/DesignSystem/Sources/SingleActionButtonView.swift b/Projects/UsertInterfaces/DesignSystem/Sources/SingleActionButtonView.swift index 6e1aeed11..835d7b426 100644 --- a/Projects/UsertInterfaces/DesignSystem/Sources/SingleActionButtonView.swift +++ b/Projects/UsertInterfaces/DesignSystem/Sources/SingleActionButtonView.swift @@ -2,6 +2,7 @@ import SnapKit import Then import UIKit +@MainActor public protocol SingleActionButtonViewDelegate: AnyObject { func tappedButtonAction() } diff --git a/Projects/UsertInterfaces/DesignSystem/Sources/VerticalAlignButton.swift b/Projects/UsertInterfaces/DesignSystem/Sources/VerticalAlignButton.swift index 81d13eb9f..c862645d3 100644 --- a/Projects/UsertInterfaces/DesignSystem/Sources/VerticalAlignButton.swift +++ b/Projects/UsertInterfaces/DesignSystem/Sources/VerticalAlignButton.swift @@ -33,11 +33,6 @@ open class VerticalAlignButton: UIButton { fatalError("init(coder:) has not been implemented") } - override open func awakeFromNib() { - super.awakeFromNib() - self.contentHorizontalAlignment = .left - } - public func setTextColor(color: UIColor) { self.configuration?.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { var attribute = $0 diff --git a/Projects/UsertInterfaces/DesignSystem/Sources/WMNavigationBarView.swift b/Projects/UsertInterfaces/DesignSystem/Sources/WMNavigationBarView.swift index a455f6462..012e95e14 100644 --- a/Projects/UsertInterfaces/DesignSystem/Sources/WMNavigationBarView.swift +++ b/Projects/UsertInterfaces/DesignSystem/Sources/WMNavigationBarView.swift @@ -2,6 +2,7 @@ import SnapKit import Then import UIKit +@MainActor public final class WMNavigationBarView: UIView { private let leftStackView = UIStackView().then { $0.axis = .horizontal @@ -65,7 +66,7 @@ public final class WMNavigationBarView: UIView { $0.removeFromSuperview() rightStackView.removeArrangedSubview($0) } - views.forEach(rightStackView.addArrangedSubview(_:)) + views.forEach { rightStackView.addArrangedSubview($0) } } public func setLeftViews(_ views: [UIView]) { @@ -73,7 +74,7 @@ public final class WMNavigationBarView: UIView { $0.removeFromSuperview() leftStackView.removeArrangedSubview($0) } - views.forEach(leftStackView.addArrangedSubview(_:)) + views.forEach { leftStackView.addArrangedSubview($0) } } } diff --git a/Projects/UsertInterfaces/DesignSystem/Sources/WMRetryWarningView.swift b/Projects/UsertInterfaces/DesignSystem/Sources/WMRetryWarningView.swift index fbaf43b6a..3275bed70 100644 --- a/Projects/UsertInterfaces/DesignSystem/Sources/WMRetryWarningView.swift +++ b/Projects/UsertInterfaces/DesignSystem/Sources/WMRetryWarningView.swift @@ -3,6 +3,7 @@ import SnapKit import Then import UIKit +@MainActor public protocol WMRetryWarningViewDelegate: AnyObject { func tappedRetryButton() } diff --git a/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift b/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift index 306c1dfa2..963b626e9 100644 --- a/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift +++ b/Tuist/ProjectDescriptionHelpers/GenerateEnvironment.swift @@ -21,7 +21,7 @@ public extension GenerateEnvironment { return [.firebaseInfoByConfiguration, .firebaseCrashlytics] case .dev: - return [.swiftLint, .needle] + return [.swiftLint] } } }