2014年12月14日日曜日

HttpFileUploadが動かなくなった

何やらHttpFileUploadが動かなくなった。Apache側のアクセスログに何も表示されないので、iOSアプリ側からHTTPリクエストを送れてすらいないっぽい。ただ、理由が分からない。

HTTPFileUpload.mのdelegateメソッドの最後尾にログを吐かせる一行を入れる。

メソッド: - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error


追加する行: NSLog(@"NSError is %@", error);

で、何が見えるか。コンソールを見ると、

NSError is Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo=0x15d9f8a0 {NSUnderlyingError=0x15df9bf0 "unsupported URL", NSLocalizedDescription=unsupported URL}

という感じ。これを調べてみると、

NSURLErrorDomain Code -1002 downloading pdf

にコメントが載ってる。曰く「The reason for this error is that your URL contains some characters that have to be percent escaped.」ということである。

どうもパッと見では分からないのだけれど、アプリに埋め込んだURLの末尾の文字がおかしかったみたい。書き直したら何故かすんなり通った。見た目からは全く分からないんだけれど。

PHPのMySQLドライバの警告

そう言えば、PHPのMySQLドライバがずっと警告を吐き続けている。

Warning: mysql_connect(): Headers and client library minor version mismatch. Headers:50605 Library:50540 in /usr/local/httpd-2.4.2/htdocs/***** on line **

で、何なのこれって話ですが、とりあえずインストールされているMySQLのバージョンを調べる。

apt-cache show mysql-server
(省略)
Version: 5.5.40-0ubuntu0.12.04.1
(省略)

ということで5.5.40っぽい。これが「Library: 50540」って事なのかな。で、phpinfo()の結果を見てみると以下の様な感じに。

MySQL Supportenabled
Active Persistent Links 0
Active Links 0
Client API version 5.5.40
MYSQL_MODULE_TYPE external
MYSQL_SOCKET /tmp/mysql565.sock
MYSQL_INCLUDE -I/usr/local/mysql-5.6.5-m8/include
MYSQL_LIBS -L/usr/local/mysql-5.6.5-m8/lib -lmysqlclient_r  
これのinclude部分のバージョンが5.6.5な所が「Headers:50605」って事なのかな。仕方ないので、これは再コンパイルかな。

さて、ソースからビルドして入れるか。

> sudo apt-get build-dep php5-mysql

すると、結局php5をソースからビルドする事になる。

パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
'php5-mysql' の代わりに 'php5' をソースパッケージとして選出しています
以下のパッケージが新たにインストールされます:
  apache2-prefork-dev apache2-utils apache2.2-bin apache2.2-common aspell
  aspell-en chrpath comerr-dev debhelper dh-apparmor dictionaries-common
  diffstat flex freetds-common freetds-dev gettext groff-base
  hardening-wrapper html2text intltool-debian krb5-multidev lemon libapr1-dev
  libaprutil1-dbd-sqlite3 libaprutil1-dev libaprutil1-ldap libaspell-dev
  libaspell15 libbsd-dev libbz2-dev libcroco3 libct4 libcurl4-openssl-dev
  libdb-dev libdb5.1-dev libedit-dev libenchant-dev libenchant1c2a libfl-dev
  libgcrypt11-dev libgd2-xpm libgd2-xpm-dev libgettextpo0 libglib2.0-bin
  libglib2.0-data libglib2.0-dev libgmp-dev libgmp3-dev libgnutls-dev
  libgnutls-openssl27 libgnutlsxx27 libgpg-error-dev libgssrpc4
  libhunspell-1.3-0 libice-dev libicu-dev libicu48 libidn11-dev
  libkadm5clnt-mit8 libkadm5srv-mit8 libkdb5-6 libkrb5-dev libldap2-dev
  libmagic-dev libmhash-dev libmhash2 libmysqlclient-dev libp11-kit-dev
  libpam0g-dev libperl-dev libperl5.14 libpipeline1 libpq-dev libpq5
  libpspell-dev librecode-dev librecode0 librtmp-dev libsasl2-dev
  libsensors4-dev libsm-dev libsnmp-base libsnmp-dev libsnmp-perl libsnmp15
  libsybdb5 libt1-5 libt1-dev libtasn1-3-dev libtidy-0.99-0 libtidy-dev
  libunistring0 libwrap0-dev libxaw7-dev libxmltok1 libxmltok1-dev libxmu-dev
  libxmu-headers libxpm-dev libxt-dev man-db po-debconf quilt re2c uuid-dev
アップグレード: 0 個、新規インストール: 105 個、削除: 0 個、保留: 0 個。
1 個のパッケージが完全にインストールまたは削除されていません。
49.8 MB のアーカイブを取得する必要があります。
この操作後に追加で 166 MB のディスク容量が消費されます。
続行しますか [Y/n]? Y
(中略)
以下のパッケージの処理中にエラーが発生しました:
 grub-pc
E: Sub-process /usr/bin/dpkg returned an error code (1)
E: ビルド依存関係の処理に失敗しました

えらい事になったぜ。エラーはコマンド実行の途中に表示されていた。

/usr/sbin/grub-probe: エラー: cannot find a device for / (is /dev mounted?).
dpkg: grub-pc の処理中にエラーが発生しました (--configure):
 サブプロセス インストール済みの post-installation スクリプト はエラー終了ステータス 1 を返しました

なんでだろう。dfすると一応/devも見えるのだが。

> df -a
Filesystem     1K-blocks    Used Available Use% Mounted on
aufs             5104816 2362200   2486616  49% /
proc                   0       0         0    - /proc
sysfs                  0       0         0    - /sys
udev              113528       4    113524   1% /dev
devpts                 0       0         0    - /dev/pts
tmpfs              49084     212     48872   1% /run
none                   0       0         0    - /sys/fs/fuse/connections
none                   0       0         0    - /sys/kernel/debug
none                   0       0         0    - /sys/kernel/security
none                5120       0      5120   0% /run/lock
none              122700       0    122700   0% /run/shm
hostfs                 0       0         0    - /rackhub

なんか、何となく根が深そうなのでちょっとここは避けて通りたい

2014年12月13日土曜日

PHPの設定をもう少し追加


PHPでは直接ユーザからアクセスされないファイルを".inc"って感じの拡張子で作るけれど、このファイルがRackhubのApacheのデフォルト設定ではPHPとして認識されない様になっているので、それを設定する。

設定ファイル/usr/local/httpd-2.4.2/conf/conf.d/defaultの下記を変更。

変更前: AddHandler php5-script .php
変更後: AddHandler php5-script .php .inc

でもってApacheを再起動すると、無事に.incがPHPとして認識され、直接URLを叩いても真っ白な画面になる(PHPロジックを実行しても何も表示されないんで)。

> sudo /usr/bin/apachectl restart

Emacsもちょっと整備する

開発はMacBook Proでやっていて、当然iOSアプリの開発はX-codeなんですけれど、PHPの開発はやっぱりEmacsが良いなあと言う事で、とりあえずターミナルアプリ上でemacs -nwしてやってましたが、やはり生産性がイマイチなのでGUI版のEmacsでやることに。

最近は~/.emacsじゃなくて~/.emacs.d/init.elに書くんですね。とりあえず、PHPモードを使いたいので、init.elに下記を追加。

(autoload 'php-mode "php-mode" "Major mode for editing php code." t)
(add-to-list 'auto-mode-alist '("\\.php$" . php-mode))
(add-to-list 'auto-mode-alist '("\\.inc$" . php-mode))


これだけだとPHPモード本体が無いので、下記からダウンロードしてZIPファイルを解凍しphp-mode.elを取り出す。
でもってとりあえずphp-mode.el~/emacs.d/に置いて、M-x load-fileする。

あと、Spotlightの起動キーバインドがCtrl-spaceなので、これをCtrl-cmd-spaceに変更する。これ変えとかないとEmacsでコピペが出来ない。

RackhubのMySQLを整備する

なんか、触ってないとUNIXのコマンドとかMySQLの使い方とか、本当にスパッと忘れてしまうものだなあと実感。

とりあえず、Rackhubのrootパスワードを変更する。

> sudo su -
% passwd
New password:


そして一般ユーザに戻って

> mysql -u root -p

でさっき変更したrootのパスワードを入れると無事mysqlコマンドラインツールが起動した。UNIXユーザのパスワードを変更するのがpasswdコマンドだと思い出すのに2分掛かった。

さて、MySQLについてもすっかり忘れている。まずはデータベースから作らなくちゃ。作ったら一度コマンドラインツールからquitして再度データベースを指定してログインするよ。

mysql> CREATE DATABASE hoge;
Query OK, 1 row affected (0.01 sec)


mysql> quit
 

> mysql -u root -p hoge

mysql> show tables;
Empty set (0.00 sec)


ちなみにこんなサイトを発見。面白いし便利。

2014年12月7日日曜日

画像のアップロード時にジオタグが抜け落ちる

正確に言うと、iOSアプリ内で画像データを扱う際はUIImageクラスのインスタンスを使うわけであるが、このオブジェクトにはExifやその中のジオタグ(ロケーション情報。緯度とか経度とか)が入っていないので、UIImageオブジェクトをそのままHttpFileUploadでアップロードする(つまりバイナリをBase64エンコードして送る)だけでは、当然Exifやジオタグはアップロードされない。

逃げ方としては、ロケーション情報は普通にテキスト情報としてPOSTする、というのが簡単。

本筋から言うと、UIImageオブジェクトとしてでは無く、Exifやジオタグが入った「単なるバイナリファイル」としてアップロードすれば、この問題は回避出来るのかも知れない。時間があれば手を付けたいところ。

あと、PhotoLibraryから読み込んだ画像のデータでは、Exifやジオタグが取れていないっぽい。ここはどう解決するかまだ考えていない。

一つの逃げとしては、とにかく「画像をアップロードする時点の」位置情報で代替するという所か。

なんか中途半端だけれど。

2014年11月30日日曜日

HTTPFileUpload

HTTPFileUploadを試させて頂くが、コンパイルエラーになる。

まず、HTTPFileUpload.hは、UIKitをインポートしないとUIImageが解決出来ない。

#import <UIKit/UIKit.h>

まぁこれは簡単だ。更に、HTTPFileUpload.hでもう一個ビルドエラーが。

"Existing instance variable 'delegate_' for property 'delegate' with  assign attribute must be __unsafe_unretained"

というエラーが出る。何のこっちゃ。どうも、Objective-Cでのメモリ管理に関連するらしい。ARC (Automatic Reference Counting)を使っている場合は問題が出るとか。とりあえず、id@propertyするなら@privateに書いちゃいかんって事らしい。

@interface HTTPFileUpload : NSObject
{
 @private
  id <HTTPFileUploadDelegate> delegate_; // ← つまりこれが問題

  NSMutableArray *postStrings_, *postImages_;
  NSMutableData *resultData_;
}
@property(nonatomic, assign) id <HTTPFileUploadDelegate> delegate;


とりあえず上の行を削除する。この後はHTTPFileUload.mでビルドエラーの嵐。

'release' is unavailable: not available in automatic reference counting mode

これもARCだな。ふむふむ。ARCは参照カウントをマニュアルで操作するreleaseだのretainだのを呼んだらいかんらしいからな。下記を参照させて頂く。
とりあえず、reelaseだのautoreleaseだのでエラーが出まくるので、そこを全部削除するとビルドは通った。あと、まだちょこちょこワーニングが。

Values of type 'NSUInteger' should not be used as format arguments; add an explicit cast to 'unsigned long' instead

これ、言うとおりに[postData length]からunsigned longへのキャストを入れると、今度はフォーマットがintを要求しているのに何事ぢゃ!と怒るので、結局intへキャストするのが正解。postWithUriメソッドの最終行について下記のワーニングが出るが、とりあえず無視する。

Expression result unused

以上で一応エラーとワーニングの嵐は落ち着く。

PHP on Rackhub

さて、Webサーバが上がったからには、何かロジックを動かしてPOSTを受け止めねばならぬ。というわけで下記を参考にさせて頂く。


ふむふむ、そう言えばapt-getで色々とアップデート出来るんだっけな、Ubuntuは。Ubuntuが出始めたばかりの7年くらい前に色々いじった記憶があるが、その後すっかり仕事を変えてしまったのでご無沙汰でした。とりあえず、

% apt-get update
% apt-get upgrade
% apt-get dist-upgrade

してひたすらアップデートする。そして、Apacheがあるらしいので確かめる。

% which apachectl
/usr/bin/apachectl


おお、Apachそんなところにおったか。というわけで起動してみる。

% sudo apachectl start

HTTPでアクセスしてみるとちゃんとページが表示される。じゃあ、PHPも試しちゃおうって事で、

% cd /usr/local/httpd/htdocs/
% sudo vi phpinfo.php

<?php phpinfo(); ?>
%

って感じでドキュメントルートにPHPファイルを作り、早速HTTPで叩いてみるとちゃんと表示される。素晴らしい!

(追記)
上記のページに従ってApacheの設定を少し直す。
  1. Options Indexesの削除
  2. AllowOverrideをAllへ
  3. DirectoryIndexを追加

Nginx

「えんじんえっくす」と読むそうだ。
ちなみに「えんじんえっくす」とタイプすると私のATOK君は「はてなキーワード」から変換候補として「nginx」を拾ってくる。何てこった。知らないのは自分一人だったのか。

Wikipedia曰く、

nginx(発音は「えんじんえっくす」[3])は、フリーかつオープンソースWebサーバである。加えて、HTTPSMTPPOP3IMAPリバースプロキシの機能も持つ。処理性能・高い並行性・メモリ使用量の小ささに焦点を当てて開発されている。

だそうだ。凄いな。

Rackhub

アプリから写真をアップロードする先のクラウドって何が良いかなと迷ったのだけれど、Rackhubというのがあるので試してみる事に。下記のサイトを参考にさせて頂くと、とても簡単にサーバの立ち上げとSSHでのログインが出来た。


そんでもって、SSHでログインして

% which nginx
/usr/sbin/nginx
% sudo nxinx

でWebサーバが立ち上がってしまった。楽ちん・・・

で、nginxって何!?

2014年11月29日土曜日

NSDictionaryでハマる

ネットで色々とサンプルコードを参照させて頂くわけだが、時々そのままではうまく動かない事がある。

あるサンプルでNSDictionary型のデータ(画像情報のプロパティ)をNSMutableDictionary型にキャストしただけで、そのキャスト後の変数に新たなDictionary値を設定しているものがあったが、これを実行するとSIGABORTしてクラッシュする。NSDictionary型のオブジェクトには新たな値を設定するメソッドが実装されていない為だ。まぁ、こう書いてみれば当たり前だが。

というわけで、NSDictionary型のデータを一旦mutableCopyし、NSMutableDictionary型のデータに入れてから新たなDictionary値を設定する事で回避した。 具体的には、画像データにGPS情報を入れ込む際に、下記の様にした。

UIImage *image = info[UIImagePickerControllerOriginalImage];
NSMutableDictionary *metadata = [info[UIImagePickerControllerMediaMetadata] mutableCopy];
// self.latestLocationには現在位置が記録されている想定
metadata[(NSString *)kCGImagePropertyGPSDictionary] = [self GPSDictionaryForLocation:self.latestLocation];
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
[assetsLibrary writeImageToSavedPhotosAlbum:image.CGImage metadata:metadata completionBlock:^(NSURL *assetURL, NSError *error) {
    if (error) {
        NSLog(@"保存失敗: %@", error);
    }
}];

赤字の部分のmutableCopyを忘れるとエラーになるので注意

CLLocationManagerでハマる

何やらLocationManagerがうまく動かないので色々調べてみると、iOS8から仕様変更があった様だ。まず、こちらを参照すると無事ロケーションがアップデートされる様になる。

また、参考にしたサンプルコードは画像にGPS情報を埋め込む為に CLLocationManager.location を直に呼んで最新のロケーションを取得しようとしていたが、これはCLLocationManagerのヘッダファイルを見ると、ロケーションのアップデート中はnilになる模様。
「The last location received. Will be nil until a location has been received.」

とりあえず、直接CLLocationManager.locationを参照せず、didUpdateLocationsが呼ばれたらViewControllerのlatestLocationプロパティに最新位置を設定する事にする。

2014年11月15日土曜日

週末プログラマ活動を再開する・・・の巻

一時期本業が忙しかったのですっかり遠ざかっていたが、やっぱりコード書いてみたくなった。

というわけでiOS向けのアプリでも書こうかと思い色々勉強中。ネットには色々有益な情報があるなあ、と久々に技術情報を検索しながら感心しきり。早速参考にさせて頂く。

これだけ情報があって、後は手元にMacとテストに使えるiPhone実機があって、Xcodeがインストールされてて、Objective-Cはよく分からんが他言語でのプログラマとしての経験があるって言ったら、他に何が要るのかって感じだな。あと開発に必要なのは(1)やる気と(2)アイデアと(3)時間だが、(1)が無いなら開発しなけりゃ良いわけだし、(3)は自分で作るものだから、後の問題は(2)だけ。さーて、腕まくりして頑張るかね。

  1. [iOS]UIImagePickerControllerで撮影した写真にExif・位置情報を書き込んで保存する方法 - カメラアプリを作った時に写真のファイルにジオタグを埋め込む方法
  2. iPhoneの画像からEXIF,GPSデータを取得する - こちらも写真にジオタグを埋め込む方法
  3. Xcodeで作るカメラアプリ開発入門 その① - そもそものカメラアプリの作り方
  4. 実践! iPhoneアプリ開発 カメラアプリの作り方(1) - こちらもカメラアプリの作り方
  5. iOSのカメラ機能を使う方法まとめ【13日目】 - これまたカメラアプリの作り方。世の中にはカメラアプリを作りたい人が一杯いるんだなあと
  6. iphoneで撮った写真をherokuにアップロードする(iosアプリ) - 撮った写真をWebにアップする方法。これはHTTPSを使う奴かな??
  7. 評判のいいiOSアプリ開発のTips ベスト20 - アプリ開発のTIPS集的な
  8. iOSアプリ開発に役立つTips100連発!  - これもTIPS集。凄い数

さて、今回は三日坊主にならない様にしないとな。
つーか、現職ではこういうディジタル、モバイル系のセンスは本業にも活きる筈だから、積極的にシナジーを出していかないとなあ。

(11/16 追記)
カメラで撮ったら保存したいわけだが、その際のコードは下記を参照。

(11/16 追記2)
CoreLocationで現在の位置情報を取得する点は下記を参照。

(11/16 追記3)
参考にさせて頂いたコードでNSDictionaryへの値置き換え周りでコンパイルエラーが起きるのでこちらも参照。