@blog.justoneplanet.info

日々勉強

HMAC-MD5を計算する

■PHP

hash_hmac('md5', $str, 'key');

■Python

import hmac
from hashlib import sha1
from hashlib import md5
hmac.new("key", "value", md5).hexdigest()
hmac.new("key", "value", sha1).hexdigest()

■Android

public class HmacMD5 {
private static final String ALGORISM = “HmacMD5”;
private static final String S = “key”;
public static String get(String str) {
SecretKeySpec secretKeySpec = new SecretKeySpec(S.getBytes(), ALGORISM);
try {
Mac mac = Mac.getInstance(ALGORISM);
mac.init(secretKeySpec);
byte[] result = mac.doFinal(str.getBytes());
return byteToString(result);
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (InvalidKeyException e) {
e.printStackTrace();
}
return “”;
}

private static String byteToString(byte [] b) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < b.length; i++) { int d = b[i]; d += (d < 0)? 256 : 0; if (d < 16) { buffer.append("0"); } buffer.append(Integer.toString(d, 16)); } return buffer.toString(); } } [/sourcecode]

■iOS

日本語入力に対応するためにstackoverflowから持ってきたコードに少々手を入れた。

+ (NSString *)HMACMD5WithKey:(NSString *)data
{
const char *cKey = [@”key” cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
const unsigned int blockSize = 64;
char ipad[blockSize];
char opad[blockSize];
char keypad[blockSize];

unsigned int keyLen = strlen(cKey);
CC_MD5_CTX ctxt;
if (keyLen > blockSize) {
CC_MD5_Init(&ctxt);
CC_MD5_Update(&ctxt, cKey, keyLen);
CC_MD5_Final((unsigned char *)keypad, &ctxt);
keyLen = CC_MD5_DIGEST_LENGTH;
}
else {
memcpy(keypad, cKey, keyLen);
}

memset(ipad, 0x36, blockSize);
memset(opad, 0x5c, blockSize);

int i;
for (i = 0; i < keyLen; i++) { ipad[i] ^= keypad[i]; opad[i] ^= keypad[i]; } CC_MD5_Init(&ctxt); CC_MD5_Update(&ctxt, ipad, blockSize); CC_MD5_Update(&ctxt, cData, strlen(cData)); unsigned char md5[CC_MD5_DIGEST_LENGTH]; CC_MD5_Final(md5, &ctxt); CC_MD5_Init(&ctxt); CC_MD5_Update(&ctxt, opad, blockSize); CC_MD5_Update(&ctxt, md5, CC_MD5_DIGEST_LENGTH); CC_MD5_Final(md5, &ctxt); const unsigned int hex_len = CC_MD5_DIGEST_LENGTH*2+2; char hex[hex_len]; for(i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { snprintf(&hex[i*2], hex_len-i*2, "%02x", md5[i]); } NSData *HMAC = [[NSData alloc] initWithBytes:hex length:strlen(hex)]; NSString *hash = [[[NSString alloc] initWithData:HMAC encoding:NSUTF8StringEncoding] autorelease]; [HMAC release]; return hash; } [/sourcecode]

ZXingをXcodeプロジェクトに組み込む

■組込

zxing-x.x/iphone/ZXingWidget/を自身のプロジェクトのプロジェクトナビゲーターにドロップする。

Targets > Build Phases

Target DependenciesでZXingWidgetを追加した後、Link Binary With Librariesで以下を追加する。

  • libZXingWidget.a
  • libiconv.dylib
  • CoreMedia.framework
  • CoreVideo.framework
  • AVFoundation.framework
  • AudioToolbox.framework
  • AddressBook.framework
  • AddressBookUI.framework
  • CoreGraphics.framework

Project > Build Settings

Header Search Pathsに以下の項目を追加する。

  • /User/[your name]/zxing-x.x/iphone/ZXingWidget/Classes
  • /User/[your name]/zxing-x.x/cpp/core/src

前者はrecursiveにチェックを入れて、後者はチェックを入れない。

参考

■実装

ScanViewController.h

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

@interface ScanViewController : UIViewController<ZXingDelegate>{
    UITextView *resultView;
    NSString *resultStr;
}
@property (nonatomic, retain) UITextView *resultView;
@property (nonatomic, copy) NSString *resultStr;
- (void)scanPressed:(id)sender;
@end

ScanViewController.mm

ファイルの拡張子を.mmとしないとコンパイルできない。

#import "ScanViewController.h"
#import "QRCodeReader.h"

@implementation ScanViewController
@synthesize resultView;
@synthesize resultStr;

- (void)scanPressed:(id)sender
{
    ZXingWidgetController *widController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
    QRCodeReader* qrcodeReader = [[QRCodeReader alloc] init];
    NSSet *readers = [[NSSet alloc ] initWithObjects:qrcodeReader,nil];
    [qrcodeReader release];
    widController.readers = readers;
    [readers release];

    [self presentModalViewController:widController animated:YES];
    [widController release];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
    }
    return self;
}
- (void)dealloc {
    [resultView release];
}

#pragma mark - View lifecycle
- (void)loadView
{
    [super loadView];
    
    UIButton *btnStamp = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [btnStamp setFrame:CGRectMake(40, 220, 240, 50)];
    [btnStamp setTitle:@"QRコードを読み取る" forState:UIControlStateNormal];
    [btnStamp setTag:1];
    [btnStamp addTarget:self action:@selector(scanPressed:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnStamp];
    
    resultView = [[UITextView alloc] initWithFrame:CGRectMake(40 , 280, 240, 50)];
    [self.view addSubview:resultView];
}
- (void)viewDidUnload
{
    [super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark -
#pragma mark ZXingDelegateMethods
- (void)zxingController:(ZXingWidgetController*)controller didScanResult:(NSString *)result {
    self.resultStr = result;
    if (self.isViewLoaded) {
        [resultView setText:resultStr];
        [resultView setNeedsDisplay];
    }
    [self dismissModalViewControllerAnimated:NO];
}
- (void)zxingControllerDidCancel:(ZXingWidgetController*)controller {
    [self dismissModalViewControllerAnimated:YES];
}
@end

こんな感じで。

AccessoryViewをカスタマイズする

setAccessoryViewしただけだとタップした時にaccessoryButtonTappedForRowWithIndexPathが発火しなくなるので以下のようにする。

// create cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // call cell
    UITableViewCellFixed *cell = (UITableViewCellFixed *)[tableView dequeueReusableCellWithIdentifier:@"table_cell"];
    if (cell == nil) {// create cell
        cell = [[[UITableViewCellFixed alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"table_cell"] autorelease];
    }
    
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setFrame:CGRectMake(0, 0, 26, 26)];
    [button setBackgroundImage:[UIImage imageNamed:@"arrow.png"] forState:UIControlStateNormal];
    [button setBackgroundColor:[UIColor clearColor]];
    [button addTarget:self action:@selector(accessoryButtonTapped:withEvent:) forControlEvents:UIControlEventTouchUpInside];
    [cell setAccessoryView:button];
    return cell;
}
- (void)accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
{
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:[[[event touchesForView: button] anyObject] locationInView:self.tableView]];
    if (indexPath == nil) {
        return;
    }
    [self.tableView.delegate tableView: self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}

UITextViewの高さを可変にする

編集可能なUITextViewを使用するときに入力内容にあわせてUITextViewの高さを変えたい時がある。

■コード

以下のようにUITextViewを定義する。

textview = [[UITextView alloc] init];
[textview setFrame:CGRectMake(0, 0, 100, 50)];
[textview setEditable:YES];
[textview setDelegate:self];

以下のようにcontentSizeを使用する。

-(void)textViewDidChange:(UITextView *)textView {
    CGRect f = textview.frame;
    f.size.height = textview.contentSize.height;
    textview.frame = f;
}

Objective-CでSingleton

■実装

#import <Foundation/Foundation.h>

@interface TapManager : NSObject {
}
+ (id)instance;
+ (id)allocWithZone:(NSZone *)zone;
- (id)copyWithZone:(NSZone *)zone;
- (id)retain;
- (unsigned)retainCount;
- (void)release;
- (id)autorelease;
- (void)registerWithFace:(NSString *)face withTag:(NSString *)tag;
@end
#import "TapManager.h"
#import "HistoryDB.h"
#import "Util.h"

@implementation TapManager
static TapManager *_instance = nil;

+ (TapManager *)instance {
    @synchronized(self) {
        if (_instance == nil) {
            [[self alloc] init];//代入はしない
        }
    }
    return _instance;
}

// zoneからオブジェクトを生成する
+ (id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (_instance == nil) {
            _instance = [super allocWithZone:zone];// 最初のみ代入する
            return _instance;// 最初のみ値を返す
        }
    }
    return nil;
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)retain {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;
}

- (void)release {
}

- (id)autorelease {
    return self;
}

- (void)registerWithFace:(NSString *)face withTag:(NSString *)tag {
    UIPasteboard *pb = [UIPasteboard generalPasteboard];
    [pb setValue:face forPasteboardType:@"public.utf8-plain-text"];
    [Util showAlert:@"Hello" text:@"World"];
    // ...その他の処理
}
@end

NSNullとnilを判定する

■失敗例1

UITableViewのcellを設定する部分で以下のようなコードを書きクラッシュした。

NSString *tag = (NSString *)[row objectForKey:@"tag"];
[cell.tag setText:tag];
return cell;

■失敗例2

nilの場合があるなと考えて以下のようにする。

NSString *tag = (NSString *)[row objectForKey:@"tag"];
if (tag == nil) {
    [cell.tag setText:@""];
}
else {
    [cell.tag setText:tag];
}
return cell;

残念ながらクラッシュは依然として起こる。

■最終版

tagはNSNullであり以下の判定に修正。

NSString *tag = (NSString *)[row objectForKey:@"tag"];
if (tag == nil || [tag isEqual:[NSNull null]]) {
    [cell.tag setText:@""];
}
else {
    [cell.tag setText:tag];
}
return cell;

参考

NSNullは配列の要素のようなオブジェクトしか許されない所でnilを表すのに用いられる

GHAsyncTestCaseを使ってASIHTTPRequestの非同期通信のテストをする

ドキュメントが分かりにくかったので自分で書いてみた。

■テスト対象のクラス

ASIHTTPRequestを使った普通のクラス。

MyHTTP.h

#import <Foundation/Foundation.h>
#import "MyHTTPDelegate.h"

@class ASIHTTPRequest;
@class MyHTTPDelegate;

@interface MyHTTP : NSObject {
	id <MyHTTPDelegate> delegate;
}
@property (nonatomic, retain) id delegate;
- (void)loadData;
- (void)requestFinished:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
@end

MyHTTP.m

以下のように実装する。ASIHTTPRequestを使った事があるならばお手の物だ。

#import "MyHTTP.h"
#import "ASIHTTPRequest.h"
#import "SBJson.h"

// 今回は触れないけど通信するので圏外判定に使っている
#import "UIDevice-Reachability.h"
#import "Reachability.h"

#define URL @"http://example.org/api/data"

@implementation MyHTTP
@synthesize delegate;

// 通信して更新する
- (void)loadData
{
    if([[UIDevice currentDevice] networkAvailable] == YES){
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:URL]];
        [request setDelegate:self];
        [request setRequestMethod:@"GET"];
        [request startAsynchronous];// 非同期通信の開始
    }
    else{
        // 圏外の時 = cahceから再現させる
    }
}

#pragma mark -
#pragma mark ASIHTTPRequestDelegate
- (void)requestFinished:(ASIHTTPRequest *)request
{
    SBJsonParser *parser = [[SBJsonParser alloc] init];
    NSArray *json = [parser objectWithString:[request responseString] error:nil];
    if(json != nil && [json count] > 0){// success
        [delegate reflectData:json];
    }
    [parser release];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
    NSError *error = [request error];
}
@end

■テストクラス

MyHTTPTest.h

以下のようにGHAsyncTestCaseを継承する。

#import <GHUnitIOS/GHUnit.h>

@class MyHTTP;

@interface MyHTTPTest : GHAsyncTestCase {
    MyHTTP *http;
}
@property (nonatomic, retain) MyHTTP *http;
@end

MyHTTPTest.m

以下のように実装する。

#import "MyHTTPTest.h"
#import "MyHTTP.h"

@implementation MyHTTPTest
@synthesize http;

- (void)setUpClass
{
    http = [[MyHTTP alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadData
{
    // 非同期処理が始まる前の準備
    [self prepare];
    
    // 非同期処理を実行する
    [self performSelector:@selector(startAsynchronous) withObject:nil afterDelay:0.1];
    
    // どのくらい[結果]が帰ってくるのを待つか指定する(ここでは3秒待つことにした)
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)startAsynchronous
{
    [http loadData];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer for popular");
        [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
    @catch (NSException *exception) {// アサーションに失敗した場合
        [self notify:kGHUnitWaitStatusFailure forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
}
@end

GHAssert系のメソッドはテストが失敗した場合にExceptionを発生させるので、上述のようにcatchしないとユニットテスト自体がクラッシュする。ちなみに以下のように書いても問題なくテストできた。


#import "MyHTTPTest.h"
#import "MyHTTP.h"

@implementation MyHTTPTest
@synthesize http;

- (void)setUpClass
{
    http = [[MyHTTP alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadData
{
    // 非同期処理が始まる前の準備
    [self prepare];
    
    // 非同期処理を実行する
    [http loadData];
    
    // どのくらい[結果]が帰ってくるのを待つか指定する(ここでは3秒待つことにした)
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer for popular");
        [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
    @catch (NSException *exception) {// アサーションに失敗した場合
        [self notify:kGHUnitWaitStatusFailure forSelector:@selector(testLoadData)];// テストの[結果]を通知
    }
}
@end

performSelectorメソッドの有用性が分からない。

■おまけ

実はMyHTTPクラスには通信ができなかった時の事を考慮して、キャッシュから呼び出す機能がある。以下のようにSELをプロパティに持たせる事で通知先を分岐するようにした。

#import "MyHTTPProxyTest.h"
#import "MyHTTPProxy.h"

@implementation MyHTTPProxyTest
@synthesize http;
@synthesize selector;

- (void)setUpClass
{
    http = [[MyHTTPProxy alloc] init];
    [http setDelegate:self];
}
- (void)tearDownClass
{
    [http release];
}

- (void)testLoadCache
{
    [self setSelector:@selector(testLoadCache)];
    [self prepare];
    [self performSelector:@selector(startLoadCache) withObject:nil afterDelay:0.01];
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)testLoadData
{
    [self setSelector:@selector(testLoadData)];
    [self prepare];
    
    // Do asynchronous task here
    [self performSelector:@selector(startAsynchronous) withObject:nil afterDelay:0.01];
    
    // Wait for notify
    [self waitForStatus:kGHUnitWaitStatusSuccess timeout:3.0];
}

- (void)startLoadCache
{
    [http loadCache];
}

- (void)startAsynchronous
{
    [http loadData];
}

#pragma mark -
#pragma mark MyHTTPDelegate
- (void)reflectData:(NSArray *)json
{
    NSLog(@"json:%d", [json count]);
    @try {
        GHAssertFalse(
                      !!([json count] == 0),
                      @"success to transfer");
        [self notify:kGHUnitWaitStatusSuccess forSelector:[self selector]];
    }
    @catch (NSException *exception) {
        [self notify:kGHUnitWaitStatusFailure forSelector:[self selector]];
    }
}
@end

カスタマイズされたUITableViewCellを使う

UITableViewのCellは使い回しされているのでサブビューを加えたりカスタマイズする時は注意が必要って備忘録。

■実装

UITableViewCellCustomized.h

#import <UIKit/UIKit.h>
@interface UITableViewCellCustomized : UITableViewCell {
    UILabel *title;
}
@property (nonatomic, retain) UILabel *title;
@end

UITableViewCellCustomized.m

#import "UITableViewCellCustomized.h"
@implementation UITableViewCellCustomized
@synthesize title;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {// Initialization code
        // title
        title = [[[UILabel alloc] initWithFrame:CGRectMake(20, 5, 280, 20)] autorelease];
        [title setFont:[UIFont systemFontOfSize:18]];
        [self addSubview:title];
        // accessory
        [self setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
}

- (void)dealloc
{
    [title release];
}
@end

クライアントコード

// create cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // call cell
    UITableViewCellCustomized *cell = (UITableViewCellCustomized *)[tableView dequeueReusableCellWithIdentifier:@"table_cell"];
    if (cell == nil) {// create cell
        cell = [[[UITableViewCellCustomized alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"table_cell"] autorelease];
    }
    [cell.title setText:@"なんらかの文字列"];
    return cell;
}

上述のようにUITableViewCellを継承したクラスを用意してセットアップする。以下の例のようにしてはならない。

悪い例

以下のように通常の使い回しされているUITableViewCellに直にaddSubviewする。

// create cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"table_cell"];
    cell = nil;
    if (cell == nil) {
        cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"table_cell"] autorelease];
    }
    [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
    // title
    UILabel *title = [[[UILabel alloc] initWithFrame:CGRectMake(20, 5, 280, 20)] autorelease];
    [title setText:@"なんらかの文字列"];
    [cell addSubview:title];
    return cell;
}

Objective-CでURLエンコードする

■実装

Util.h

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

Util.m

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

// decode
+ (NSString *)urldecode:(NSString *)text
{
    CFStringRef strRef = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(
        NULL,
        (CFStringRef) text,
        CFSTR(""),
        kCFStringEncodingUTF8);
    NSString * str = [NSString stringWithString:(NSString *)strRef];
    CFRelease(strRef);
    return str;
}
@end

■テスト

ちゃんと書いておく。

UtilTest.h

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

UtilTest.m

#import "UtilTest.h"
#import "Util.h"
@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!");
    
    // 記号などはしっかりテストしましょう
    NSString *url5 = [NSString stringWithString:@"%21%22%23%24%25%26%27%28%290%3D%7E%7C%60%7B%7D%2A%2B%3C%3E%3F_"];
    NSString *url6 = [NSString stringWithString:[Util urlencode:@"!\"#$%&'()0=~|`{}*+<>?_"]];
    GHAssertEqualObjects(url5, url6, @"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!");
    
    // 記号などはしっかりテストしましょう
    NSString *url5 = [NSString stringWithString:[Util urldecode:@"%21%22%23%24%25%26%27%28%290%3D%7E%7C%60%7B%7D%2A%2B%3C%3E%3F_"]];
    NSString *url6 = [NSString stringWithString:@"!\"#$%&'()0=~|`{}*+<>?_"];
    GHAssertEqualObjects(url5, url6, @"match!");
}
@end

つーかNSURLとかを拡張したほうが良かったかな。

ASIHTTPRequestを使う

■準備

Reachability

通信処理を行う場合は必ず通信ができる状態か確認する必要がある。

ダウンロードしてReachability.hとReachability.mをプロジェクトに取り込む。

ライブラリ
  • SystemConfiguration.framework

ASIHTTPRequest

ライブラリ
  • CFNetwork.framework
  • MobileCoreService.framework
  • MobileCoreService.framework
  • libxml2.dylib

「Build Settings > Search Paths > Header Search Paths」に「$SDKROOT/usr/include/libxml2」を追記する。