Siri Shortcutsまとめ

Siri Shortcutsについて体系的にまとまっている日本語記事が少なかったため、調べるときに分かりやすいようにメモ。

参考記事にとても参考になったサイトへのリンクを貼ってあるので、そこから飛んで記事を見ることで、大まかに理解できる気がします。

SiriKit(iOS10~)

iOS10以降、SiriKitフレームワークが追加されている。

SiriKitはIntents app extensionとIntents UI app extensionという2つのapp extensionをサポートしている。

  • Intents app extensionはユーザのRequestを受け取ってメッセージを表示したりする
  • Intents UI app extensionは独自UIを表示するときに使用する

Siri Shortcuts(iOS12~)

iOS12から、Siri Shortcutsという機能が追加されている。

この機能では、「ショートカット」として複数のアクションを組み合わせることで、ユーザの定型的な作業を楽にすることができる。

このShortcutsをSiriから呼び出すことで、Widgetなどと同様にアプリを開かなくても、アプリの機能を利用することができる。

ショートカットの登録方法

以下の2つの方法で登録することができる。

  • アプリ内部から呼び出す(INUIAddVoiceShortcutButtonを使用)
  • Shortcutsのギャラリーから登録(アプリ内でINVoiceShortcutCenter.shared.setShortcutSuggestionsのメソッドでINShortcutを登録)

donation

INInteractionのdonateメソッドを使用することで、Spotlight検索にショートカットを表示することができる。

donateではSpotlight検索にshortcutsへの登録処理自体を行うわけではないが、ユーザがよく行う行動を簡単に行うことができる。

iOS13でできること

iOS13では、Siri Shortcuts内に独自パラメータを持たせ、より対話的にSiri Shortcutsを利用する事が可能になっている。

Intentsの実装方法

この辺りは他の記事やAppleのSample(Soup Chef)が大変参考になるので、本記事内では割愛します。

Intents UI

Shortcutsで表示されるIntentsをカスタムなUIとする機能。

Intents作成時に「Incluede UI Extension」にチェックを入れるか、「Intents UI ExtensionのTargetを追加」することで使用することができる。

Sample

Soup ChefというSampleコードがあるので、ここからSampleを試すことが出来る。

参考資料

書籍

iOS12 Programmingの中に、Siri Shortcutsの章があり、体系的にまとまっていて大変分かりやすかったです。

peaks.cc

OS対応の本は、その年以降も参考になると感じたので、iOS13についても体系的に変更点を把握し、検索方法を知っておくことが重要だと感じました。

ContainerViewに対して最大2分の1まで伸びる子Viewを、コードを書かずにAutoLayoutで組む

作りたかった画面

以下のような画面サイズの最大2分の1まで伸びる2つのViewを作りたかったですが、Textサイズの計算などをしたくなかったので、AutoLayoutのみでレイアウトを組んでました。良い感じにレイアウトが組めたので、残しておきます。

最大サイズまで伸びているときは、以下のような画面になります。

f:id:kuro-46:20191112073846p:plain

2つとも短いときは、以下のようにくっ付くような形になります。

ここで、Viewの横幅を完全に2分割にするだけであれば、単純なレイアウトとなります。

f:id:kuro-46:20191112073836p:plain

どちらかが短いときは以下のような感じになるように実装しました。

f:id:kuro-46:20191112073839p:plain

f:id:kuro-46:20191112073843p:plain

難しいポイント

  • 完全に2分割ではなく、それよりも短いときにはくっ付くようなレイアウトにする必要がある点
  • テキストだけでなく、ImageなどのViewを置いている点
  • 両端からテキストまでの間や、それぞれのテキストの間にSpacingがあるので、Spacingを考慮してレイアウトを組まなければいけない点

これらを幅の計算によって実装を行うと、定数定義が多く煩雑になってしまいます。

実現方法

子ViewのMultiplierを親Viewの1:2以下にするようにレイアウトを組むことで、実現することが出来ました。

Viewの構成要素としては、以下のようになります。

f:id:kuro-46:20191112192050p:plain

制約

ContainerStackViewの制約

上下左をContainerViewに、右はContainerViewに対して、ContainerStackViewのSpacing分マイナス値を指定します。

このViewを組むときに、一番下に置いているContainerViewのConstraintを、ContainerStackViewのSpacing分マイナスに指定することがポイントでした。

f:id:kuro-46:20191112192741p:plain

Left/RightStackViewの制約

ContainerViewに対してMultipriorを1:2にして、Less Than or Equalにすることで、ContainerViewの2分の1以下と指定することが出来ます。

f:id:kuro-46:20191112192925p:plain

コードについて

Sample Projectを置いているので、試してみて頂けますと幸いです。

github.com

App Store Connectへのアップロード時、Fastlaneで2FAを使用する際のメモ

Fastlaneを通じてApp Store Connectへバイナリをアップロードしようとしたところ、2FA認証でエラーが発生してしまいました。

こちらを解決するために、何をしたら良いか調べる機会があったので、Fastlane公式の記事を参考に、どういう環境変数が必要になるかと、手順のメモを残しました。

必要になる環境変数

  • FASTLANE_USER / FASTLANE_PASSWORD

2FAに関わらず必要となる値です。

  • FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD

2FAを使用する際に必要となるパスワードのようです。

  • FASTLANE_SESSION

Fastlaneを使用してApp Store Connectへアップロードするための2FAのセッションを事前に保存しておくための環境変数です。

ログインセッションが切れるたびに変更になる?ようです。

手順

手順については、Fastlane公式の記事と下記記事が大変参考になりました。

techblog.lclco.com

こちらを参考に環境変数を設定することで、2FAを突破することができます。

SwiftUIのGestureについてまとめ

Gestureについて

SwiftUIでは、Gestureを使用することで、TapやSwipeなどを検知し、画面を更新することができます。

色々調べて実際に触ってみたところ、想像以上に強力であることが分かったため、記事として残しておきました。公式Documentはコチラです。

Gestureの種類

  • TapGesture
  • LongPressGesture
  • DragGesture
  • MagnificationGesture
  • RotationGesture

が提供されているようです。またCustomで作ることもできるようです。

GestureのCallback処理

Gestureでは以下の3つのreceiverを使うことができるようです。

  • updating(Gestureの値が更新されたときに発火:一時的なUIの状態を変更したいとき、@GestureStateを使用してGestureの状態を管理する)
  • onChange(Gestureの値が更新されたときに発火: 永続的な値を変更したいとき)
  • onEnded(Gesture終了時に発火)

three gesture composition types

  • Simultaneous
  • Sequenced
  • Exclusive

を使用して複数のGestureを管理することで、さらに複雑な操作が出来るようです。

実際に作る

実際に使われている事例があったので、本記事内ではリンクを載せておきます。

参考資料

【読書】SOFT SKILLS【2回目】

SOFT SKILLSを読みました。2回目です。1回目はこちら

前回との差分を見ながら、学んだことを書いておきます。

学んだこと

事業者のマインドセット

ソフトウェア開発というあなたの事業の顧客として、雇用主を考えた方がいい。

マインドセットを、雇用契約で縛られた奴隷から、自分自身の事業を経営している事業者に切り替えるのである。キャリアの最初の時点でこのマインドセットを持っているだけで、自分のキャリアについての考え方が変わり、キャリアを積極的にマネジメントするという意識が持てるようになる。

事業者として、会社と対等の立場というマインドセットは、今まで持てていなかったため、考え方を変えたほうが良いなと感じました。

専門性について

専門を持たないソフトウェア開発者は、山ほどいる。ほとんどのソフトウェア開発者は、どのプログラミング言語を使うかによって、それぞれの専門分野を定義している。「私はC#プログラマーです」とか「私はJavaプログラマーです」という言い方を聞くことがあるだろう。この種の専門の表し方は広すぎる。

専門特化の法則:専門性が高ければ高いほど、チャンスの数は減る。その半面、チャンスを獲得する可能性は高くなる。

できる限り多くのことを学び、できる限り柔軟な開発者になろう。同時に、専門分野を持って自分をユニークな存在として目立たせることも大切だ。このふたつからどちらかを選ばなければならないときには、まず専門特化に取りかかり、あとで多才になればいい。

自分の好きなこと・得意なことにステータスを振るようにしていますが、さらに尖らせていこうと思いました。

責任について

どの会社でも、地位を上げるためにできるもっとも大切なことは、それまでよりも重い責任を引き受けることだ。

当然に見えるかもしれないが、キャリアのなかでは、収入が増えるのと責任が重くなるのとの間で、選択を迫られることがよくある。少なくとも長期的な視野に立った場合、正しいのはほぼ必ず責任の方である。

この本を読むと毎回大事だなと思う「責任」についての考え方です。

地位やポジションにこだわる必要はないと思いますが、キャリアを考える上で責任範囲を広げるように新しいチャレンジをしていきたいと思いました。

人前で話すこと

謙虚で率直な人ではなく、エキスパートとして見られたいことにこだわる人が多すぎる。欠点や弱点もある人間らしい人でいた方が、潜在顧客からの信頼を獲得しやすく、バカのように見える可能性もずっと低くなるはずだ)。

社内外へ晒し、作ったアプリケーションを広めるためのスキルももっと磨く必要があると思いました。

SNS管理

私は今、Buffer(https://buffer.com/)というSNS予約投稿サービスを使っているが、同様のサービスはほかにもたくさんある。Bufferを使えば、各種SNSにポストするスケジュールを管理できる。

業務以外のスケジューリングも、感覚ではなく考えた方が良いと感じました。

LinkedIn

次にお勧めしたいのは、LinkedInである。LinkedInはプロフェッショナルのためのSNSなので、あなたも当然LinkedInのプロフィールを用意してあることだろう。LinkedInでは、オンラインバージョンの履歴書を作ることができ、ほかのプロフェッショナルとつながることができる。

自分を1人の事業者として見たときに、やっていることをさらっと証明できるようなページがないなと思い、LinkedInのページも作ろうと感じました。

感想

1回目との差分

1回目は「プレゼンテーション・講演を行う」行動をしたいと思っていました。

2回目に読んだときには、「プレゼンテーション・講演を行う」上での注意点などにハイライトを引いていることに気が付きました。

1回目は概念的な部分に線を引くことが多かったですが、2回目は実践的なテクニックを意識して本を読めたことから、前回読んだことを行動面に取り入れて、着実にステップアップしている気がします。

一方で、「お金に関すること」や「健康でいる」など、現在できていないことも多いので、行動に反映させていく必要があると感じました。

【読書】Being Geek ギークであり続けるためのキャリア戦略

読んだ本

Being Geek ―ギークであり続けるためのキャリア戦略

Being Geek ―ギークであり続けるためのキャリア戦略

学んだこと

ギークの定義

ギークは、基本的に「システム思考」をする人間である。世界をコンピューターと同じようなものとして認識しているということだ。

キャリア形成

大切なことは3つ

  • 方向性
  • 成長
  • 成果

それぞれ以下のような問いを立てる。

  • 自分の開発する製品の技術的な方向性の決定に能動的に関われているか
  • 成長のために自分が何をすべきか分かっているか
  • 期日を守って仕事をしているか。自分に課せられた責任を十分に果たしているか。言葉と行動が一致しているか。

成長

  • 最近、何か失敗をしたか?
  • 自分に意義を唱える人が身近にいるか?
  • この1週間の間に何か学んだことはあるか?それについて説明できるか?

上記について、一つでもNoであれば、成長が止まっている可能性がある。

取り組む姿勢

ワークライフバランスは重要だが、業界上「9時5時」の仕事は存在しない。

24時間休みなく仕事をする必要はないが、取り組んでいる仕事が自分の一部であると感じられるくらいには没頭する必要がある。

何もしないときに、一切仕事のことを考えなくなったとしたら、心が離れていることを示す兆候。

コミュニケーション/マネジメント

  • マネージャをマネジメントする
  • 上司とのコミュニケーション方法

について、様々なことが書かれていました。

ゲームの要素

面白いゲームには、主に次の3つの特徴が存在するため、これらの要素を仕事に取り入れることで組織を活性化させる。

  • 発見 → 混乱から秩序へ
  • 最適化と繰り返し → ゲームのパラドックス
  • 達成 → 他人との競争

ゲームを取り入れるという視点がとても面白いと思いました。

人脈

ギークのキャリア形成に役立つものには、「無形の資産」と呼ぶべきものが多い。資産の例の1つとして、「人脈」もあげられる。

人脈は社内と社外に分かれる。社外のエンジニアとのコミュニケーションはハードルが高いが、テクノロジーについて熱心に話せる人の存在は重要。

採用

優秀な人材の採用は自らのキャリアにもプラスになる

会社の危機

戦略的会話の中心にいる人が離れる(第1派) → 戦術的な会話しかいない人も離れる(第2派) → 残っている人も離れる(第3派)

このような場合のアプローチは、「ただ見ている」「離れる」「食い止めようとする」の3つになる。

感想

ギークであり続けるために、会社と共に自分も成長していくキャリア戦略の立て方を学びました。

自らのキャリアを自分で切り開いていくために、自分自身も組織の中心に近いところへ入り(社内政治に積極的になるということではないですが)、積極的に優秀な人と働くための土台を作る必要があると感じました。

プログラミングを学び、技術を学ぶことが楽しいため、最初のころは技術だけできれば良いと思っていましたが、マネジメントもするようになり

会社の中で自由に立ち回るには、自由に立ち回るだけの権限委譲をしてもらえるほどの信頼が必要ということを学びました。

そのため、ギークでありつつ、「責任」「権限」をもらって自由に立ち回れるようになりたいと感じている中で読んだ本だったので、とても学びがありました。

今までは「責任」を持って成長を続けられる環境を会社の中に求めることが多かったですが

今後は自由な環境を求めるだけでなく、自分で作っていこうと感じました。

Sourceryでのボイラープレートコードの自動生成入門

kuro-46.hatenablog.com

上のブログのSourceryについての詳細を記述した記事です。

今回試したコードは以下のリポジトリに記載されていますので、興味があれば見て頂けますと幸いです。

github.com

やりたいこと

public class SampleView: UIView {
}

上記のようなクラスがあります。このクラスの内部に下記のようなコードを書くことを想定します。

public class SampleView: UIView {

    @IBOutlet private var titleLabel: UILabel!
    @IBOutlet private var subTitleLabel: UILabel!

    public var title: String? {
        get {
            titleLabel.text
        }
        set {
            titleLabel.text = newValue
            titleLabel.isHidden = newValue == nil 
        }
    }

    public var subTitle: String? {
        get {
            subTitleLabel.text
        }
        set {
            subTitleLabel.text = newValue
            subTitleLabel.isHidden = newValue == nil 
        }
    }
}

このときに、titleとsubTitleについて同じようなロジックのコードを繰り返し書いています。

このような繰り返しのコード(ボイラープレートコード)を毎回手で書くのは面倒なため、自動生成の手段を調べました。

以前から機会があればSourceryを使ってみようと思っていたため、自分でStencilのテンプレートを書いて実装してみました。

今回初めてSourceryとStencilを使用する際に、分からないことが多く時間がかかったため、SourceryとStencilについてメモを書いておきます。

Sourcery

Sourceryは、Swiftでメタプログラミングを行うためのツールです。

詳細はリポジトリを見ていただければと思います。今回はHomebrewでinstallして、CommandLine上で実行しました。

$brew install sourcery

Command Line Option

Sourceryには、多くのOptionがあります。これらのOptionファイルは .sourcery.yml に書くこともできますが、今回は手作業で実行したかったため、Command Line上でOptionを入力しています。

必要になったOptionについて説明を記載しておきます。

  • --sources: SourceファイルのPathを記載します。複数指定可能。
  • --templates - TemplateファイルのPathを記載します。複数指定可能。
  • --output: OutputファイルのPathを記載します。
  • --args: その他の入力となるパラメータを入れることができます。(--args arg1=value,arg2 のような形)。 Argsはテンプレート内でargument.nameのような形でアクセスできます。

これらのOptionを組み合わせて、下記のように書くことで、現在使用されているファイルに対してOutputすることも可能です。

$sourcery --sources SampleView.swift --templates Sample.stencil --output SampleView.swift --args title=String,subTitle=String

Inline code generation

上のbashコマンドを入力すると、コードをファイル内に挿入するのではなく、ファイル全体が書き換わってしまいます。

コードは基本的にファイル単位で生成されますが、下のようなコメントをStencilファイル内に入れることで、コードを行間に入れることができます。

// sourcery:inline:{{ type.name }}.TemplateName
// sourcery:end

SwiftファイルにもStencilに対応して下のようなコメントを書く必要があります。このコメントの間にCodeを入れることができます。

// sourcery:inline:SampleView.TemplateName
// sourcery:end

参考:github.com

テンプレート作成

上で少し言及していましたが、SourceryではTemplateファイルを利用してコードを生成します。

独自のテンプレートを作成するには、Stencilというテンプレート言語でコードを書く必要があります。

StencilのGithubはコチラです。内容は上のサイトとほぼ同じです。

TemplateファイルのHello World(とりあえず実行してみる)

Stencilファイルを作り、下のようなコードを書いて、Sourceryのコマンドで引数を渡してコードを実行してみます。

StencilでSourceryから受け取るパタメータの形式については、Sourecry Reference内のWriting TemplateおよびTypesを参考にしました。

{% for type in types.classes %}
// sourcery:inline:{{ type.name }}.TemplateName
{{ type }}
// sourcery:end
{% endfor %}

実行するコマンドは以下。

$sourcery --sources Samples/Sample1.swift --templates Stencils/Sample1.stencil --output Samples/Sample1.swift

下のようなClassに関するtypeの情報が出力されることが確認できます。

class Sample1View: UIView {
    // sourcery:inline:Sample1View.TemplateName
    Class: module = nil, typealiases = [:], isExtension = false, kind = class, accessLevel = internal, name = Sample1View, isGeneric = false, localName = Sample1View, variables = [], methods = [], subscripts = [], initializers = [], annotations = [:], staticVariables = [], staticMethods = [], classMethods = [], instanceVariables = [], instanceMethods = [], computedVariables = [], storedVariables = [], inheritedTypes = ["UIView"], containedTypes = [], parentName = nil, parentTypes = AnyIterator<Type>(_box: Swift._IteratorBox<Swift._ClosureBasedIterator<SourceryRuntime.Type>>), attributes = [:], kind = class, isFinal = false
    // sourcery:end
}

引数を渡す

今回行いたいことは、title, subTitleのような文字列に対してコードを自動生成することです。

そのためSourceryのoptionの1つであるargsを利用します。argsはStencil内でargumentとして使用することができます。

Stencilファイルは、勉強のためにあえて改行や空白を入れて書いてみます。

{% for type in types.classes %}
// sourcery:inline:{{ type.name }}.TemplateName
{{ argument }}

    {% for key, value in argument %}
    {{ key }}: {{value}}
    {% endfor %}
// sourcery:end
{% endfor %}

実行するbashファイルは以下。

$sourcery --sources Samples/Sample2.swift --templates Stencils/Sample2.stencil --output Samples/Sample2.swift --args title=String,subTitle=String

出力結果は以下のようになります。

class Sample2: UIView {
    
    // sourcery:inline:Sample2.TemplateName
    ["subTitle": String, "title": String]

        subTitle: String
        title: String
    // sourcery:end
}

argumentを受け取って表示できていることを確認できたと思います。

また、stencilファイルで改行や空白を入れたところにも、結果が反映されていることが分かったと思います。

if, for文などの構文を書く

Stencilではif, for文などの構文はTagsという概念で扱うことができるようです。

コチラに、全ての対応しているTagsが記載されています。

上記の構文を利用して、下記のようなStencilファイルを作成します。

{% for type in types.classes %}
// sourcery:inline:{{ type.name }}.TemplateName
{% for key, value in argument %}
{% if value == "String" %}
@IBOutlet private var {{ key }}Label: UILabel!
{% endif %}
{% endfor %}
{% for key, value in argument %}
{% if value == "String" %}

public var {{ key }}: String? {
    get {
        {{ key }}Label.text
    }
    set {
        {{ key }}Label.text = newValue
        {{ key }}Label.isHidden = newValue == nil 
    }
}
{% endif %}
{% endfor %}
// sourcery:end
{% endfor %}

bashファイルは下記。

$sourcery --sources Samples/Sample3.swift --templates Stencils/Sample3.stencil --output Samples/Sample3.swift --args title=String,subTitle=String

出力されるSwiftファイルは下記です。今回やりたかったことが実現できました。

class Sample3: UIView {

    // sourcery:inline:Sample3.TemplateName
    @IBOutlet private var subTitleLabel: UILabel!
    @IBOutlet private var titleLabel: UILabel!

    public var subTitle: String? {
        get {
            subTitleLabel.text
        }
        set {
            subTitleLabel.text = newValue
            subTitleLabel.isHidden = newValue == nil 
        }
    }

    public var title: String? {
        get {
            titleLabel.text
        }
        set {
            titleLabel.text = newValue
            titleLabel.isHidden = newValue == nil 
        }
    }
    // sourcery:end
}

最後に

Sourceryはコードを生成する強力なツールであり、Stencilテンプレートを使用することでコードを自動生成できることを理解できました。

繰り返しになりますが、上記のコードは以下のリポジトリに記載されています。

github.com

興味があれば、下のブログも併せて読んでいただけますと幸いです。

kuro-46.hatenablog.com