M.S.の技術メモ

UIWebViewとNSURLConnectionでセッションを共有する

UIWebViewとNSURLConnection間でセッションを共有するためのクラスをSwiftで作成しました。
Androidでは簡単に実装できましたが、iOSではうまくセッション共有されずなかなか苦労しました。

今回はNSURLSessionではなくNSURLConnectionを使用しましたが、NSMutableURLRequest#setAllHTTPHeaderFieldsにクッキーを設定するので、どちらも動作するはずです。
NSMutableURLRequest#setAllHTTPHeaderFieldsへクッキーヘッダを設定するには、
下記クラスのgetCookiesHeaderFromCookieStorage()または、getCookiesHeaderFromInappData()を使用してください。

また、POST送信後のレスポンスに含まれるクッキーは、getCookiesHeaderFromCookieStorage()でアプリ内データに保存できます。

下記サンプルではアプリ起動直後にPOST通信後、UIWebViewでページを開き、またそのページが読み込み完了してからPOST通信を実行します。
その後アプリを終了し、次回起動時にPOST通信をする際にクッキーが保持された状態になっています。

 

ダウンロードする場合はこちらからどうぞ:
SessionManager.swift
Post.swift

 

使い方

var sessionManager: SessionManager!

override func viewDidLoad() {
	// セッション共有クラスの初期化
	var sessionManager: SessionManager = SessionManager()
	
	// POST通信情報の準備
	var post: Post = Post()  // 簡易POST通信クラス(参照: 下記post.swift)
	var url: String = "http://www.example.com/post.php"    // データの送信先
	var postData: Dictionary<String, AnyObject> = ["key": "val"]
	
	// POSTする前にアプリ内データに保存しているクッキーを、共有クッキーに設定する
	sessionManager.getSession(NSURL(string: url)!)
	
	// POST通信
	post.post(url, data: postData)
	
	
	// UIWebViewのアクセス情報を準備する
	var loadUrl: NSURL = NSURL(string: "http://www.example.com/test.php")!
	var request: NSURLRequest = NSURLRequest(URL: loadUrl)
	
	// UIWebViewでPOST通信のセッションを保持したままアクセスする
	webView.loadRequest(request)
}

func webViewDidFinishLoad(webView: UIWebView) {
	if let url: String = webView.stringByEvaluatingJavaScriptFromString("document.URL") {
	   sessionManager.getSession(NSURL(string: url)!)
	
	   // POSTしてセッションを保存してからページを読み込む
	   var post: PostClass = PostClass()
	   var url: String = "http://www.example.com/post.php"
	   var data: Dictionary<String, AnyObject> = ["data": "webViewDidFinishLoad", "now": NSDate().timeIntervalSince1970]
	
	   // POST
	   PostClass().post(url, data: data)
	   // POST通信後に共有クッキーからクッキーを取得し、アプリ内データに保存する
	   sessionMan.sessionManager(NSURL(string: url)!)
	}
}

 

セッション共有クラス: SessionManager.swift

//
//  SessionManager.swift
//  TEST_session
//
//  Created by M.S. on 2015/04/26.
//  Copyright (c) 2015年 M.S. All rights reserved.
//

import Foundation

class SessionManager: NSObject {
    /**
    アプリ内データに保存されているクッキーを指定URLの共有クッキーにセットする
    
    :param: url String URL
    */
    internal func setSession(url: NSURL) -> Void {
        var prefs: NSUserDefaults = NSUserDefaults.standardUserDefaults()
        
        if let cookieData: NSData = prefs.dataForKey("cookies") {
            var cookiesArray: Array<NSHTTPCookie> = NSKeyedUnarchiver.unarchiveObjectWithData(cookieData) as! Array<NSHTTPCookie>
            var cookieStorage: NSHTTPCookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
            
            cookieStorage.setCookies(cookiesArray, forURL: url, mainDocumentURL: nil)
        }
    }
    
    /**
    共有クッキーから指定URLのクッキーを取得する
    
    :param: url String URL
    */
    internal func getSession(url: NSURL) -> Void {
        var cookieStorage: NSHTTPCookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
        
        if let cookiesArray: Array<NSHTTPCookie> = cookieStorage.cookiesForURL(url) as? Array<NSHTTPCookie> {
            var cookieData: NSData = NSKeyedArchiver.archivedDataWithRootObject(cookiesArray)
            var prefs: NSUserDefaults = NSUserDefaults.standardUserDefaults()
            
            prefs.setValue(cookieData, forKey: "cookies")
            prefs.synchronize()
        }
    }
    
    /**
    HTTPレスポンスヘッダからクッキーを取得し保存する
    
    :param: httpResponse NSHTTPURLResponse HTTPレスポンスヘッダ
    :param: url String URL
    */
    internal func getSessionFromResponseHeader(httpResponse: NSHTTPURLResponse, url: NSURL) -> Void {
        if let cookiesArray: Array<NSHTTPCookie> = NSHTTPCookie.cookiesWithResponseHeaderFields(httpResponse.allHeaderFields, forURL: url) as? Array<NSHTTPCookie> {
            var cookieData: NSData = NSKeyedArchiver.archivedDataWithRootObject(cookiesArray)
            var prefs: NSUserDefaults = NSUserDefaults.standardUserDefaults()
            
            prefs.setValue(cookieData, forKey: "cookies")
            prefs.synchronize()
        }
    }
 
    /**
    共有クッキーからリクエスト用クッキーヘッダを生成する

    :param: url String URL
    
    :returns: [NSObject, AnyObject]? リクエスト用クッキーヘッダ
    */
    internal func getCookiesHeaderFromCookieStorage(url: NSURL) -> Dictionary<NSObject, AnyObject>? {
        var cookieStorage: NSHTTPCookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
        
        if let cookiesArray: Array<NSHTTPCookie> = cookieStorage.cookiesForURL(url) as? Array<NSHTTPCookie> {
            return NSHTTPCookie.requestHeaderFieldsWithCookies(cookiesArray)
        }
       
        return nil
    }

    /**
    アプリ内データに保存されているクッキーからリクエスト用クッキーヘッダを生成する
    
    :param: url String URL

    :returns: [NSObject, AnyObject]? リクエスト用クッキーヘッダ
    */
    internal func getCookiesHeaderFromInappData(url: NSURL) -> Dictionary<NSObject, AnyObject>? {
        setSession(url)
        
        return getCookiesHeaderFromCookieStorage(url)
    }
}

 

簡易POST通信クラス: Post.swift

//
//  Post.swift
//  TEST_session
//
//  Created by M.S. on 2015/04/26.
//  Copyright (c) 2015年 M.S. All rights reserved.
//

import Foundation

class PostClass : NSObject, NSURLSessionDelegate {
    internal func post(url: String, data: Dictionary<String, AnyObject>) -> Void {
        var url: NSURL! = NSURL(string: "http://msdevapps.net/test/post.php")
        var paramsArray: Array<String> = Array()
        var body: String = ""
        var sessionMan: SessionManager = SessionManager()
        var sessionConfig: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
        var session: NSURLSession = NSURLSession(configuration: sessionConfig)
        var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)

        for key: String in data.keys {
            if let val: AnyObject = data[key] {
                paramsArray.append(String("\(key)=\(val)"))
            } else {
                paramsArray.append(String("\(key)="))
            }
        }
        body = join("&", paramsArray)

        request.HTTPMethod = "POST"
        request.allHTTPHeaderFields = sessionMan.getCookiesHeaderFromCookieStorage(url)
        request.HTTPBody = body.dataUsingEncoding(NSUTF8StringEncoding)

        var httpResponse: NSHTTPURLResponse = NSHTTPURLResponse()
        var recvData: NSData = NSData()
        var rl: CFRunLoopRef = CFRunLoopGetCurrent()

        NSURLConnection.sendAsynchronousRequest(
            request, queue: NSOperationQueue.mainQueue(),
            completionHandler: { (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
                if (error == nil) {
                    httpResponse = response as! NSHTTPURLResponse
                    recvData = data
                }

                CFRunLoopStop(rl)
            }
        )
        CFRunLoopRun()

        sessionMan.getSessionFromResponseHeader(httpResponse, url: url)
    }
}
コメントはありません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*