やること: API → 表示
Calil のAPI から図書館情報を取得して一覧で表示。
https://calil.jp/doc/api.html
同じ構成のjavascript実装はこちら
http://qiita.com/nakadoribooks/items/a31a85c788bbc90baf56
成果物
#準備
環境
xcode8.2.1
###ライブラリ取り込み
use_frameworks!
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
target 'CalilClient' do
pod 'ReSwift'
pod 'Alamofire', '~> 4.4'
end
pod update
処理の流れ
0: globalにstoreを作る (Global)
import UIKit
import ReSwift
// The global application store, which is responsible for managing the appliction state.
let mainStore = Store<AppState>(
reducer: CalilReducer(),
state: nil
)
// ↑ これ
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
1: 読み込みイベントを作る (View)
let loadAction = ActionCreator.loadLibraries() /*← こっち*/
mainStore.dispatch(loadAction)
1.5: 読み込む and Actionを作って返す (ActionCreator)
static func loadLibraries(prefName:String="秋田県")->Action{
let parameters: Parameters = ["appkey": Config.API_KEY, "pref":prefName, "format":"json", "callback":""]
Alamofire.request(Config.API_LIBRARY, parameters: parameters).responseJSON { (response) in
// 〜 略 〜
/* 読み込んだら loadedActionをdispatchして 5 へ*/
let loadedAction = LibraryListLoadedAction(libraryList: results)
mainStore.dispatch(loadedAction)
}
// 読み込み開始アクションを返す 2 へ
return LibraryListLoadAction()
2: (from 1) 作ったイベントをdispatch (View)
let loadAction = ActionCreator.loadLibraries()
mainStore.dispatch(loadAction) /*← こっち*/
3: loadActionを受け取ってStateのisLoading を trueに (Reducer)
case _ as LibraryListLoadAction:
state.errorMessage = nil
state.isLoading = true
4: stateの変更を受け取ってViewを更新 (View)
func newState(state: AppState) {
// 〜 略 〜
// loading
if state.isLoading{
reloadButton.isEnabled = false
reloadButton.alpha = 0.5
statusLabel.text = "Loading..."
loadingIndicator.startAnimating()
return;
}
5: (from 1.5) 読み込み完了のイベントを受け取ってStateを更新 (Reducer)
case let action as LibraryListLoadedAction:
state.errorMessage = nil
state.libraryList = action.libraryList
state.isLoading = false
6: stateの変更を受け取ってViewを更新 (View)
func newState(state: AppState) {
// 〜 略 〜
// loaded
reloadButton.isEnabled = true
reloadButton.alpha = 1.0
statusLabel.text = ""
loadingIndicator.stopAnimating()
self.libraryList = state.libraryList
tableView.reloadData()
以下、コード全体
##State 3つ
読み込み中フラグ
表示するデータ
エラー有無
import ReSwift
struct AppState: StateType {
var isLoading:Bool = false
var libraryList:[Library] = []
var errorMessage:String?
}
##Action 3つ
読み込み開始
読み込み完了
エラー
import ReSwift
struct LibraryListLoadAction:Action {}
struct LibraryListLoadedAction:Action{
let libraryList:[Library]
}
struct LibraryListFailLoadAction:Action{}
Reducer: Actionを受け取ってStateを変更
import ReSwift
// the reducer is responsible for evolving the application state based
// on the actions it receives
struct CalilReducer: Reducer {
func handleAction(action: Action, state: AppState?) -> AppState {
var state = state ?? AppState()
switch action {
case _ as LibraryListLoadAction:
state.errorMessage = nil
state.isLoading = true
case _ as LibraryListFailLoadAction:
state.errorMessage = "読み込みに失敗しました"
state.isLoading = false
case let action as LibraryListLoadedAction:
state.errorMessage = nil
state.libraryList = action.libraryList
state.isLoading = false
default:
break
}
return state
}
}
ActionCreator: Action を作って返却 or 通信 → Action作って dispatch
この中でdispatchするのはどうなんだろう。
import ReSwift
import Alamofire
struct ActionCreator{
static func loadLibraries(prefName:String="秋田県")->Action{
let parameters: Parameters = ["appkey": Config.API_KEY, "pref":prefName, "format":"json", "callback":""]
Alamofire.request(Config.API_LIBRARY, parameters: parameters).responseJSON { (response) in
guard let json = response.result.value as? [NSDictionary] else{
print("fail json")
print(response)
let loadFailAction = LibraryListFailLoadAction()
mainStore.dispatch(loadFailAction)
return
}
print("ok json")
let results = Library.createList(list: json)
let loadedAction = LibraryListLoadedAction(libraryList: results)
// mainStore.dispatch(loadedAction)
// 時間かかっているてい
DispatchQueue.global(qos: .default).async {
Thread.sleep(forTimeInterval: 1)
DispatchQueue.main.async {
mainStore.dispatch(loadedAction)
}
}
}
return LibraryListLoadAction()
}
}
View: Stateの変更を受け取って表示を更新
import UIKit
import ReSwift
class LibraryListViewController: UIViewController, StoreSubscriber, UITableViewDelegate, UITableViewDataSource {
// 〜 略 〜
override func viewDidLoad() {
super.viewDidLoad()
// 〜 略 〜
// storeの通知を受け取る
mainStore.subscribe(self)
// 読み込みアクションDispatch
let loadAction = ActionCreator.loadLibraries()
mainStore.dispatch(loadAction)
}
// 〜 略 〜
private dynamic func tapReload(){
// 読み込みアクションDispatch
let loadAction = ActionCreator.loadLibraries()
mainStore.dispatch(loadAction)
}
// MARK: StoreSubscriber
func newState(state: AppState) {
// Stateの変更を受け取って View の更新 //
// error
if let errorMessage = state.errorMessage{
// 〜 略 〜
return;
}
statusLabel.textColor = UI.textColor
// loading
if state.isLoading{
// 〜 略 〜
return;
}
// loaded
// 〜 略 〜
// 受け取ったデータでView更新
self.libraryList = state.libraryList
tableView.reloadData()
}
// 〜 略 〜
}
その他
class Config: NSObject {
static let API_ROOT = "https://api.calil.jp/"
static let API_LIBRARY = API_ROOT + "library"
static let API_KEY = "*****" // 書き換えてね
}
カーリルに開発者登録してAPI_KEYをゲットして書き換えてね。
ありがとう Calil APi
引用
未来の図書館を作るのはあなただ!
全国6000の図書館に簡単アクセス! カーリルの図書館APIを使ってみよう
全国の図書館を対象としたリアルタイム蔵書検索を可能にするAPI群を開発者向けに提供します。
法人・個人を問わず無償で利用できます。