2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ReSwift vol.1

Last updated at Posted at 2017-03-10

やること: API → 表示

Calil のAPI から図書館情報を取得して一覧で表示。
https://calil.jp/doc/api.html

同じ構成のjavascript実装はこちら
http://qiita.com/nakadoribooks/items/a31a85c788bbc90baf56

成果物

#準備

環境

xcode8.2.1

###ライブラリ取り込み

Podfile
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

###ファイル構成
スクリーンショット 2017-03-10 21.54.02.png

処理の流れ

0: globalにstoreを作る (Global)

AppDelegate.swift
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)

LibraryListViewController.swift
let loadAction = ActionCreator.loadLibraries() /*← こっち*/
mainStore.dispatch(loadAction)

1.5: 読み込む and Actionを作って返す (ActionCreator)

ActionCreator.swift
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)

LibraryListViewController.swift
let loadAction = ActionCreator.loadLibraries()
mainStore.dispatch(loadAction) /*← こっち*/

3: loadActionを受け取ってStateのisLoading を trueに (Reducer)

CalilReducer.swift
case _ as LibraryListLoadAction:
    state.errorMessage = nil
    state.isLoading = true

4: stateの変更を受け取ってViewを更新 (View)

LibraryListViewController.swift
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)

CalilReducer.swift
    case let action as LibraryListLoadedAction:
        state.errorMessage = nil
        state.libraryList = action.libraryList
        state.isLoading = false

6: stateの変更を受け取ってViewを更新 (View)

LibraryListViewController.swift
func newState(state: AppState) {

    // 〜 略 〜

    // loaded
    reloadButton.isEnabled = true
    reloadButton.alpha = 1.0
    statusLabel.text = ""
    loadingIndicator.stopAnimating()
        
    self.libraryList = state.libraryList
    tableView.reloadData()

以下、コード全体

##State 3つ
読み込み中フラグ
表示するデータ
エラー有無

AppState.swift
import ReSwift

struct AppState: StateType {
    var isLoading:Bool = false
    var libraryList:[Library] = []
    var errorMessage:String?
}

##Action 3つ
読み込み開始
読み込み完了
エラー

Actions.swift
import ReSwift

struct LibraryListLoadAction:Action {}
struct LibraryListLoadedAction:Action{
    let libraryList:[Library]
}
struct LibraryListFailLoadAction:Action{}

Reducer: Actionを受け取ってStateを変更

CalilReducer.swift
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するのはどうなんだろう。

ActionCreator.swift
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の変更を受け取って表示を更新

LibraryListViewController.swift
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()
    }

    // 〜 略 〜
}

その他

Config.swift

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群を開発者向けに提供します。
法人・個人を問わず無償で利用できます。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?