Skip to content

Commit b029717

Browse files
committed
Part 6
1 parent 363bed8 commit b029717

File tree

6 files changed

+199
-35
lines changed

6 files changed

+199
-35
lines changed

MusicPlayer.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
0270481F19C8E08B00FDA1C5 /* MusicPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0270481E19C8E08B00FDA1C5 /* MusicPlayerTests.swift */; };
1616
0270482919C8E2E700FDA1C5 /* APIController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0270482819C8E2E700FDA1C5 /* APIController.swift */; };
1717
0270482B19C8EC3000FDA1C5 /* Blank52.png in Resources */ = {isa = PBXBuildFile; fileRef = 0270482A19C8EC3000FDA1C5 /* Blank52.png */; };
18+
0270482D19C8F35700FDA1C5 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0270482C19C8F35700FDA1C5 /* Album.swift */; };
19+
0270482F19C8F43500FDA1C5 /* DetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0270482E19C8F43500FDA1C5 /* DetailsViewController.swift */; };
1820
/* End PBXBuildFile section */
1921

2022
/* Begin PBXContainerItemProxy section */
@@ -40,6 +42,8 @@
4042
0270481E19C8E08B00FDA1C5 /* MusicPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicPlayerTests.swift; sourceTree = "<group>"; };
4143
0270482819C8E2E700FDA1C5 /* APIController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIController.swift; sourceTree = "<group>"; };
4244
0270482A19C8EC3000FDA1C5 /* Blank52.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Blank52.png; sourceTree = "<group>"; };
45+
0270482C19C8F35700FDA1C5 /* Album.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; };
46+
0270482E19C8F43500FDA1C5 /* DetailsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailsViewController.swift; sourceTree = "<group>"; };
4347
/* End PBXFileReference section */
4448

4549
/* Begin PBXFrameworksBuildPhase section */
@@ -89,6 +93,8 @@
8993
0270481119C8E08B00FDA1C5 /* LaunchScreen.xib */,
9094
0270480619C8E08B00FDA1C5 /* Supporting Files */,
9195
0270482819C8E2E700FDA1C5 /* APIController.swift */,
96+
0270482C19C8F35700FDA1C5 /* Album.swift */,
97+
0270482E19C8F43500FDA1C5 /* DetailsViewController.swift */,
9298
);
9399
path = MusicPlayer;
94100
sourceTree = "<group>";
@@ -222,6 +228,8 @@
222228
0270480B19C8E08B00FDA1C5 /* SearchResultsViewController.swift in Sources */,
223229
0270480919C8E08B00FDA1C5 /* AppDelegate.swift in Sources */,
224230
0270482919C8E2E700FDA1C5 /* APIController.swift in Sources */,
231+
0270482D19C8F35700FDA1C5 /* Album.swift in Sources */,
232+
0270482F19C8F43500FDA1C5 /* DetailsViewController.swift in Sources */,
225233
);
226234
runOnlyForDeploymentPostprocessing = 0;
227235
};

MusicPlayer/APIController.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ protocol APIControllerProtocol {
1414

1515
class APIController {
1616

17-
var delegate: APIControllerProtocol?
17+
var delegate: APIControllerProtocol
1818

19-
init() {
19+
init(delegate: APIControllerProtocol) {
20+
self.delegate = delegate
2021
}
2122

2223
func searchItunesFor(searchTerm: String) {
@@ -26,7 +27,7 @@ class APIController {
2627

2728
// Now escape anything else that isn't URL-friendly
2829
if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {
29-
let urlPath = "http://itunes.apple.com/search?term=\(escapedSearchTerm)&media=software"
30+
let urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music&entity=album"
3031
let url: NSURL = NSURL(string: urlPath)
3132
let session = NSURLSession.sharedSession()
3233
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
@@ -43,7 +44,7 @@ class APIController {
4344
println("JSON Error \(err!.localizedDescription)")
4445
}
4546
let results: NSArray = jsonResult["results"] as NSArray
46-
self.delegate?.didReceiveAPIResults(jsonResult)
47+
self.delegate.didReceiveAPIResults(jsonResult)
4748
})
4849

4950
task.resume()

MusicPlayer/Album.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// Album.swift
3+
// MusicPlayer
4+
//
5+
// Created by Jameson Quave on 9/16/14.
6+
// Copyright (c) 2014 JQ Software LLC. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
class Album {
12+
var title: String
13+
var price: String
14+
var thumbnailImageURL: String
15+
var largeImageURL: String
16+
var itemURL: String
17+
var artistURL: String
18+
19+
init(name: String, price: String, thumbnailImageURL: String, largeImageURL: String, itemURL: String, artistURL: String) {
20+
self.title = name
21+
self.price = price
22+
self.thumbnailImageURL = thumbnailImageURL
23+
self.largeImageURL = largeImageURL
24+
self.itemURL = itemURL
25+
self.artistURL = artistURL
26+
}
27+
28+
class func albumsWithJSON(allResults: NSArray) -> [Album] {
29+
30+
// Create an empty array of Albums to append to from this list
31+
var albums = [Album]()
32+
33+
// Store the results in our table data array
34+
if allResults.count>0 {
35+
36+
// Sometimes iTunes returns a collection, not a track, so we check both for the 'name'
37+
for result in allResults {
38+
39+
var name = result["trackName"] as? String
40+
if name == nil {
41+
name = result["collectionName"] as? String
42+
}
43+
44+
// Sometimes price comes in as formattedPrice, sometimes as collectionPrice.. and sometimes it's a float instead of a string. Hooray!
45+
var price = result["formattedPrice"] as? String
46+
if price == nil {
47+
price = result["collectionPrice"] as? String
48+
if price == nil {
49+
var priceFloat: Float? = result["collectionPrice"] as? Float
50+
var nf: NSNumberFormatter = NSNumberFormatter()
51+
nf.maximumFractionDigits = 2
52+
if priceFloat != nil {
53+
price = "$"+nf.stringFromNumber(priceFloat!)
54+
}
55+
}
56+
}
57+
58+
let thumbnailURL = result["artworkUrl60"] as? String ?? ""
59+
let imageURL = result["artworkUrl100"] as? String ?? ""
60+
let artistURL = result["artistViewUrl"] as? String ?? ""
61+
62+
var itemURL = result["collectionViewUrl"] as? String
63+
if itemURL == nil {
64+
itemURL = result["trackViewUrl"] as? String
65+
}
66+
67+
var newAlbum = Album(name: name!, price: price!, thumbnailImageURL: thumbnailURL, largeImageURL: imageURL, itemURL: itemURL!, artistURL: artistURL)
68+
albums.append(newAlbum)
69+
}
70+
}
71+
return albums
72+
}
73+
74+
}

MusicPlayer/Base.lproj/Main.storyboard

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="7E4-YX-Fm0">
33
<dependencies>
44
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
5+
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
56
</dependencies>
67
<scenes>
78
<!--Search Results View Controller-->
@@ -39,6 +40,9 @@
3940
</label>
4041
</subviews>
4142
</tableViewCellContentView>
43+
<connections>
44+
<segue destination="RmI-eG-YqA" kind="show" id="bDR-vB-eU4"/>
45+
</connections>
4246
</tableViewCell>
4347
</prototypes>
4448
<connections>
@@ -49,12 +53,72 @@
4953
</subviews>
5054
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
5155
</view>
56+
<navigationItem key="navigationItem" id="yYp-Li-PKz"/>
5257
<connections>
5358
<outlet property="appsTableView" destination="ODz-Kv-YZf" id="GMq-6f-r3P"/>
5459
</connections>
5560
</viewController>
5661
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
5762
</objects>
63+
<point key="canvasLocation" x="601" y="-557"/>
64+
</scene>
65+
<!--Details View Controller-->
66+
<scene sceneID="UqD-Dn-mc1">
67+
<objects>
68+
<viewController storyboardIdentifier="DetailsViewController" id="RmI-eG-YqA" customClass="DetailsViewController" customModule="MusicPlayer" customModuleProvider="target" sceneMemberID="viewController">
69+
<layoutGuides>
70+
<viewControllerLayoutGuide type="top" id="jIA-3D-enV"/>
71+
<viewControllerLayoutGuide type="bottom" id="ZRL-jn-HDo"/>
72+
</layoutGuides>
73+
<view key="view" contentMode="scaleToFill" id="tx9-2C-w8M">
74+
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
75+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
76+
<subviews>
77+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tgX-ID-J9t">
78+
<rect key="frame" x="131" y="87" width="453" height="52"/>
79+
<fontDescription key="fontDescription" type="system" pointSize="17"/>
80+
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
81+
<nil key="highlightedColor"/>
82+
</label>
83+
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kNM-6v-NNF">
84+
<rect key="frame" x="16" y="87" width="100" height="100"/>
85+
</imageView>
86+
</subviews>
87+
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
88+
<constraints>
89+
<constraint firstItem="kNM-6v-NNF" firstAttribute="top" secondItem="jIA-3D-enV" secondAttribute="bottom" constant="23" id="1tc-ry-Twa"/>
90+
<constraint firstItem="tgX-ID-J9t" firstAttribute="top" secondItem="jIA-3D-enV" secondAttribute="bottom" constant="23" id="GsN-uA-1Oz"/>
91+
<constraint firstItem="kNM-6v-NNF" firstAttribute="leading" secondItem="tx9-2C-w8M" secondAttribute="leadingMargin" id="Kov-2n-EHq"/>
92+
<constraint firstItem="tgX-ID-J9t" firstAttribute="leading" secondItem="kNM-6v-NNF" secondAttribute="trailing" constant="15" id="pdf-qR-eTd"/>
93+
<constraint firstItem="tgX-ID-J9t" firstAttribute="trailing" secondItem="tx9-2C-w8M" secondAttribute="trailingMargin" id="sZi-kR-uEl"/>
94+
</constraints>
95+
</view>
96+
<connections>
97+
<outlet property="albumCover" destination="kNM-6v-NNF" id="Gd3-ly-zMD"/>
98+
<outlet property="titleLabel" destination="tgX-ID-J9t" id="2Ji-Nc-kgE"/>
99+
</connections>
100+
</viewController>
101+
<placeholder placeholderIdentifier="IBFirstResponder" id="pYL-YU-PSv" userLabel="First Responder" sceneMemberID="firstResponder"/>
102+
</objects>
103+
<point key="canvasLocation" x="1233" y="-557"/>
104+
</scene>
105+
<!--Navigation Controller-->
106+
<scene sceneID="YCI-QZ-c2A">
107+
<objects>
108+
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="7E4-YX-Fm0" sceneMemberID="viewController">
109+
<toolbarItems/>
110+
<navigationBar key="navigationBar" contentMode="scaleToFill" id="PlP-C8-xU2">
111+
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
112+
<autoresizingMask key="autoresizingMask"/>
113+
</navigationBar>
114+
<nil name="viewControllers"/>
115+
<connections>
116+
<segue destination="vXZ-lx-hvc" kind="relationship" relationship="rootViewController" id="Yu2-8e-45k"/>
117+
</connections>
118+
</navigationController>
119+
<placeholder placeholderIdentifier="IBFirstResponder" id="nVz-HO-V1X" userLabel="First Responder" sceneMemberID="firstResponder"/>
120+
</objects>
121+
<point key="canvasLocation" x="-22" y="-557"/>
58122
</scene>
59123
</scenes>
60124
</document>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// DetailsViewController.swift
3+
// MusicPlayer
4+
//
5+
// Created by Jameson Quave on 9/16/14.
6+
// Copyright (c) 2014 JQ Software LLC. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
class DetailsViewController: UIViewController {
12+
13+
var album: Album?
14+
15+
@IBOutlet weak var titleLabel: UILabel!
16+
@IBOutlet weak var albumCover: UIImageView!
17+
18+
required init(coder aDecoder: NSCoder) {
19+
super.init(coder: aDecoder)
20+
}
21+
22+
override func viewDidLoad() {
23+
super.viewDidLoad()
24+
titleLabel.text = self.album?.title
25+
albumCover.image = UIImage(data: NSData(contentsOfURL: NSURL(string: self.album!.largeImageURL)))
26+
}
27+
}

MusicPlayer/SearchResultsViewController.swift

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,41 @@ import UIKit
1111
class SearchResultsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, APIControllerProtocol {
1212

1313
@IBOutlet var appsTableView : UITableView?
14-
var tableData = []
15-
var api = APIController()
14+
var albums = [Album]()
15+
var api : APIController?
1616
var imageCache = [String : UIImage]()
1717
let kCellIdentifier: String = "SearchResultCell"
1818

1919
override func viewDidLoad() {
2020
super.viewDidLoad()
21-
api.searchItunesFor("Angry Birds")
22-
api.delegate = self
21+
api = APIController(delegate: self)
22+
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
23+
api!.searchItunesFor("Beatles")
2324
}
2425

2526
override func didReceiveMemoryWarning() {
2627
super.didReceiveMemoryWarning()
2728
// Dispose of any resources that can be recreated.
2829
}
30+
2931

3032
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
31-
return tableData.count
33+
return albums.count
3234
}
3335

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

3638
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as UITableViewCell
3739

38-
var rowData: NSDictionary = self.tableData[indexPath.row] as NSDictionary
39-
40-
// Add a check to make sure this exists
41-
let cellText: String? = rowData["trackName"] as? String
42-
cell.textLabel?.text = cellText
40+
let album = self.albums[indexPath.row]
41+
cell.textLabel?.text = album.title
4342
cell.imageView?.image = UIImage(named: "Blank52")
4443

45-
4644
// Get the formatted price string for display in the subtitle
47-
let formattedPrice: NSString = rowData["formattedPrice"] as NSString
48-
49-
// Jump in to a background thread to get the image for this item
45+
let formattedPrice = album.price
5046

5147
// Grab the artworkUrl60 key to get an image URL for the app's thumbnail
52-
let urlString = rowData["artworkUrl60"] as String
48+
let urlString = album.thumbnailImageURL
5349

5450
// Check our image cache for the existing key. This is just a dictionary of UIImages
5551
//var image: UIImage? = self.imageCache.valueForKey(urlString) as? UIImage
@@ -93,27 +89,21 @@ class SearchResultsViewController: UIViewController, UITableViewDataSource, UITa
9389
return cell
9490
}
9591

92+
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
93+
var detailsViewController: DetailsViewController = segue.destinationViewController as DetailsViewController
94+
var albumIndex = appsTableView!.indexPathForSelectedRow()!.row
95+
var selectedAlbum = self.albums[albumIndex]
96+
detailsViewController.album = selectedAlbum
97+
}
98+
9699
func didReceiveAPIResults(results: NSDictionary) {
97100
var resultsArr: NSArray = results["results"] as NSArray
98101
dispatch_async(dispatch_get_main_queue(), {
99-
self.tableData = resultsArr
102+
self.albums = Album.albumsWithJSON(resultsArr)
100103
self.appsTableView!.reloadData()
104+
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
101105
})
102106
}
103-
104-
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
105-
// Get the row data for the selected row
106-
var rowData: NSDictionary = self.tableData[indexPath.row] as NSDictionary
107-
108-
var name: String = rowData["trackName"] as String
109-
var formattedPrice: String = rowData["formattedPrice"] as String
110-
111-
var alert: UIAlertView = UIAlertView()
112-
alert.title = name
113-
alert.message = formattedPrice
114-
alert.addButtonWithTitle("Ok")
115-
alert.show()
116-
}
117107

118108
}
119109

0 commit comments

Comments
 (0)