Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[iOS] 이벤트가 발생할 NowPlaying 관련 기능 구현 #348 #365

Merged
merged 10 commits into from
Dec 10, 2020
18 changes: 13 additions & 5 deletions MiniVibe/MiniVibe.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
44F9797C256D04EA00C9C224 /* RecommandedPlayListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44F9797B256D04EA00C9C224 /* RecommandedPlayListItem.swift */; };
44F9797F256D069600C9C224 /* RecommandedPlayListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44F9797E256D069600C9C224 /* RecommandedPlayListSection.swift */; };
44FDE99B2580D3480032E5C6 /* KingfisherSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 44FDE99A2580D3480032E5C6 /* KingfisherSwiftUI */; };
80045DDF25758C5A00F32E6C /* MultiselectTabBarItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80045DDE25758C5A00F32E6C /* MultiselectTabBarItems.swift */; };
80045DDF25758C5A00F32E6C /* TabbarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80045DDE25758C5A00F32E6C /* TabbarButton.swift */; };
80045DE225758D1600F32E6C /* MultiselectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80045DE125758D1600F32E6C /* MultiselectTabBar.swift */; };
80045DFA257602A900F32E6C /* Lyrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80045DF9257602A900F32E6C /* Lyrics.swift */; };
80045DFE257608B000F32E6C /* PlayerSliderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80045DFD257608B000F32E6C /* PlayerSliderView.swift */; };
Expand All @@ -85,6 +85,8 @@
805E28D3256BDC4A007AF5F1 /* TrackRowA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805E28D2256BDC4A007AF5F1 /* TrackRowA.swift */; };
805E28D7256BDE90007AF5F1 /* SectionTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805E28D6256BDE90007AF5F1 /* SectionTitle.swift */; };
805E28DA256BDFE6007AF5F1 /* ChartSectionA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805E28D9256BDFE6007AF5F1 /* ChartSectionA.swift */; };
806D272925812DA8002580B3 /* TrackUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 806D272825812DA8002580B3 /* TrackUseCase.swift */; };
806D273125815903002580B3 /* UseCaseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 806D273025815903002580B3 /* UseCaseError.swift */; };
807B698D256FAC2F00DAF867 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 807B698C256FAC2F00DAF867 /* NetworkService.swift */; };
80813997257E657A00A5B6AD /* MagazineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80813996257E657A00A5B6AD /* MagazineView.swift */; };
8084AB6D256E403900F2AAC2 /* ThumbnailRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8084AB6C256E403900F2AAC2 /* ThumbnailRow.swift */; };
Expand Down Expand Up @@ -192,7 +194,7 @@
44F9797B256D04EA00C9C224 /* RecommandedPlayListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommandedPlayListItem.swift; sourceTree = "<group>"; };
44F9797E256D069600C9C224 /* RecommandedPlayListSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommandedPlayListSection.swift; sourceTree = "<group>"; };
4B64F8DD7C3A90466A7DD817 /* Pods-MiniVibe.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MiniVibe.debug.xcconfig"; path = "Target Support Files/Pods-MiniVibe/Pods-MiniVibe.debug.xcconfig"; sourceTree = "<group>"; };
80045DDE25758C5A00F32E6C /* MultiselectTabBarItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiselectTabBarItems.swift; sourceTree = "<group>"; };
80045DDE25758C5A00F32E6C /* TabbarButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarButton.swift; sourceTree = "<group>"; };
80045DE125758D1600F32E6C /* MultiselectTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiselectTabBar.swift; sourceTree = "<group>"; };
80045DF9257602A900F32E6C /* Lyrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lyrics.swift; sourceTree = "<group>"; };
80045DFD257608B000F32E6C /* PlayerSliderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSliderView.swift; sourceTree = "<group>"; };
Expand All @@ -212,6 +214,8 @@
805E28D2256BDC4A007AF5F1 /* TrackRowA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackRowA.swift; sourceTree = "<group>"; };
805E28D6256BDE90007AF5F1 /* SectionTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionTitle.swift; sourceTree = "<group>"; };
805E28D9256BDFE6007AF5F1 /* ChartSectionA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartSectionA.swift; sourceTree = "<group>"; };
806D272825812DA8002580B3 /* TrackUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackUseCase.swift; sourceTree = "<group>"; };
806D273025815903002580B3 /* UseCaseError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCaseError.swift; sourceTree = "<group>"; };
807B698C256FAC2F00DAF867 /* NetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = "<group>"; };
80813996257E657A00A5B6AD /* MagazineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagazineView.swift; sourceTree = "<group>"; };
8084AB6C256E403900F2AAC2 /* ThumbnailRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailRow.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -301,7 +305,7 @@
4478F71F256E89EB00755656 /* ChartList.swift */,
8087AC7A256F3E4D001F6369 /* PlayerPreview.swift */,
80045DE125758D1600F32E6C /* MultiselectTabBar.swift */,
80045DDE25758C5A00F32E6C /* MultiselectTabBarItems.swift */,
80045DDE25758C5A00F32E6C /* TabbarButton.swift */,
);
path = Common;
sourceTree = "<group>";
Expand Down Expand Up @@ -406,12 +410,14 @@
800FA858257DD756008926A5 /* UseCases */ = {
isa = PBXGroup;
children = (
800FA848257DC557008926A5 /* EndPoint.swift */,
806D273025815903002580B3 /* UseCaseError.swift */,
800FA850257DC71E008926A5 /* TodayUseCase.swift */,
44B22A56257E0D450068A11B /* AlbumUseCase.swift */,
44ACE45E257E6B9B00EA5479 /* PlaylistUseCase.swift */,
80D02E03257E0DD300A6748F /* SearchUseCase.swift */,
44D5FAC1257FD89E00A3E91C /* ArtistUseCase.swift */,
800FA848257DC557008926A5 /* EndPoint.swift */,
806D272825812DA8002580B3 /* TrackUseCase.swift */,
);
path = UseCases;
sourceTree = "<group>";
Expand Down Expand Up @@ -874,6 +880,7 @@
80051692257F17FA00D7EF4C /* ViewLogger.swift in Sources */,
4478F723256E8B2B00755656 /* PlayAndShuffle.swift in Sources */,
44F97976256CFBA900C9C224 /* AlbumSection.swift in Sources */,
806D273125815903002580B3 /* UseCaseError.swift in Sources */,
44ACE463257E7E9500EA5479 /* PlaylistViewModel.swift in Sources */,
800FA851257DC71E008926A5 /* TodayUseCase.swift in Sources */,
80A5215C25793A3E0051A925 /* View+dismissKeyboard.swift in Sources */,
Expand All @@ -887,7 +894,7 @@
44B22A53257E0C8F0068A11B /* Track.swift in Sources */,
445D3A22257F441B0002D2DE /* TransitionLogModifier.swift in Sources */,
8087AC7B256F3E4D001F6369 /* PlayerPreview.swift in Sources */,
80045DDF25758C5A00F32E6C /* MultiselectTabBarItems.swift in Sources */,
80045DDF25758C5A00F32E6C /* TabbarButton.swift in Sources */,
80DA955B257817A3008C9DFF /* Chart.swift in Sources */,
805E28AE256BBD52007AF5F1 /* MiniVibeApp.swift in Sources */,
447ECD3825791B9800EE1435 /* LibraryArtistRow.swift in Sources */,
Expand All @@ -904,6 +911,7 @@
8033A7AA257FA7B100B1C321 /* NowPlaying.swift in Sources */,
444272BB256CE701008BB87B /* PreviewSection.swift in Sources */,
800FA84D257DC5EA008926A5 /* Albums.swift in Sources */,
806D272925812DA8002580B3 /* TrackUseCase.swift in Sources */,
80EB9FE5256D04A000CD99FD /* MagazineItem.swift in Sources */,
80A52145257903550051A925 /* Library.swift in Sources */,
);
Expand Down
3 changes: 1 addition & 2 deletions MiniVibe/MiniVibe/MainTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ struct MainTab: View {
}
}
.sheet(isPresented: $nowPlaying.isPlayerPresented) {
PlayerView(title: "Dynamite",
artist: "방탄소년단")
PlayerView()
}
)
}
Expand Down
37 changes: 35 additions & 2 deletions MiniVibe/MiniVibe/Models/NowPlaying.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
//

import Foundation
import Combine

class NowPlaying: ObservableObject {

final class NowPlaying: ObservableObject {
@Published var isPlaying: Bool = false
@Published var isPlayerPresented: Bool = false
@Published var upNext = [TrackInfo]()
Expand All @@ -18,15 +18,48 @@ class NowPlaying: ObservableObject {
}

func addTrack(track: TrackInfo) {
isPlaying = true
if let index = upNext.firstIndex(of: track) {
upNext.insert(upNext.remove(at: index), at: 0)
} else {
upNext.insert(track, at: 0)
}
}

func deleteTrack() {
selectedTracks.forEach { upNext.remove(at: upNext.firstIndex(of: $0) ?? -1) }
Sueaty marked this conversation as resolved.
Show resolved Hide resolved

}

func playNextTrack() {
isPlaying = true
upNext.insert(upNext.remove(at: 0), at: upNext.count)
}

func likeTrack(id: Int) {
let usecase = TrackUseCase()
var cancellables = Set<AnyCancellable>()
usecase.likeTrack(like: LikeTrack(trackId: id)) //sink로 받고 에러처리만 하면 될 듯?
.sink { result in
print(result)
} receiveValue: { _ in

}
.store(in: &cancellables)
Sueaty marked this conversation as resolved.
Show resolved Hide resolved
upNext[0].liked = 1
}

func cancelLikedTrack(id: Int) {
let usecase = TrackUseCase()
var cancellables = Set<AnyCancellable>()
usecase.cancelLikedTrack(id: id)
.sink { result in
print(result)
} receiveValue: { _ in

}
.store(in: &cancellables)
upNext[0].liked = 0
}

}
4 changes: 3 additions & 1 deletion MiniVibe/MiniVibe/Models/Track.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ let trackinfo: TrackInfo = TrackInfo(id: 0,
title: "",
imageUrl: ""),
artist: TrackInfo.Artist(id: 1,
name: "스윗소로우"))
name: "스윗소로우"),
liked: 1)

struct TrackResponse: Decodable {
let data: [TrackInfo]
Expand All @@ -34,6 +35,7 @@ struct TrackInfo: Decodable, Hashable {
let albumId: Int?
let album: TrackAlbum
let artist: Artist
var liked: Int // 1 = like, 0 = hate

func hash(into hasher: inout Hasher) {
hasher.combine(id)
Expand Down
2 changes: 1 addition & 1 deletion MiniVibe/MiniVibe/Models/UseCases/AlbumUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct AlbumUseCase {
}

func loadAlbum(with id: Int) -> AnyPublisher<Album, UseCaseError> {
return network.request(url: EndPoint.album(id: id).urlString)
return network.request(url: EndPoint.album(id: id).urlString, request: .get, body: nil)
.decode(type: AlbumResponse.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand Down
4 changes: 2 additions & 2 deletions MiniVibe/MiniVibe/Models/UseCases/ArtistUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct ArtistUseCase {
}

func loadArtists() -> AnyPublisher<[Artist], UseCaseError> {
return network.request(url: EndPoint.artists.urlString)
return network.request(url: EndPoint.artists.urlString, request: .get, body: nil)
.decode(type: Artists.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand All @@ -36,7 +36,7 @@ struct ArtistUseCase {
}

func loadArtist(with id: Int) -> AnyPublisher<ArtistInfo, UseCaseError> {
return network.request(url: EndPoint.artist(id: id).urlString)
return network.request(url: EndPoint.artist(id: id).urlString, request: .get, body: nil)
.decode(type: ArtistResponse.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand Down
9 changes: 9 additions & 0 deletions MiniVibe/MiniVibe/Models/UseCases/EndPoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ enum EndPoint {
case artists
case artist(id: Int)


case like // 좋아요
case cancelLike(id: Int) // 좋아요 취소

static private let baseURL = "http://101.101.209.213:3000"

private var path: String {
Expand All @@ -43,6 +47,11 @@ enum EndPoint {
return "/api/artists"
case let .artist(id):
return "/api/artists/\(id)"

case .like:
return "/api/library/tracks"
case let .cancelLike(id):
return "/api/library/tracks/\(id)"
}
}

Expand Down
2 changes: 1 addition & 1 deletion MiniVibe/MiniVibe/Models/UseCases/PlaylistUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct PlaylistUseCase {
}

func loadPlaylist(with id: Int) -> AnyPublisher<Playlist, UseCaseError> {
return network.request(url: EndPoint.playlist(id: id).urlString)
return network.request(url: EndPoint.playlist(id: id).urlString, request: .get, body: nil)
.decode(type: PlaylistResponse.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand Down
2 changes: 1 addition & 1 deletion MiniVibe/MiniVibe/Models/UseCases/SearchUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct SearchUseCase {
}

func loadNews() -> AnyPublisher<[News], UseCaseError> {
return network.request(url: EndPoint.newsList.urlString)
return network.request(url: EndPoint.newsList.urlString, request: .get, body: nil)
.decode(type: NewsList.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand Down
15 changes: 5 additions & 10 deletions MiniVibe/MiniVibe/Models/UseCases/TodayUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@
import Foundation
import Combine

enum UseCaseError: Error {
case networkError
case decodingError
}

struct TodayUseCase {
private let network: NetworkServiceType

Expand All @@ -21,7 +16,7 @@ struct TodayUseCase {
}

func loadMixtapes() -> AnyPublisher<[Mixtape], UseCaseError> {
return network.request(url: EndPoint.mixtapes.urlString)
return network.request(url: EndPoint.mixtapes.urlString, request: .get, body: nil)
.decode(type: Mixtapes.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand All @@ -37,7 +32,7 @@ struct TodayUseCase {
}

func loadAlbums() -> AnyPublisher<[Album], UseCaseError> {
return network.request(url: EndPoint.albums.urlString)
return network.request(url: EndPoint.albums.urlString, request: .get, body: nil)
.decode(type: Albums.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand All @@ -53,7 +48,7 @@ struct TodayUseCase {
}

func loadPlaylists() -> AnyPublisher<[Playlist], UseCaseError> {
return network.request(url: EndPoint.playlists.urlString)
return network.request(url: EndPoint.playlists.urlString, request: .get, body: nil)
.decode(type: Playlists.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand All @@ -71,7 +66,7 @@ struct TodayUseCase {
}

func loadTracks() -> AnyPublisher<[TrackInfo], UseCaseError> {
return network.request(url: EndPoint.tracks.urlString)
return network.request(url: EndPoint.tracks.urlString, request: .get, body: nil)
.decode(type: TrackResponse.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand All @@ -87,7 +82,7 @@ struct TodayUseCase {
}

func loadMagazines() -> AnyPublisher<[Magazine], UseCaseError> {
return network.request(url: EndPoint.magazines.urlString)
return network.request(url: EndPoint.magazines.urlString, request: .get, body: nil)
.decode(type: Magazines.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
Expand Down
67 changes: 67 additions & 0 deletions MiniVibe/MiniVibe/Models/UseCases/TrackUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// TrackUseCase.swift
// MiniVibe
//
// Created by Sue Cho on 2020/12/10.
//

import Foundation
import Combine

struct LikeTrack: Encodable {
let trackId: Int
}

struct LikeTrackResponse: Decodable {
let success: Bool
}

struct CancelLikeTrackResponse: Decodable {
Sueaty marked this conversation as resolved.
Show resolved Hide resolved
let success: Bool
}

struct TrackUseCase {
private let network: NetworkServiceType

init(network: NetworkServiceType = NetworkService()) {
self.network = network
}

func likeTrack(like: LikeTrack) -> AnyPublisher<Bool, UseCaseError> {
// #1 : Just로 트랙을 방출시키고 encode 하고 플랫맵으로 network 퍼블리셔로 매핑 (stream으로 이어짐)
// #2 : Encode
guard let data = try? JSONEncoder().encode(like) else {
return Fail(error: UseCaseError.encodingError)
.eraseToAnyPublisher()
}
return network.request(url: EndPoint.like.urlString, request: .post, body: data)
.decode(type: LikeTrackResponse.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
case is NetworkError:
return .networkError
default:
return .decodingError
}
}
.map(\.success)
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}

func cancelLikedTrack(id: Int) -> AnyPublisher<Bool, UseCaseError> {
return network.request(url: EndPoint.cancelLike(id: id).urlString, request: .delete, body: nil)
.decode(type: CancelLikeTrackResponse.self, decoder: JSONDecoder())
.mapError { error -> UseCaseError in
switch error {
case is NetworkError:
return .networkError
default:
return .decodingError
}
}
.map(\.success)
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
12 changes: 12 additions & 0 deletions MiniVibe/MiniVibe/Models/UseCases/UseCaseError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// UseCaseError.swift
// MiniVibe
//
// Created by Sue Cho on 2020/12/10.
//

enum UseCaseError: Error {
case networkError
case decodingError
case encodingError
}
Loading