macOS Catalina で Karabiner を使う

これをやるだけ。 Catalina が出たばかりなので暫定処置となる。

github.com

日本語にすると、

セキュリティとプライバシーの入力監視に /Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_grabber を追加。

f:id:star__hoshi:20190605221157p:plain

Terminal で sudo killall karabiner_grabber を実行。

これだけで動くようになった。

セキュリティとプライバシーに「+」ボタンが表示されない人は Krabiner-EventViewer.app を開くとここにアプリケーションが表示される。

Karabiner は Vi Mode、コマンドで英かなを使っているけどどちらも動いてて問題なさそう。

皆さまにご報告。

いつも応援していただいている皆さまへ、私事で恐縮ですがご報告があります。

本日令和元年五月一日に私星川健介は入籍致しましたことをここにご報告させていただきます。
お相手は一般の方です。

元号が令和に変わった初日で、大安という事もあり、この良き日に入籍できたことを嬉しく思います。

自分自身まだまだ本当に未熟な人間ではありますが、どんな時も楽しく思いやりに満ちた家庭を築いていきたいと思っております。

23歳からプログラミングのお仕事を始めて以来、本当にありがたいことに毎日充実した日々を送らせていただき、今日に至るまでたくさんの経験、ご縁が出来た事に心から感謝の気持ちでいっぱいです。
これからもソフトウェアエンジニアとして、人として、より精進していきたいと改めて感じています。
もちろん、今後も変わらずお仕事は続けていきます。

これまで支えてくださった家族、友達、お仕事関係の皆さま、そしてファンの皆さまに感謝の気持ちを忘れず、これからもアプリ・サービス開発などで恩返しをしていけるよう、より一層精進してまいります。

突然のご報告になってしまいましたが、どうか温かく見守っていただけますと幸いです。
今後とも皆さまのご支援賜りますよう、どうぞよろしくお願い致します。

お祝いお待ちしています。
https://www.amazon.jp/hz/wishlist/ls/38J4VLC6989GL

星川健介


この記事は戸松遥様のブログを参考にさせていただきました。

ameblo.jp

2018年振り返り

2018年色々あった。

気楽に生きた

2017 年振り返り で、2018年は気楽に生きたいと書いた。
2018年前半は頑張ってしまったのだが、後半は気持ちの向くまま気楽に生きれたのでいい一年だった。

今までは業務で必要になる知識は就業後にやっていたのだが、それを業務時間内でやるようにしている。これはなかなか難しくて、正直新しい知識というのはあまりつけられなかった。しかし、業務はなんとかなっているのでこれでいいかという気持ちになっている。

家でコード書かなくなった結果、資産運用や不動産投資、人間、筋肉などに興味が移っている。

こうして最新技術の知識がなくなり老害化していくのだろう、胸が熱くなる。

Github

f:id:star__hoshi:20190103100320p:plain

前半は個人開発やOSS作成などしていたが、後半はほぼ全くやらなかった。

OSS をそこそこ出したが、業務で使っているコードを OSS として切り出したという程度で大したものは作っていない。というか、しょうもないコードを切り出すと正直メンテがだるいので OSS やめて業務のレポジトリに戻したりもしている。ここら辺の塩梅がなんとなく掴めたのがよかった。

個人開発の収益はやっぱり落ちてきているのでなんとかしたいところがあるが、気持ちが全くないので半年くらいは個人開発しなさそう。

blog / Qiita

このブログに20記事Qiita に11記事 書いた。2018年前半に書いたのがほとんどなので、今年はもっとアウトプットが減るであろうことが予想される。

聴覚情報処理障害かもしれない が心に残った記事であるが、電話してても相手の話を聞き取れるしやっぱり問題ないのかな、とも思う。しかし俺は困っている。

Twitter

2018年前半はサブ垢を使っていたが、後半からはサブ垢を使うのをやめて鍵垢に引きこもっている。鍵垢はめちゃくちゃ居心地が良い、過激な発言やネガティブな発言を躊躇しなくて良い。どんなツイートをしても炎上しない、という安心感もある。
ID が knskgroup であり、めちゃくちゃ気に入っている。これは kskgroup2017 をリスペクトしている。

twitter.com

代わりに本垢ではしょうもないツイートばかりするようになった。

筋トレ

半年前くらいから筋トレを始めた。1年前と比べて体重が10kg近く増え、標準体重に近くなって嬉しい。今までは170kgで50kgとかでBMI異常値だったが、60kgになり正常値に入ってきた。

筋トレをしたことによる効能は特に感じていない。体温が高くなった気はする。

f:id:star__hoshi:20190103102311p:plain

Withings という体重計が Wifi 連携できるので、起きたら毎朝測るようにしている。

ポケモンGO

6月から復帰して12月末までやった。めちゃ楽しかった、コミュニティデイは歩き回ったし、レイドバトルも楽しくて、ポケモンGOオフ会もしたし、最高のゲームだった。

しかし12月末で引退した、歩きながらスマホ見るのは危険。

WWDC

6月にアメリカに行ったが、言語が通じなくてなかなか辛い気持ちになった。iOS エンジニアとしては一度行きたいという気持ちがあったので行けてよかった。

しかし意識が低いので、今年は申し込まないと思う。

2019年

2018年後半のように、とにかく気の向くまま行きていきたい。とはいえ給料をあげたいという強い気持ちがあるので、キャリアプランなどを考えるかもしれない。週末働くなどはしたくないので、純粋に今の給料をあげたい。それと不動産投資で一発当てたい。

今年もよろしくお願いします。

Xcode10 にしたら Multiple commands produce ... Info.plist で消耗した

個人プロジェクトを Xcode10 対応してたらこんなエラーが出た。

:-1: Multiple commands produce '/Users/hoge/Library/Developer/Xcode/DerivedData/KotaichiDex-eiyecmvfctikcwcspitwpzskudue/Build/Products/Debug-iphonesimulator/PokemonRealm/PokemonRealm.framework/Info.plist':
1) Target 'PokemonRealm' (project 'Pods') has copy command from '/Users/hoge/Xcode/KotaichiDex/Pods/PokemonRealm/PokemonRealm/Info.plist' to '/Users/hoge/Library/Developer/Xcode/DerivedData/KotaichiDex-eiyecmvfctikcwcspitwpzskudue/Build/Products/Debug-iphonesimulator/PokemonRealm/PokemonRealm.framework/Info.plist'
2) Target 'PokemonRealm' (project 'Pods') has process command with output '/Users/hoge/Library/Developer/Xcode/DerivedData/KotaichiDex-eiyecmvfctikcwcspitwpzskudue/Build/Products/Debug-iphonesimulator/PokemonRealm/PokemonRealm.framework/Info.plist'

PokemonRealm というのは private pods として利用しているライブラリ。こいつの Info.plist がどうこう怒られている。

PokemonRealm の .podspec はこう書いていた。

Pod::Spec.new do |s|
  ...
  s.source_files       = "PokemonRealm/*"
  ...
end

こうしていると、 PokemonRealm/Info.plist もライブラリ管理されてしまって、それが重複してしまうのでダメらしい。

なので、 Swift ファイルだけ対象にするようにした。

Pod::Spec.new do |s|
  ...
  s.source_files       = "PokemonRealm/*.swift"
  ...
end

そしたら無事エラーが消えて Xcode10 でビルドできた。

参考

github.com

聴覚情報処理障害かもしれない

poem.mizdra.net

この記事を読んで、心当たりがありすぎる。自分は耳が悪いのかも知れないと思いつつも、聴力検査は問題がなかったし、みんなそういうものなのかなと思っていた。しかし症状を見ると聴覚情報処理障害っぽく思える。

例えばこれは1年半前のツイート。

これは今でも全く治ってない。いつからこの症状が出ているのかもわからない、大学生の時はすでに悩んでいた気がする。

相手の話の一部分が聞き取れないことが頻繁にあって、聞き流してしまう事も多い。聞き流した結果会話が途切れて、変な空気になる。変な空気になるともう聞き返せないし、相手が何を言っていたかわからないので次の会話をはじめにくい。そうして人間とのコミュニケーションが嫌になっていく。

聴覚情報処理障害の特徴チェックリストはこうなった。(そもそもこのチェックリストの信憑性が怪しい気もするが、それはそれ)

  • [ ] 言葉の発達が遅い
  • [x] 聞き返しが多い
    • 相手の話の一部分だけ全く理解できないことが多々ある
  • [x] 騒がしい場所での聞き取りが苦手
    • 駅で電車がきてる時は絶対に話しかけないようにしているし、大人数での飲み会はストレス
  • [x] 文字情報の方が正確に理解できる
    • これはそもそもリアルタイム言語処理より文字情報の方が繰り返し読めるので当然では?
  • [x] 音韻が似ている言葉を聞き間違える
    • 字幕を見ると違う単語であることが多々ある
  • [x] 言葉での指示への反応が遅い
    • 相手の真意を確かめるために自分の解釈を相手に伝えて正しいか確かめることをよくする
  • [x] 聴覚情報の記憶力が弱い
    • ちょっと長い話をされると古い話が頭から抜けていく。
    • みんなそういうものだと思ってたが違うのか?

こうして見ると完全に聴覚情報処理障害なのか?とも思ったが、本当に聴覚情報処理障害の人はコールセンターで働く事も困難らしい。私は新卒の頃2ヶ月コールセンター研修を行なったが特に問題はなかった。なので私は軽度の聴覚情報処理障害か、ただの勘違い野郎ということになる。

しかし実際のところめっちゃ困っている。人と会話するのが億劫になるし電話がめちゃくちゃ嫌だ、なるべく文字情報でのコミュニケーションで済ませたい。

聴覚情報処理障害だと英会話は致命的なのか?と思うがどうなんだろう。日本語ですら聞き取れないのに英語はもっと聞き取れないのでは、という気もする。

全て気のせいであってほしい。

Tesseract-OCR-iOS を使う

【Swift】文字認識ライブラリ、TesseractOCR for iOSを試してみた - Qiita に使い方があるが、少し古かったので Xcode9.4, Swift4 で動かすための手順。

Podfile

pod 'TesseractOCRiOS', '4.0.0'

そして pod install、終わったら xcworkspace を開く。
Build すると warning が大量に出るが気にしてはならない...

traineddata の入手

https://github.com/tesseract-ocr/tessdata/tree/bf82613055ebc6e63d9e3b438a5c234bfd638c93 から必要なデータを入手。
注意点があって、https://github.com/tesseract-ocr/tessdata_best から DL したやつは動かない。
Using traineddata from tesseract-ocr · Issue #299 · gali8/Tesseract-OCR-iOS

今回は eng と jpn を DL した。

Project に traineddata を追加

https://github.com/gali8/Tesseract-OCR-iOS/wiki/Installation にあるように、このようなディレクトリ構成にする。

f:id:star__hoshi:20180819223629p:plain

注意点は、 tessdata をディレクトリにすること。 Group にしたら動いてくれなかった。

OCR する

Qiita には Bridging-header の話があるが、Swift 4 でははもう不要。

import UIKit
import TesseractOCR

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let tesseract: G8Tesseract = G8Tesseract(language: "eng+jpn")
        tesseract.delegate = self
        tesseract.image = UIImage(named: "ep.jpg")!
        tesseract.recognize()

        print(tesseract.recognizedText)
    }
}

extension ViewController: G8TesseractDelegate {
    func shouldCancelImageRecognitionForTesseract(tesseract: G8Tesseract!) -> Bool {
        return false // return true if you need to interrupt tesseract before it finishes
    }
}

結果

ポケモンGOのエーフィを読み込んだらこんな感じになった。

some("CPー889 丶\' 蓼蘭\n工一フイ \'\n92/92HP\n28.77kg @ Q9ーm\n一 重さ 工スバ一 高さ\n\n")

f:id:star__hoshi:20180819224115p:plain

Firebase SDK for Cloud Functions を 1.0 に Migration した

The Firebase Blog: Launching Cloud Functions for Firebase v1.0 にあるように、 Cloud Functions の SDK が 1.0 になった。と言っても SDK が 1.0 になっただけで、 Cloud Functions のベータが外れたわけではない。

Migration

Firebase SDK for Cloud Functions Migration Guide: Beta to version 1.0  |  Firebase という親切なドキュメントがある。
Firestore を使っている Project で、実際に 1.0 に Migration した。

noImplicitAny

まず、 TypeScript で使おうとするとエラーが出る。

github.com

any 型が使われてしまっているようで、 tsconfig に "skipLibCheck": true を追加してひとまずしのぐようにした。

New initialization syntax for firebase-admin

今まではこう書いていた admin の initializeApp が簡潔になった。

// before
admin.initializeApp(functions.config().firebase)

// after
admin.initializeApp()

こうなったのは、 functions.config().firestore が廃止され process.env.FIREBASE_CONFIG を使うようになったからのようだ。
今後 projectId などが必要な場合は環境変数から取るように、と書かれている。

let firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
/* {  databaseURL: 'https://databaseName.firebaseio.com',
       storageBucket: 'projectId.appspot.com',
       projectId: 'projectId' }
*/

SDK changes by trigger type

今までの Event trigger では、 event というパラメータだけを使っていたが、それが contextsnapshot (or change) という 2 つのパラメータに変更された。

// before
exports.dbWrite = functions.firestore.document('/path').onWrite((event) => {
  const beforeData = event.data.previous.data(); // data before the write
  const afterData = event.data.data(); // data after the write
});

// after
exports.dbWrite = functions.firestore.document('/path').onWrite((change, context) => {
  const beforeData = change.before.data(); // data before the write
  const afterData = change.after.data(); // data after the write
});

context

型定義はこうなっている。

/** The context in which an event occurred.
 * An EventContext describes:
 * - The time an event occurred.
 * - A unique identifier of the event.
 * - The resource on which the event occurred, if applicable.
 * - Authorization of the request that triggered the event, if applicable and available.
 */
export interface EventContext {
    /** ID of the event */
    eventId: string;
    /** Timestamp for when the event occured (ISO string) */
    timestamp: string;
    /** Type of event */
    eventType: string;
    /** Resource that triggered the event */
    resource: Resource;
    /** Key-value pairs that represent the values of wildcards in a database reference */
    params: {
        [option: string]: any;
    };
    /** Type of authentication for the triggering action, valid value are: 'ADMIN', 'USER',
     * 'UNAUTHENTICATED'. Only available for database functions.
     */
    authType?: 'ADMIN' | 'USER' | 'UNAUTHENTICATED';
    /** Firebase auth variable for the user whose action triggered the function. Field will be
     * null for unauthenticated users, and will not exist for admin users. Only available
     * for database functions.
     */
    auth?: {
        uid: string;
        token: object;
    };

今までの event が持っていたパラメータに加えて、 Callable Functions の context が合わさったような感じ。 (Callable Functions の説明は こっち を参照)

snapshot

今までは DeltaDocumentSnapshot という型があったが、それが廃止され firebase.firestore.DocumentSnapshot が使われるようになった。
型定義はこうなっているが、前から使っていたものだし特に変更点もないと思う。

  /**
   * A `DocumentSnapshot` contains data read from a document in your Firestore
   * database. The data can be extracted with `.data()` or `.get(<field>)` to
   * get a specific field.
   *
   * For a `DocumentSnapshot` that points to a non-existing document, any data
   * access will return 'undefined'. You can use the `exists` property to
   * explicitly verify a document's existence.
   */
  export class DocumentSnapshot {
    protected constructor();

    /** True if the document exists. */
    readonly exists: boolean;

    /** A `DocumentReference` to the document location. */
    readonly ref: DocumentReference;

    /**
     * The ID of the document for which this `DocumentSnapshot` contains data.
     */
    readonly id: string;

    /**
     * The time the document was created. Not set for documents that don't
     * exist.
     */
    readonly createTime?: string;

    /**
     * The time the document was last updated (at the time the snapshot was
     * generated). Not set for documents that don't exist.
     */
    readonly updateTime?: string;

    /**
     * The time this snapshot was read.
     */
    readonly readTime: string;

    /**
     * Retrieves all fields in the document as an Object. Returns 'undefined' if
     * the document doesn't exist.
     *
     * @return An Object containing all fields in the document.
     */
    data(): DocumentData | undefined;

    /**
     * Retrieves the field specified by `fieldPath`.
     *
     * @param fieldPath The path (e.g. 'foo' or 'foo.bar') to a specific field.
     * @return The data at the specified field location or undefined if no such
     * field exists in the document.
     */
    get(fieldPath: string|FieldPath): any;

    /**
     * Returns true if the document's data and path in this `DocumentSnapshot`
     * is equal to the provided one.
     *
     * @param other The `DocumentSnapshot` to compare against.
     * @return true if this `DocumentSnapshot` is equal to the provided one.
     */
    isEqual(other: DocumentSnapshot): boolean;
  }

change

onUpdate と onWrite 時は snapshot ではなく Change<DocumentSnapshot> が使われる、Change の型を見てみる。

/** Change describes a change of state - "before" represents the state prior
 * to the event, "after" represents the state after the event.
 */
export declare class Change<T> {
    before: T;
    after: T;
    constructor(before?: T, after?: T);
}

今まで event.data.previous, event.data とデータを取得していたが、それが before, after と取れるようになった。わかりやすくて良い。

おわり

こんな感じで Migration が完了した、特に大きな混乱もなく、動作も問題なさそう。 (noImplicitAny は困っているが...)

Cloud Functions はとにかく重複発火をなくしてほしい。