SwiftUI 로 음악 다운로드 어플 만들기 1 : 검색 및 다운로드

버들비·2020년 9월 6일
0

to do : GET 요청으로 음악 리스트를 검색하고, 30초 샘플을 다운받자.

아이튠즈 api 를 사용한다. (https://itunes.apple.com/search)

View

  1. 음악 검색창이랑, search 버튼을 만든다.
  2. 검색결과를 List 형태로 보여준다. 리스트 각 행마다 download 버튼이 있다.

메인 뷰에 들어갈 VStack.

VStack{
            
            TextField("search...", text: $searchText).padding()
            Button(action: getSearchResults) {
                Text("search")
            }
            List(searchResults, id: \.self) { result in
                VStack{
                    SearchResultCell(artistName: result.artistName, trackName: result.trackName, albumName: result.albumName, releaseDate: result.releaseDate, previewUrl: result.previewUrl)
                    Button(action: {
                        self.startDownload(track: result)
                    }) {
                        Text("start download")
                    }
                }
            }
        }

검색결과를 보여줄 행. 일단은 아티스트 이름과 곡제목, 앨범명, 발매날짜만 보여주는걸로 하자.


struct SearchResultCell : View {
    
    var artistName: String
    var trackName: String
    var albumName: String
    var releaseDate: String
    var previewUrl: String
    
    var body: some View {
        VStack(alignment: .leading){
            Text("Artist: \(artistName)")
            Text("Track: \(trackName)")
            Text("Album: \(albumName)")
            Text("Release: \(releaseDate)")
        }.padding()
    }
}

검색결과를 담는 Model.

struct SearchResult: Hashable {
    var artistName: String
    var trackName: String
    var albumName: String
    var releaseDate: String
    var previewUrl: String
}

아직 Button 을 눌렀을때 작동하는 함수를 구현하지 않았지만, View 는 다음과 같은 모양이 된다(iPad 7)

Functions

검색요청 함수와 다운로드 함수를 구현하자. URLSession 을 이용한다.

URLSession 과 관련된 글
https://o-o-wl.tistory.com/50

검색 요청 함수

func getSearchResults() {
        self.searchResults = []
        guard var urlComponents = URLComponents(string: "https://itunes.apple.com/search") else { return }
        urlComponents.query = "media=music&entity=song&term=\(searchText)"
        guard let url = urlComponents.url else {return}
            let task = URLSession.shared.dataTask(with: url) { data, response, error in
                if let e = error {
                    NSLog("error: \(e.localizedDescription)")
                    return
                }
                
                DispatchQueue.main.async() {
                    do {
                        let object = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
                        
                        guard let jsonObject = object else {return}
                        
                        let searchResults = jsonObject["results"] as! [NSDictionary]
                        searchResults.forEach { result in
                            let searchResult = SearchResult(artistName: result["artistName"] as! String, trackName: result["trackName"] as! String, albumName: result["collectionName"] as! String, releaseDate: result["releaseDate"] as! String, previewUrl: result["previewUrl"] as! String)
                            self.searchResults.append(searchResult)
                        }
                    } catch let e as NSError {
                        print("error: \(e.localizedDescription)")
                    }
                }
            }
            task.resume()
    }

다운로드 함수

    func startDownload (track: SearchResult) {
        guard let url = URL(string: track.previewUrl) else { return }
        let task = URLSession.shared.downloadTask(with: url) { localURL, urlResponse, error in
            if let localURL = localURL {
                let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
                let destinationURL = documentsPath.appendingPathComponent(url.lastPathComponent)
                // delete original copy
                try? FileManager.default.removeItem(at: destinationURL)
                // copy from temp to Document
                do {
                    try FileManager.default.copyItem(at: localURL, to: destinationURL)
                } catch let error {
                    print("Copy Error: \(error.localizedDescription)")
                }
            }
        }
        task.resume()
    }

downloadTask 메소드를 사용하면, 파일이 tmp 폴더로 다운로드된뒤 얼마 안가 삭제된다. tmp 폴더에 다운로드한 파일을 복사해서 documents 폴더에 넣기 위해 FileManager 를 사용한다.

iOS 파일 시스템 관련글
https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html

downloadTask 의 클로저에 들어간 completion handler 의 localURL은 파일이 저장되는 tmp 폴더의 위치를 의미한다.

print() 나 NSLog() 등으로 localURL 을 콘솔에 띄운뒤 나오는 주소를 복사한다.

파인더를 열어 cmd + shift + G 를 눌러서 복사한 url 을 입력하면 Xcode simulator 의 tmp 폴더 위치로 이동할 수 있다.

또는 디버그 콘솔에 po NSHomeDirectory() 를 입력하면 시뮬레이터의 파일시스템 주소가 뜨는데, Xcode 에 버그가 있는건지 안될때가 있었다

Xcode 에서 빌드를 하고, 음악을 검색해보자.

start download 가 적혀있는 행을 클릭하면, 해당하는 트랙의 30초 preview 를 다운받는다.


to do
다운로드 받은 음악들 UI 에 반영.
다운로드 받은 음악들 삭제할 수 있는 기능 추가.

0개의 댓글