ASIHTTPRequest と XCode4.3.1

XCode 4.3.1を利用して、ASIHTTPRequestを利用しようとして、手間取ったのでそれをまとめます。
target OSは5.1を想定して作っています。


ASHIHTTPRequest Github
https://github.com/pokeb/asi-http-request


設定手順
http://allseeing-i.com/ASIHTTPRequest/Setup-instructions


手順にも書かれていますが、以下のファイルをプロジェクトにコピーする必要があります。
逆にいうと、他のファイルは不要。

  • ASIHTTPRequestConfig.h
  • ASIHTTPRequestDelegate.h
  • ASIProgressDelegate.h
  • ASICacheDelegate.h
  • ASIHTTPRequest.h
  • ASIHTTPRequest.m
  • ASIDataCompressor.h
  • ASIDataCompressor.m
  • ASIDataDecompressor.h
  • ASIDataDecompressor.m
  • ASIFormDataRequest.h
  • ASIInputStream.h
  • ASIInputStream.m
  • ASIFormDataRequest.m
  • ASINetworkQueue.h
  • ASINetworkQueue.m
  • ASIDownloadCache.h
  • ASIDownloadCache.m
  • ASIAuthenticationDialog.h
  • ASIAuthenticationDialog.m
  • Reachability.h (External/Reachability フォルダにあります)
  • Reachability.m ( 〃 )


また、frameworkの追加から以下を追加

  • CFNetwork.framework,
  • SystemConfiguration.framework,
  • MobileCoreServices.framework,
  • CoreGraphics.framework,
  • libz.dylib



Xcode 4.2以降だと、ARC(Automatic Reference Counting)がデフォルトで有効になっており、コンパイルしようとしても、retainやautoreleaseなどのリファレンスカウンタを制御する文で、エラーとなっていまいコンパイルできない。
ファイル単位でARCを解除できるので、Build Phasesから取り込んだファイルだけ、ARCを解除する。


ソースの横にある -fno-objc-arc を追加してやる。




下のObjective-Cプログラムは、現在書き途中のカメラアプリの一部サンプルソース

cameraBtnActionメソッドで、カメラを起動して postBtnActionメソッドで撮影した画像を ASIHTTPRequest を利用しサーバーに送信しています。サーバーはbasic認証で Contents-Type は multipart/form-data のデータを受け取ります。

  • uploadRequestFinished 送信完了 delegate
  • uploadRequestFailed エラーだった場合 delegate
#import "ViewController.h"
#import "ASIHTTPRequest.h"
#import "ASIFormDataRequest.h"

@interface ViewController ()

@end

@implementation ViewController
@synthesize pictureImageView = _pictureImageView;

#define URL @"http://example.com/post/"
#define USER_ID @"user_id"
#define PASSWORD @"password"

- (void)viewDidUnload
{
    [self setPictureImageView:nil];
    [super viewDidUnload];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (IBAction)cameraBtnAction:(id)sender {
    if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        NSLog(@"カメラがついてない機種です。");
        return;
    }
    
    UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
    imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
    imagePickerController.delegate = self;
    [self presentModalViewController:imagePickerController animated:YES];
}
- (IBAction)postBtnAction:(id)sender {
    
    NSURL *url = [NSURL URLWithString:URL];
    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    [request setUsername:USER_ID];
    [request setPassword:PASSWORD];
    
    NSData *imageData = UIImageJPEGRepresentation(self.pictureImageView.image, 0.9);
    [request setData:imageData withFileName:@"coordi.jpg" andContentType:@"image/jpeg" forKey:@"image"];
    
    [request setDelegate:self];
    [request setDidFinishSelector:@selector(uploadRequestFinished:)];
    [request setDidFailSelector:@selector(uploadRequestFailed:)];
    
    [request startAsynchronous];
}

- (void)uploadRequestFinished:(ASIHTTPRequest *)request{    
    NSString *responseString = [request responseString];
    NSLog(@"Upload response %@", responseString);
}

- (void)uploadRequestFailed:(ASIHTTPRequest *)request{
    
    NSLog(@" Error - Statistics file upload failed: \"%@\"",[[request error] localizedDescription]); 
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [self dismissModalViewControllerAnimated:YES];
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo {
    [self dismissModalViewControllerAnimated:YES];
    self.pictureImageView.image = image;
}
@end

Mac homebrewとgitを入れる

  • homebrewを入れる
$ ruby -e "$(curl -fsSLk https://gist.github.com/raw/323731/install_homebrew.rb)"
  • gitを入れようとする
$ brew install git
  • 以下のエラーがでる場合は、XCodeを開いて Preferences-Download-Command Line Toolsをダウンロードしてから、できるようになります。
  • エラー内容
==> Downloading http://git-core.googlecode.com/files/git-1.7.9.2.tar.gz
File already downloaded in /Users/aaa/Library/Caches/Homebrew
==> make prefix=/usr/local/Cellar/git/1.7.9.2 CC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clan
GIT_VERSION = 1.7.9.2
    * new build flags or prefix
    * new link flags
./generate-cmdlist.sh > common-cmds.h+ && mv common-cmds.h+ common-cmds.h
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -o hex.o -c -MF ./.depend/hex.o.d -MMD -MP  -Os -w -pipe -march=native -Qunused-arguments -I. -DUSE_ST_TIMESPEC -DNO_GETTEXT  -DHAVE_DEV_TTY -DSHA1_HEADER='<openssl/sha.h>'  -DNO_MEMMEM  hex.c
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -o ident.o -c -MF ./.depend/ident.o.d -MMD -MP  -Os -w -pipe -march=native -Qunused-arguments -I. -DUSE_ST_TIMESPEC -DNO_GETTEXT  -DHAVE_DEV_TTY -DSHA1_HEADER='<openssl/sha.h>'  -DNO_MEMMEM  ident.c
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -o kwset.o -c -MF ./.depend/kwset.o.d -MMD -MP  -Os -w -pipe -march=native -Qunused-arguments -I. -DUSE_ST_TIMESPEC -DNO_GETTEXT  -DHAVE_DEV_TTY -DSHA1_HEADER='<openssl/sha.h>'  -DNO_MEMMEM  kwset.c
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -o levenshtein.o -c -MF ./.depend/levenshtein.o.d -MMD -MP  -Os -w -pipe -march=native -Qunused-arguments -I. -DUSE_ST_TIMESPEC -DNO_GETTEXT  -DHAVE_DEV_TTY -DSHA1_HEADER='<openssl/sha.h>'  -DNO_MEMMEM  levenshtein.c
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -o list-objects.o -c -MF ./.depend/list-objects.o.d -MMD -MP  -Os -w -pipe -march=native -Qunused-arguments -I. -DUSE_ST_TIMESPEC -DNO_GETTEXT  -DHAVE_DEV_TTY -DSHA1_HEADER='<openssl/sha.h>'  -DNO_MEMMEM  list-objects.c
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -o ll-merge.o -c -MF ./.depend/ll-merge.o.d -MMD -MP  -Os -w -pipe -march=native -Qunused-arguments -I. -DUSE_ST_TIMESPEC -DNO_GETTEXT  -DHAVE_DEV_TTY -DSHA1_HEADER='<openssl/sha.h>'  -DNO_MEMMEM  ll-merge.c
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -o lockfile.o -c -MF ./.depend/lockfile.o.d -MMD -MP  -Os -w -pipe -march=native -Qunused-arguments -I. -DUSE_ST_TIMESPEC -DNO_GETTEXT  -DHAVE_DEV_TTY -DSHA1_HEADER='<openssl/sha.h>'  -DNO_MEMMEM  lockfile.c
In file included from hex.c:1:
In file included from ./cache.h:In file included from ident.c:8:
In file included from 4:
./git-compat-util.h:93:10:./cache.h:4:
./git-compat-util.h:93:10: fatal error: 'unistd.h' file not found
 fatal error: 'unistd.h' file not found
#include <unistd.h>
         ^
#include <unistd.h>
         ^
In file included from kwset.c:37:
In file included from ./cache.h:4:
./git-compat-util.h:93:10: fatal error: 'unistd.h' file not found
#include <unistd.h>
         ^
In file included from levenshtein.c:1:
In file included from ./cache.h:4:
./git-compat-util.h:93:10: fatal error: 'unistd.h' file not found
#include <unistd.h>
         ^
In file included from ll-merge.c:7:
In file included from ./cache.h:4:
./git-compat-util.h:93:10: fatal error: 'unistd.h' file not found
#include <unistd.h>
         ^
In file included from list-objects.c:1:
In file included from ./cache.h:4:
./git-compat-util.h:93:10: fatal error: 'unistd.h' file not found
#include <unistd.h>
         ^
In file included from lockfile.c:4:
In file included from ./cache.h:4:
./git-compat-util.h:93:10: fatal error: 'unistd.h' file not found
#include <unistd.h>
         ^
1 error generated.
1 error generated.
1 error generated.
make: *** [levenshtein.o] Error 1
make: *** Waiting for unfinished jobs....
make: *** [hex.o] Error 1
1 error generated.
make: *** [ident.o] Error 1
make: *** [lockfile.o] Error 1
1 error generated.
make: *** [ll-merge.o] Error 1
1 error generated.
make: *** [kwset.o] Error 1
1 error generated.
make: *** [list-objects.o] Error 1
==> Exit Status: 2
http://github.com/mxcl/homebrew/blob/master/Library/Formula/git.rb#L47
==> Environment
HOMEBREW_VERSION: 0.8.1
HEAD: (none)
HOMEBREW_PREFIX: /usr/local
HOMEBREW_CELLAR: /usr/local/Cellar
Hardware: 8-core 64-bit sandybridge
OS X: 10.7.3
Kernel Architecture: x86_64
Xcode: 3.2.2
GCC-4.0: N/A
GCC-4.2: N/A
LLVM: build 2336
Clang: N/A
MacPorts or Fink? false
X11: /usr/X11
System Ruby: 1.8.7-249
/usr/bin/ruby => /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
Which Perl:   /usr/bin/perl
Which Python: /usr/bin/python
Which Ruby:   /usr/bin/ruby => /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
==> Build Flags
CC: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
CXX: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ => /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
LD: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
CFLAGS: -Os -w -pipe -march=native -Qunused-arguments
CXXFLAGS: -Os -w -pipe -march=native -Qunused-arguments
MAKEFLAGS: -j8

Error: Failed executing: make prefix=/usr/local/Cellar/git/1.7.9.2 CC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang CFLAGS=-Os\ -w\ -pipe\ -march=native\ -Qunused-arguments LDFLAGS= install
These existing issues may help you:
    https://github.com/mxcl/homebrew/issues/9023
    https://github.com/mxcl/homebrew/issues/9618
    https://github.com/mxcl/homebrew/issues/10225
    https://github.com/mxcl/homebrew/issues/10544
Otherwise, please report the bug:
    https://github.com/mxcl/homebrew/wiki/checklist-before-filing-a-new-issue

Corona SDKで、Google画像検索+Jsonパース+画像取得+アニメーションさせてみた

Corona SDKサンプルソースを見ながら、コードを書いてみた。
たった、数十行でこれだけの事ができるなんて、感動しちゃいます。

内容)
HTTPのリクエストで、Google 画像サーチAPIをたたいて、Jsonの結果をデコード

require("json")
http = require("socket.http")
data = json.decode(http.request("http://ajax.googleapis.com/ajax/services/search/images?q=KARA&v=1.0"))


画像のURLから、画像の取得したオブジェクトをつくる

function networkListener( event )
        if ( event.isError ) then
                print ( "Network error - download failed" )
        else
                event.target.x = math.random(300)
                event.target.y = - 100 - math.random(600)
                physics.addBody( event.target, { density=0.9, friction=0.2, bounce=0.3} )
        end
end

display.loadRemoteImage(value.tbUrl, "GET", networkListener, value.imageId, system.TemporaryDirectory)

出来上がった、全ソース

physics = require("physics")  --重力設定
physics.start()                       --重力開始
require("json")
http = require("socket.http")  -- HTTPのRequest
display.setStatusBar( display.HiddenStatusBar ) --ステータスバーを消す

data = json.decode(http.request("http://ajax.googleapis.com/ajax/services/search/images?q=KARA&v=1.0"))
function networkListener( event )
        if ( event.isError ) then
                print ( "Network error - download failed" )
        else
                event.target.x = math.random(300)                  -- 画像の位置をランダムに散らす
                event.target.y = - 100 - math.random(600)      -- 画像の位置をランダムに散らす
                physics.addBody( event.target, { density=0.9, friction=0.2, bounce=0.3} )
        end
end

local function onTilt( event )
        physics.setGravity( 10 * event.xGravity, -10 * event.yGravity )  -- 加速度センサーで、重力の設定を変える
end

Runtime:addEventListener( "accelerometer", onTilt )  -- 加速度センサーのイベント追加

for key, value in pairs(data.responseData.results) do
        -- ネットの画像を取得する
        display.loadRemoteImage(value.tbUrl, "GET", networkListener, value.imageId, system.TemporaryDirectory)
end


ただ、実機確認しようとするとコンパイルするのに、ソースをサーバーに送らないとコンパイルしてくれないので、1分弱待たされます。シュミレーターもあるんですが、ジャイロセンサーなどを使おうとなると、すぐにデバッグできないので結構イライラしました。

Androidアプリ リリースバージョンでClassNotFoundException

Debug中では起こらなかった、ClassNotFoundExceptionが必ず発生するエラーを仕込んだままリリースするという事故が起きた。
デバッグでは、コンパイルできて正しく実行できているのに、リリースでコンパイルしたapkファイルを逆コンパイルして確認してみると、あるはずのclassファイルが消えてなくなっている・・・。

丸1日調査して解決したのが、どうもProGuardでやらかしているようだ。

もともとの原因は、今まで開発していた環境から Android SDK Toolsのバージョンアップをr16にアップデートした事が始まりっぽい。
どうも古いProGuardの設定が悪さをするようだ・・・。

下記ブログその手順どおりやって、どうにか対処できました。
http://wada811.blog.fc2.com/blog-entry-62.html


Android toolのExport Signed Application packeageからビルドすると、最後に「Conversion to Dalvik format failed with error 1」などが出た場合は、プロジェクトのcleanをしてから再度実施。

当然だと思われるかもしれないが、リリースする前にコンパイルしたapkファイルは一度実機で動作確認してから、リリースしたほうがよさそうだ。

Corona SDK勉強会 参加しました

Corona SDK 勉強会 in 渋谷 に参加してきました。

[ATND]
http://atnd.org/events/23395

Corona SDK

Flashエンジニアが今までの資産などを使って、簡単にゲームができるという雰囲気でした。Unity3Dと比べると、今までのFlashで作った素材などを利用できるのは、大きいかなおと思います。
Flashでアニメーションを作ってから、一枚の画像ファイルに書き直して、CSSスプライトのような事をして画像の読み込みを少なくして、速度をあげるようです。
その後、Corona SDKでその素材をもとにゲームなどをつくる手順っぽい。

http://www.anscamobile.com/


ただ、Unity3Dと比べると、GREESDKなどに簡単につながる機能がないので、iTunes StoreAndroid Marketでゲーム単体として売る事を目的にしか利用できなさそうな感じでした。


日本コロナの会 山本 直也さん(発表資料)
https://sites.google.com/site/corona20120201/

Coronaでよく聞かれること 木村さん

Storyboard API 鍵山さん
http://keygx.blogspot.com/2012/02/storyboard-api-corona-sdk.html

flasherがcoronaを使う 寺井さん
http://nijibox.jp/smap/category/coronasdk/

drops
http://www.calm-design.com/lifestyle/Apps.html


あと、Corona SDKの日本語書籍が、2/24に発売されるそうです。
http://www.7netshopping.jp/books/detail/-/accd/1106122300

大きい画像をImageViewで表示しようとするとOutOfMemory起こすよ

カメラで撮った画像などをそのままImageViewで表示しようとすると、画像がサイズが大きすぎて、簡単にOutOfMemoryが発生してたので、file_pathを渡すと小さい画像のBitmapに変換するプログラムを書いて利用しています。

カメラで撮った画像をそのまま利用する事がないという想定です・・・。

たとえば、サーバーに画像を送信するときなど、どの画像を選択したのかサムネイル表示する際に利用していただければいいと思います。

サイズは適当です・・・。

package jp.sharakova.util;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class BitmapResize {

	public static Bitmap decodeLargeFile(String path) {
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;

		int scaleW = options.outWidth / 380 + 1;
		int scaleH = options.outHeight / 420 + 1;

		int scale = Math.max(scaleW, scaleH);

		options.inJustDecodeBounds = false;
		options.inSampleSize = scale;

		return BitmapFactory.decodeFile(path, options);
	}

}

Google Map API keyをデバッグ、リリースで分ける

Google Map API Keyは、リリースとデバッグで分ける必要があったので、プログラムでdebugとreleaseの判定をできるようにしました。
ApplicationInfo.FLAG_DEBUGGABLEのフラグがたっているかで、判定しています。

ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
return ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE);

サンプルコード

MapView map = new MapView(this, GoogleMapKey.getKey());
package jp.sharakova.app.android.golfcaddie.utils;

import android.content.Context;
import android.content.pm.ApplicationInfo;

public class GoogleMapKey {

	public static String getKey (Context context) {
		// TODO: DEBUGとRELEASEのキーを記述
		return isDebug(context) ? "DEBUG_GOOGLE_MAP_API_KEY" : "RELEASE_GOOGLE_MAP_API_KEY";
 	}
	
	private static Boolean isDebug(Context context){
		try {
			ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
			return ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE);
		} catch (Exception e) {
			e.getStackTrace();
			return false;
		}
	}
}