iTunes APIを使ったiOSアプリハンズオン(Swift)

More than 3 years have passed since last update.


iTunes APIとは

https://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html

登録不要で使えて、例えば、

https://itunes.apple.com/search?term=beatles&country=JP&lang=ja_jp&media=music

で、「beatles」を検索すると、

{

"resultCount":50,
"results": [
{
"wrapperType":"track",
"kind":"song",
"artistId":136975,
"collectionId":458032395,
"trackId":458032429,
"artistName":"ビートルズ",
"collectionName":"1",
"trackName":"Let It Be",
"collectionCensoredName":"1",
"trackCensoredName":"Let It Be",
"artistViewUrl":"https://itunes.apple.com/jp/artist/bitoruzu/id136975?uo=4",
"collectionViewUrl":"https://itunes.apple.com/jp/album/let-it-be/id458032395?i=458032429&uo=4",
"trackViewUrl":"https://itunes.apple.com/jp/album/let-it-be/id458032395?i=458032429&uo=4",
"previewUrl":"http://a1770.phobos.apple.com/us/r1000/059/Music4/v4/cf/b4/36/cfb43624-f3e0-269f-f81a-911f9876732c/mzaf_4722855266064184302.plus.aac.p.m4a",
"artworkUrl30":"http://is5.mzstatic.com/image/pf/us/r30/Features/85/fe/95/dj.kfrgxzbp.30x30-50.jpg",
"artworkUrl60":"http://is4.mzstatic.com/image/pf/us/r30/Features/85/fe/95/dj.kfrgxzbp.60x60-50.jpg",
"artworkUrl100":"http://is1.mzstatic.com/image/pf/us/r30/Features/85/fe/95/dj.kfrgxzbp.100x100-75.jpg",
"collectionPrice":2000.00,
"trackPrice":250.00,
"releaseDate":"2000-11-13T08:00:00Z",
"collectionExplicitness":"notExplicit",
"trackExplicitness":"notExplicit",
"discCount":2,
"discNumber":1,
"trackCount":27,
"trackNumber":26,
"trackTimeMillis":230773,
"country":"JPN",
"currency":"JPY",
"primaryGenreName":"ロック"
}, {
...
}, {
...
}
]
}

が返ってきます。

今回は、


  • trackName(曲や動画のタイトル)

  • artistName(アーティスト名)

  • artworkUrl100(アートワーク画像URL)

  • previewUrl(曲や動画の試聴用データURL)

あたりを使って、iOSアプリを作ってみます。


CocoaPodsをインストールする

CocoaPodsは、Objective-CやSwiftで書かれたライブラリを管理するために広く使われているツールです。AFNetworkingのインストールもこれを使います。すでにインストールされている場合は飛ばしてください。

https://cocoapods.org

$ sudo gem install cocoapods


プロジェクトを作成する

Xcodeを開きます。以下のWelcome to Xcodeな画面が表示される場合はCreate a new Xcode Project、表示されない場合はメニューバーからFile > New > Project...。

スクリーンショット 2015-06-02 9.26.19.png

スクリーンショット 2015-06-02 9.30.42.png

Product Nameはアプリ名です。Organization Nameは個人名や会社名です。Organization Identifierは、世界でユニークな識別子を書く必要があり、普通は会社やサービスのドメインの逆順ですが、サンプルなので適当でOKです。Languageは今回はSwiftを使います。Devicesは今回はiPhoneを使いますが、Universalを使うとiPhoneとiPadを両方対応したアプリになります。Core DataはローカルDBの一種で今回は使いません。

スクリーンショット 2015-06-02 9.31.18.png

適当な場所に保存してください。後でターミナルやFinderでプロジェクトディレクトリまで移動しますので、場所を覚えておいてください。

スクリーンショット 2015-06-02 9.31.49.png


AFNetworkingをインストールする


Podfileを作成する

File > New > File... > iOS Other > Emptyから、プロジェクトディレクトリ直下にPodfileをCreate。


Podfileに設定を書く

platform :ios, "8.0"

pod 'AFNetworking', '~> 2.0'
pod 'AFNetworkActivityLogger', '~> 2.0'


インストールする

$ cd プロジェクトディレクトリ

$ pod install


xcworkspaceファイルからプロジェクトを開き直す

Xcodeを閉じます。Finderでプロジェクトディレクトリに移動して、xcworkspaceファイルを開きます。

プロジェクトを作成するとxcodeprojファイルが作成され、pod installするとxcworkspaceファイルが作成されます。CocoaPodsでライブラリ管理しているプロジェクトは、xcodeprojファイルではなく、xcworkspaceファイルから開きます。


AFNetworkingを設定する


Objective-Cのクラスを使うために、bridge headerを設定する

File > New > File... > iOS Source > Objective-C Fileから、適当なObjective-Cのファイルを作ると、bridge headerを自動設定できます(Yesを選択)。自分で名付けたファイルの他に、プロジェクト名-Bridging-Header.hが作成されて、これがbridge headerです。作ったObjective-Cのファイル(hファイルとmファイル)は不要なので、fn + deleteで消しちゃっていいです(Move to Trashを選択)。手動でやってもいいけど、やり方を忘れるので、この方法が楽です。

スクリーンショット 2015-06-01 23.48.20.png


bridge headerにimportを書く

プロジェクト名-Bridging-Header.hを開いて、以下を書きます。

#import <AFNetworking.h>

#import <UIImageView+AFNetworking.h>
#import <AFNetworkActivityLogger.h>


ログを出力する設定を書く

AppDelegate.swiftを開いて、以下を書きます。application:didFinishLaunchingWithOptions:は、アプリの起動が終わったら呼ばれるところです。

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

AFNetworkActivityLogger.sharedLogger().level = .AFLoggerLevelDebug
AFNetworkActivityLogger.sharedLogger().startLogging()
return true
}


iTunes APIと通信して、結果を一覧画面に表示する


StoryboardにUINavigationControllerとUITableViewControllerを追加する

Main.storyboardを開きます。もともと書かれているViewControllerは消しちゃってください(放置しててもいいですけど)。

スクリーンショット 2015-06-01 15.14.49.png

スクリーンショット 2015-06-01 15.13.39.png

NavigationControllerを選択して、Initial View Controllerに設定します。Initial View Controllerは、このStoryboardにおける最初のViewControllerになります。左端に、切れた矢印が付いているのがInitial View Controllerです。

スクリーンショット 2015-06-01 15.16.34.png


UITableViewControllerにUISearchBarを追加する

スクリーンショット 2015-06-01 15.22.53.png

スクリーンショット 2015-06-01 15.23.32.png


ListViewControllerを作る

File > New > File... > iOS Source > Cocoa Touch Classから作成します。

スクリーンショット 2015-06-01 15.29.03.png

Main.storyboardを開いて、ListViewControllerをUITableViewControllerのCustom Classに設定します。

スクリーンショット 2015-06-01 15.30.34.png


CellのreuseIdentifierを設定する

スクリーンショット 2015-06-01 15.32.33.png


ListViewControllerにUISearchBarのoutletを繋ぐ

これを押して、Editorを2ペイン表示にします。左ペインにStoryboardを表示しているときは、右ペインに対応するViewController or Viewのソースファイルが表示されます。

スクリーンショット 2015-06-01 15.43.26.png

スクリーンショット 2015-06-01 15.35.20.png

スクリーンショット 2015-06-01 15.35.49.png

黒ポチにマウスオーバーすると、outletしてるViewがハイライトします。

スクリーンショット 2015-06-01 15.36.10.png


ListViewControllerに検索結果を覚えておくプロパティを追加する

ListViewController.swiftを開いて、以下を追加します。

class ListViewController: UITableViewController {

private var results: [NSDictionary]?
}


UISearchBarDelegateを使って、検索の処理を実装する

ListViewController.swiftの一番下に以下を追加します。

extension ListViewController: UISearchBarDelegate {

// searchBarのSearchボタンをタップしたときの処理
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchBar.resignFirstResponder() // キーボードを閉じる

// url encode 例. スピッツ > %83X%83s%83b%83c
let text = searchBar.text.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
if let text = text {
AFHTTPSessionManager().GET(
"https://itunes.apple.com/search?term=\(text)&country=JP&lang=ja_jp&media=music",
parameters: nil,
success: { (task: NSURLSessionDataTask!, response: AnyObject!) -> Void in
if let data = response as? NSDictionary, results = data["results"] as? [NSDictionary] {
self.results = results
self.tableView.reloadData() // 再描画
}
},
failure: nil)
}
}
}


ListViewControllerをUISearchBarのdelegateに設定する

スクリーンショット 2015-06-01 16.08.13.png


検索結果を表示する処理(UITableViewDetaSource)を実装する

ListViewController.swiftには、同じメソッドがすでに書かれていますので、そこに追加してください。コメントアウトされている場合は、コメントを外して書きます(「/*」と「*/」を消します)。

セクション数(Contactでいうところの「あ」「か」「さ」などにあたる部分)は1つだけ使います。

   override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}

行数は検索結果の個数です。検索結果がない場合(まだ検索していない場合)は0です。

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

// #warning Incomplete method implementation.
// Return the number of rows in the section.
return results?.count ?? 0
}

行に表示するセルを準備します。

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

let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
if let result = results?[indexPath.row] {
cell.textLabel?.text = result["trackName"] as? String
}
return cell
}


実行してみましょう

スクリーンショット 2015-06-01 16.03.42.png

シミュレータが大きくて画面からはみ出す場合は、command + 2〜command + 5で小さくなります。command + 1で原寸に戻ります。


一覧画面のセルをカスタマイズする、画像も表示する


UIImageViewやUILabelをUITableViewCellに配置する

スクリーンショット 2015-06-01 16.31.33.png

スクリーンショット 2015-06-01 16.32.03.png

スクリーンショット 2015-06-01 16.35.01.png

スクリーンショット 2015-06-01 16.36.16.png


ListCellを作成する

ListViewController.swiftを開いて、一番下に追加します。新しいswiftファイルを作ってもいいです。

class ListCell: UITableViewCell {

}

Main.storyboardを開いて、ListCellをUITableViewCellのCustom Classに設定します。

スクリーンショット 2015-06-01 16.46.53.png


ListCellにUIImageViewやUILabelのoutletを繋ぐ

スクリーンショット 2015-06-01 16.50.31.png

スクリーンショット 2015-06-01 16.52.00.png

残り2つも同様に。

スクリーンショット 2015-06-01 16.52.44.png

スクリーンショット 2015-06-01 16.53.52.png

スクリーンショット 2015-06-01 16.54.05.png


セルを準備する処理も変更する

ListViewController.swiftを開いて、先ほど書いた処理のセルを準備する部分(tableView:cellForRowAtIndexPath:)を、以下の様に書き換えます。

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

let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! ListCell
if let result = results?[indexPath.row] {
if let artworkUrl = result["artworkUrl100"] as? String {
cell.artworkImageView.setImageWithURL(NSURL(string: artworkUrl))
} else {
cell.artworkImageView.image = nil
}
cell.trackLabel.text = result["trackName"] as? String
cell.artistLabel.text = result["artistName"] as? String
}
return cell
}


Auto Layoutを設定する

Auto Layoutを設定すると、デバイス(3.5 inch, 4 inch, 4.7 inch, 5.5 inch, iPad...)や縦横によって変わる画面サイズに合わせて、その制約に基づいて配置してくれます。

今回、設定するAuto Layoutのイメージはこんな感じです。加えて、UIImageViewの上下左は0 pxです。それ以外の記載のないところはフリーになっています。UIImageViewの位置やサイズは常に固定で、2つのUILabelの幅が伸び縮みするようになります。

スクリーンショット 2015-06-01 18.40.23.png

UIImageViewの上下左をCellから0 pxの距離に固定、幅と高さを44 pxに固定します。

スクリーンショット 2015-06-01 17.20.23.png

上のUILabelの上をCellから4 pxの距離に固定、右を8 pxの距離に固定します。

スクリーンショット 2015-06-01 17.21.17.png

UIImageViewと上のUILabelの間の水平距離を8 pxに固定します。

スクリーンショット 2015-06-01 17.22.45 のコピー 2.png

スクリーンショット 2015-06-01 17.21.36.png

スクリーンショット 2015-06-01 17.47.48.png

スクリーンショット 2015-06-01 17.48.00.png

下のUILabelの下をCellから4 pxの距離に固定、右を8 pxの距離に固定します。

スクリーンショット 2015-06-01 17.22.18.png

UIImageViewと下のUILabelの間の水平距離を8 pxに固定します。

スクリーンショット 2015-06-01 17.22.45 2.png

スクリーンショット 2015-06-01 17.21.36.png

スクリーンショット 2015-06-01 17.53.12.png

スクリーンショット 2015-06-01 17.48.00.png

Auto Layoutとプレビューに矛盾があるため、警告(黄色の矢印)が表示されています。警告をクリックして、Auto Layoutに合わせてプレビューを自動修正します。

スクリーンショット 2015-06-01 17.54.34.png

スクリーンショット 2015-06-01 17.23.03.png

スクリーンショット 2015-06-01 17.23.11.png

Update Frameは、Auto Layoutに合わせてプレビューを自動修正します。Update Constraintsは、プレビューに合わせてAuto Layoutを自動修正します。Update ConstraintsでAuto Layoutの方を自動修正されるとわけがわからなくなるので、自分でAuto Layoutを正しく設定した後でUpdate Frameでプレビューを自動修正させた方がいいです。

Auto Layoutが不足している、重複しているAuto Layoutがある、Auto Layout同士が矛盾している、などの場合は、警告(黄色の矢印)ではなく、エラー(赤色のバツ)が表示されます。エラーの状態によっては、Xcodeが修正案を提案してくれますが、どこが自動修正されたのかよくわからず、正しく動かない場合もあるので、自分で全て設定した方がいいです。わけがわからなくなった場合は、面倒くさいですが、一度Auto Layoutを削除して、設定し直します。

スクリーンショット 2015-06-01 18.07.03.png


実行してみましょう

スクリーンショット 2015-06-01 16.03.42.png

command + ←とcommand + →でシミュレータが回転します。標準設定では、逆さ位置は無効になっているので、回転して逆さにすると、直前の横のままの画面が表示されます(4方向のうち、どこを有効するかは設定可能)。


詳細画面に遷移して、試聴する


StoryboardにAVPlayerViewControllerを追加する

Main.storyboardを開きます。

スクリーンショット 2015-06-01 13.58.31.png


UITableViewCellからAVPlayerViewControllerへsegue(show)をつなぐ

スクリーンショット 2015-06-01 14.01.30.png

矢印をクリックしてハイライトしてるときに、同時にCell全体がハイライトされていれば正しく接続されています。UILabelなど、Cellの一部がハイライトしている場合は、矢印をdeleteで消してやり直し。

スクリーンショット 2015-06-01 14.02.03.png

Cellが掴みづらいときは、こっちの方法でもOK。

スクリーンショット 2015-06-01 14.06.25.png


DetailViewControllerを作る

File > New > File... > iOS Source > Cocoa Touch Classから作成します。

スクリーンショット 2015-06-01 13.54.53.png

DetailViewController.swiftを開いて、AVFoundationとAVKitのimportを追加します。

import UIKit

import AVFoundation
import AVKit

class DetailViewController: AVPlayerViewController {

}

Main.storyboardを開いて、DetailViewControllerをAVPlayerViewControllerのCustom Classに設定します。

スクリーンショット 2015-06-01 14.27.08.png


DetailViewControllerにデータを渡す

DetailViewController.swiftを開いて、以下のプロパティを追加します。

class DetailViewController: AVPlayerViewController {

var trackName: String!
var previewUrl: String?
}

ListViewController.swiftを開いて、ListViewControllerからDetailViewControllerのプロパティを設定します。prepareForSegue:sender:は、segue(≒ 画面遷移)が実行される前に呼ばれるところです。おそらく下の方に、もともとコメントアウトされて書かれてますので、コメントを外して書いてください(「/*」と「*/」を消します)。

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

if let vc = segue.destinationViewController as? DetailViewController {
if let indexPath = tableView.indexPathForSelectedRow(), result = results?[indexPath.row] {
vc.trackName = result["trackName"] as! String
vc.previewUrl = result["previewUrl"] as? String
}
}
}


AVPlayerを使って再生する

DetailViewController.swiftを開いて、以下を追加します。viewDidLoadは、ViewControllerの準備ができたら呼ばれるところです。

class DetailViewController: AVPlayerViewController {

override func viewDidLoad() {
super.viewDidLoad()

title = trackName // title設定

if let previewUrl = previewUrl {
player = AVPlayer(URL: NSURL(string: previewUrl))
player.play() // 自動再生
}
}
}


実行してみましょう

スクリーンショット 2015-06-01 16.03.42.png

おわり。お疲れさまでした。


完成品はここにあります

https://codebreak.com/git/kenchan1837/iTunesMusicSearch/

CocoaPodsをインストールして、ターミナルでプロジェクトディレクトリに移動してpod installして、Finderでプロジェクトディレクトリに移動してxcworkspaceファイルからXcodeを起動して、再生ボタンを押したら動きます。