SwiftでFlickrの人気写真を見るアプリを100行で作ったよ
Swiftのビッグウェーブに乗って自分も何かアプリを一つ作ってみようと思い、FlickrのAPIを叩いて人気写真一覧を表示するアプリを作ってみました。 この記事は @himara2 さんの記事「SwiftでTiqav APIを叩くビューワアプリを100行でつくったよ」に触発されて書いたものです。TableViewのサンプルは見かけたので、このアプリではCollectionViewで実装したのと、通信をする際に今までのObjective-Cで多くの人が使っているAFNetworkingを使ってみたのでそこらへんが参考になれば幸いです。ソースコードはgithubにあげたので、おかしなところがあればこちらにpull requestください。

アプリの仕様
- アプリを起動するとFlickr人気写真一覧を取得するAPIにリクエスト
- グリッドビューで取得した写真を表示
- 上のナビゲーションバーのセグメントをタップするとレイアウトを切り替え
- 写真をタップすると写真詳細Viewへ画面遷移
Movie
Screenshot
実装の要点
- Flickr APIへGETリクエスト
- AFHTTPRequestOperationManagerのGET()メソッドを使う
- 写真をグリッド表示
- UICollectionViewを使う
- 画像の非同期読み込み処理
- AFNetworkingのUIImageView拡張(UIImageView+AFNetworking)を使う
ソースコード
Githubにあげました。Xcode6でビルドできます。落としたあと、pod installしてから開いてください。
実装は主にこのファイル( ViewController.swift )にあります。
// ViewController.swift
import UIKit
// レイアウトタイプをenumで定義
enum LayoutType: Int {
case Grid = 0
case List = 1
}
class ViewController: UICollectionViewController {
var photos:Dictionary[] = []
var layoutType = LayoutType.Grid
override func viewDidLoad() {
super.viewDidLoad()
getFlickrPhotos()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// Flickr APIにリクエスト
func getFlickrPhotos() {
let manager :AFHTTPRequestOperationManager = AFHTTPRequestOperationManager()
let url :String = "https://api.flickr.com/services/rest/"
let parameters :Dictionary = [
"method" : "flickr.interestingness.getList",
"api_key" : "86997f23273f5a518b027e2c8c019b0f",
"per_page" : "300",
"format" : "json",
"nojsoncallback" : "1",
"extras" : "url_q,url_z",
]
SVProgressHUD.show()
manager.GET(url, parameters: parameters, success: requestSuccess, failure: requestFailure)
}
func requestSuccess (operation :AFHTTPRequestOperation!, responseObject :AnyObject!) -> Void {
SVProgressHUD.dismiss()
self.photos = responseObject.objectForKey("photos").objectForKey("photo") as Array
self.collectionView.reloadData()
NSLog("requestSuccess \(responseObject)")
}
func requestFailure (operation :AFHTTPRequestOperation!, error :NSError!) -> Void {
SVProgressHUD.dismiss()
NSLog("requestFailure: \(error)")
}
// CollectionView関連のメソッド
override func collectionView(collectionView: UICollectionView!, numberOfItemsInSection section: Int) -> Int {
return self.photos.count;
}
override func collectionView(collectionView: UICollectionView!, cellForItemAtIndexPath indexPath: NSIndexPath!) -> UICollectionViewCell! {
var photoCell: PhotoCell = self.collectionView.dequeueReusableCellWithReuseIdentifier("PhotoCell", forIndexPath: indexPath) as PhotoCell
var photoInfo = photos[indexPath.item] as Dictionary
var photoUrl = (self.layoutType == LayoutType.Grid) ? photoInfo["url_q"] : photoInfo["url_z"]
// UIImageView+AFNetworkingの画像を非同期で読んでくれるメソッドを実行
photoCell.photoImageView.setImageWithURL(NSURL.URLWithString(photoUrl))
photoCell.photoInfo = photoInfo
return photoCell;
}
func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize {
var itemSize :CGSize = (self.layoutType == LayoutType.Grid) ? CGSizeMake(80, 80) : CGSizeMake(320, 150)
return itemSize
}
// SegmentedControlの値が変わった時に呼ばれるメソッド
@IBAction func segmentedControlDidChanged(control : UISegmentedControl) {
switch control.selectedSegmentIndex {
case 0:
self.layoutType = LayoutType.Grid
case 1:
self.layoutType = LayoutType.List
default:
self.layoutType = LayoutType.Grid
}
self.collectionView.reloadData()
}
// 画面遷移時にデータを受け渡すための実装
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "ShowPhoto" {
var photoCell : PhotoCell = sender as PhotoCell
var photoViewController = segue.destinationViewController as PhotoViewController
photoViewController.photoInfo = photoCell.photoInfo
}
}
}
やってみての気付き
- CocoaPodsが問題なく使える
- Objective-Cで書かれた既存のライブラリが問題なく(簡単に)使える
- 少なくとも今回使ったAFNetworkingやSVProgressHUDは問題なく使えた
- Objective-Cのライブラリを使う手順は下↓に書きます
- 探してないけど
#pragma mark -に相当するものはなくなったのだろうか。寂しい - 今までクラスを作る度に生成されていた .h .m と2つのファイルが .swift の一つのファイルにまとまりスッキリする
- コード自体もより簡潔になってスッキリする
- 慣れないところは前のObjective-Cのほうがいいなと思ってしまうけど、Swiftに慣れたらすぐ忘れそう
- たまにObjective-Cとの違いに戸惑う
- initメソッドを実装していないと怒られるとか、Dictionaryに型指定が必要とか
Objective-Cで書かれた既存のライブラリを使う方法
このApple公式ドキュメント「Swift and Objective-C in the Same Project」に書かれている。要は「ProjectName-Bridging-Header.h」ファイルを作って、そこに使いたいObjective-Cのヘッダーファイルをインポートすればよい。
- Objective-C Fileを新規で一つ作る
- すると公式ドキュメントにあるようにダイアログで聞かれるので「YES」を選択する
- ProjectName-Bridging-Header.h ファイルが作られる
- ここに既存のライブラリのヘッダーファイルをインポートするコードを書いていく → こんな感じ
- Swift側で読み込んだObjective-Cのライブラリが使えるようになっている
感想
Swiftを使ってみる前は、今までのUIKitとかの資産はどうなるんだろう、ちゃんと使えるのかな、という疑問があったのですが、UIKitの資産は既に問題なく使えるようになっていました。それだけでなく、既存のOSSなどのObjective-Cソースも上記の方法で簡単に使えることが分かりました。「ProjectName-Bridging-Header.h」を作ってそこでファイルをインポートするだけで、今まで [SVProgressHUD show]; とか書いていたのが SVProgressHUD.show() として使えるようになります。SwiftとObjective-Cの共存もできますし、移行は想像以上にスムーズにできそうだと感じました。発表されたばかりで、大きな問題なくもうiPhoneアプリが作れてしまう状態になっているのに驚きです。それよりも、今作ってもまだアプリのリリースができないことと、現在はXcode自体が落ちたりエラーをはかずによく分からないところでアプリが落ちたりするので、もうちょっと安定してからがっつり取り組んだほうが効率がよいかもしれません。
参考
以下の記事を参考にさせていただきました。ありがとうございます。
- SwiftのRSS Readerを100行で作ったよ
- SwiftでTiqav APIを叩くビューワアプリを100行でつくったよ
- 今 Swift や iOS 8 について書くのは NDA 違反か調べてみた
- https://www.facebook.com/groups/ios.dev.jp/permalink/741904485830935/
- Swift Cheat Sheet and Quick Reference
- [iOS] 新言語SwiftがObjective-Cよりも良いところ
- Swift and Objective-C in the Same Project
- The Swift Programming Language