Category Archives: Ios

UIWebViewやASIHTTPRequestではNSHTTPCookieStorageが管理しているクッキーを共有して使うことができる(逆に使わない設定も可能)ので、
起動中はセッションの設定などでクッキーのやり取りを気にしなくてもいいので便利なのですが、さすがに永続化は自動ではやりません
そのため、アプリを再起動してもUIWebVIewで表示するサイトのログイン状態を継続したい場合等はNSHTTPCookieStorageからクッキーを取り出して保存して置く必要があります。

NSUserDefaultsを使い単純にファイルとして保存することを考えた場合、クッキーが変化するごとに(iOSだとそもそもキャッチできなさそう)保存するのはI/Oが足を引っ張りそうなので、
起動時と終了時にそれぞれ読み込み、保存処理を行うようにします。
AppDelegateクラスのdelagateメソッドを使ってこれを実装します。

マルチタスクが有効なiOSだと、applicationWillTerminateが呼ばれない事も多いのでapplicationDidEnterBackgroundのタイミングでも保存処理を行うようにしています。

参考

こんにちはこんばんわ!azuの記事に触発されて使ってみる事にしました!!!

■ダウンロード

gh-unit -> Downloadsで特別な理由がない場合は最新版を使いましょう。

■ターゲットの追加

Project navigatorでProjectを選択し、Add Targetをクリックしてください。

次に、Window Based ApplicationでProduct NameをTestsとして適当にターゲットを追加します。

frameworkの追加

Project navigatorでProjectを選択し、TargetでTestsを選択した後、Build PhasesからLink Binary With Librariesを開き、ダウンロードしてできたframeworkを追加してください。

Info.plist

Test/Supporting Files/Test-Info.plistを選択してMain nib file base nameの属性を削除します。

ファイルの削除

Test配下の以下のファイルを削除してください。

  • TestAppDelegate.h
  • TestAppDelegate.m
  • MainWindow.xib
  • Supporting Files以外のディレクトリ

■リンク

Project navigatorでProjectを選択し、TargetでTestsを選択した後、Build SettingからOther Linker Flagを選択し、以下の記述を加えてください。

  • -ObjC
  • -all_load

■Test/main.m

以下の部分を

int retVal = UIApplicationMain(argc, argv, nil, nil);

以下のように変更します。

int retVal = UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate");

■テストケース

Test配下に書いていきす。但し、ターゲットはTestsにしてファイルを新しく追加します。

Test/MyTest.m

以下のように試しに書いてみました。

#import <GHUnitIOS/GHUnit.h>
@interface MyTest : GHTestCase {  
}
@end

@implementation MyTest
- (void)testStrings {
    NSString *string1 = @"a string";
    GHTestLog(@"I can log to the GHUnit test console: %@", string1);
   
    // Assert string1 is not NULL, with no custom error description
    GHAssertNotNULL(string1, nil);
   
    // Assert equal objects, add custom error description
    NSString *string2 = @"a string";
    GHAssertEqualObjects(string1, string2, @"A custom error message. string1 should be equal to: %@.", string2);
    GHAssertEquals(1, 1, @"test");
}

// わざとエラーを出してみる
- (void)testNums {
    GHAssertEquals(1, 2, @"test");
}
@end

テストメソッド名はtestから始めなくてはいけないですね。ちなみにGHAssertまで入力すれば大体どんなアサーションがあるのか確認できます。

■実際のテスト

ほとんどの記事を見ても実際のテストコードに近いコードがみつかりませんでした。困ったので実際に使えそうなコードを自力で書いてみました。

Util.m

Utilクラスです。URLエンコードしたりデコードしたりするメソッドがあります。

#import <Foundation/Foundation.h>
@interface Util : NSObject {
}
+ (NSString *)urlencode:(NSString *)text;
+ (NSString *)urldecode:(NSString *)text;
@end

@implementation Util
//encode
+ (NSString *)urlencode:(NSString *)text
{
    return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,(CFStringRef)text,NULL,(CFStringRef)@"!*'();:@&=+$,/?%#[]",kCFStringEncodingUTF8);
}

// decode
+ (NSString *)urldecode:(NSString *)text
{
    return (NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,(CFStringRef) text,CFSTR(""),kCFStringEncodingUTF8);
}
@end

注意点としては、テストクラスからインポートする為に、Util.mのファイルのターゲットにTestsを加える点です。

UtilTest.m

#import <GHUnitIOS/GHUnit.h>
#import "Util.h"
@interface UtilTest : GHTestCase {
}
@end

@implementation UtilTest
- (void)test_urlencode
{
    NSString *url1 = [NSString stringWithString:@"%E3%83%86%E3%82%B9%E3%83%88%E3%82%B1%E3%83%BC%E3%82%B9"];
    NSString *url2 = [NSString stringWithString:[Util urlencode:@"テストケース"]];
    GHAssertEqualObjects(url1, url2, @"match!");
   
    NSString *url3 = [NSString stringWithString:@"%E9%9A%A3%E4%BA%BA%E3%81%AF%E7%BE%8E%E4%BA%BA"];
    NSString *url4 = [NSString stringWithString:[Util urlencode:@"隣人は美人"]];
    GHAssertEqualObjects(url3, url4, @"match!");
}

- (void)test_urldecode
{
    NSString *url1 = [NSString stringWithString:[Util urldecode:@"%E3%83%86%E3%82%B9%E3%83%88%E3%82%B1%E3%83%BC%E3%82%B9"]];
    NSString *url2 = [NSString stringWithString:@"テストケース"];
    GHAssertEqualObjects(url1, url2, @"match!");
   
    NSString *url3 = [NSString stringWithString:[Util urldecode:@"%E9%9A%A3%E4%BA%BA%E3%81%AF%E7%BE%8E%E4%BA%BA"]];
    NSString *url4 = [NSString stringWithString:@"隣人は美人"];
    GHAssertEqualObjects(url3, url4, @"match!");
}
@end

これでTestsをターゲットにRunすればテストができますね。ちなみにですが、テストクラスの名前にはTestがつかなくても特に問題ないようです。

参考

■まとめ

殆ど公式ドキュメントと変わりませんが日本語がスキな方に読んでいただければと思います。

こんにちはこんばんは!暑いですね!~“Q。(‘‐’。)” パタパタ。凄く暑いですね。暑いのでiPhoneのネイティブアプリでFacebookにログインするアプリが作りたくなりました。無茶な流れですが暑いので許してください。

■インポート

ソースを取得

以下のコマンドでgithubから取得できます。

git clone git://github.com/facebook/facebook-ios-sdk.git

SDKをgithubで配布ですよ!日本企業も見習わなくちゃいけませんね!持ってきたディレクトリのsrcディレクトリに必要なファイルは揃っていますので、そのままプロジェクトに突っ込みましょう。(.xcodeprojなどは不要です)

■ログインの実装

AppDelegate.h

以下のようにFBConnect.hをインポートし、FBSessionDelegateを実装します。

#import <UIKit/UIKit.h>
#import "FBConnect.h"

@class MainViewController;

@interface facebookAppDelegate : NSObject <UIApplicationDelegate, FBSessionDelegate> {
    Facebook *facebook;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) Facebook *facebook;
@property (nonatomic, retain) MainViewController *mainViewController;
@end

AppDelegate.m

#import "facebookAppDelegate.h"

/**
 * @see https://developers.facebook.com/apps/
 * アプリを登録すると発行されます(公開までsandboxモードにしておきましょう)
 */

#define APP_ID @"123456789"

@implementation facebookAppDelegate

@synthesize window=_window;
@synthesize facebook;
@synthesize mainViewController;

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    return [facebook handleOpenURL:url];
}

// ブラウザのログイン画面から戻ってきた時に実行される
// ログイン情報をNSUserDefaultsに保存する
- (void)fbDidLogin {
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    [ud setObject:[facebook accessToken] forKey:@"FBAccessTokenKey"];
    [ud setObject:[facebook expirationDate] forKey:@"FBExpirationDateKey"];
    [ud synchronize];
   
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // APP IDをセットしてインスタンス化
    facebook = [[Facebook alloc] initWithAppId:APP_ID];

    // ログイン情報があればセット
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    if ([ud objectForKey:@"FBAccessTokenKey"] && [ud objectForKey:@"FBExpirationDateKey"]) {
        [facebook setAccessToken:[ud objectForKey:@"FBAccessTokenKey"]];
        [facebook setExpirationDate:[ud objectForKey:@"FBExpirationDateKey"]];
    }
   
    // パーミッションをセットして有効なセッションがなければブラウザでログインする
    NSArray* permissions =  [[NSArray arrayWithObjects:@"email", @"read_stream", nil] retain];
    if (![facebook isSessionValid]) {
        [facebook authorize:permissions delegate:self];
    }
    [self.window makeKeyAndVisible];
    return YES;
}

info.plist

Facebook SDKではinfo.plistに記述したURLによってアプリケーションを起動できる仕組みを使っています。以下のように変更してください。

「(Array) URL types > (Dictionary) Item 0 > (Array) URL Schemes > (Dictionary) Item 0 > (String) fb[APP ID]」となるように入力します。

起動

起動するとちゃんとログインができるかと思います。

■API

まぁ、ログインだけというのもなんなのでGraph APIを叩いてみましょう。

AppDelegate.h

FBRequestDelegateを実装します。

#import <UIKit/UIKit.h>
#import "FBConnect.h"

@class MainViewController;

@interface facebookAppDelegate : NSObject <UIApplicationDelegate, FBSessionDelegate, FBRequestDelegate> {
    Facebook *facebook;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) Facebook *facebook;
@property (nonatomic, retain) MainViewController *mainViewController;
@end

AppDelegate.m

makeKeyAndVisibleの前あたりでrequestWithGraphPathを呼びます。

    [facebook requestWithGraphPath:@"me" andDelegate:self];
    [self.window makeKeyAndVisible];
結果を受け取る

以下の4メソッドをAppDelegate.mに実装することで、NSURLConnection的に結果を受け取れます。

/**
 * Called just before the request is sent to the server.
 */

- (void)requestLoading:(FBRequest *)request
{
   
}

/**
 * Called when the server responds and begins to send back data.
 */

- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response
{
   
}

/**
 * Called when an error prevents the request from completing successfully.
 */

- (void)request:(FBRequest *)request didFailWithError:(NSError *)error
{
   
}

/**
 * Called when a request returns and its response has been parsed into
 * an object.
 *
 * The resulting object may be a dictionary, an array, a string, or a number,
 * depending on thee format of the API response.
 */

- (void)request:(FBRequest *)request didLoad:(id)result
{
    NSLog(@"%@", result);
}

/**
 * Called when a request returns a response.
 *
 * The result object is the raw response from the server of type NSData
 */

- (void)request:(FBRequest *)request didLoadRawResponse:(NSData *)data
{
   
}

以上となります。公式ドキュメントを翻訳しただけのようになってしまいましたが暑いので許してください?

■参考

Mobile Apps