だいたいそれでいいんじゃないの

つれつれなるkixixixixiの技術的なストック。http://reload.co.jp

Alamofireでsession(cookie)を永続化する

Bridge-Headerを使えば簡単にObjective-Cのコードを使えるのでずっとAFNetworkingをつかってました。
だけど、今更だがSwiftだけにしてみようと思ってAlamofireを使ってみた。
(単にGAがいらない案件がきたってわけではないよ...w)

HTTPリクエストを投げる際はいつもHelper作っているので今回もその方針でAlamofireを叩きにいくHelperメソッドをつくりました。

      func callAPI(
        path path:String,
        method:Alamofire.Method,
        params:[String: AnyObject]?,
        notificationName:Notice,
        success:SuccessCallbackBlock,
        failure:FailureCallbackBlock
        )
    {
        
        Alamofire.request(
            method,
            APIHost + path,
            parameters: params
            )
            .responseJSON { request, response, result in
                
                if response?.statusCode >= 400
                {
                    failure(error: result.error)
                    self.sendNotification(notificationName, success: false, responseObject: nil)
                    
                }
                else
                {
                    self.sendNotification(notificationName, success: true, responseObject: success(response: result.value))
                }
        }
    }

このHelperを作っておいて

        callAPI(
            path: "/login.json",
            method: .GET,
            params: nil,
            notificationName: .Login,
            success: { (response) -> (AnyObject?) in
                
                if let my = response as? NSDictionary
                {
                    // do something
                }
                
                return nil
            },
            failure: { (error) -> () in
                // do something
            }
        )

こういった感じでAPIをたたく。
NSNocificationを使って通信結果のハンドリングしているのでsendnotificationメソッドやNoticeのenumは別で作ってあります。
APIHostはいわずもがな。 ブロックの型は以下のようにつくった。

public typealias SuccessCallbackBlock = (response:AnyObject?) -> (AnyObject?)
public typealias FailureCallbackBlock = (error: ErrorType?) -> ()

これでとりあえずできて、AFNetworkingよりswiftっぽくて使いやすいなと思ったけどアプリを再起動するとsessionが切れてしまうのがいただけない。cookieをどこにも保存してないからなー。
ということでHelperをいじってCookieを保存する機構を作ってSessionを永続化してみた。

    var manager : Alamofire.Manager?
    func configureManager() -> Alamofire.Manager?
    {
        if manager == nil
        {
            if let cookiesData:NSData = NSUserDefaults.standardUserDefaults().objectForKey(CookieKey) as? NSData
            {
                for cookie:NSHTTPCookie in NSKeyedUnarchiver.unarchiveObjectWithData(cookiesData) as! [NSHTTPCookie]
                {
                    NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(cookie)
                }
            }
            
            let cfg = NSURLSessionConfiguration.defaultSessionConfiguration()
            cfg.HTTPCookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
            manager = Alamofire.Manager(configuration: cfg)
        }
        return manager
    }

        
    func callBasicAPI(
        path path:String,
        method:Alamofire.Method,
        params:[String: AnyObject]?,
        notificationName:Notice,
        success:SuccessCallbackBlock,
        failure:FailureCallbackBlock
        )
    {
        
        configureManager()?.request(
            method,
            APIHost + path,
            parameters: params
            )
            .responseJSON { request, response, result in
                
                let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(response?.allHeaderFields as! [String:String], forURL: response!.URL!)
                for var i = 0; i < cookies.count; i++ 
                {
                    NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(cookies[i])
                }
                
                let cookiesData:NSData = NSKeyedArchiver.archivedDataWithRootObject(NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies!)
                NSUserDefaults.standardUserDefaults().setObject(cookiesData, forKey: CookieKey)
                
                if response?.statusCode >= 400
                {
                    failure(error: result.error)
                    self.sendNotification(notificationName, success: false, responseObject: nil)
                    
                }
                else
                {
                    self.sendNotification(notificationName, success: true, responseObject: success(response: result.value))
                }
        }
    }

Helperでシングルトンっぽいmanagerをつくり、そこで NSUserDefaults に保存しておいたCookieを引っ張ってきて当てる。
通信の成功時に都度、Hederに付与されたCookieNSUserDefaults に保存。
これでsessionは永続化できました。
Helper作っておくと、モデル等からAPI叩きにいくときは意識しないでいいので楽でいいです。

以上。