看板 KnucklesNote
作者 標題 [Xcode][Swift3] 使用 Alamofire 存取網站資料
時間 2017-03-07 Tue. 18:06:38
延續上一篇 [Xcode][Swift3] 使用 Table View 產生列表頁 - KnucklesNote板 - Disp BBS
接下來要抓網路上的資料來顯示
要用 Swift 存取網路資料,有個好用的第三方類別庫 Alamofire
是與 Object-C 的 AFNetworking 同一個作者建立的
![[圖]](http://i.imgur.com/pgepnyS.png)
參考 GitHub: https://github.com/Alamofire/Alamofire
依照這篇 [Xcode][Swift3] 安裝套件管理工具 CocoaPods - KnucklesNote板 - Disp BBS
使用 CocoaPods 安裝 Alamofire 後
為了要顯示網路上的圖檔,還要再安裝 AlamofireImage
參考 GitHub: https://github.com/Alamofire/AlamofireImage
修改專案資料夾下的 Podfile
在 pod 'Alamofire' 下再加上一行
pod 'AlamofireImage'
使用終端機,切換到專案資料夾執行 pod install
$ cd ~/Xcode/DispBBS
$ pod install
目前安裝的版本為
Alamofire 4.4.0
AlamofireImage 3.2.0
回到 Xcode
修改 HotTextViewController.swift
在 import UIKit 下加上
import Alamofire
import AlamofireImage
import AlamofireImage
如果出現錯誤訊息「cannot load underlying module for 'Alamofire'
![[圖]](http://i.imgur.com/Bm0NJLV.png)
點一下「Product」/「Clean」就可以了
![[圖]](http://i.imgur.com/ryjginp.png)
取消只能用加密連線的限制
在 Xcode 7 之後的版本,預設網路存取要使用加密連線 (https://) 才行
否則會無法連線,並出現錯誤訊息「App transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.」
要取消限制,到專案設定的「info」,在「Custom iOS Target Properties」項目裡
點一下任一項目後面的✚
![[圖]](http://i.imgur.com/s0fui3r.png)
輸入「App Transport Security Settings」,按 Enter
![[圖]](http://i.imgur.com/X13XCxD.png)
點一下左邊的向右箭頭,變成下向箭頭後,點右邊的✚
![[圖]](http://i.imgur.com/331nV80.png)
選擇「Allow Arbitrary Loads」,按 Tab,選擇 Value 為「YES」
![[圖]](http://i.imgur.com/xDaa05N.png)
熱門文章的 JSON 檔格式
先看一下我們要抓的 JSON檔 http://disp.cc/api/hot_text.json
這是由 Server 端產生的 API,裡面的資料格式是由網頁後端程式(例如PHP)來產生的
格式為每個網站自訂的,所以要抓其他網站的資料時要再修改一下
可以用 Chrome 的網站管理工具將 JSON 檔用樹狀結構顯示出來看看
![[圖]](http://i.imgur.com/fIXyP5i.png)
這個熱門文章列表的 JSON 格式是像這樣
{"isSuccess":1,"err":0,"totalNum":28,"list":[Row1, Row2, ..., RowN] }
最外層是一個JS物件{"key":value},"isSuccess" 用來檢查資料有沒有產生成功,沒問題的話就是 1
"totalNum" 總共有幾篇熱門文章
"list" 為一個JS陣列[value],裡面存了N個JS物件,記錄了每篇熱門文章的資料
每個JS物件的格式是像這樣
Row1 = {"hot_num":"目前人氣值","bi":"看板編號","ti":"文章編號","title":"文章標題","board_name":"看板名稱","ai":"作者編號","author":"作者帳號","desc":"文章摘要","img_list":["縮圖網址1","縮圖網址2","縮圖網址3"], "url":"文章網址"}
在 Swift 裡,要將 JS物件 存成 Dictionary
將 JS陣件 存成 Array
使用 Alamofire 讀取網站的 json 檔
修改 HotTextViewController.swift
在 class HotTextViewController: UITableViewController { 這行下面加上
// 1. 新增成員變數
var hotTextArray:[Any]?
// 2. 新增成員函數
func loadData() {
let urlString = "https://disp.cc/api/hot_text.json"
// 3. 使用 Alamofire 存取網址
Alamofire.request(urlString).responseJSON { response in
if let JSON = response.result.value {
// 4.
print("JSON: \(JSON)")
}
}
}
1. 新增一個成員變數 hotTextArray,型別為[Any],代表任意物件的陣列 var hotTextArray:[Any]?
// 2. 新增成員函數
func loadData() {
let urlString = "https://disp.cc/api/hot_text.json"
// 3. 使用 Alamofire 存取網址
Alamofire.request(urlString).responseJSON { response in
if let JSON = response.result.value {
// 4.
print("JSON: \(JSON)")
}
}
}
型別後面加上問號代表是一個可以為 nil 值的 optional 變數
用來儲存列表的資料
2. 新增一個成員函數 loadData(),用來讀取網路上的 json 檔
使用 let urlString 代表宣告一個常數,urlString 的值之後不能再變動
3. 當 Alamofire.request() 執行完後,會使用非同步的方法執行 responseJSON()
responseJSON 傳入了一個 callback 匿名函數:{ 輸入值 in 函數內容 }
也就是 responseJSON({匿名函數}),
當參數只有傳入一個函數時可簡寫為:resposnseJSON {匿名函數}
4. 先將讀取到的 JSON 資料顯示在 Console 視窗,看看有沒有讀取成功
然後在成員函數 viewDidLoad() 裡加上
loadData()
viewDidLoad 是 view 載入後會執行的函數在這邊呼叫 loadData() 載入資料
執行結果
![[圖]](http://i.imgur.com/vbcwGqz.png)
點程式碼左下角的按鈕可顯示下方的 Debug Area
點 Debug Area 右下角的按鈕可將左邊的 Variable View 隱藏
在 Console 視窗可看到用 print() 指令顯示的結果
可以確定網站的 json 檔有成功的讀取到了
修改成員函數 loadData() 將列表的資料存到成員變數 hotTextArray
func loadData() {
let urlString = "https://disp.cc/api/hot_text.json"
Alamofire.request(urlString).responseJSON { response in
// 1.
guard response.result.isSuccess else {
let errorMessage = response.result.error?.localizedDescription
print(errorMessage!)
return
}
guard let JSON = response.result.value as? [String: Any] else {
print("JSON formate error")
return
}
//print("JSON: \(JSON)")
// 2.
if let list = JSON["list"] as? [Any] {
self.hotTextArray = list
self.tableView.reloadData()
}
}
}
1. 使用 guard 檢查網路存取是否成功,若失敗的話使用 return 跳出let urlString = "https://disp.cc/api/hot_text.json"
Alamofire.request(urlString).responseJSON { response in
// 1.
guard response.result.isSuccess else {
let errorMessage = response.result.error?.localizedDescription
print(errorMessage!)
return
}
guard let JSON = response.result.value as? [String: Any] else {
print("JSON formate error")
return
}
//print("JSON: \(JSON)")
// 2.
if let list = JSON["list"] as? [Any] {
self.hotTextArray = list
self.tableView.reloadData()
}
}
}
guard 條件 else { //條件失敗要做的事 }
等同於 if 條件 == nil { //條件失敗要做的事 }
第二個 guard 將 response.result.value 轉型為 [String: Any] 的 Dictionary,
轉型的 as? 後面加問號代表傳型失敗就直接傳回 nil 值
轉型後的結果存為常數 JSON
2. 讀取 JSON["list"],轉型為 [Any] 陣列,存為常數 list
成功的話將 list 存到成員變數 hotTextArray
有變更資料就要執行一次 reloadData(),更新 tableView 顯示的資料
修改成員函數 tableView(_:numberOfRowsInSection)
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let num = self.hotTextArray?.count {
return num
} else {
return 0
}
}
依照陣列儲存的數目來顯示 tableView 的列數if let num = self.hotTextArray?.count {
return num
} else {
return 0
}
}
因為 self.hotTextArray?.count 可能會取得 nil 值
所以不能直接 return self.hotTextArray?.count
修改成員函數 tableView(_:cellForRowAt)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HotTextCell", for: indexPath) as! HotTextCell
// 1. 讀取第 indexPath.row 的值
guard let hotText = self.hotTextArray?[indexPath.row] as? [String: Any] else {
print("Get row \(indexPath.row) error")
return cell
}
// 2. 填入 Label 的值
cell.titleLabel?.text = hotText["title"] as? String
cell.descLabel?.text = hotText["desc"] as? String
// 3. 顯示縮圖
let img_list = hotText["img_list"] as? [String]
let placeholderImage = UIImage(named: "displogo120")
if img_list?.count != 0 {
let url = URL(string: (img_list?[0])!)!
cell.thumbImageView?.af_setImage(withURL: url, placeholderImage: placeholderImage)
} else {
cell.thumbImageView?.image = placeholderImage
}
return cell
}
1. 讀取成員變數 hotTextArray 的第 indexPath.row 個值,let cell = tableView.dequeueReusableCell(withIdentifier: "HotTextCell", for: indexPath) as! HotTextCell
// 1. 讀取第 indexPath.row 的值
guard let hotText = self.hotTextArray?[indexPath.row] as? [String: Any] else {
print("Get row \(indexPath.row) error")
return cell
}
// 2. 填入 Label 的值
cell.titleLabel?.text = hotText["title"] as? String
cell.descLabel?.text = hotText["desc"] as? String
// 3. 顯示縮圖
let img_list = hotText["img_list"] as? [String]
let placeholderImage = UIImage(named: "displogo120")
if img_list?.count != 0 {
let url = URL(string: (img_list?[0])!)!
cell.thumbImageView?.af_setImage(withURL: url, placeholderImage: placeholderImage)
} else {
cell.thumbImageView?.image = placeholderImage
}
return cell
}
存成型態為 [String: Any] 的 Dictionary 變數 hotText
2. 讀取 hotText 中的值時,例如 hotText?["title"]
要加上 as? String 將型態由 Any 轉為 String
3. hotText 中的縮圖 img_list 是一個存有 0~3 個圖片網址的陣列
所以將型態由 Any 轉為 [String] 後存在常數 img_list
用 if 檢查若縮圖的數目不為 0,將第一個縮圖顯示出來
對 UIImageView 使用 AlamofireImage 提供的 af_setImage(withURL:placeholderImage:)
輸入圖片網址 url 與載入圖片前要先顯示的圖 placeholderImage
若縮圖數目為0,則使用之前存在專案裡的 displogo120 來顯示
執行結果
![[圖]](http://i.imgur.com/YUEVIK1.png)
存取網路時顯示載入中的圖示
要讓 Alamofire 在存取網路時自動在左上方狀態列顯示載入中圖示
![[圖]](http://i.imgur.com/ZOB5NVX.png)
要再另外安裝 AlamofireNetworkActivityIndicator
GitHub: https://github.com/Alamofire/AlamofireNetworkActivityIndicator
修改 Podfile 加上
pod 'AlamofireNetworkActivityIndicator'
使用終端機在專案目錄執行
$ pod install
目前安裝的版本為 AlamofireNetworkActivityIndicator 2.1.0
修改 AppDelegate.swift
在 import UIKit 下一行加上
import AlamofireNetworkActivityIndicator
在第一個成員函數 application(_:didFinishLaunchingWithOption:)
的 return true 之前加上
NetworkActivityIndicatorManager.shared.isEnabled = true
NetworkActivityIndicatorManager.shared.startDelay = 0
這樣使用 Alamofire 存取網路時就會自動顯示載入中圖示了NetworkActivityIndicatorManager.shared.startDelay = 0
startDelay 預設值是 1,代表網路開始存取後過一秒還在存取的話才顯示
若是想要網路開始存取時就馬上顯示的話可以改成 0
如果不是使用 Alamofire 時,也想顯示載入中圖示的話
可以使用內建的
UIApplication.shared.isNetworkActivityIndicatorVisible = true
要關閉時使用
UIApplication.shared.isNetworkActivityIndicatorVisible = false
加入重整按鈕與下拉重整功能
修改 HotTextViewController.swift
新增一個成員函數 refresh(_:)
@IBAction func refresh(_ sender: Any) {
loadData()
}
@IBAction 是用來連結按鈕點擊動作與成員函數用的loadData()
}
在 storyboard 拉一個 Bar Button Item 到 Navigation Bar 右邊
在屬性設定選擇 System Item: Refresh
![[圖]](http://i.imgur.com/zFML6dQ.png)
在 Table View Controller 的連結設定
將 Received Actions 裡的 refresh: 右邊的圈圈拉到重整按鈕上
![[圖]](http://i.imgur.com/4ZoCedZ.png)
接著開啟 Table View 原本就內建的下拉重整功能 (pull-to-refresh)
在 Table View Controller 的屬性設定
將 Refreshing 設定為「Enabled」
下方 Title 設定置中,文字輸入「更新熱門文章」
![[圖]](http://i.imgur.com/RKQ5rUG.png)
修改 HotTextViewController.swift
在成員函數 viewDidLoad() 裡加上一行
self.refreshControl?.addTarget(self, action: #selector(refresh(_:)), for: UIControlEvents.valueChanged)
將 refreshControl 要執行的動作選擇我們剛剛加上的成員函數 refresh(_:)然後修改載入資料的函數 loadData()
在 Alamofire.request(urlString).responseJSON { response in 的下一行加上
self.refreshControl?.endRefreshing()
在下載完資料時使用 endRefreshing() 關閉載入中的圖示執行看看
將列表往下拉時,就會出現「更新熱門文章」與載入中的圖示
放開後等載入完成就會關閉圖示
![[圖]](http://i.imgur.com/hkLNkX7.png)
要在點擊列表之後,將取得的文章網址在另一個頁面顯示出來,請看下一篇:
[Xcode][Swift3] 點擊列表開啟並傳送資料至新的頁面 - KnucklesNote板 - Disp BBS
參考
AppCoda Swift 網路程式設計指南:如何使用 Alamofire
RayWenderlich Alamofire Tutorial: Getting Started
Basics of Pull to Refresh for Swift Developers
--
※ 作者: Knuckles 時間: 2017-03-07 18:06:38
※ 編輯: Knuckles 時間: 2017-03-29 22:14:59
※ 看板: KnucklesNote 文章推薦值: 0 目前人氣: 0 累積人氣: 1452
回列表(←)
分享