SwiftでAlamofireを使ってみた。
パースする内容はQiitaのSwiftに関する投稿。
gifアニメーション
Github
使用したAPI
Qiita APIを使用。(https://qiita.com/docs)
以下はDocumentより抜粋。
GET /api/v1/search
[{"id": 1,
"uuid": "1a43e55e7209c8f3c565",
"user":
{"name": "Hiroshige Umino",
"url_name": "yaotti",
"profile_image_url": "https://si0.twimg.com/profile_images/2309761038/1ijg13pfs0dg84sk2y0h_normal" },
"title": "てすと",
"body": "<p>foooooooooooooooo</p>\n",
"created_at": "2012-10-03 22:12:36 +0900",
"updated_at": "2012-10-03 22:12:36 +0900",
"created_at_in_words": "18 hours ago",
"updated_at_in_words": "18 hours ago",
"tags":
[{"name": "FOOBAR",
"url_name": "FOOBAR",
"icon_url": "http://qiita.com/icons/thumb/missing.png",
"versions": ['1.2', '1.3']}],
"stock_count": 0,
"stock_users": [],
"comment_count": 0,
"url": "http://qiita.com/items/1a43e55e7209c8f3c565",
"gist_url": null,
"tweet": false,
"private": false,
"stocked": false
},
...
]
ソースコード
ViewController
ViewController (UITableViewでリスト表示)
WebViewController (UIWebViewで記事ページを表示)
import UIKit
import Alamofire
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var articles: Array<Article>?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
articles = Array()
// Get articles
var result: NSArray?
Alamofire.request(.GET, "https://qiita.com/api/v1/search?q=swift",parameters: nil, encoding: .JSON)
.responseJSON { (request, response, JSON, error) in
result = (JSON as NSArray)
// Make models from Json data
for (var i = 0; i < result?.count; i++) {
let dic: NSDictionary = result![i] as NSDictionary
var article: Article = Article(
title: dic["title"] as String,
userName: dic["user"]!["url_name"] as String,
linkURL: dic["url"] as String,
imageURL:dic["user"]!["profile_image_url"] as String
)
self.articles?.append(article)
}
self.tableView.reloadData()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return articles!.count
}
func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath:NSIndexPath!) -> UITableViewCell! {
let cell: MyTableViewCell = tableView?.dequeueReusableCellWithIdentifier("Cell") as MyTableViewCell
cell.article = articles?[indexPath.row]
return cell;
}
func tableView(tableView: UITableView?, didSelectRowAtIndexPath indexPath:NSIndexPath!) {
// When a cell has selected, open WebViewController.
let cell: MyTableViewCell = tableView?.cellForRowAtIndexPath(indexPath) as MyTableViewCell
self.performSegueWithIdentifier("WebViewController", sender: cell)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue(segue, sender: sender)
// Assign a model to WebViewController
if segue.identifier == "WebViewController" {
var cell : MyTableViewCell = sender as MyTableViewCell
let vc: WebViewController = segue.destinationViewController as WebViewController
vc.article = cell.article
}
}
}
import UIKit
class WebViewController: UIViewController {
@IBOutlet weak var webView: UIWebView!
@IBAction func backButtonPressed(sender: UIButton) {
self.dismissViewControllerAnimated(true, completion: nil)
}
var article: Article?
override func viewDidLoad() {
super.viewDidLoad()
var url: NSURL = NSURL.URLWithString(self.article!.linkURL)
var urlRequest: NSURLRequest = NSURLRequest(URL: url)
self.webView.loadRequest(urlRequest)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
View
MyTableViewCell (カスタマイズしたCell)
import UIKit
class MyTableViewCell: UITableViewCell {
@IBOutlet weak var userImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var userLabel: UILabel!
var article: Article? {
// When the data has been set, labels and imageView will have the values.
didSet {
self.titleLabel.text = self.article?.title
self.userLabel.text = self.article?.userName
self.userImageView.image = UIImage(named: "qiita-logo.png")
var q_global: dispatch_queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
var q_main: dispatch_queue_t = dispatch_get_main_queue();
dispatch_async(q_global, {
let url: NSURL = NSURL.URLWithString(self.article!.imageURL)
var error: NSError?
let imageData: NSData = NSData(contentsOfURL: url, options: nil, error:&error)
let image: UIImage = (error !== nil) ? UIImage(named: "qiita-logo.png") : UIImage(data: imageData)
dispatch_async(q_main, {
self.userImageView.image = image
})
})
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Model
Article (記事タイトル、記事URL、ユーザのプロフィール画像、ユーザ名)
import UIKit
class Article: NSObject {
var title: String, userName: String, linkURL: String, imageURL: String
init(title: String, userName: String, linkURL: String, imageURL: String) {
self.title = title
self.userName = userName
self.linkURL = linkURL
self.imageURL = imageURL
}
}
Alamofireで返ってくるJSONの処理
Alamofireのソースコードを見てみると、返ってくるJSONはNSDictionaryを持ったArray型であることがわかる。
従って以下のようにJSONを分解してモデルに代入した。
var result: NSArray?
Alamofire.request(.GET, "https://qiita.com/api/v1/search?q=swift",parameters: nil, encoding: .JSON)
.responseJSON { (request, response, JSON, error) in
result = (JSON as NSArray)
for (var i = 0; i < result?.count; i++) {
let dic: NSDictionary = result![i] as NSDictionary
var article: Article = Article(
title: dic["title"] as String,
userName: dic["user"]!["url_name"] as String,
linkURL: dic["url"] as String,
imageURL:dic["user"]!["profile_image_url"] as String
)
self.articles?.append(article)
}
self.tableView.reloadData()
}
XCodeのバグ?
作業中に何度か見舞われたが、HTTP通信で取得した画像がシミュレータ上で表示されない問題。
以下にも同様な記述があったため、Xcode6のバグだと思われる。
http://stackoverflow.com/questions/25372318/error-domain-nsurlerrordomain-code-1005-the-network-connection-was-lost
所感
!と?をもう少しきちんと理解したい。
Objective-Cよりも型にうるさいので、注意が必要。
TableViewCell中の画像の表示は非同期にしているが、Cellの再利用をしているからか、勢い良くスクロールダウンした際に以前のCellのデータが表示されてしまうことがある。
このあたり、詳しい人がいれば解決法を教えて下さい。
軽くドキュメントを読んだだけでも数時間で作成できたので、Objective-Cでアプリを作ったことがあれば比較的簡単にSwiftに移行できそう。