[UIKit] Spotify Clone: Remove from Playlist

Junyoung Park·2022년 9월 11일
0

UIKit

목록 보기
24/142
post-thumbnail

Building Spotify App in Swift 5 & UIKit - Part 24 - Remove from Playlist (Xcode 12, 2021, Swift 5)

Spotify Clone: Remove from Playlist

구현 목표

  • 음원 목록에서 특정 음원을 삭제하기

구현 태스크

  1. 플레이리스트 뷰 컨트롤러의 음원 목록 컬렉션 뷰의 LongTapGesture → 삭제 함수 호출
  2. 플레이리스트, 음원 → 스포티파이 API를 통해 특정 플레이리스트에서 음원 삭제
  3. 컬렉션 뷰 패치 (셀, 헤더 이미지 등)

핵심 코드

@objc private func didLongPress(_ gesture: UILongPressGestureRecognizer) {
        guard gesture.state == .began, isOwner == true else { return }
        let touchPoint = gesture.location(in: collectionView)
        guard let indexPath = collectionView.indexPathForItem(at: touchPoint) else { return }
        let trackToDelete = tracks[indexPath.row]
        let actionSheet = UIAlertController(title: "Delete",
                                            message: "Would you like to delete \(trackToDelete.name) from this playlist?",
                                            preferredStyle: .actionSheet)
        actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        actionSheet.addAction(UIAlertAction(title: "Remove", style: .destructive, handler: { [weak self] _ in
            guard let self = self else { return }
            APICaller.shared.removeTrackFromPlaylist(track: trackToDelete, playlist: self.playlist) { success in
                if success {
                    DispatchQueue.main.async {
                        self.tracks.remove(at: indexPath.row)
                        self.viewModels.remove(at: indexPath.row)
                        self.collectionView.reloadData()
                    }
                } else {
                    print("Failed to remove")
                }
            }
        }))
        present(actionSheet, animated: true, completion: nil)
    }
  • 컬렉션 뷰의 특정 셀 터치 이벤트 감지(LongTapGesture)
  • 현재 플레이리스트가 현재 유저 자신의 것일 때에만 삭제 이벤트 호출
  • isOwner 플래그 비트 사영
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let playlist = playlists[indexPath.row]

        guard selectionHandler == nil else {
            self.selectionHandler?(playlist)
            self.dismiss(animated: true, completion: nil)
            return
        }
        
        let vc = PlaylistViewController(playlist: playlist)
        vc.navigationItem.largeTitleDisplayMode = .never
        vc.isOwner = true
        navigationController?.pushViewController(vc, animated: true)
    }
  • LibraryPlaylistsViewController의 테이블 뷰에서 특정 플레이리스트로 들어가기 전 isOwner 플래그 비트를 조정

    라이브러리가 아니라 SearchViewController를 통해 유저가 만든 플레이리스트를 검색했을 경우에는 그 플레이리스트의 isOwner 값을 조정할 수 없기 때문에 플레이리스트 자체의 Owner를 현재 유저의 프로퍼티와 비교해서 불리언 체크를 해야 할 것 같음

        APICaller.shared.getCurrentUserProfile { [weak self] result in
            guard let self = self else { return }
            switch result {
            case .success(let profile):
                self.isOwner = profile.id == self.playlist.owner.id
            case .failure(let error):
                print(error.localizedDescription)
                break
            }
        }
  • LibraryPlaylistsViewController의 테이블 뷰 클릭을 통해 네비게이션을 띄우기 전 isOwner를 로컬 상에서 조정하는 코드 삭제 후 리팩토링
  • PlaylistViewContoller 내에서 스포티파이 API 직접 호출 → 현재 다루고 있는 플레이리스트의 Owner 아이디와 현재 유저의 아이디가 같은 지 직접적으로 확인 가능
public func removeTrackFromPlaylist(track: AudioTrack, playlist: Playlist, completionHandler: @escaping (Bool)->()) {
        createRequest(with: URL(string: Constants.baseAPIURL + "/playlists/\(playlist.id)/tracks"), type: .DELETE) { request in
            var request = request
            let json:[String:Any] = [
                "tracks" : [
                    [
                    "uri":"spotify:track:\(track.id)"
                    ]
                ]
            ]
            request.httpBody = try? JSONSerialization.data(withJSONObject: json, options: .fragmentsAllowed)
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
            URLSession.shared.dataTask(with: request) { data, response, error in
                guard
                    let data = data,
                    error == nil else {
                    completionHandler(false)
                    return
                }
                do {
                    let result = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed)
                    print(result)
                    if let response = result as? [String:Any], response["snapshot_id"] as? String != nil {
                        completionHandler(true)
                    } else {
                        completionHandler(false)
                    }
                } catch {
                    completionHandler(false)
                }
            }
            .resume()
        }
    }
  • 스포티파이 API를 통해 특정 플레이리스트에서 특정 음원 트랙 제거

구현 화면

  • LongTapGesture보다도 swipe to delete가 보다 익숙한 UX를 제공하기 때문에 리팩토링할 경우 swipe to delete할 수 있도록 사용하기
  • 테이블 뷰의 editStyle에서는 곧바로 swipe to delete 기능을 사용할 수 있는데, 컬렉션 뷰의 경우 커스텀해야 하는 듯
profile
JUST DO IT

0개의 댓글