2015-05-08 38 views
0

我正在學習iOS Swift並創建一個應用程序,以瞭解獲取JSON數據並在使用iTunes搜索API時將此數據保存到CoreData。我有一個表格視圖,並使用自定義表格視圖單元格,它有一些標籤,一個圖像和一個下載按鈕。我的目的是在單擊該單元格的按鈕後,將該專輯信息中的專輯和所有歌曲發送到CoreData。以下是正在工作和不工作的列表:同樣的方法在ViewDidLoad中工作,但在自定義TableViewCell按鈕操作中不起作用

  1. 單擊此按鈕會爲我提供相冊的正確CollectionId。
  2. 專輯信息已成功添加到CoreData中。
  3. 我無法在我的下載操作方法中調用api後填充我的歌曲數組。它保持空着。請注意,當我使用手動輸入的集合ID在ViewDidLoad中調用api時,歌曲數組將被填充。

代碼: 獲取歌曲信息的API控制器。

import Foundation 

protocol songAPIControllerForCoreDataProtocol { 
    func didReceiveAPISongResults(results: NSDictionary) 
} 

class songAPIControllerForCoreData { 

    var delegate: songAPIControllerForCoreDataProtocol 

    init(delegate: songAPIControllerForCoreDataProtocol) { 
     self.delegate = delegate 
    } 

    func searchItunesForSongsBelongingTo(searchTerm: String) { 

    // The iTunes API wants multiple terms separated by + symbols, so I'm replacing spaces with + signs 
     let itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil) 

    // Escape anything else that isn't URL-friendly 
     if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) { 
     // Using Itunes search api to find people that has a music album with the entered search term 
      let urlPath = "https://itunes.apple.com/lookup?id=\(escapedSearchTerm)&entity=song" 
      let url: NSURL = NSURL(string: urlPath)! 
      let session = NSURLSession.sharedSession() 
      let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in 
      println("Task completed") 
       if(error != nil) { 
       // If there is an error in the web request, print it to the console 
        println(error.localizedDescription) 
       } 
       var err: NSError? 

       var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as! NSDictionary 
       println(jsonResult[0]) 
       if(err != nil) { 
       // If there is an error parsing JSON, print it to the console 
        println("JSON Error \(err!.localizedDescription)") 
       } 
       self.delegate.didReceiveAPISongResults(jsonResult) 
       println(jsonResult) 
      }) 

      task.resume() 
     } 
    } 
} 

宋級(不CoreData):

import Foundation 

class Song { 
    var title: String 
    var previewURL: String 
    var collectionID: Int 

    init(title: String, previewURL: String, collectionID: Int) { 
     self.title = title 
     self.previewURL = previewURL 
     self.collectionID = collectionID 
    } 

    class func songsWithJSON(allResults: NSArray) -> [Song] { 

     // Create an empty array of Albums to append to from this list 
     var songs = [Song]() 

     // Store the results in our table data array 
     if allResults.count>0 { 

      // Sometimes iTunes returns a collection, not a track, so we check both for the 'name' 
      for result in allResults { 

       var title = result["trackName"] as? String 
       if title == nil { 
        title = result["collectionName"] as? String 
       } 
       if title == nil { 
        title = result["collectionName"] as? String 
       } 
       let previewURL = result["previewUrl"] as? String ?? "" 
       let collectionID = result["collectionId"] as? Int ?? 0 

       var newSong = Song(title: title!, previewURL: previewURL, collectionID: collectionID) 
       songs.append(newSong) 
      } 
     } 
     return songs 
    } 

} 

最後AlbumViewController:

import UIKit 
import CoreData 

class AlbumViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, searchAPIControllerProtocol, songAPIControllerForCoreDataProtocol { 

    @IBOutlet 
    var tableView: UITableView! 

    @IBOutlet weak var artistNameOutlet: UILabel! 

    var songapi : songAPIControllerForCoreData? 
    var api : searchAPIController? 
    var albums = [Album]() 
    var songs = [Song]() 
    var imageCache = [String : UIImage]() 

    //Variables that take the values after segue from uTableViewController 
    var artistID, artistName: String? 

    let cellIdentifier: String = "albumCell" 

    //for CoreData 
    var error:NSError? 
    let managedObjectContext = (UIApplication.sharedApplication().delegate 
     as! AppDelegate).managedObjectContext 

    func numberOfSectionsInTableView(tableView: UITableView) -> Int { 
     // #warning Potentially incomplete method implementation. 
     // Return the number of sections. 
     return 1 
    } 

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return self.albums.count 
    } 

    func download(sender: AnyObject){ 
     var senderButton : UIButton = sender as! UIButton 
     let newAlbum = NSEntityDescription.insertNewObjectForEntityForName("Albums", inManagedObjectContext: managedObjectContext!) as! Albums 
     let newSong = NSEntityDescription.insertNewObjectForEntityForName("Songs", inManagedObjectContext: managedObjectContext!) as! Songs 

     songapi!.searchItunesForSongsBelongingTo((String)(self.albums[senderButton.tag].collectionID)) 

     newAlbum.albumArt = self.albums[senderButton.tag].largeImageURL 
     newAlbum.albumID = (String)(self.albums[senderButton.tag].collectionID) 
     newAlbum.albumName = self.albums[senderButton.tag].title 
     newAlbum.albumPrice = self.albums[senderButton.tag].price 
     newAlbum.artistID = self.artistID! 
     newAlbum.artistName = self.artistName! 
     newAlbum.numberOfSongs = (String)(self.albums[senderButton.tag].trackCount) 
     newAlbum.has = [] 

     println(self.songs) 

     for(var i = 1; i < self.albums[senderButton.tag].trackCount - 1; i++){ 
      newSong.collectionID = String(self.songs[i].collectionID) 
      newSong.previewURL = self.songs[i].previewURL 
      newSong.songName = self.songs[i].title 
     } 

     self.managedObjectContext?.save(&self.error) 
     println(newAlbum) 
    } 

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 

     let cell: AlbumTableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! AlbumTableViewCell 

     cell.albumCellButton.tag = indexPath.row 
     cell.albumCellButton.addTarget(self, action: "download:", forControlEvents: .TouchUpInside) 

     let album = self.albums[indexPath.row] 
     cell.albumName.text = album.title 
     cell.artistImage.image = UIImage(named: "user7.png") 
     cell.numberOfSongs.text = (String)(album.trackCount) + " Songs" 


     // Get the formatted price string for display in the subtitle 
     let formattedPrice = album.price 

     // Grab the artworkUrl60 key to get an image URL for the app's thumbnail 
     let urlString = album.thumbnailImageURL 

     // Check our image cache for the existing key. This is just a dictionary of UIImages 
     var image = self.imageCache[urlString] 


     if(image == nil) { 
      // If the image does not exist, we need to download it 
      var imgURL: NSURL = NSURL(string: urlString)! 

      // Download an NSData representation of the image at the URL 
      let request: NSURLRequest = NSURLRequest(URL: imgURL) 
      NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in 
       if error == nil { 
        image = UIImage(data: data) 

        // Store the image in to our cache 
        self.imageCache[urlString] = image 
        dispatch_async(dispatch_get_main_queue(), { 
         if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) as?AlbumTableViewCell { 
          cellToUpdate.artistImage.image = image 
         } 
        }) 
       } 
       else { 
        println("Error: \(error.localizedDescription)") 
       } 
      }) 

     } 
     else { 
      dispatch_async(dispatch_get_main_queue(), { 
       if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) as?AlbumTableViewCell { 
        cellToUpdate.artistImage.image = image 
       } 
      }) 
     } 

     cell.priceOfAlbum.text = formattedPrice 

     return cell 
    } 

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 

    } 

    func didReceiveAPIResults(results: NSDictionary) { 
     var resultsArr: NSArray = results["results"] as! NSArray 
     dispatch_async(dispatch_get_main_queue(), { 
      self.albums = Album.albumsWithJSON(resultsArr) 
      self.tableView!.reloadData() 
      UIApplication.sharedApplication().networkActivityIndicatorVisible = false 
     }) 
    } 

    func didReceiveAPISongResults(results: NSDictionary) { 
     var resultsArr: NSArray = results["results"] as! NSArray 
     dispatch_async(dispatch_get_main_queue(), { 
      self.songs = Song.songsWithJSON(resultsArr) 
      self.tableView!.reloadData() 
      UIApplication.sharedApplication().networkActivityIndicatorVisible = false 
     }) 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     self.navigationItem.title = artistName 
     artistNameOutlet.text = " Albums" 

     api = searchAPIController(delegate: self) 
     songapi = songAPIControllerForCoreData(delegate: self) 

     UIApplication.sharedApplication().networkActivityIndicatorVisible = true 

     api!.searchItunesForAlbumsBelongingTo(self.artistName!, id: self.artistID!) 

     // Do any additional setup after loading the view. 
    } 

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 

     let songsController = segue.destinationViewController as! SongsViewController 

     var albumCollectionID = self.albums 
     var albumIndex = tableView!.indexPathForSelectedRow()!.row 
     var collectionID = self.albums[albumIndex].collectionID 
     var albumName = self.albums[albumIndex].title 

     songsController.albumName = albumName 
     songsController.collectionID = collectionID 

    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 

} 
+0

我會遍歷'searchItunesForSongsBelongingTo'並檢查您是否傳遞正確的字符串。嘗試分解調用這個函數的行來查看你正在組裝的各種組件。 – Mundi

+0

我檢查並且正確的字符串正在通過。然而,在songapi!.searchItunesForSongsBelongingTo((String)(self.albums [senderButton.tag] .collectionID))調用之後,函數didReceiveAPISongResults永遠不會被調用,即使它應該是。我只是不知道爲什麼:S也許代理或協議有問題,我仍然沒有確切的處理它們,或者可能存在線程問題? – mcsahin

回答

0

你需要寫你的協議的定義,就像如下:

protocol songAPIControllerForCoreDataProtocol : class { 
    func didReceiveAPISongResults(results: NSDictionary) 
} 

這會使它只對類進行協議並強制確認類型具有引用語義。如果沒有指定'class'關鍵字,它將具有值語義。

沒有'class'關鍵字,我想這裏的問題是通過初始化設置委託。當您通過委託,如:

songapi = songAPIControllerForCoreData(delegate: self) 

這將承擔委託參數去是對價值型和複製的價值,而不是發送它的一個參考。所以當你在init()中設置該值時,委託成員將指向一個新對象,而不是傳遞給UIViewController。

如果設置喜歡的委託:

songapi.delegate = self 

將沒有協議定義的「類」的關鍵字工作。

+0

將類添加到協議定義中給了我相同的結果:在songapi!.searchItunesForSongsBelongingTo((String)(self.albums [senderButton.tag]。collectionID))調用之後,函數didReceiveAPISongResults永遠不會被調用,即使它應該是。設置代表如songapi.delegate = self而不添加'class'關鍵字也不起作用。現在songapi變成零,在調用songapi時不能解開包裝!.searchItunesForSongsBelongingTo((String)(self.albums [senderButton.tag] .collectionID)) – mcsahin

相關問題