のほほん停留所

つぶやきのなりそこないの溜まり場

In-App Purchaseで、プロダクトIDがinvalidになる場合のチェック項目

In-App PurchaseのSandbox環境のテストをしたいときにプロダクト情報を取得するリクエストを送ったら、送ったプロダクトIDがinvalidで返ってくるときがあります

let productRequest = SKProductsRequest(productIdentifiers: Set([ProductID]))
productRequest.delegate = self
productRequest.start()

こういうときにチェックすべき項目は下のようなブログなどでとても丁寧にまとまっています。しかし、自分はそれ以前のところでつまって2~3日無駄にしてしまいました。

d.hatena.ne.jp

In-App PurchaseのアイテムをiTunes Connectで登録するには口座情報を登録することが必須であると

nonchalanttan.hatenablog.com

で紹介させていただいたのですが、登録したアイテムのSandbox環境でのテストをするのにはさらに追加の情報を登録する必要がありました。

f:id:Nonchalanttan:20161024023304p:plain

In-App Purchaseのアイテムの登録自体はBank Infoを登録した段階で可能になるのですが、Sandbox環境でのテスト・本番リリースなどではContact Info, Tax Infoが共に必要になります。ですので、プロダクトIDがinvalidになってしまっているという方は上のチェック項目に加えて、iTunes Connect上でContact Info, Tax Infoが登録されていることを確認したほうが良さそうです。(In-App Purchaseのアイテムを登録できてしまったのでテストも出来るだろうと自分は完全に思い込んでしまってました

こんな間抜けなミスを踏まないように...

potatotips #34にブログ枠で行ってきました

potatotips #34に行ってきましたので、気になったiOSに関する発表をいくつか抜粋してまとめました。

ブラウザアプリ「Smooz」を作る中で会得したWKWebviewのTips

speakerdeck.com

UIWebviewに比べてWKWebviewはクラッシュ率が下がりセキュリティが上がりました。昨今のiOSではUIWebviewはDeprecatedになりWKWebviewに移行していく流れになっています。それで、実際にWKWebviewを採用した際のTipsに関する発表でした。

WKWebviewで表示しているWebページ上のリンクのタッチアップイベントはクライアント側から検知できるのですが、デフォルトではタッチダウンのイベントを取得できません。回避策としてはクライアント側で座標を検知して、JSでその座標にリンクがあるか判定してクライアント側に返すといった方法を取ったそうです。また、WKWebviewで表示しているページのJSをデバッグしたいとき、SafariのWebインスペクタ機能でデバッグできるというTipsも紹介してくださいました。

5分でわかるSE-0116(id-as-any)

speakerdeck.com

Swift2ではObj-Cのid型はAnyObject型として表現されているが、Swift3からはAny型として表現されるようになりました。この変更の理由や何が変わるのかといった発表でした。 github.com AnyObjectは参照型の型全般を表すもの、Anyは値型、参照型含めた型全般を表すものです。Obj-Cのid型はオブジェクト型で、Swiftの値型はオブジェクト型ではないので、ブリッジ可能な型はこれまでは暗黙的にオブジェクト型に変換されていました。しかし、Swift3でSwiftの値型からObj-Cのid型にブリッジできるuniversal bridging conversionが実装されました。これによりAnyObjectへの依存が排除することが出来ました。

Useful Command Line Tools in Swift 3

speakerdeck.com

Swift3でコマンドラインからPlaygroundを作成するコマンドツールを作成するという発表でした。コードレビューの際に書き方が気になったりするときにPlaygroundで挙動を確認してコメントするといったことがけっこうあり、このツールを作成したのはXcodeからPlaygroundを開くのはかなり面倒だというのがモチベーションだそうです。作成自体はCarthage/Commandantを参考にして作成したそうで、かなり気軽に取り掛かれそうな印象を受けました。こういった業務効率化のツールはどんどん作って共有していきたいですね。

github.com

Layered Architectureで各層の依存関係解決をSwinjectを用いて行う

speakerdeck.com

Layerd Architectureの層間の依存は疎にすべしという原則を実現するためにSwiftのDIフレームワークであるSwinjectを採用した設計に関する発表でした。各層でContainerを用意して、提供しているprotocolに実際に使われる具体クラスをセットします。これにより、依存する側が全く具体クラスを知らなくてよいので、各層が知る必要がないことが染み出さないように出来ます。また、テストしやすいことやシングルトンに依存した設計から解放されることも利点としてあげていました。

5分でApple Pay

qiita.com

PAY.JPApple Pay対応をしてみたといった発表でした。2016/10/25にApple Payが日本でも利用できるようになり、iOS App, Safariでも利用できます。既に決済ゲートウェイを利用していれば、PAY.JPを使う必要はないのですが、SDKが公開されておりサンドボックス環境でテストできるので、始めるのは簡単だそうです。なので、アプリ内の支払いでApple Payを採用したいときはPAY.JPを導入するのが良さそうです。

まとめ

potatotipsに参加するのは初めてですが、どの発表内容もレベルが高くてとても面白かったです。今度はネタを探して発表枠で申し込もうと思います。

iTunes ConnectでApp内課金のアイテムを登録できない

f:id:Nonchalanttan:20161024022417p:plain

上の画像のようにApp内課金を選択しても作成ボタンが有効化されていない。App内課金ができない、なんでだーと思って試行錯誤したらとんでもなく下らないことで詰まってました(日曜返して...

App内課金(In-App Purchase)に関して調べると細かいやり方はたくさん出てくるのですが、App内課金のアイテムの登録方法に関しては何も出てこない。「登録します」としか書いていない。一回アプリを審査しないといけないのかとか色々と試したのですが、どれもダメ。もしやと思って、調べたらまさかまさか

口座情報を登録してなかった

そりゃ出来ませんよね...ということでiTunes Connectの「契約/税金/口座情報」から自分の口座情報を登録してあげましょう。細かい方法はiTunes Connectで入金口座の登録をしようが参考になりました。

f:id:Nonchalanttan:20161024023304p:plain

無事に登録を完了すると

f:id:Nonchalanttan:20161024023245p:plain

App内課金の作成ボタンが有効になり、無事に課金アイテムを登録することができました!

Swift3: カリー化

Swift3ではカリー化用のシンタックスが廃止されました。廃止には用途が限定的などの理由が挙げられていますが、たまに使うことがあるのでSwift3での記法のメモ書きです。

英語ですが廃止の詳しい理由は0002-remove-currying.mdに書かれています。

// Swift2
func curried(x: Int)(y: String) -> Float {
    return Float(x) + Float(y)!
}

curried(1)("2") // 3

// Swift3
func curried(_ x: Int) -> (String) -> Float {
    return {(y: String) -> Float in
        return Float(x) + Float(y)!
    }
}

curried(1)("2") // 3

ただカリー化された関数を使いたいときは上のコードでいけますが、ついでにカリー化の一般的な関数を定義してみました。

func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C {
    return {(a: A) -> (B) -> C in {
        (b: B) -> C in
            function(a, b)
        }
    }
}

func plus(x: Int, y: String) -> Float {
    return Float(x) + Float(y)!
}

curry(plus)(1)("2") // 3

これで特定の関数をカリー化せずに、使う際にcurry関数に関数を渡してあげると使えます。ただ、上のコードでも分かる通り、定義したcurry関数は引数を2つしか取れないので、引数が3つや4つになるときは都度定義する必要があります。

Swift3: 同じ値を繰り返す文字列、配列

Swift3で同じ値を繰り返す文字列、配列のコンストラクタの記法が変わっていたため、個人的にメモしました。

// Swift 2.3
let str = String(count: 10, repeatedValue: Character("s")) // "ssssssssss"
let arr = Array(count: 10, repeatedValue: "s") // ["s", "s", "s", "s", "s", "s", "s", "s", "s", "s"]

// Swift 3.0
let str = String(repeating: "s", count: 10) // "ssssssssss"
let arr = Array(repeating: "s", count: 10) // ["s", "s", "s", "s", "s", "s", "s", "s", "s", "s"]

Swift3からStringとArrayのコンストラクタのpublic init(count: Int, repeatedValue: Element)public init(repeating repeatedValue: Element, count: Int)に変更されました。どちらもtupleをコンストラクタに渡して生成する方法なのですが、個人的にはイマイチSwift3の変更点と噛み合ってない気がします。

// Swift 2.3
func hoge(param1: Int, param2: Int) {
    ...
}

func fuga(param0 param1: Int, param2: Int) {
    ...
}

hoge(0, param2: 1)
fuga(param0: 0, param2: 1)

// Swift 3.0
func hoge(param1: Int, param2: Int) {
    ...
}

func fuga(param0 param1: Int, param2: Int) {
    ...
}

hoge(param1: 0, param2: 1)
fuga(param0: 0, param2: 1)

Swift3からfunction callの第1引数のラベル名の省略がされなくされました。ですので、上で紹介したコンストラクタもrepeatingというラベル名をわざわざ設定しなくてもrepeatedValueというラベル名が必須なるので必要ないのかなと感じました。

ちなみに型を調べるのに使うdynamicTypetype(of: Element)に変更されていました。

// Swift 2.3
let tpl1 = (count: 10, repeatedValue: "s")
let tpl2 = (10, "ssssssssss")

print(tpl1.dynamicType) // (Int, String)
print(tpl2.dynamicType) // (Int, String)

print(tpl1.count) // 10
print(tpl2.0) // 10

// Swift 3.0
let tpl1 = (count: 10, repeatedValue: "s")
let tpl2 = (10, "ssssssssss")

print(type(of: tpl1)) // (Int, String)
print(type(of: tpl2)) // (Int, String)

print(tpl1.count) // 10
print(tpl2.0) // 10

同じ値を繰り返す文字列、配列などはプロダクションコードではあまり使う機会がないかもしれないのですが、デバッグなどのちょっとした処理を書くときに結構使うことがあります。こういう点も意外と変更されているので注意が必要ですね。ただ、今回の変更に関してはイマイチ納得感はなかったです。何かこの変更の理由をご存じの方がいらっしゃいましたら、教えていただけると助かります。

分かっているようで分かってない言葉

会議などで営業さんなどが使う言葉でなんとなくは分かっているけど厳密には分かっていない言葉が出てくるときがけっこうあるので、個人的なメモとしてまとめました。そもそも会議などで出て来る言葉が分かっていないようでは参加してもしょうがないので、いい機会だと思って調べてみました。不定期更新予定です。

ARPU

  • ユーザー1人あたりの平均売上金額を示す指標
  • Average Revenue Per User

ASO

  • アプリをアプリ一覧のより上位に表示させ、より多く露出させるための一連の施策のこと
  • App Store Optimization

CPA

  • 商品購入や会員登録などの、利益につながる成果を1件獲得するのにかかるコスト
  • Cost Per Action

DAU

  • 1日にサービスを利用したユーザー数
  • Daily Active Users
  • 他にもWAU (Weekly Active Users), MAU (Monthly Active Users)などがある

FYI

  • ご参考までに
  • For Your Information

FS

KGI

  • 重要目標達成指標 (Key Goal Indicator)
  • KGIは「結果」を見る指標
  • 例) 1年後に売上124%向上

KPI

  • 重要業績評価指標 (Key Performance Indicator)
  • KPIは「過程」を見る指標
  • 例) KGIが「1年後に売上124%向上」なら、KPIは「1ヶ月で売上2%向上」

LTV

  • 顧客生涯価値
  • Life Time Value

OEM

  • 他社ブランドの製品を製造すること、またはその企業
  • Original Equipment Manufacturer

UU

  • 決まった集計期間内に訪問したユーザー数
  • Unique User

アップセル

  • お客様が当初希望された製品よりも同一系統で高いグレードの製品をお勧めして販売すること

オンプレ

  • 自社運用
  • on-premises

各論

  • 全体を構成する各項目についての議論・論文

起票

  • 取引が起こったときに、社内外で処理が存在したことを証拠づけるために、伝票を発行すること

コンバージョン

  • 獲得できる最終的な成果
  • 例) ECサイトでは商品購入、情報提供サイトやコミュニティサイトでは会員登録、企業サイトや商品情報サイトでは問い合わせなど

昨対/昨対比

  • 前年比と同様

チャネル

  • 製品を消費者まで届けるための流通経路

伴走

  • そばについて走ること。転じて、顧客とともに進めること

反転授業

  • ブレンド型学習の形態のひとつで、生徒たちは新たな学習内容を、通常は自宅でビデオ授業を視聴して予習し、教室では講義は行わず、逆に従来であれば宿題とされていた課題について、教師が個々の生徒に合わせた指導を与えたり、生徒が他の生徒と協働しながら取り組む形態の授業

リスティング広告

  • ユーザーが検索したキーワードに連動して表示される広告
  • 検索連動型広告とも呼ばれる

リファーラル

  • 委託、紹介、推薦

体重計を自作してみた (仮)

最近、飲み会が増えてきて本格的に体重が気になってきました。しかし、体重計に乗るだけで満足して記録を忘れてしまい、全然管理できない毎日です。そもそも、自分の体重なんていう向き合いたくないものを記録するということがもう苦痛です、自分みたいな人間には無理です、不可能です。ですので、乗っただけで自動記録する体重計を自作してみることにしました。

といっても「そもそも体重計ってどうやって作るんだ?」というレベルからスタートしたので、プロトタイプから作ってみました。

プロトタイプのゴール

  1. 重さを測れること
  2. 測った重さを外部にPOSTすること

部品表

合計: 9,360円

構成

f:id:Nonchalanttan:20160929022039j:plain

上のような回路を組んで、圧力センサーにかかる荷重を計算していきます。Arduino IDEを用いて、Sketchと呼ばれるプログラムを組んで測定します。

f:id:Nonchalanttan:20160929030856p:plain

// 変数の宣言
double Vo, Rf, fg, kg;
int ain = analogRead(1);
// アナログ入力値から出力電圧を計算
Vo = ain * 5.0 / 1024;
// 出力電圧からFRSの抵抗値を計算
Rf = R1*Vo / (5.0 - Vo);
// FRSの抵抗値から圧力センサの荷重を計算
fg = 880.79/Rf + 47.96;
kg = (fg / 4448) * 10;

上が測定部分のプログラムです。アナログ入力値からセンサーの固有値を用いて荷重を測定します。センサーは負荷をかければかけるほど抵抗値が下がって、負荷をかけていないときはこの回路では無限として扱います。そして、測定した値をFlash Airのテキストファイルに書き込みます。

File dataFile = SD.open("DATALOG.TXT", FILE_WRITE);
if (dataFile) {
  dataFile.println(fg);
  dataFile.close();
  Serial.println(fg);
} else {
  Serial.println("error opening datalog.txt");
}

datalog.txtというファイルを開いて、先程測定した値を書き込みます。これで、Arduino上で行うことは以上です。

Sketchのソースコード

測定したデータを外部にPOSTする部分はFlash Airで行います。Flash Airは「撮った写真をその場でシェアできる」というキャッチコピーで東芝から売り出されているSDカードです。Flash Airは単体でWebサーバーとして機能して、luaという軽量スクリプトを動かすことができます。Flash Airに書き込みされたことをフックしてluaスクリプトを走らせて、測定したデータを外部にPOSTします。

// Flash Airの設定ファイル

[Vendor]
APPSSID=TokyoWifi // Wifi
APPNETWORKKEY=*************
STA_RETRY_CT=3
LUA_SD_EVENT=/post_script.lua // 書き込み時に走るスクリプト

APPNAME=flashair
APPMODE=5
PRODUCT=FlashAir
VENDOR=TOSHIBA
MASTERCODE=28a02b85ed79
LOCK=1
VERSION=FA9CAW3AW3.00.00
CID=02544d535731364731dc16867900f901
file, msg = io.open("DATALOG.TXT", "r")

if file then
    data = file:read("*a")

    t = {}
    for line in string.gmatch(data, "(.-)\n") do
        table.insert(t, string.sub(line, 1, #line))
    end

    latest = t[table.maxn(t)]
    if latest == "" then
        return
    end

    boundary = "--61141483716826"
    contentType = "multipart/form-data; boundary=" .. boundary

    token = "**************************************"
    local ENDPOINT_URL = "https://slack.com/api/chat.postMessage?token=" .. token .. "&channel=%23toybox&text=" .. latest .. "kg&as_user=true&link_names=channel"

    b, c, h = fa.request {
        url = ENDPOINT_URL,
        headers = {
            ["Content-Type"] = contentType
        }
    }

    print(b)
    print(c)
    print(h)
else
    print(msg)
end

DATALOG.TXTというファイルを読み込んで最新の書き込みを探して、今回はSlackに測定データをPOSTしています。

f:id:Nonchalanttan:20160929030617p:plain

luaのソースコード

これで最初に目標としてプロトタイプは完成しました!

まとめ

10kgまでですが圧力を感知してPOSTするデバイスが完成しました。これを100kg程度まで測れるように改良して、グラフとかを表示するWebクライアントを作れば完成だと思ってた矢先、見つけてしまいました...自分が求めてたものが...

Withings

Bluetoothで接続して、乗るだけで体重を記録してくれて、割りとおしゃれなWebクライアントもある。即ポチりました(涙

f:id:Nonchalanttan:20160929031037p:plain

色々と勉強にはなったので良かったといえば良かったのですが、まずはちゃんと調査する大切さがよく分かりました。

参考にしたサイト