MoneyViewがMac Peopleに紹介されました。
先ほどMoneyViewがMac Fan誌に紹介されたと書いたばかりですが、実は同じ日に発売されたMac People誌にも紹介されました。ただ、ここでは特にレビュー記事とかではなく、オンラインウェアの紹介ページの一部として掲載されました。
知り合いの人に自慢したら「おお、マック本デビューだね!」と言われました。とてもうれしいです!
MoneyViewのレビュー記事がMac Fanに掲載されました。
今日発売されたMac FanにMoneyViewのレビュー記事が掲載されました。分量は1ページでかなりいい評価をして頂きました。
MoneyViewについて客間的な観点でのレビュー記事を読むのは初めてだったので、ただ面白かっただけでなく、かなり役に立ちました。
さて、大事なのはこれからですね。MoneyViewはまだまだバージョンも低く、機能も足りてないと思います。実はお客さんからの貴重な意見を直接頂いたりもしておりますので、これからもより充実なものにするために頑張らないといけないですね。うん、うん、頑張らせて頂きます!
それと、読者プレゼントとしてMoneyViewのライセンスを2つ差し上げることとなっております。ご興味のある方はどうぞ、ご応募ください。プレゼント応募は下記のページからできますよ。
Micro-ISVをやっているもののしては、このように雑誌の記事になった場合にどれくらいの効果があるかを知るチャンスなのですごく興味深いと思っています。
COCOAでのASIHTTPRequestとJSONの使い方
今回はCOCOAのHttp通信ライブラリASIHTTPRequestとJSONライブラリSBJONを紹介したいと思います。
ASIHttpRequest 英文ドキュメント
http://allseeing-i.com/ASIHTTPRequest/How-to-use
JSON
http://blog.zachwaugh.com/post/309924609/how-to-use-json-in-cocoaobjective-c
基本的な使い方は
NSURL *ns_url = [NSURL URLWithString:@“http://test.com”]; ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:ns_url]; [request setPostValue:user_id forKey:@“loginId”]; [request setPostValue:pass forKey:@“loginPass”];
ASIRequest ObjectにURLはtest.com、パラメタはpost形式の値を設定します。
同期(synchronous)の場合はrequestを送ってエラーがなかったらresposeを取得する処理を順次、記述します。
[request startSynchronous];
NSError *error = [request error];
NSString *response;
if (!error) {
response = [request responseString];
}else{
return false;
}
...
非同期の場合は
受信処理を行うデリゲートを設定して非同期的な処理を任せます。
主メッソドの例
※この場合はデリゲートとして自分を指定します。
[request setDelegate:self]; [request startAsynchronous];
デリゲートメソッドを実装
- (void)requestFinished:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
処理
........
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
エラー処理
........
}
STRINGデータをJSONに変換するには SBJSONを使います。
例)
SBJSON *json = [SBJSON new];
NSArray *statuses = [parser objectWithString:json_string error:nil];
[json release];
// Each element in statuses is a single status
// represented as a NSDictionary
for (NSDictionary *status in statuses)
{
// You can retrieve individual values using objectForKey on the statusNSDictionary
// This will print the tweet and username to the console
NSLog(@"%@ - %@", [status objectForKey:@"text"],
[[status objectForKey:@"user"] objectForKey:@"screen_name"]);
}
上記のようにjson_stringからDictionary Objectを生成して変換します。
実際のASIHTTPと連携したソースはこんな感じになります。
+ (NSArray *)scheduleWithId:(NSString*)user_id pass:(NSString*)pass date:(NSDate *)date {
//日付から年と月を取得
NSDateFormatter *outputFormatter = [[[NSDateFormatter alloc] init] autorelease];
[outputFormatter setDateFormat:@“yyyy-MM-dd”];
NSString *newDateString = [outputFormatter stringFromDate:date];
NSArray *dateArray = [newDateString componentsSeparatedByString:@“-“];
NSLog(@“current date : %@“,dateArray);
//URL及びパラメターを設定
NSURL *ns_url = [NSURL URLWithString:TEST_URL];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:ns_url];
[request setPostValue:user_id forKey:@“loginId”];
[request setPostValue:pass forKey:@“loginPw”];
[request setPostValue:[dateArray objectAtIndex:0] forKey:@“currentYear”];
[request setPostValue:[dateArray objectAtIndex:1] forKey:@“currentMonth”];
[request startSynchronous];
NSError *error = [request error];
NSString *response;
if (!error) {
//responseデータをTRIMする
response = [[request responseString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//responseデータから特殊文字を処理 " -> \
response = [response stringByReplacingOccurrencesOfString:@“"” withString:@“\””];
}else{
return false;
}
NSLog(@“response : %@“,response);
SBJSON *json = [SBJSON new];
NSDictionary *statuses = [json objectWithString:response error:nil];
[json release];
NSLog(@“json : %@“,statuses);
// Each element in statuses is a single status
// represented as a NSDictionary
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:@“yyyy-MM-dd”];
NSArray *scheduleItems = [statuses objectForKey:@“scheduleItems”];
for (NSMutableDictionary *schedule in scheduleItems)
{
[schedule setObject:[dateFormatter dateFromString:[schedule objectForKey:@“startDate”]] forKey:@“startDate”];
[schedule setObject:[dateFormatter dateFromString:[schedule objectForKey:@“endDate”]] forKey:@“endDate”];
// You can retrieve individual values using objectForKey on the status NSDictionary
// This will print the tweet and username to the console
NSLog(@“title : %@ , contents : %@ , startDate : %@ , endDate : %@ “,
[schedule objectForKey:@“title”] ,
[schedule objectForKey:@“contents”] ,
[schedule objectForKey:@“startDate”] ,
[schedule objectForKey:@“endDate”]
);
}
return scheduleItems;
}
サーバからは
scheduleItems : {"title": “タイトル”, “contents”: “” , “startDate”:”2010-3-10” ,“endDate”:”2010-3-11”}
このような形式のデータか送られて来るので最初にDictionaryからscheduleItemsを取得し、
配列データを取得します。
AquaticPrimeでのライセンスメールの文字化け
AquaticPrimeはライセンスフレームワークで有名なオープンソースのフレームワークです。
Paypal及びKagiなどの決済サイトと連動しライセンスの自動発行も可能でサンプルソースも提供していますが、php阪のUNICODE対応にちょっと不具合があるので修正が必要です。
元ソース
AquaticPrimePayPal.phpの60行目
for ($i = 1; $i < count($lines); $i++)
{
list($lineKey, $lineValue) = explode("=", $lines[$i]);
$keyarray[urldecode($lineKey)] = urldecode($lineValue);
}
$product = $keyarray['item_name'];
$name = $keyarray['first_name']." ".$keyarray['last_name'];
$email = $keyarray['payer_email'];
$amount = $keyarray['mc_gross'];
$count = $keyarray['quantity'];
// RFC 2822 formatted date
$timestamp = date("r", strtotime($keyarray['payment_date']));
$transactionID = $keyarray['txn_id'];
// Create our license dictionary to be signed
$dict = array("Product" => $product,
"Name" => $name,
"Email" => $email,
"Licenses" => $count,
"Timestamp" => $timestamp,
"TransactionID" => $transactionID);
$license = licenseDataForDictionary($dict, $key, $privateKey);
元スースはurldecode後のデータをそのままライセンス発行とメール送信に使用しています。
しかしもしデータの中にASCII以外の文字列が入った場合には正しく動くという保証はありません。
UNICODE文字に対応する為には下記のような処理が必要です。
...
$name = convert_to($keyarray['first_name']." ".$keyarray['last_name'] , "UTF-8");
$base64_name = base64_encode($name);
.....
$dict = array("Product" => $product,
"Name" => $base64_name,
"Email" => $email,
"Licenses" => $count,
"Timestamp" => $timestamp,
"TransactionID" => $transactionID);
$license = licenseDataForDictionary($dict, $key, $privateKey);
convert関数
function convert_to ( $source, $target_encoding )
{
$encoding = mb_detect_encoding( $source, "auto" );
$target = str_replace( "?", "[question_mark]", $source );
$target = mb_convert_encoding( $target, $target_encoding, $encoding);
$target = str_replace( "?", "", $target );
$target = str_replace( "[question_mark]", "?", $target );
return $target;
}
まず 日本語の$name 属性はUTF-8に変換します、ライセンス発行ロジックには
UTF-8文字列をそのままでは使えない為、$name変数をbase64形式に変換します。
メールにはUTF-8にencodeした$nameを、ライセンス発行には$base64_nameを
使います。
AquaticPrime.php 196行目
function sendMail($to, $from, $subject, $message, $license, $name, $bcc='')
{
// Create a random boundary
$boundary = base64_encode(MD5((string)rand()));
$message = mb_convert_encoding($message,"ISO-2022-JP", "UTF-8");
最後にメール送信ヘッダにUTF-8のデータをISO-2022-JPの形式で変換して送信すれば完了です。
NSDateのTips
COCOA開発で日付の処理はよくある事です。
今日はいくつかの個人的Tipを共有します。
1. 日付間の日数を計算
#define TIME_INTERVAL_FOR_DAY 86400
// Calculate and return number of days between two dates.
+ (int) numberOfDaysBetween:(NSDate*)firstDate and:(NSDate*)secondDate {
NSTimeInterval interval = [secondDate timeIntervalSinceDate:firstDate];
// Add 1 for correct number of days
return (((int)interval) / TIME_INTERVAL_FOR_DAY)+1;
}
2. 日付に月を加算
//Add Month from Date
+ (NSDate *) date:(NSDate *)fromDate byAddingMonth:(int)monthOffset
{
NSDateComponents *month = [[NSDateComponents alloc] init];
[month setMonth:monthOffset];
NSDate *monthStartDateWithOffset =
[[NSCalendar currentCalendar] dateByAddingComponents:month toDate:fromDate options:0];
[month release];
return monthStartDateWithOffset;
}
3. 日付から一日のデータ取得
iPhoneの開発でデータを日付で絞って取得することはよくある処理でしょう。
※下記のソースはiPhone SDKでcore dataを使った場合です
- (NSArray *)dailyDataFromDate:(NSDate *)from_date {
NSCalendar *calendar= [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
//①
NSDateComponents *initComponents = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:from_date];
//②
NSDateComponents *componentsToAdd = [[NSDateComponents alloc] init];
[componentsToSubtract setDay:+1];
NSDate *fromDate = [calendar dateFromComponents:initComponents];
NSDate *toDate = [calendar dateByAddingComponents: componentsToAdd toDate:fromDate options:0];
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:@"(timeStamp >= %@ ) and (timeStamp < %@)",fromDate,toDate];
//③
NSArray *matchingFetchedObjects = [fetchedResultsController.fetchedObjects filteredArrayUsingPredicate:requestPredicate];
[calendar release];
[componentsToSubtract release];
return matchingFetchedObjects;
}
まず、①は日付の時刻を初期化するコンポーネントを生成して一日の始発時間を取ります。
②では一日を追加するコンポーネントを生成します。
③でArrayに①と②で取得した日付をフィルタリングを掛けてデータを取得します。
アップルのサイトを参考
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/DatesAndTimes/Articles/dtDates.html
MoneyViewがGoogleの1ページに!!
恥ずかしいことながら、今まで特にSEO対策などはそれらしく行っていませんでしたが、なんとMoneyViewがGoogleで検索した時に最初のページに表示されるまでなりました。今確認しているキーワードは下記の通りです。
- マック 家計簿
- Mac 家計簿
- マック 小遣い帳
- Mac 小遣い帳
上記のキーワードの組み合わせで検索した時にちゃんと1ページ目にMoneyViewが表示されます。これは、うれしいですね!
今年の2月初に最初にリリースした時点では13ページ目でした。1ページ目に表示されるようになるまでちょうど2ヶ月かかったことになります。
これはSEOにほとんど力を入れてなかった事を考えると結構の成果だと考えてもいいことですよね?ちなみに、今までSEO対策としてやったこと、或は影響があったんだろうと思っていることは以下の通りです。
- Google Adwordを3万円分だけ試してみました。3万円はわずか数日で消費されてしまいました。訪問者数は確かに増えましたが、当時のコンバージョン率(訪問者数と販売数との割合)はコストを考えるとあまり好ましくなかったので3万円を使い切った時点でやめました。
- Vectorを含め、複数のソフト紹介・販売サイトにMoneyViewを登録しました。各サイト毎に効果はまちまちで、登録した日から一気に訪問者数が増えるサイトもあれば、ほぼ影響が見えないサイトもありました。ただ、どのサイトも長期間に続く効果は見えませんでした。
- アップル・ジャパンのダウンロードページでスタッフのおすすめ製品として表示されるようになりました。(これについては高く評価してくれたアップル・ジャパンの方に感謝!)ダウンロードページのトップ画面にも表示されるようになりました。おかげさまでGoogle Adwordsをやめてから急激に減っていた訪問者数が激増しました。ただし、それも数日後、アップル・ジャパンのサイトからダウンロードのメニューがiPadのメニューにリプレースされ、分かりづらい場所にリンクが移動されながら訪問者数はまた減ってしまいました。
ここまでがSEOと関連した今までの出来事です。
以上、最近多忙でSEOに対する次の手を打てずにいましたが今日確認したらなぜか(!)1ページ目に載っていたという面白いお話でした。
ちなみに、4月末にはMac関連の有名雑誌にもMoneyViewが載ることとなります。その話はまた後でやらせて頂きますが、その時にはまた訪問者数が増えるのではないかと期待しております。
今日はチームメンバーと一緒に飲み会を予定していた日ですが、いい話題ができてビールがもっと旨くなると思います。ワラ




