From c70a0536d14c361577ba003c53fff94e4ccac5ff Mon Sep 17 00:00:00 2001 From: baegteun Date: Sun, 22 Sep 2024 12:57:24 +0900 Subject: [PATCH 1/6] =?UTF-8?q?:art:=20::=20[#1296]=20SwiftLint=20error=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../After/ViewControllers/ListSearchResultViewController.swift | 2 +- .../ViewControllers/BeforeSearchContentViewController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift b/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift index f88933871..42c4e97c1 100644 --- a/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift +++ b/Projects/Features/SearchFeature/Sources/After/ViewControllers/ListSearchResultViewController.swift @@ -18,7 +18,7 @@ final class ListSearchResultViewController: BaseReactorViewController, PlaylistDetailNavigator { private let wakmusicRecommendComponent: WakmusicRecommendComponent private let textPopupFactory: TextPopupFactory - private (set) var playlistDetailFactory: any PlaylistDetailFactory + private(set) var playlistDetailFactory: any PlaylistDetailFactory private let tableView: UITableView = UITableView().then { $0.register(RecentRecordTableViewCell.self, forCellReuseIdentifier: "RecentRecordTableViewCell") From 702f01d5616641e0f155e84a6d543318a420f3ab Mon Sep 17 00:00:00 2001 From: baegteun Date: Sun, 22 Sep 2024 13:07:41 +0900 Subject: [PATCH 2/6] :sparkles: :: [#1296] UniquedSequence --- .../Extensions/Extension+Sequence.swift | 33 ++++++++++++ .../Sources/Utils/UniquedSequence.swift | 54 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 Projects/Modules/Utility/Sources/Extensions/Extension+Sequence.swift create mode 100644 Projects/Modules/Utility/Sources/Utils/UniquedSequence.swift diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+Sequence.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+Sequence.swift new file mode 100644 index 000000000..2d60714e9 --- /dev/null +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+Sequence.swift @@ -0,0 +1,33 @@ +import Foundation + +extension Sequence where Element: Hashable { + @inlinable + public func uniqued() -> UniquedSequence { + UniquedSequence(base: self, projection: { $0 }) + } +} + +extension Sequence { + @inlinable + public func uniqued( + on projection: (Element) throws -> Subject + ) rethrows -> [Element] { + var seen: Set = [] + var result: [Element] = [] + for element in self { + if seen.insert(try projection(element)).inserted { + result.append(element) + } + } + return result + } +} + +extension LazySequenceProtocol { + @inlinable + public func uniqued( + on projection: @escaping (Element) -> Subject + ) -> UniquedSequence { + UniquedSequence(base: self, projection: projection) + } +} diff --git a/Projects/Modules/Utility/Sources/Utils/UniquedSequence.swift b/Projects/Modules/Utility/Sources/Utils/UniquedSequence.swift new file mode 100644 index 000000000..1616cc018 --- /dev/null +++ b/Projects/Modules/Utility/Sources/Utils/UniquedSequence.swift @@ -0,0 +1,54 @@ +import Foundation + +public struct UniquedSequence { + @usableFromInline + internal let base: Base + + @usableFromInline + internal let projection: (Base.Element) -> Subject + + @usableFromInline + internal init(base: Base, projection: @escaping (Base.Element) -> Subject) { + self.base = base + self.projection = projection + } +} + +extension UniquedSequence: Sequence { + public struct Iterator: IteratorProtocol { + @usableFromInline + internal var base: Base.Iterator + + @usableFromInline + internal let projection: (Base.Element) -> Subject + + @usableFromInline + internal var seen: Set = [] + + @usableFromInline + internal init( + base: Base.Iterator, + projection: @escaping (Base.Element) -> Subject + ) { + self.base = base + self.projection = projection + } + + @inlinable + public mutating func next() -> Base.Element? { + while let element = base.next() { + if seen.insert(projection(element)).inserted { + return element + } + } + return nil + } + } + + @inlinable + public func makeIterator() -> Iterator { + Iterator(base: base.makeIterator(), projection: projection) + } +} + +extension UniquedSequence: LazySequenceProtocol where Base: LazySequenceProtocol {} From 6da91646344f77c1dd77833b731b47895bcfec58 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sun, 22 Sep 2024 13:08:03 +0900 Subject: [PATCH 3/6] =?UTF-8?q?:recycle:=20::=20[#1296]=20MyPlaylist=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=A4=91=EB=B3=B5=EA=B3=A1=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reactors/MyPlaylistDetailReactor.swift | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/Projects/Features/PlaylistFeature/Sources/Reactors/MyPlaylistDetailReactor.swift b/Projects/Features/PlaylistFeature/Sources/Reactors/MyPlaylistDetailReactor.swift index b20103107..8a86c9d9f 100644 --- a/Projects/Features/PlaylistFeature/Sources/Reactors/MyPlaylistDetailReactor.swift +++ b/Projects/Features/PlaylistFeature/Sources/Reactors/MyPlaylistDetailReactor.swift @@ -208,6 +208,8 @@ private extension MyPlaylistDetailReactor { fetchPlaylistDetailUseCase.execute(id: key, type: .my) .asObservable() .flatMap { data -> Observable in + let songs = data.songs.map { PlaylistItemModel(id: $0.id, title: $0.title, artist: $0.artist, isSelected: false) } + .uniqued() return .concat([ Observable.just(Mutation.updateHeader( PlaylistDetailHeaderModel( @@ -220,28 +222,10 @@ private extension MyPlaylistDetailReactor { ) )), Observable.just( - Mutation.updatePlaylist( - data.songs.map { - PlaylistItemModel( - id: $0.id, - title: $0.title, - artist: $0.artist, - isSelected: false - ) - } - ) + Mutation.updatePlaylist(Array(songs)) ), Observable.just( - Mutation.updateBackUpPlaylist( - data.songs.map { - PlaylistItemModel( - id: $0.id, - title: $0.title, - artist: $0.artist, - isSelected: false - ) - } - ) + Mutation.updateBackUpPlaylist(Array(songs)) ) ]) } From d3895ccbfd94bcd08f9c749b18e3dc29d36099a5 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sun, 22 Sep 2024 13:09:58 +0900 Subject: [PATCH 4/6] =?UTF-8?q?:recycle:=20::=20[#1296]=20UnoknownPlaylist?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=20=EC=A4=91=EB=B3=B5=EA=B3=A1=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Reactors/UnknownPlaylistDetailReactor.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Projects/Features/PlaylistFeature/Sources/Reactors/UnknownPlaylistDetailReactor.swift b/Projects/Features/PlaylistFeature/Sources/Reactors/UnknownPlaylistDetailReactor.swift index 9a071ee81..82e552dc4 100644 --- a/Projects/Features/PlaylistFeature/Sources/Reactors/UnknownPlaylistDetailReactor.swift +++ b/Projects/Features/PlaylistFeature/Sources/Reactors/UnknownPlaylistDetailReactor.swift @@ -170,6 +170,7 @@ private extension UnknownPlaylistDetailReactor { .asObservable() .withUnretained(self) .flatMap { owner, data -> Observable in + let songs = data.songs.uniqued() return .concat([ Observable.just(Mutation.updateHeader( PlaylistDetailHeaderModel( @@ -181,7 +182,9 @@ private extension UnknownPlaylistDetailReactor { songCount: data.songs.count ) )), - Observable.just(Mutation.updateDataSource(data.songs)), + Observable.just( + Mutation.updateDataSource(Array(songs)) + ), PreferenceManager.userInfo == nil ? .just(.updateSubscribeState(false)) : owner .checkSubscription() ]) From ef56130e8b3f07b6f80d12654d4a9bdb007b7acd Mon Sep 17 00:00:00 2001 From: baegteun Date: Sun, 22 Sep 2024 13:58:55 +0900 Subject: [PATCH 5/6] =?UTF-8?q?:recycle:=20::=20[#1296]=20=EC=9E=AC?= =?UTF-8?q?=EC=83=9D=EB=AA=A9=EB=A1=9D=20=EC=A4=91=EB=B3=B5=EA=B3=A1=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift | 2 +- .../BaseFeature/Sources/Etc/PlayState/PlaylistItem.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift b/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift index fd0cf6938..7a844c459 100644 --- a/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift +++ b/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlayState.swift @@ -19,7 +19,7 @@ public final class PlayState { private var subscription = Set() public var count: Int { playlist.count } public var isEmpty: Bool { playlist.isEmpty } - public var currentPlaylist: [PlaylistItem] { playlist.list } + public var currentPlaylist: [PlaylistItem] { Array(playlist.list.uniqued()) } public var listChangedPublisher: AnyPublisher<[PlaylistItem], Never> { playlist.subscribeListChanges() } private init() { diff --git a/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlaylistItem.swift b/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlaylistItem.swift index 96e603776..1f96e2df6 100644 --- a/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlaylistItem.swift +++ b/Projects/Features/BaseFeature/Sources/Etc/PlayState/PlaylistItem.swift @@ -1,7 +1,7 @@ import Foundation import SongsDomainInterface -public struct PlaylistItem: Equatable { +public struct PlaylistItem: Equatable, Hashable { public let id: String public let title: String public let artist: String From 4f0f8bb73d3b15323505328e12222d26df8257e2 Mon Sep 17 00:00:00 2001 From: baegteun Date: Sun, 22 Sep 2024 13:59:04 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=8E=A8=20::=20=EC=BD=94=EB=93=9C=20Fo?= =?UTF-8?q?rmatting=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Reactors/MyPlaylistDetailReactor.swift | 9 +++++++-- .../Sources/Extensions/Extension+Sequence.swift | 14 +++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Projects/Features/PlaylistFeature/Sources/Reactors/MyPlaylistDetailReactor.swift b/Projects/Features/PlaylistFeature/Sources/Reactors/MyPlaylistDetailReactor.swift index 8a86c9d9f..deb8b2603 100644 --- a/Projects/Features/PlaylistFeature/Sources/Reactors/MyPlaylistDetailReactor.swift +++ b/Projects/Features/PlaylistFeature/Sources/Reactors/MyPlaylistDetailReactor.swift @@ -208,8 +208,13 @@ private extension MyPlaylistDetailReactor { fetchPlaylistDetailUseCase.execute(id: key, type: .my) .asObservable() .flatMap { data -> Observable in - let songs = data.songs.map { PlaylistItemModel(id: $0.id, title: $0.title, artist: $0.artist, isSelected: false) } - .uniqued() + let songs = data.songs.map { PlaylistItemModel( + id: $0.id, + title: $0.title, + artist: $0.artist, + isSelected: false + ) } + .uniqued() return .concat([ Observable.just(Mutation.updateHeader( PlaylistDetailHeaderModel( diff --git a/Projects/Modules/Utility/Sources/Extensions/Extension+Sequence.swift b/Projects/Modules/Utility/Sources/Extensions/Extension+Sequence.swift index 2d60714e9..4ee95c90a 100644 --- a/Projects/Modules/Utility/Sources/Extensions/Extension+Sequence.swift +++ b/Projects/Modules/Utility/Sources/Extensions/Extension+Sequence.swift @@ -1,21 +1,21 @@ import Foundation -extension Sequence where Element: Hashable { +public extension Sequence where Element: Hashable { @inlinable - public func uniqued() -> UniquedSequence { + func uniqued() -> UniquedSequence { UniquedSequence(base: self, projection: { $0 }) } } -extension Sequence { +public extension Sequence { @inlinable - public func uniqued( + func uniqued( on projection: (Element) throws -> Subject ) rethrows -> [Element] { var seen: Set = [] var result: [Element] = [] for element in self { - if seen.insert(try projection(element)).inserted { + if try seen.insert(projection(element)).inserted { result.append(element) } } @@ -23,9 +23,9 @@ extension Sequence { } } -extension LazySequenceProtocol { +public extension LazySequenceProtocol { @inlinable - public func uniqued( + func uniqued( on projection: @escaping (Element) -> Subject ) -> UniquedSequence { UniquedSequence(base: self, projection: projection)