Skip to content

Commit

Permalink
Merge pull request #155 from boostcampwm-2022/feature/search-and-play…
Browse files Browse the repository at this point in the history
…-music

음악 검색 및 재생 라이브러리 연동
  • Loading branch information
radiantchoi authored Dec 6, 2022
2 parents 344967e + c41e8b0 commit aaa1d24
Show file tree
Hide file tree
Showing 14 changed files with 482 additions and 32 deletions.
37 changes: 37 additions & 0 deletions Segno/Segno.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
objects = {

/* Begin PBXBuildFile section */
4F307A4629387C1100FA36A0 /* ShazamSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F307A4529387C1100FA36A0 /* ShazamSession.swift */; };
4F307A482938832900FA36A0 /* MusicSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F307A472938832900FA36A0 /* MusicSession.swift */; };
4F307A4A293889C200FA36A0 /* SearchMusicUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F307A49293889C200FA36A0 /* SearchMusicUseCase.swift */; };
4F307A4C29388D4600FA36A0 /* MusicRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F307A4B29388D4600FA36A0 /* MusicRepository.swift */; };
4F307A512939031A00FA36A0 /* MarqueeLabel in Frameworks */ = {isa = PBXBuildFile; productRef = 4F307A502939031A00FA36A0 /* MarqueeLabel */; };
4F31777F291BE4710019BDFC /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F31777E291BE4710019BDFC /* SceneDelegate.swift */; };
4F317786291BE4720019BDFC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4F317785291BE4720019BDFC /* Assets.xcassets */; };
4F317789291BE4720019BDFC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4F317787291BE4720019BDFC /* LaunchScreen.storyboard */; };
Expand Down Expand Up @@ -95,6 +100,10 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
4F307A4529387C1100FA36A0 /* ShazamSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShazamSession.swift; sourceTree = "<group>"; };
4F307A472938832900FA36A0 /* MusicSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicSession.swift; sourceTree = "<group>"; };
4F307A49293889C200FA36A0 /* SearchMusicUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchMusicUseCase.swift; sourceTree = "<group>"; };
4F307A4B29388D4600FA36A0 /* MusicRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicRepository.swift; sourceTree = "<group>"; };
4F317779291BE4710019BDFC /* Segno.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Segno.app; sourceTree = BUILT_PRODUCTS_DIR; };
4F31777C291BE4710019BDFC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
4F31777E291BE4710019BDFC /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -187,6 +196,7 @@
4F3177A7291BF70B0019BDFC /* RxRelay in Frameworks */,
4F3177AB291BF70B0019BDFC /* RxSwift in Frameworks */,
4F3177B5291BF7BC0019BDFC /* Kingfisher in Frameworks */,
4F307A512939031A00FA36A0 /* MarqueeLabel in Frameworks */,
4F3177B0291BF7950019BDFC /* SnapKit in Frameworks */,
4F3177A3291BF70B0019BDFC /* RxCocoa in Frameworks */,
66F0D7F729260BA20074872E /* GoogleSignIn in Frameworks */,
Expand Down Expand Up @@ -314,6 +324,7 @@
791529D72932F364005A8DDB /* DiaryEditUseCase.swift */,
66A8CF682937945300C17F84 /* UserDetailUseCase.swift */,
9894EAF429373385005F2B15 /* SettingsUseCase.swift */,
4F307A49293889C200FA36A0 /* SearchMusicUseCase.swift */,
9825F41A29377875005F2163 /* ChangeNicknameUseCase.swift */,
);
path = UseCase;
Expand Down Expand Up @@ -394,6 +405,7 @@
4F4E0D3D2924B925005ABA8F /* LoginRepository.swift */,
66A8CF642937934A00C17F84 /* MyPageRepository.swift */,
9825F41C29377ACF005F2163 /* SettingsRepository.swift */,
4F307A4B29388D4600FA36A0 /* MusicRepository.swift */,
);
path = Repository;
sourceTree = "<group>";
Expand Down Expand Up @@ -437,6 +449,8 @@
isa = PBXGroup;
children = (
4FCAC5C1292B5C9000BF9CDD /* LoginSession.swift */,
4F307A4529387C1100FA36A0 /* ShazamSession.swift */,
4F307A472938832900FA36A0 /* MusicSession.swift */,
);
path = Session;
sourceTree = "<group>";
Expand Down Expand Up @@ -491,6 +505,7 @@
4F3177AF291BF7950019BDFC /* SnapKit */,
4F3177B4291BF7BC0019BDFC /* Kingfisher */,
66F0D7F629260BA20074872E /* GoogleSignIn */,
4F307A502939031A00FA36A0 /* MarqueeLabel */,
);
productName = Segno;
productReference = 4F317779291BE4710019BDFC /* Segno.app */;
Expand Down Expand Up @@ -525,6 +540,7 @@
4F3177AE291BF7950019BDFC /* XCRemoteSwiftPackageReference "SnapKit" */,
4F3177B3291BF7BC0019BDFC /* XCRemoteSwiftPackageReference "Kingfisher" */,
66F0D7F529260BA20074872E /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */,
4F307A4F2939031A00FA36A0 /* XCRemoteSwiftPackageReference "MarqueeLabel" */,
);
productRefGroup = 4F31777A291BE4710019BDFC /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -562,6 +578,7 @@
983AE9D82935CEE2006547BD /* SettingsViewModel.swift in Sources */,
9825F41D29377ACF005F2163 /* SettingsRepository.swift in Sources */,
66F0D7EE2925FF8B0074872E /* DiaryCell.swift in Sources */,
4F307A482938832900FA36A0 /* MusicSession.swift in Sources */,
4F9A00202922337F007D9057 /* LoginViewController.swift in Sources */,
982A2A472924AE74006F6ACD /* UserDefaultsKey.swift in Sources */,
66A8CF6B2937947A00C17F84 /* UserDetailDTO.swift in Sources */,
Expand Down Expand Up @@ -614,18 +631,21 @@
983AE9D6293514E8006547BD /* SettingsActionSheetCell.swift in Sources */,
4FA3242B2923646F00DB04D5 /* DiaryListItemEndpoint.swift in Sources */,
791837FF292225DC00BC6992 /* UIColor+.swift in Sources */,
4F307A4A293889C200FA36A0 /* SearchMusicUseCase.swift in Sources */,
666E6F7F291CF46800CECD4B /* AppCoordinator.swift in Sources */,
791529DC29332CF2005A8DDB /* ImageDTO.swift in Sources */,
9841D61B2926131200318EA9 /* LoginViewModel.swift in Sources */,
988414D72923304F007C9132 /* KeychainError.swift in Sources */,
98BEE36E2934907700B20143 /* LoginError.swift in Sources */,
98B5263E292CA46C00446413 /* TagView.swift in Sources */,
4F307A4C29388D4600FA36A0 /* MusicRepository.swift in Sources */,
7940FB2F292E063100276EFC /* DiaryDetailDTO.swift in Sources */,
4F4E0D7629252236005ABA8F /* LoginEndpoint.swift in Sources */,
791529D82932F364005A8DDB /* DiaryEditUseCase.swift in Sources */,
4FA324262923600C00DB04D5 /* DiaryListDTO.swift in Sources */,
666E6F87291CF4B400CECD4B /* DiaryCoordinator.swift in Sources */,
4F31777F291BE4710019BDFC /* SceneDelegate.swift in Sources */,
4F307A4629387C1100FA36A0 /* ShazamSession.swift in Sources */,
4F4E0D74292521D0005ABA8F /* UserInfoDTO.swift in Sources */,
98B52640292CB92900446413 /* MusicContentView.swift in Sources */,
4FA32428292363AB00DB04D5 /* DiaryRepository.swift in Sources */,
Expand Down Expand Up @@ -771,6 +791,8 @@
DEVELOPMENT_TEAM = 4QG3GC35LA;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Segno/Resource/Info.plist;
INFOPLIST_KEY_NSAppleMusicUsageDescription = "";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "음악 검색 기능을 위해 마이크 사용 허가가 필요합니다.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
Expand Down Expand Up @@ -800,6 +822,8 @@
DEVELOPMENT_TEAM = 4QG3GC35LA;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Segno/Resource/Info.plist;
INFOPLIST_KEY_NSAppleMusicUsageDescription = "";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "음악 검색 기능을 위해 마이크 사용 허가가 필요합니다.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
Expand Down Expand Up @@ -842,6 +866,14 @@
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
4F307A4F2939031A00FA36A0 /* XCRemoteSwiftPackageReference "MarqueeLabel" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/cbpowell/MarqueeLabel";
requirement = {
branch = master;
kind = branch;
};
};
4F3177A1291BF70B0019BDFC /* XCRemoteSwiftPackageReference "RxSwift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/ReactiveX/RxSwift";
Expand Down Expand Up @@ -877,6 +909,11 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
4F307A502939031A00FA36A0 /* MarqueeLabel */ = {
isa = XCSwiftPackageProductDependency;
package = 4F307A4F2939031A00FA36A0 /* XCRemoteSwiftPackageReference "MarqueeLabel" */;
productName = MarqueeLabel;
};
4F3177A2291BF70B0019BDFC /* RxCocoa */ = {
isa = XCSwiftPackageProductDependency;
package = 4F3177A1291BF70B0019BDFC /* XCRemoteSwiftPackageReference "RxSwift" */;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@
"version" : "7.4.1"
}
},
{
"identity" : "marqueelabel",
"kind" : "remoteSourceControl",
"location" : "https://github.com/cbpowell/MarqueeLabel",
"state" : {
"branch" : "master",
"revision" : "980925156200e94062e31db9e203f535c05b27ee"
}
},
{
"identity" : "rxswift",
"kind" : "remoteSourceControl",
Expand Down
4 changes: 2 additions & 2 deletions Segno/Segno/Application/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ final class AppCoordinator: Coordinator {

func start() {
// TODO: login이 안되어있으면 LoginCoordinator 실행
startLoginCoordinator()
// startLoginCoordinator()
// TODO: login이 되어있으면 TabBarCoordinator 실행
// startTabBarCoordinator()
startTabBarCoordinator()
}

func startLoginCoordinator() {
Expand Down
26 changes: 26 additions & 0 deletions Segno/Segno/Data/Repository/MusicRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// MusicRepository.swift
// Segno
//
// Created by Gordon Choi on 2022/12/01.
//

import RxSwift

protocol MusicRepository {
func searchMusic()
func playMusic()
}

final class MusicRepositoryImpl: MusicRepository {
private let shazamSession = ShazamSession()
private let musicSession = MusicSession()

func searchMusic() {

}

func playMusic() {

}
}
55 changes: 36 additions & 19 deletions Segno/Segno/Data/Session/LoginSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,34 +82,51 @@ extension LoginSession: ASAuthorizationControllerDelegate {
extension LoginSession {
// MARK: - jwt 해석 메서드
func parseEmailFromJWT(_ jwtData: Data?) -> String? {

var email: String?
guard let jwtData = jwtData,
let jwt = String(data: jwtData, encoding: .ascii) else { return nil }

// parse body from jwt
let jwtElement = jwt.split(separator: ".").map {
String($0)
}.compactMap {
Data(base64Encoded: $0)
}

guard jwtElement.count != 0 else { return nil }

let data = parseBodyFromJWTData(jwtElement)

if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],
let emailFromJson = json["email"] as? String {
email = emailFromJson
do {
let payload = try decode(jwtToken: jwt)
email = payload["email"] as? String
} catch(let err) {
print(err)
return nil
}
print(email)

return email
}

private func parseBodyFromJWTData(_ jwtData: [Data]) -> Data {
if jwtData.count > 1 {
return jwtData[1]
} else {
return jwtData[0]
private func decode(jwtToken jwt: String) throws -> [String: Any] {

enum DecodeErrors: Error {
case badToken
case other
}

func base64Decode(_ base64: String) throws -> Data {
let base64 = base64
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let padded = base64.padding(toLength: ((base64.count + 3) / 4) * 4, withPad: "=", startingAt: 0)
guard let decoded = Data(base64Encoded: padded) else {
throw DecodeErrors.badToken
}
return decoded
}

func decodeJWTPart(_ value: String) throws -> [String: Any] {
let bodyData = try base64Decode(value)
let json = try JSONSerialization.jsonObject(with: bodyData, options: [])
guard let payload = json as? [String: Any] else {
throw DecodeErrors.other
}
return payload
}

let segments = jwt.components(separatedBy: ".")
return try decodeJWTPart(segments[1])
}
}
74 changes: 74 additions & 0 deletions Segno/Segno/Data/Session/MusicSession.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// MusicSession.swift
// Segno
//
// Created by Gordon Choi on 2022/12/01.
//

import MusicKit

final class MusicSession {
private var song: Song?

private lazy var player = ApplicationMusicPlayer.shared
private lazy var playerState = player.state

private var isPlaying: Bool {
return playerState.playbackStatus == .playing
}

func fetchMusic(term: MusicInfo?) {
guard let term else { return }

Task {
let status = await MusicAuthorization.request()
switch status {
case .authorized:
do {
let request = MusicCatalogResourceRequest<Song>(matching: \.isrc, equalTo: term.isrc)
let response = try await request.response()
if let item = response.items.first {
song = item
}

} catch (let error) {
// TODO: 에러 처리
print(error.localizedDescription)
}
default:
// TODO: 에러 처리
debugPrint("no")
}
}
}

// 음악을 재생하는 함수
func togglePlayer() {
guard let song else { return }
if !isPlaying {
playMusic(song: song)
} else {
player.pause()
}

// 뷰 컨트롤러를 나갈 때 큐를 비워 준다.
// 뮤직세션, 샤잠세션은 싱글턴 인스턴스로 만들어 주는 것이 좋겠다.
}

private func playMusic(song: Song) {
player.queue = [song]

Task {
do {
try await player.play()
} catch let error {
// TODO: 에러 처리
print(error.localizedDescription)
}
}
}

func stopMusic() {
player.stop()
}
}
Loading

0 comments on commit aaa1d24

Please sign in to comment.