読者です 読者をやめる 読者になる 読者になる

のほほん停留所

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

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: カリー化

iOS Swift3 Swift

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: 同じ値を繰り返す文字列、配列

iOS Swift 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サイトでは商品購入、情報提供サイトやコミュニティサイトでは会員登録、企業サイトや商品情報サイトでは問い合わせなど

昨対/昨対比

  • 前年比と同様

チャネル

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

伴走

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

反転授業

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

リスティング広告

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

リファーラル

  • 委託、紹介、推薦

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

Arduino FlashAir IoT

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

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

プロトタイプのゴール

  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

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

参考にしたサイト

「AKIBA.swift 第5回」に行ってきた

Swift iOS AKIBA.swift

9/27(火)にクラスメソッド株式会社で開催されたAKIBA.swift 第5回に行ってきました。テーマは「フリートーク」ということで、Storyboardを駆使したUIの話からSwiftでサーバーサイドを書くなど多岐に渡っていました。メモ書き程度ですが、各発表をまとめさせていただきました。

More Server Side? Swift

speakerdeck.com

上の資料は同じ題材で発表者の田中孝明さんがHackerTackleで発表されたスライドを引用させていただいてます

Swiftがオープンソースなったことにより、Swiftでサーバーサイドを書くことも可能になりました。それに伴い、Swiftで書かれたサーバーサイドフレームワークの開発も活発になってきました。有名なフレームワークといえば、Taylor、Swifter、VaporやPerfectなどがありますが、今回の発表はその中でも一番GitHubのスターが多いPerfectを使った発表でした。

Perfectを用いてGETとPOSTのAPIの簡単な実装を紹介してくださいました。さらにPerfectでサポートしているDB Connectorも多岐に渡り、今回はPerfect PostgreSQLを使ってDB操作の実装も紹介してくださいました。導入はSwift Package Managerをサポートしているので、比較的簡単そうでした。また、デプロイもPerfect用のAWSのプランがあるそうです。

SwiftでServer Sideを書いた経験はないのですが、大変そうだと感じた点はビルド時間がかかるのと SQL文が生で書く必要がある という点でした。まだまだ、発展途上という感じで実用にはまだまだ難しそうです。

Server Side Swift, Vapor を触ってみた

speakerdeck.com

1件目と同じServer Side SwiftでフレームワークVaporを使ったという発表でした。これもまた導入方法から説明してくださいました。印象としてはrailsの導入と近いという印象を受けました。デモはSlack上で話しかけると関連しているGitHubのスターが多いレポジトリの情報を返すというSlack botでした。調べたところVaporに関するサンプルやドキュメントはそれなりに豊富で、今回作成されたSlack botもサンプルに少し手を加えた程度のものとのことでした。取っ掛かりやすそうな印象を受けました。

PerfectでもVaporでもそうですが、言語に引っ張られてフレームワーク自体も仕様がコロコロと変わっていて、まだまだ安定期は遠そうといった印象を受けました。iOSアプリに限る話ですが、サーバーサイドとエンティティ等を共通化できそうなどのメリットは感じるので今後どのような発展を遂げるか楽しみです。

watchOS 開発ことはじめ

speakerdeck.com

3件目の発表はwatchOSに関するお話でした。AppleWatchアプリをハッカソンで1から作った話で、プロジェクトの作成方法や各画面の名称などのwatchOS特有な部分を説明してくださいました。更に開発で詰まった点などのTIPSも紹介してくださいました。

AppleWatchアプリは必ずiOSアプリとペアで作られ、AppleWatch単体のアプリというものは現状では作成できません。通信も完全にiOS側に依存しており、iOS側で行われた通信の情報をBluetoothを通してAppleWatch側に渡されるという仕組みだそうです。また、アプリのインストールもBluetoothを通して行われるので、それなりに時間がかかるそうです。また、watchOS側からiOS側に通知を送るということも可能だそうです。例えば、watchOSからiOS側のメソッドを呼び出して、iOSバイスの画面に何かしらの描画をするなどですね。ただ、送信側で[String: AnyObject]型のメッセージしか送れず、受信側でどのようなものが送られてきたか1つのメソッドでハンドリングしないといけない点がツラそうでした。#selectorみたいので1対1対応して書けたら、まだマシになるとは思うのですが...

AppleWatchアプリは基本的にiPhoneアプリのリモコンのような役割しかないアプリがほとんどなようです。AppleWatchだからこそ価値があるアプリを作ってみたいとのことでした。

ContainerViewとStoryboardとSwift3.0の交響曲(シンフォニー)

www.slideshare.net

4件目はStoryboard上でContainerViewを駆使して、複雑なUIを作成するといったものでした。ContainerViewとは親子関係を持つViewControllerをInterface Builder上で簡単に設定できるViewといったものです。親と子どちらのViewControllerからもそれぞれのメソッドを呼べます。デモではよくあるデザインだけど自前で実装するとけっこうコード量が増えてしまうような例をStoryboardを主に使い実装するというものでした。また、おまけとしてSwift3対応のTIPSについて紹介してくださいました。

iOS10からStackView(AndroidのRelativeLayoutのようなもの)なども導入されて、Storyboardで表現できる幅がかなり広くなってきてるなと感じました。ただ、StoryboardはXcode上で開いただけで差分が出るのと、PR上でレビューするのがほぼ不可能という問題が付きまとってしまうので、そういったデメリットとのトレードオフになるのかなと感じました。

まとめ

4件の発表を聞いて、Swiftも様々なプラットフォームで動くようになってきたなと感じました。情報のキャッチアップが大変そうですので、今後共このような勉強会に参加して情報を逃さないようにしようと思います。