gosyuincho-case.jpg
総桐仕上げの豪華御朱印帳ケース! 千糸繍院 御朱印帳ケース / 御朱印帳保管用 総桐箱 12冊用

『御朱印マップ』に関するお問い合わせは、下記の記事のコメントでお寄せ下さい。

お問い合わせ用記事

秋の京都、青蓮院の将軍塚です✨ - Spherical Image - RICOH THETA
ワンショットで360°全天球写真・動画が撮れる! RICOH THETA S 360°全天球カメラ 910720

2016年07月16日

App内課金の問題対応(購入したApp内課金が動作しない場合)

Xcode「このApp内課金はすでに購入済みです。 無料で復元できます。」
と表示されても、App内課金(In-App purchase)がダウンロードできず、復旧(restore)もできない場合があります。
その問題を発生させないためのソースコードメモです。

iPhoneアプリ『御朱印マップ』を提供していて、ユーザーの方から「App内課金でお金を払ったのに使用できない」というお問い合わせを数件受けました。

課金はされたが購入したものは使えず、再購入もできず、復旧もできないということで、お金を払ったのにむしろ使えなくなるという、とても申し訳ない状況となります。

Appleにも問い合わせを行った結果、ユーザー側でAppleに問い合わせ、課金処理を取り消してもらうことで解消することがわかっています。

  ◆購入したマップやマップセットが使えない場合の対処方法

上記記事は「問題が発生してしまった後の対処方法」ですので、この記事では「問題を発生させない対応方法」を記載していきます。
なお以下の記載は、Objective-C、iOS 9.3.2、Xcode 7.3 の環境で、App内課金の種類は非消耗型(Non-consumable)です。


(1) 問題の現象

この問題は、以下のメッセージが表示される場合が該当します。

このApp内課金はすでに購入済みです。
無料で復元できます。

20160708_01.jpg

※英語版メッセージ
This In-App Purchase has already been bought.
it will be restored for free.

このメッセージが表示されると、この画面で「OK」をタップしても、SKPaymentのTransactionObserverが何もレスポンスを返しません。結果として、以降の処理のハンドリングができなくなります。

この現象は、Apple側で課金処理が完了した後に発生します。
従って、ユーザーはお金を支払っている状態です。

アプリ内では、購入処理が完了していません。そのため再購入処理が可能であり、本来は無料で再購入が完了するところ、上記メッセージが表示されてそれ以上先に進めません。
従って、ユーザーは購入した機能を使用することができません。

Apple側に購入履歴が存在するので、本来ならばrestoreできるはずです(非消耗型なので)。しかし上記メッセージが表示される商品は、restoreもできません。

このメッセージが表示される場合でも、他のApp内課金は正常に処理できることもわかっています。restoreもできます。
このメッセージが表示されるコンテンツだけが、ユーザーは使用できない状態となってしまいます。

ちなみにこの問題は、アプリを削除して再インストールしても、解消しないようです。


(2) 問題発生の原因

この現象は、iOSの処理の仕組み上発生してしまう問題です。

App内課金を、アプリの外側で購入してしまうと発生します。

20160708_02_08.jpg

実際に報告された問題は、以下の場合に発生しました。

購入しようとしたらクレジットカードの有効期限が切れていて、カード情報を更新して購入した。
購入しようとしたらiTunesカードの残高が不足していて、残高を追加して購入した。

このような場合、アプリはバックグラウンドに移動するのですが、iOSではユーザーの利便性の為か、購入処理を継続します。
そして、アプリがバックグラウンドのまま購入してしまった商品が、アプリ内で使用できなくなってしまうのです。

iOSの仕組みで発生してしまう問題なので、Appleに課金処理をキャンセルしてもらったら解消できるかと思って問い合わせたところ、解消しました。

  ◆購入したマップやマップセットが使えない場合の対処方法

プログラムで未然に防止できない場合、ユーザー様に上記の対応をとって頂ければ、お手間をかけてはしまいますが不満の残る課金はなくなります。

この問題は、SKPaymentのQueueに、未完了のtransactionが残ってしまうために発生します。

SKPaymentのdefaultQueueはアプリの外側にあるため、アプリを削除しても、未完了のtransactionはQueueの中に残ったままになってしまいます。
そのため、アプリを再インストールしても解消しません。

ですので対応方法としては、未完了のtransactionをQueueから消してあげる、ということになります。


(3) Sandbox での問題再現方法

この問題を、Sandbox環境で再現する方法は以下の通りです。
シミュレータではなく、実機を使用します。

@Macと実機を接続し、Xcodeから実機上でアプリを起動します。

20160716_01.jpg

私のアプリでは、この画面右上の「地図購入」ボタンから、非消耗型のApp内課金が購入できます。

Aアプリ上で購入処理を開始します。

20160716_02.jpg

iTunes storeへのログイン画面が表示されたら、テスト用IDでログインしてください。

B購入確認画面が表示されたら、Xcodeでアプリの実行を停止します。

Aの手順の後、Apple側の処理で下記のような購入確認画面が表示されます。

20160716_03.jpg


この画面が表示されたら、Xcodeでアプリの実行を停止してください。
下の画面のように、アプリは停止しますが、購入確認画面が残ります。

20160716_04.jpg

C残っている購入確認画面で、購入を完了させます。

20160716_05.jpg

これで、この問題が発生する原因の作成が完了しました。

Dもう一度Xcodeからアプリを起動し、同じ商品の購入処理を開始します。

20160716_01.jpg

20160716_03.jpg

購入処理を進めると、問題のメッセージが表示されます。

20160716_07.jpg

この後、Observerからは何もレスポンスが返って来ません。
私のアプリの場合は、Observerから返答がくるまでインジケータを表示しているのですが、それがいつまでたっても消えません。

20160716_08.jpg

この状態が、問題の状態です。
購入したものが使えず、再購入もできない状態。復旧もできません。

この状態で、SKPaymentのQueueを確認すると、transactionが残っていることがわかります。

    // 未完了トランザクションの個数確認。
NSLog(@"%lu", [SKPaymentQueue defaultQueue].transactions.count);
  2016-07-16 13:07:38.229 GOSYUIN Map[779:60b] 1

Queueの中に、トランザクションが1件残っている状態です。

この状態を解消すると、App内課金の再購入や復旧ができるようになります。


(4) 対応方法

Queueの中に残っている未完了トランザクションを完了させると、この問題を解消することができます。

未完了トランザクションの完了には、finishTransaction:を使用します。

この処理をどのタイミングで実施するかですが、AppDelegateの中で実施するとQueueの中の未完了トランザクション数が常に0で検知されるという噂もあるため、私の場合は購入画面のviewWillAppear:で実施しています。

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];

// 他のviewWillAppear:の処理。

// 未完了トランザクションがないか確認する。
if ([SKPaymentQueue defaultQueue].transactions.count > 0) {
// 未完了トランザクションが存在する場合は、個別に処理を行う。
for (SKPaymentTransaction *tempTransaction in [SKPaymentQueue defaultQueue].transactions) {
// 未完了トランザクションのステータスを確認する。
if (tempTransaction.transactionState != SKPaymentTransactionStatePurchasing) {
// 未完了トランザクションが「購入中」以外の場合は終了させる。
[[SKPaymentQueue defaultQueue] finishTransaction:tempTransaction];
}
}
}
}

14行目で未完了トランザクションを完了させています。

この処理は、未完了トランザクションが無い場合は不要な処理です。
そのため、8行目で未完了トランザクション数の確認をしています。

未完了トランザクションが存在する場合、複数件の配列形式なので、10行目でforを回して個別に処理を行います。

トランザクションのステータスが
 「購入中(=SKPaymentTransactionStatePurchasing)」
のものをfinishTransaction:させると、エラーが出てしまうことがあるそうです。
ですので、12行目で「購入中」以外のものを処理するようにしています。

そして、14行目でトランザクションを完了させます。
これで、この問題は解消されます。


(5) 問題の解消確認

先ほど(3)の手順で再現したApp内課金を使用して、問題が解消していることを確かめましょう。

もう一度購入処理を開始します。

20160716_01.jpg

20160716_09.jpg

20160716_10.jpg

非消耗型を購入済みなので、この画面が表示されます。
ここで「OK」をタップすると、無料で取得できます。

20160716_11.jpg

無事解消できました。

ちなみに消耗型のApp内課金の場合は、再購入できるようにはなりますが、無料で復旧できず、再課金されるようです。
消耗型でこの問題が発生した場合は、ご紹介している別の記事に記載している通り、ユーザー様からAppleに返金処理を依頼して頂いた方が良いかもしれません。

以上です。

この記事が参考になったら下記バナーをクリック!
ランキングUPにご協力をお願いします。m(_ _)m

ico_GOSYUIN-Map.png

iPhoneで御朱印集めを楽しくするアプリ『御朱印マップ』を公開しています。ダウンロードはこちらからどうぞ。

ラベル:SKPaymentQueue
posted by o(^0^)o ほくほく at 10:51| Comment(0) | Xcodeメモ | このブログの読者になる | 更新情報をチェックする

2015年01月27日

アプリの起動時間を記録する方法

Xcodeアプリを使用したトータル時間で、アプリの挙動を制御したい時があります。

そこでここでは、UserDefaultsを使用して、ユーザーがアプリを使用した時間を記録する方法をご紹介します。

『御朱印マップ』のバージョン1.0.2がようやくリリースされて、次のバージョンに向けたカスタマイズ作業を進めています。

その中で、ユーザーさんがアプリを使用した時間が一定の値を超えると、アプリに別の動きをさせたいと思うようになりました。

そのためにはまず、アプリの使用時間を計測できるようにしないと。

ということで、ユーザーのアプリ使用時間を簡単に記録する方法をご紹介したいと思います。

実装イメージは、以下の通りです。

@アプリがアクティブになった時に、その時刻を取得する
Aアプリがアクティブではなくなった時に、その時刻を取得する
BAの直後に、Aと@の差分をUserDefaultsに加算する

実装先はAppDelegate。
記録先はUserDefaultsです。

それでは、実装していきましょう。

まずは、@は複数のメソッドで使うので、インスタンス変数として定義します。
@implementation AppDelegate {
NSDate *applicationBecomeActive; // @アプリがアクティブになった時刻
NSDate *applicationResignActive; // Aアプリがアクティブではなくなった時刻
}

Aは念のためです。

次に@の実装。
これは、applicationDidBecomeActive: に実装します。
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// @アプリケーションが起動した時刻を取得。
applicationBecomeActive = [NSDate date];
}


更に、AとBの実装。
これは、applicationWillResignActive: に実装します。
- (void)applicationWillResignActive:(UIApplication *)application
{
// Aアプリケーションが終了する時刻を取得。
applicationResignActive = [NSDate date];

// Bアプリケーションの稼働時間を算出する。
NSTimeInterval thisTimeApplicationActiveTime = [applicationResignActive timeIntervalSinceDate:applicationBecomeActive];

// Bアプリケーションの稼働時間をUserDefaultsに加算。
// まずUserDefaultsを定義し、既存の値を取得する。
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
double pastApplicationActiveTime = [userDefaults doubleForKey:TIME_TOTALACTIVE];
// 既存の値に今回の稼働時間を加算する。
double totalApplicationActiveTime = pastApplicationActiveTime + thisTimeApplicationActiveTime;
// 加算した値を再びUserDefaultsに格納する。
[userDefaults setDouble:totalApplicationActiveTime forKey:TIME_TOTALACTIVE];
[userDefaults synchronize];

// ●Bの時間を出力するTestLog //
NSLog(@"totlaApplicationActiveTime : %f", [userDefaults doubleForKey:TIME_TOTALACTIVE]);
}

TIME_TOTALACTIVE は、UserDefaultsのキー項目を定数化したものです。
ご自分でご利用になるときは、好きな名称を@"キー項目"でセットしてください。
値としては、秒数がdouble型で格納されます。

最後の●の処理はテスト用です。
UserDefaultsに格納されたアプリの稼働時間(過去分との積算)がログに出力されるので、確認してみてください。

ちなみにこの実装だと、@とAの時刻はJSTではなくGMTで取得されます。
それでもBを記録するための一時的なものなので、それで良しとしましょう。

意外と簡単に実装できます。

ico_GOSYUIN-Map.png

iPhoneで御朱印集めを楽しくするアプリ『御朱印マップ』を公開しています。ダウンロードはこちらからどうぞ。

この記事が参考になったら下記バナーをクリック!
ランキングUPにご協力をお願いします。m(_ _)m


posted by o(^0^)o ほくほく at 22:27| Comment(0) | Xcodeメモ | このブログの読者になる | 更新情報をチェックする

2015年01月25日

SyntaxHighlighterを設置しました!

XcodeXcodeのソースコードをブログに掲載したい!

ということで、ブログにソースコードを綺麗に表示できるSyntax Highlighterを設置しました。

まだまだiPhoneアプリ開発修行中の私。

Appleのマニュアルやネット上の情報を調べながらコーディングしているのですが、一方で自分が書いたコードなどをメモとしてブログに掲載したいとも思っています。

でも、Xcodeのソースコードを普通にブログに掲載すると・・・
例えばこんな感じになってしまいます。

@interface GSMHowToTableViewController () {
UIFont *textFont;
UIFont *explanatoryFont;
GSMHowToExplainationTableViewCell *dummyCell;
}

色がない・・・

ということで、自分で色をつけてみると・・・

@interface GSMHowToTableViewController () {
UIFont *textFont;
UIFont *explanatoryFont;
GSMHowToExplainationTableViewCell *dummyCell;
}

途中でやめましたが、1つ1つのキーワードにそれぞれ色をつけていくのがものすごく面倒なんです!

でも、ネット上では、綺麗にソースコードを掲載しているサイトがたくさんあるんですよね。
どうやっているんだろう・・・

そこで調べてみたら、みなさん、SyntaxHighlighterなどのツールを使っているようなのです。
(他にもいくつか種類があるみたいです)

これはいい!
ということで、さっそく私もこのブログに導入してみました。

#import "GSMHowToTableViewController.h"

@interface GSMHowToTableViewController () {
UIFont *textFont;
UIFont *explanatoryFont;
GSMHowToExplainationTableViewCell *dummyCell;
}

@end

@implementation GSMHowToTableViewController

// --------------------------------------------------------------------------|
// 【1】定数定義
// 説明文言は、全てここで定義する。
// --------------------------------------------------------------------------|
// 画面TOPの文章。
NSString *const HOWTO_TOP = @"『御朱印マップ』は御朱印集めを楽しむためのアプリです。\n寺社をマップで確認したり、情報を見たり、頂いた御朱印を記録できます。\nこれを使ってどんどん御朱印を集めましょう!";

- (void)viewDidLoad {
[super viewDidLoad];

// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;

// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;

// 説明用フォントの設定。
textFont = [UIFont systemFontOfSize:13.0];
explanatoryFont = [UIFont systemFontOfSize:12.0];

// タイトルバーにボタンを表示する。
UIButton *topButton = [UIButton buttonWithType:UIButtonTypeCustom];
topButton.backgroundColor = [UIColor clearColor];

おお、素晴らしい!
ソースコードをXcodeから貼り付けて、1種類のHTMLタグで囲むだけでこんなに綺麗に表示されるとは!!

感動です。

ちなみに、「さっそく導入してみました」と書きましたが、実は不慣れなので結構時間がかかってしまいました。(^_^;)

このブログへの導入方法がわからなかったり、SyntaxHighlighterの設定方法がわからなかったり。
そんな時、下記のサイトに助けられました。

  ◆知らなきゃ絶対損するPCマル秘ワザ

こちらは、SyntaxHighlighterのダウンロードから設定方法までを詳細に説明してくださっているサイト。

  ◆パソコン・FXなどに関する日記

こちらは、SeesaaブログにSyntaxHighlighterを導入する作業で参考にさせて頂きました。

素晴しい情報提供、ありがとうございます。
大変助かりました。

久しぶりにCSSやJavaScriptを編集したので、かなり四苦八苦。
こういうのを簡単にできてしまう人って尊敬してしまいます。

さて、設置したSyntaxHighlighterに何を書いていきましょうか・・・

ico_GOSYUIN-Map.png

iPhoneで御朱印集めを楽しくするアプリ『御朱印マップ』を公開しています。ダウンロードはこちらからどうぞ。

この記事が参考になったら下記バナーをクリック!
ランキングUPにご協力をお願いします。m(_ _)m

ラベル:SyntaxHighlighter
posted by o(^0^)o ほくほく at 01:00| Comment(0) | Xcodeメモ | このブログの読者になる | 更新情報をチェックする

2015年01月12日

iPhoneアプリのバージョンアップの方法(2)

Xcodeアプリをバージョンアップする手順の2番目です。

ここでは、Xcodeでバージョンを設定し、バイナリを作成してiTunes Connectに送信します。

iPhoneアプリのバージョンアップの方法(1)の続きです。
今回はXcodeでの作業となります。


(2)Xcodeでアプリを「Validate」⇒「Submit」する。

まずXcodeで対象のプロジェクトを開き、バージョンを変更しましょう。
プロジェクトを選択し、「TARGETS」でアプリを選択して、「Identity」の「Version」と「Build」を変更します。

20150112_03_001.jpg

この「Version」と「Build」は、iTunes Connectで設定した「Storeバージョン番号」と一致させましょう。

バージョンを変更したら、次はOrganizerでプロジェクトをValidateし、SubmitしてiTunes Connectにアップロードします。

Xcodeのメニューから「Product」>「Archive」を選択しましょう。

20150112_01_004.jpg

なおこのタイミングで、私の場合は「Archive」が選択できない!という問題にハマりました。
対策はこちらの記事に記載しています。

  ◆Xcodeで「Product」>「Archive」ができない

さてOrganizerを起動したら、対象を選択して「Validate」をクリックします。

20150112_03_002.jpg

Validateが成功したら、次は「Submit」をクリックします。

20150112_03_003.jpg

「Submit」をクリックした後、しばらくアップロードプロセスが走って、最後に下記の画面が表示されるとアップロード終了です。

20150112_03_004.jpg

上記画面が表示されたら「Done」ボタンをクリックして完了しましょう。

次は、iTunes Connectで、アップロードしたバイナリをセットしてレビューに提出します。

◆iPhoneアプリのバージョンアップの方法◆
(1)iTunes Connectで「新規バージョン」を作成する。
(2)Xcodeでアプリを「Validate」⇒「Submit」する。
(3)iTunes Connectで「レビューへ提出」する。

ico_GOSYUIN-Map.png

iPhoneで御朱印集めを楽しくするアプリ『御朱印マップ』を公開しています。ダウンロードはこちらからどうぞ。

この記事が参考になったら下記バナーをクリック!
ランキングUPにご協力をお願いします。m(_ _)m

posted by o(^0^)o ほくほく at 16:27| Comment(0) | Xcodeメモ | このブログの読者になる | 更新情報をチェックする

Xcodeで「Product」>「Archive」ができない

XcodeXcodeでリリース用のアーカイブを作成しようとしたら「Archiveメニューが選択できない!」ことがあります。

スキーム(Scheme)の選択で解決します。

iPhoneアプリをリリースすると、Xcodeでリリースバージョンを作成したり、iTunes Connectを設定したり。
Objective-Cでコーディングするのとは全く違う慣れない作業で、初心者の私は結構ハマります(汗)

今回は、それこそ基本中の基本のところでハマりましたので、それをちょっとメモっとこうかと思いました。


問題の現象

Xcodeで、リリース用のiPhoneアプリのコーディングが完了したので、メニューから「Product」>「Archive」を選択しようとしたら、「Archive」がグレーアウトされていて選択できない。

20150112_01_001.jpg

「Archiveできないと、バイナリをiTunes Connectに送信できないじゃん!」と焦りました。


原因と対策

Xcode上で、スキーム(画面左上のビルド先)をiOS Simulatorにしていたのが原因でした。

20150112_01_002.jpg

このスキームをiOSデバイス(iPhone実機など)に変更すると、「Archive」メニューが選択できるようになります。

20150112_01_003.jpg

この画面で、一番上のiOSデバイスを選択。
そうすると、「Archive」メニューが選択できるようになります。

20150112_01_004.jpg

ご覧の通り。

慣れないうちは、こういう基本的なところで何度もつまづいてしまいますね。
という自戒の意味を込めて、メモメモメモ・・・

ico_GOSYUIN-Map.png

iPhoneで御朱印集めを楽しくするアプリ『御朱印マップ』を公開しています。ダウンロードはこちらからどうぞ。

この記事が参考になったら下記バナーをクリック!
ランキングUPにご協力をお願いします。m(_ _)m

posted by o(^0^)o ほくほく at 11:47| Comment(0) | Xcodeメモ | このブログの読者になる | 更新情報をチェックする