今回は、昨日作成した音再生ライブラリを使って、ガワネイティブアプリに音を組み込んでいました。
音再生ライブラリの使い勝手が良く、効率よく音を組み込むことが出来ました。
吾輩はやれば出来る子である。
∩∩
(´・ω・)
_| ⊃/(___
/ └-(____/
 ̄ ̄ ̄ ̄ ̄ ̄ ̄
今日はタイトル画面とテキスト漫画一覧画面に組み込んだので、明日は編集画面やそのほかの画面に組み込みます。明日には音の組み込み作業は終了しそうです。
今回は、昨日作成した音再生ライブラリを使って、ガワネイティブアプリに音を組み込んでいました。
音再生ライブラリの使い勝手が良く、効率よく音を組み込むことが出来ました。
吾輩はやれば出来る子である。
∩∩
(´・ω・)
_| ⊃/(___
/ └-(____/
 ̄ ̄ ̄ ̄ ̄ ̄ ̄
今日はタイトル画面とテキスト漫画一覧画面に組み込んだので、明日は編集画面やそのほかの画面に組み込みます。明日には音の組み込み作業は終了しそうです。
Web Audio APIによるmp3の簡易再生ライブラリが完成しました!
複数の音の同時再生に対応しています!!
sound.js
if (typeof g_audioContext === 'undefined') {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
g_audioContext = new AudioContext();
loadSound = function(url, keyName, fn) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
g_audioContext.decodeAudioData(request.response, function(buffer) {
fn(keyName, buffer);
});
}
request.send();
};
getScriptParam = function(name) {
var scripts = document.getElementsByTagName('script');
var src = scripts[scripts.length - 1].src;
var query = src.substring(src.indexOf('?') + 1 );
var params = query.split('&');
var result = new Object();
for(var i = 0; i < params.length; i++) {
var keyValue = params[i].split( '=' );
if (name == keyValue[0]) {
return keyValue[1];
}
}
};
g_sounds = {};
playSound = function(name) {
if (name in g_sounds) {
g_sounds[name]();
}
}
}
var tmpName = getScriptParam('name');
loadSound('https://minnano.app/sound/' + tmpName, tmpName, function(keyName, buffer) {
g_sounds[keyName] = () => {
var source = g_audioContext.createBufferSource();
source.buffer = buffer;
source.connect(g_audioContext.destination);
source.start(0);
};
});
sound.jsファイル内の ‘https://minnano.app/sound/’ はmp3ファイルの置いてある場所です。環境に合わせて書き換えて下さい。
例えば kachi.mp3 と ok.mp3 の2つのファイルがあった場合、以下のように name に ファイル名を指定してJSファイルを読み込みます。
<script type="text/javascript" src="sound.js?name=kachi.mp3"></script>
<script type="text/javascript" src="sound.js?name=ok.mp3"></script>
その後、JavaScriptから任意の場所で playSound(‘kachi.mp3’) や playSound(‘ok.mp3’) と呼べば、それぞれの音が鳴ります!
3ファイル以上の場合も、同様の方法でscriptタグを追加すればOKです!!
便利!ヽ(´▽`)ノワーイ
音鳴りました。単純にコードミスだったようです。
。・゚・(ノ∀`)・゚・。
以下コードを記述した上で、JavaScriptから任意の場所でplaySound()を呼べば音が鳴ります。
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var g_audioContext = new AudioContext();
loadSound = function(url, fn) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
g_audioContext.decodeAudioData(request.response, function(buffer) {
fn(buffer);
});
}
request.send();
};
function playSoundBuffer(buffer) {
var source = g_audioContext.createBufferSource();
source.buffer = buffer;
source.connect(g_audioContext.destination);
source.start(0);
}
var g_soundBuffer = null;
loadSound('https://minnano.app/textmanga/assets/sound/kachi.mp3', function(buffer) {
g_soundBuffer = buffer;
});
function playSound() {
playSoundBuffer(g_soundBuffer);
}
mp3は絶対パスで指定しています。
‘https://minnano.app/textmanga/assets/sound/kachi.mp3’
の部分を変えて下さい。
しかし、これだと1種類の音しか鳴りませんね。いろんな音を鳴らしたいです。
。・゚・(ノ∀`)・゚・。
明日は、複数音源の場合の対応を考えようと思います。
今回はガワネイティブアプリで音の再生作業を行いました。
まずは、せっかくガワネイティブアプリなのでアプリ側での音の再生に挑戦しましたが、クライアント側のJSにより任意のタイミングで音を鳴らしたい場面が多々あり、またWeb側だと、Webへアクセスした後に音が鳴りずれるため、アプリ側での再生は諦めました。
そこで Web Audio API という、HTML5 でのサウンドライブラリを使うことにしました。新しいライブラリなのできっとより洗練されているのでしょう。
それで、JSによりmp3のファイル名を指定して簡単に音を鳴らせるメソッドを作ろうとました。が、webからは鳴るんですがアプリから使おうとすると何故か音がなりません。
ちゃんと音がなるコードが出来たら公開しようと思います。
今日は原因調査で終わってしまいました・・・
今日は各メニュー(1〜3)での登録/変更テストを行い、問題無いことを確認しました。
また、PCサイトで登録したメールアドレスを、アプリで”ID”として指定することで、PCサイトと共通のアカウントを使うことが出来る隠し機能を付けました。
便利。ヽ(´▽`)ノワイ
残作業は以下のたった2つだけです。
・ボタンやリンク押した時に音を出す
・秘密の作業
あれ?いつの間にか100日経ってた・・・
本命のアプリ作る前に、ちょっとだけテキスト漫画サイトをガワネイティブアプリ化する作業を行おうと思ったら、こっちがメインになってしまいました。
とは言ってももう一息で完成しそうです。今日は絵文字を入力すると正常動作しない箇所があるので対策をしました。
絵文字を入力可能とするには
・DB(MariaDB)の文字コード変更(utf8mb4に変更)
・JSの文字処理で”サロゲートペア”に対応する
の2つを行う必要があります。
しかし絵文字は各環境で同じものが出るわけではありません。iPhoneで入れた絵文字って、Androidで同じように出ないんですよ。知っていました?え?知ってる?まじで?
・・・
・・
・
やろうと思えば絵文字の対応は可能ですが、混乱を避けるため、泣く泣く入力出来ないよう、JSで以下のように入力禁止対応をしました。
function isContainSP(s) {
for (var i = 0; i < s.length; i++) {
var c = s.charCodeAt(i);
if ((0xD800 <= c && c <= 0xDBFF) || (0xDC00 <= c && c <= 0xDFFF)) {
return true;
}
}
return false;
}
・・・
・・
・
え?手抜き?
いや、絵文字が各環境で異なるので、混乱を避けるためです・・・
まじで。
今日はID不正変更対策を行っていました。
今作っているガワネイティブアプリはユーザID変更機能があります。
ユーザIDを変更できる機能があるという事は、IDを試しまくって入力していればいつかは他人のIDでログインできてしまうという事です。この力任せの総当たり攻撃の不正をブルートフォースアタックといいます。
対策は100億通りくらいありますが書ききれないです。しょうがないので3つくらいの対策を紹介します。
1.不正な入力があったら、その応答に時間をかける。あまりに長い時間待たせるとID変更しようとして単に間違えた人にも迷惑がかかるため、1、2回目の間違いは3秒くらい、3、4回以上間違えたら10秒待たせるとか工夫すると良いです。
2.同一アクセス元から短時間に高頻度のアクセスがあったら遮断する。
3.後は・・・たぶんなんかあります。
テキスト漫画のガワネイティブアプリは、1の方法を採用し、対策を行いました。
本日は、昨日の続きです。
1.Web側で、URLに特定のリクエストパラメタを乗せてサーバアクセスする。
2.アプリ側で、画面遷移のタイミングでURLをチェックする。
3.2で、特定のリクエストパラメタを見つけたら、その内容を元にアプリ側処理を実行する。
昨日は1と2が完了したので、3を行いました。
アプリ内の”UserDefaults”や”キーチェーン”にユーザIDを保存しているのですが、これらをWebから書き換えることに成功しました。
昨日作成した”getUrlParam”を使い、コードは以下のようになりました。
// IDの更新
if let id: String = getUrlParam(url: url.absoluteString, param: "特定のパラメタ名") {
// UserDefaultsに保存
UserDefaults.standard.set(id, forKey: Constants.User.idfv.rawValue)
// キーチェーンに保存
KeychainService.savePassword(service: _ksService, account: _ksAccount, data: id);
}
Constants.User.idfv.rawValue とか _ksService とか _ksAccount とかはそれぞれの環境で適切な値を指定して下さい。
今回で基本的な部分はかなり完成してきました。ただ、まだ細かい詰めの作業が残っています。
テキスト漫画サイトのガワネイティブアプリなんて、2,3日でサクッとリリース出来るかと思ったらとんでもなかったです・・・
残作業は以下です。
・絵文字を入力すると正常動作しない箇所があるので対策
・IDを総当たり攻撃で不正変更される対策
・各メニュー(1〜3)での登録/変更テスト
・ボタンやリンク押した時に音を出す
・秘密の作業
本日はWeb(WKWebView)からアプリへのアクセス処理を行う予定でした。
Webからアプリへの処理依頼は以下の手順で行います。
1.Web側で、URLに特定のリクエストパラメタを乗せてサーバアクセスする。
2.アプリ側で、画面遷移のタイミングでURLをチェックする。
3.2で、特定のリクエストパラメタを見つけたら、その内容を元にアプリ側処理を実行する。
今回は特定のリクエストパラメタが指定されているか、URLを解析する処理を作りました。
以下がコードとなります。Swift4.0.3です。最新ですよ。Webで調べても古くて動かないコードばかりで苦労しました・・・
オプショナル型とかオプショナルバインディングとか強制アンラップとか復習してコーディングしました。
func getUrlParam(url: String, param: String) -> String? {
if let urlComps: NSURLComponents = NSURLComponents(string: url) {
if let urlParamsCount: Int = urlComps.queryItems?.count {
for i in 0..<urlParamsCount {
if let item: URLQueryItem = urlComps.queryItems?[i] {
if param == item.name {
return item.value
}
}
}
}
}
return nil
}
オプショナル型は一度分かってしまえば便利ですね。
毎回nilチェックしていた日々が過去の思い出となりました。
今回はID変更処理を実装しました。
IDを参照/変更可能とすることで、以下の用途などに利用出来ます。
・新しいiPhoneなどに変えた場合、前の設定を引き継ぐ
・複数端末で同一IDを使って同じユーザとしてアプリを使う
・将来的に、そのIDがあればアプリと同じユーザでPCサイトを使える
・将来的に、AndroidとiPhone間で同じユーザとしてアプリを使える
処理内容は昨日のニックネーム変更とほぼ同じため、すんなり行くかと思いました。
が、1つだけ作業が残りました。
iOSではidfvという識別IDを初回アプリ起動時に取得・保存し、次回アプリ起動時も保存したIDを元にWebサイトにアクセスすることでユーザを識別しています。
本日の作業ではWebサイト内でこのIDを変更する処理を行ったため、ID変更後にアプリを落としてしまうと、アプリ再起動時に保存しておいたIDを読み込んでIDが元に戻ってしまいます。
対処としては、WebサイトでID変更を行った際に、アプリに保存したIDも同時に変更します。ガワネイティブアプリにおけるよくあるパターンで、Webサイト内からアプリ側へのアクセスを行う処理が必要になります。
明日はこの、Web(WKWebView)からアプリへのアクセス処理を行う予定です。