僕が作ったサービス

clanworkz

2011年7月17日日曜日

バーコードバトラーiPhoneアプリ制作過程〜第4回楽天APIの利用、XMLパーサーとか〜

バーコードバトラーアプリ制作過程の第四回目です。(第三回はこちら


1 楽天デベロッパーIDの取得
2 戦士情報画面の表示
3 リクエストURLの生成→XMLの取得
4 XMLパーサーの実装
5 結果(画像とテキスト)の表示



前回はバーコードリーダを実装して、バーコードからJANコードを取得するところまでやりました。今回はJANコードから商品情報を取得する部分まで実装します。

どうやってJANコードから商品情報を取得するのか?
※JANコードってバーコードの下に書いてある数字のことね

それはショッピングサイトのAPIを利用します。
楽天、ヤフー、アマゾン何でもいいのですが、ECサイトが提供している商品検索APIをもちいてJANコード検索を行います。

では楽天、ヤフー、アマゾンどれを使うのが一番いいのでしょうか?
アマゾンは書籍がメインで、しかも商品マスタを持っており、ほぼすべての商品がJANコードを持っています。普通に考えたらアマゾンでしょう。

が、しかしアマゾンAPIはアプリには利用できません。
規約により、アマゾンAPIのモバイル端末での利用は、書面での許可なしでは利用してはいけないことになっています。アマゾンに顧客を誘導するなどメリットを与えるなら別ですが、バーコードバトラーアプリでは全くもって論外ですね。

ちなみにこの規約によって話題のアプリが消え去ったケースもあります。
Amazonが、同社のデータを使うモバイルアプリを抹殺しようとしている


じゃあヤフーか楽天ですね。
「jan」で検索して見ましょう
ヤフー:1,268,298
楽天 :全 5,243,555件

楽天のが多そうですね。とりあえず楽天のAPIを利用することにしましょう。一番いいのは、楽天で検索したあとに商品が見つからなかった場合のみ、再度ヤフー検索を実施するのがいいとは思うのですが、今回は楽天のみに絞ります。

1 楽天デベロッパーIDの取得
楽天のAPIの利用方法についてはここを参照して下さい。
楽天商品検索APIとは

デベロッパーIDが必要のようです。ここはなんの問題もないと思います。
楽天に登録すればメールでデベロッパーIDを送信してくれます。

早速リクエストURLを生成してみましょう。
以下のURLの赤文字部分を自分自身のユーザーIDにして、ウェブブラウザで見てみましょう!

http://api.rakuten.co.jp/rws/3.0/rest?developerId=[あなたのdeveloperID]&operation=ItemSearch&version=2010-09-15&keyword=9784344016897

こんな感じでXMLが表示されればオーケーです。


このXMLのなかからタグ<itemName>と<smallImageUrl>の要素を取得します。
本来検索キーワードを使用する場合、日本語をエンコードしたりしなければならないのですが、JANコードは半角数字なので、そのまま検索可能です!!これはらくちんでありがたいですな。



2 戦士情報画面の表示
取得した商品(戦士)情報を表示するために、商品(戦士)表示画面を実装します。
編集画面とかで下からニュ〜って出てくるあの画面のことです。

全体的な流れは以下の通り。

商品(戦士)表示画面にJANコードを渡す
リクエストURLの生成
xmlのパース
商品情報表示
戦闘力の算出(次回)
戦士の端末への登録処理(次回以降)

商品情報表示までを今回やります。
ではでは早速、商品(戦士)表示画面の実装を始めます。

適当にUIViewControllerのサブクラスを作成して下さい。クラス名は適当にMonsterShowViewControllerとでもしましょうか。

前回の「4つのtabに対するビューコントローラーを作成する」と全く一緒です。今回は折角ですのでXibファイルも一緒につくりましょう。

File→New→New File→UIViewController subclassを選択→NEXTをクリック→UIViewControllerを選択してWith Xib〜チェックボックスにチェック→名前をMonsterShowViewControllerにして保存

こんな感じになればオーケー。


次にMonsterShowViewController.xibファイルを編集します。
まあこんな感じで、商品名表示用のlabelと、画像表示用のUIImageViewを貼付ければオーケー
MonsterShowViewController.xib


次にMonsterShowViewController.hで使用する変数やらを宣言します。

MonsterShowViewController.h
---------------------------------------------------------------

@interface MonsterShowViewController : UIViewController {
    
    IBOutlet UIImageView *imageView; 
    IBOutlet UILabel *itemName;
    NSString *requestUrl;
}
@property(nonatomic,retain)UIImageView *imageView;
@property(nonatomic,retain)NSString *jancode;
@property(nonatomic,retain)UILabel *itemName;

@end
---------------------------------------------------------------

リクエスト用のURLも一緒に宣言しておきます。ちなみにIBOutletはInterfaceBuilderで使用してまっせという意味です。

.mファイル側の@synthesizeも忘れずに。まあ注意出るから言わんでもわかると思うけど。



MonsterShowViewController.h
---------------------------------------------------------------


#import "MonsterShowViewController.h"

@implementation MonsterShowViewController
@synthesize imageView;
@synthesize itemName;
@synthesize jancode;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}
---------------------------------------------------------------


そしたら次にIB(InterfaceBuilder)を使用して、nibファイルとつなぎます。
MonsterShowViewController.xibを開いて、File's Ownerを右クリック。
imageViewとImage View
itemNameとLavel
の二つを接続。

これでオーケー!!

次にこのモンスター表示画面(戦士とか呼ぶよりモンスターのがしっくりきたので)をバーコード撮影後に呼び出します。

まず呼び出し側(BarcodeScanViewController)で呼び出される側(MonsterShowViewController)のクラスをインポートします。

そしたらBarcodeScanViewControllerの撮影完了後に呼び出されるメソッド内で、MonsterShowViewControllerを呼び出してみましょう。

以下のようにします。

BarcodeScanViewController.m (didFinishPickingMediaWithInfoメソッド内)
---------------------------------------------------------------

- (void)imagePickerController:(UIImagePickerController *)reader
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    id results = [info objectForKey:ZBarReaderControllerResults];
    ZBarSymbol *symbol = nil;
    
    for (symbol in results)
        break;

    [reader dismissModalViewControllerAnimated:YES];
    NSLog(@"%@",symbol.data);
    
    
    MonsterShowViewController *monsterShow = [[MonsterShowViewController alloc]initWithNibName:@"MonsterShowViewController" bundle:nil];
    
    [self.view addSubview:monsterShow.view];
    [monsterShow release];
}
---------------------------------------------------------------

これでバーコード撮影後に商品(モンスター)表示画面が表示されると思います。一応テストしてみて下さい。

3 リクエストURLの生成→XMLの取得
リクエストURLを作成して、楽天からxmlを取得してみましょう。
まず、モンスター表示クラス(MonsterShowViewController)に取得したJANコードを渡します。BarcodeScanViewController.m のdidFinishPickingMediaWithInfoメソッド内に以下のコードを付け足します。

BarcodeScanViewController.m (didFinishPickingMediaWithInfoメソッド内)
---------------------------------------------------------------
- (void)imagePickerController:(UIImagePickerController *)reader
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    id results = [info objectForKey:ZBarReaderControllerResults];
    ZBarSymbol *symbol = nil;
    
    for (symbol in results)
        break;

    [reader dismissModalViewControllerAnimated:YES];
    NSLog(@"%@",symbol.data);
    
    
    MonsterShowViewController *monsterShow = [[MonsterShowViewController alloc]initWithNibName:@"MonsterShowViewController" bundle:nil];
    monsterShow.jancode = symbol.data;
    [self.view addSubview:monsterShow.view];
    [monsterShow release];
}
---------------------------------------------------------------

これでオーケー。
次に、取得したJANコードからURLを生成するメソッドを追加します。

http://api.rakuten.co.jp/rws/3.0/rest?developerId=[あなたのdeveloperID]&operation=ItemSearch&version=2010-09-15&keyword=

の末尾にjanコードをつけ足せばいいだけですね。以下のようにします。
MonsterShowViewController.m viewDidLoadメソッド
---------------------------------------------------------------

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"monsterShowに渡されたjancode %@",jancode);
    NSString *requestUrl;
    requestUrl = [@"http://api.rakuten.co.jp/rws/3.0/rest?developerId=[あなたのdeveloperID]&operation=ItemSearch&version=2010-09-15&keyword="stringByAppendingString:jancode];
}
---------------------------------------------------------------

こんなんでオーケーなんですな。

それではリクエストを投げてみましょう。ここからは面倒ですぜ兄さん。
まず、ヘッダーファイル(MonsterShowViewController.h)で色々宣言しましょう。

MonsterShowViewController.h
---------------------------------------------------------------

@interface MonsterShowViewController : UIViewController {
    
    IBOutlet UIImageView *imageView; 
    IBOutlet UILabel *itemNameLabel;
    NSString *jancode;
    
    NSMutableData *webData;
    NSURLConnection *conn;
  
}
@property(nonatomic,retain)UIImageView *imageView;
@property(nonatomic,retain)NSString *jancode;
@property(nonatomic,retain)UILabel *itemNameLabel;
-(void) parseTag:(NSString*)requestUrl;
@end
---------------------------------------------------------------



あたらしく二つのインスタンス変数と、一つのメソッドが宣言されました。
次に実装です。以下のコードをぐばっとコピー&ペーストしちゃって下さい。

MonsterShowViewController.m 
---------------------------------------------------------------

-(void) parseTag:(NSString*)requestUrl;
{
    NSURL *url = [NSURL URLWithString:requestUrl];    
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[req addValue:0 forHTTPHeaderField:@"Content-Length"];
[req setHTTPMethod:@"GET"];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
    if (conn)
    {
webData = [[NSMutableData data] retain];
}
}

-(void) connection:(NSURLConnection *) connection 
didReceiveResponse:(NSURLResponse *) response 
{
[webData setLength: 0];    
}

-(void) connection:(NSURLConnection *) connection 
didReceiveData:(NSData *) data 
{
[webData appendData:data];
}

-(void) connection:(NSURLConnection *) connection 
  didFailWithError:(NSError *) error 
{
    //伝送中にエラーが生じた場合
[webData release];
    [connection release];
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    //接続が終了し、レスポンスのダウンロードに成功すると、次のconnectionDidFinishLoading:メソッドが呼び出される。
    
     NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes]
                                                 length:[webData length] 
                                               encoding:NSUTF8StringEncoding];
     //---shows the XML---
     NSLog(@"%@",theXML);
     [theXML release];    
     
}

---------------------------------------------------------------

んで、呼び出しはこんな感じ。
MonsterShowViewController.m viewDidLoadメソッド
---------------------------------------------------------------
- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"monsterShowに渡されたjancode %@",jancode);
    NSString *requestUrl;
    requestUrl = [@"http://api.rakuten.co.jp/rws/3.0/rest?developerId=[あなたのdeveloperID]&operation=ItemSearch&version=2010-09-15&keyword="stringByAppendingString:jancode];
 [self parseTag:requestUrl];
}
---------------------------------------------------------------

これで完了です。(あれ意外と一瞬で終わってもうたぞ)

早速実行してみましょう。
カメラでの撮影が終了すると、コンソール上にxmlの内容が大量に表示されるはずです。
コンソールってどこー??って人は、xcodeの右上にある
の真ん中をクリックしてみて下さい。


これでXML情報を取得するところまでは実装できました。
当初の予定ではXML内容を解析するところまでやる予定だったのですが、字数がやたら多くなってしまったので、XMLの解析は次回にまわします。

ではでは。


2011年7月11日月曜日

バーコードバトラーアプリ制作過程〜バーコードリーダー実装〜

バーコードバトラーアプリ制作過程三回目です。(第二回はこちら
今回はバーコードリーダーを実装したいと思います。

バーコードリーダーの実装にはZbar bar code readerを使用します。
以下のサイトを参考にしました。
「ZBar SDK」を使ってみる ーI Lib Keep

ではでは始めましょう。全体的な流れは以下の通りです。
1 ZBarSDKダウンロード
2 ダウンロードしたSDKフォルダのプロジェクトへの追加
3 その他Frameworkのプロジェクトへの追加
4 スキャン処理の実装
5 シミュレータによるテスト

1 ZBarSDKダウンロード
まずはZbar 公式サイトの「Download the ZBar iPhone SDK 1.1」よりZbarSDKをダウンロードします。以上!

2 ダウンロードしたSDKフォルダのプロジェクトへの追加
ダウンロードしたZBarSDK-1.1.dmgファイルを選択すると以下のウィンドウが表示されると思います。

そしたら指示取りにZBarSDKフォルダをXcodeのプロジェクトにドラッグします。

ここにぶち込みます

そうすると以下のように聞かれますので、
グループフォルダにコピーをチェック
グループを作成にチェック
ダーゲットにCodeFighterを設定
してFinishを選択します。どーん!

3 その他Frameworkのプロジェクトへの追加
つぎに必要なFrameworkを追加します。
■AVFoundation.framework (weak)
■CoreMedia.framework (weak)
■CoreVideo.framework (weak)
■QuartzCore.framework
■libiconv.dylib


この5つですね。Read this Firstにもこいつら入れろって書いてありますね。

左のグループ内、一番上の青いアイコンCodeFighterをクリックします
次にTARGETS内のCodeFighterをクリックします。
んでBuilds Phasesを選択
Link Binary With Librariesを選択
ここでフレームワーク追加できます。

ここにある+をクリックすると追加できるフレームワークの一覧が表示されます。
ここで先ほどの5つ

■AVFoundation.framework (weak)
■CoreMedia.framework (weak)
■CoreVideo.framework (weak)
■QuartzCore.framework
■libiconv.dylib


をバンバンaddしちゃってください。

これでZbarを使用する準備が整いました。


4 スキャン処理の実装
まずはBarcodeScanViewControllerにスキャン用のボタンを設置します。
前回ラベルを作成した部分、viewDidLoadメソッドにボタン設置のコードを書きます。

BarcodeScanViewController.m
--------------------------------------------------------------

- (void)viewDidLoad
{
    [super viewDidLoad];
    UILabel *namelabel = [[UILabel alloc] init];
    namelabel.frame = CGRectMake(80, 10, 200, 50);
    namelabel.text = @"僕はbarcodeScanViewControllerだよ";
    namelabel.textColor = [UIColor blueColor];
    namelabel.font = [UIFont fontWithName:@"AppleGothic" size:12];
    [self.view addSubview:namelabel];
}

--------------------------------------------------------------

こうなっている部分を以下のように書き換えます。
前回記述したラベルの表示部分は消してしまいましょう。



BarcodeScanViewController.m
--------------------------------------------------------------

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    btn.frame = CGRectMake(10, 200, 300, 30);
    [btn setTitle:@"バーコードスキャン" forState:UIControlStateNormal];
    // ボタンがタッチダウンされた時にscanButtonTappedメソッドを呼び出す
    [btn addTarget:self action:@selector(scanButtonTapped:)forControlEvents:UIControlEventTouchDown];
    [self.view addSubview:btn];
}
--------------------------------------------------------------

これでボタンを押したときに、scanButtonTappedメソッドが呼ばれることになります。
scanButtonTappedは自身で記述する必要があります。
記述する場所はどこでもかまわないのですが、viewDidLoadメソッドの真下にでも書いちゃいましょう。

スキャンボタンが押されたときのアクションを以下のように記述します。


BarcodeScanViewController.m
--------------------------------------------------------------


-(void)scanButtonTapped:(UIButton*)button
{
    UIAlertView *alert =[[UIAlertView alloc] initWithTitle:@"確認"
                                                   message:@"スキャンボタンがタップされました"
                                                  delegate:self
                                         cancelButtonTitle:@"確認"
                                         otherButtonTitles:nil];
    [alert show];
}

--------------------------------------------------------------

これでボタンが押されたときに、アラートが表示されるようになっていると思います。

これでボタンが実装できました。

それではいよいよ本題であるバーコード読み取り部分を実装してみましょう。
なにをするのか??
に記載のあるコードをそのまま使っちゃいましょう!!
必殺コピペです!!

先ほどボタンがタップされたときに呼ばれるメソッド、scanButtonTappedでアラートが表示されるようにしましたが、ここを書き換えます。(そんなすぐ書き換えるなら始めからやれよって??まあそうなんですけど…)

BarcodeScanViewController.m
--------------------------------------------------------------
-(void)scanButtonTapped:(UIButton*)button
{
    ZBarReaderViewController *reader = [[ZBarReaderViewController alloc] init];
    reader.readerDelegate = self;
    
    ZBarImageScanner *scanner = reader.scanner;
    
    [scanner setSymbology:ZBAR_I25
                   config:ZBAR_CFG_ENABLE
                       to:0];
    
    [self presentModalViewController:reader
                            animated:YES];
    
    [reader release];
}
--------------------------------------------------------------

このように書き換えます。
コピー&ペースト素晴らしいですね!

しかしこいつを貼付けた直後に大量にエラーが表示されたと思います。
なぜ??

これはZbarを使えるようにimportしてないからなんですね。
そういうわけで、ZBarSDK.hをインポートします。
わざわざ説明するまでもないですが、こんな感じで。

BarcodeScanViewController.h
--------------------------------------------------------------
#import 
#import "ZBarSDK.h"

@interface BarcodeScanViewController : UIViewController
{

}
@end
--------------------------------------------------------------
※UIKitのインポートがなぜか消える…

すると赤いエラーがつらつら消えます。気持ちいいですね。
と思いきやまだ
reader.readerDelegate = self;
あたりに黄色くエラーが出とります。
デリゲートがなんちゃら言うとりますね。


BarcodeScanViewController.h
--------------------------------------------------------------
#importarSDK.h"

@interface BarcodeScanViewController : UIViewController
<ZBarReaderDelegate>
{

}
@end
#import "ZB
--------------------------------------------------------------
※UIKitのインポートがなぜか消える…
※<>が大文字になっています。コピペの際はご注意を

これで解決です。
これでカメラが起動してバーコードを勝手に取ってくれるカメラ機能の実装が完了しました。
次に、撮影が完了したあとのメソッドを実装します。
これも
「ZBar SDK」を使ってみる ーI Lib Keep
のコードをコピペしちゃいましょう。
結果表示の部分は先ほど使用したアラート表示を使用しています。

またまた適当なところ(scanButtonTappedメソッドの真下あたりにでも)以下のコードをコピペします。


BarcodeScanViewController.m
--------------------------------------------------------------


- (void)imagePickerController:(UIImagePickerController *)reader
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    id results = [info objectForKey:ZBarReaderControllerResults];
    ZBarSymbol *symbol = nil;
    
    for (symbol in results)
        break;
    
    UIAlertView *alert =[[UIAlertView alloc] initWithTitle:@"スキャン完了" 
                                                   message:symbol.data
                                                  delegate:self 
                                         cancelButtonTitle:@"確認" 
                                         otherButtonTitles:nil];
    [alert show];
    
    [reader dismissModalViewControllerAnimated:YES];
}
--------------------------------------------------------------

スキャンの結果のJANコードがsymbol.dataで取れていることがわかりますね。
早速シミュレーターを起動してみましょう。

5 シミュレータによるテスト
スキャンボタンを押してみると…
げげっなんじゃこりゃあ!!
となったと思います。

大丈夫ミスってはおりませぬ。

この画面になったらaltキーを押しながら画面を長押しして見て下さい。

つぎのようにカメラロールから画像を選択する画面に切り替わったと思います。
もちろんシミュレータのカメラロールに画像なんて入っていませんね。

次の画像をデスクトップにでも保存して起動中のシミュレーターにドラック&ドロップして下さい。

そしたらブラウザが立ち上がりますので、画像を長押ししてください。保存するかどうか聞かれますので、ここで保存。これでシミュレーターに画像が保存されました。(一回でなぜか保存されない場合があるので、そのときは何度か挑戦してみてください)

そしたら再度CodeFighterアプリを起動して、スキャンボタンをタップして、alt+左クリック長押しでカメラロールを起動。保存したバーコードを選択してみてください。


つぎのように表示されればオーケーです。
これでバーコードからJANコードを取得するところまで実装できました。

感無量ですな。

JANコードが取れるとそれだけで色々できることがあるので、いろいろと活用していただけたらと思います。

すこしつかれたお。

続き「バーコードバトラーアプリ制作過程〜第4回楽天APIの利用、XMLパーサーとか〜