20200921のiOSに関する記事は12件です。

iOSDC 2020 印象に残ったトークまとめ?

はじめに

iOSDC 2020に参加しました!:hugging::sparkles:
https://iosdc.jp/2020/
初のオンライン開催でしたが、会社の仲間と一緒にわいわい見るのは楽しかったです。
SwiftUI関連のトークが多くて、昨今のトレンドを反映してるなぁという印象を受けました。

どれも勉強になるトークでしたが、印象に残ったトークを備忘録としてまとめておきたいと思います。
スライド画像多めでまとめているので、ざーっと流し見するも良し、気になるものがあればURLから飛んで詳細を見るも良しです:grin:(YouTubeのURLもアップされ次第追記予定です)

Day1

iOSには無いmacOS独自機能をCatalystで実装する

概要

内容

スクリーンショット 2020-09-21 17.44.57.png

macOSはもともとAppKitだけだったが、一部にUIKitが利用できるようになった
macOSとiOSの違いも吸収されている
Xcodeでチェックを入れるだけでMacでも使えるようになる

スクリーンショット 2020-09-21 17.47.31.png
macOS独自機能
* メニュー
* タッチバー
* ツールバー

スクリーンショット 2020-09-21 17.49.40.png
タッチバーの一部でSwiftUIと相性が悪いところがあった
SwiftUIを利用したい場合はmacOSネイティブのアプリを開発したほうが良いかも?

スクリーンショット 2020-09-21 17.55.24.png

ひとこと所感

まだ全然macのこととか考えられていませんでしたが、このトークを聞いて、やるべきことのイメージが少し持てるようになりました!

そろそろCombine

概要

内容

スクリーンショット 2020-09-21 18.08.18.png
Combineとは?
連続した多種類の非同期処理などを単一の方法で扱う宣言的なAPI

スクリーンショット 2020-09-21 18.08.49.png
iOSの非同期処理は難しい
* タイミング次第で結果が変わってしまう
* どこで処理を実装しているのか探すのが一苦労

スクリーンショット 2020-09-21 18.09.18.png
非同期を扱う方法もたくさんある

スクリーンショット 2020-09-21 18.09.38.png
Combineでは、あらゆる処理をPublisherにすることで、非同期をスマートに扱うことができる
他のフレームワークとの連携も容易になる

スクリーンショット 2020-09-21 18.43.55.png
宣言的とは?
指示的:「冷蔵庫からペットボトルの水を取り出して、コップに注いで、飲みます」
宣言的:「受け取った水を飲みます」

スクリーンショット 2020-09-21 18.44.05.png
宣言的のメリット

スクリーンショット 2020-09-21 18.10.52.png
Combineは宣言的なので、コードも短くスッキリする

ひとこと所感

Combineってなんぞや?という状態でしたが、RxSwiftと概念的・用語的に似ている部分があるとわかって少し安心しました。勉強せねば…

新規機能開発からモジュール分割を始めてみる

概要

内容

スクリーンショット 2020-09-21 19.03.38.png
機能が増えるとコードベースも巨大化して開発効率の低下につながるので、モジュール分割を行った

スクリーンショット 2020-09-21 19.04.12.png
既存アプリに取り入れたいけど、依存が深くてどこから切り出せばいいのか分からない

スクリーンショット 2020-09-21 19.11.10.png
大規模リファクタが必要だけど、新規機能開発も止めるわけには…(わかりみ)

スクリーンショット 2020-09-21 19.11.18.png
そこで、新規機能からモジュール分割する前提で設計することにした

スクリーンショット 2020-09-21 19.04.27.png
手順はこちら

スクリーンショット 2020-09-21 19.04.41.png
独立した新規画面であったり、既存ドメイン仕様への依存が少ない場合はモジュール分割しやすい

スクリーンショット 2020-09-21 19.06.44.png
スクリーンショット 2020-09-21 19.14.41.png
メリットもあるけど、デメリットもある
コンフリクト回避のために、XcodeGenを先に導入すべきだった
抽象度が上がるので、実装がどこにあるのかわかりずらくなり、新しく入ったメンバーが困る場面もあった

ひとこと所感

新規機能からモジュール分割するの良さそう。いざやろうとしたら「どこから切り出していいか分からない」と絶対なると思うので、その際に再度見返したいトークでした!

効率よくUIKitからSwiftUIへ移行する

概要

内容

スクリーンショット 2020-09-21 19.25.39.png
SwiftUIを使ったほうがいい理由
* SwiftUIではより多くのことが、より少ないコードでできる
* 色んな機能がUIKitよりも楽に実装できる(アクセシビリティ、ダークモード/ライトモード、標準レイアウトなど)
* すぐにUIKitがなくなるわけではないが、いずれSwiftUIでやるしかない場合が出てくるかもしれない

スクリーンショット 2020-09-21 19.29.25.png

SwiftUIへの移行を阻むもの(待ったほうがいい理由)
* UIKitほど歴史がないのでバグがあったり、OSによって違いがあったりする
* UIKitと混ぜて使うのは苦労する

スクリーンショット 2020-09-21 19.33.12.png
SwiftUIへの移行をどこから始めたらいいか?
* SwiftUIからObjective-Cが直接使えない(逆もしかり)ので、早期にSwift化しておく
* 移行が重なると面倒なので、Swiftの最新バージョン、機能を使っておく

スクリーンショット 2020-09-21 19.38.08.png
事前にUIKitをモダンな使い方にしておくのも大事。
* AutoLayoutを使う
* SefeAreaを使う
* 巨大なStoryboardにせず、再利用しやすくするためコンポーネントに分ける(部分的にSwiftUIに移行しやすくなる)
* 宣言的なUIKit APIを使う(StackView、Compositional Layoutsなど)

スクリーンショット 2020-09-21 19.46.29.png
* 本番のアプリにいきなりSwiftUIを導入するのではなく、同じ分野のアプリを小さい規模でプロトタイプとして作ったほうが、メリットデメリットがわかりやすい
* その中で、SwiftUIに向いてない画面が特定できるので、必要に応じてデザインの変更を検討する
* 既存のアプリ内で、iOS13限定の機能をSwiftUIで作るのもおすすめ

スクリーンショット 2020-09-21 19.50.16.png
SwiftUIに向いているアーキテクチャはこの3つ
* Redux
* TCA
* MVVM

スクリーンショット 2020-09-21 19.51.19.png
Tips
* 1つの画面の中でSwiftUIとUIKitを混ぜすぎないこと
* TableViewなどシンプルな画面から移行すること
* 急ぎすぎないこと
* SwiftUIは深いところまでCombineに依存しているので、SwiftUIを正しく使うにはCombineの知識も必要になる

ひとこと所感

すぐに使える知見がたくさん詰まったトークでした!いずれSwiftUIを本番に導入する日が来るかも?なので、計画的に準備していきたいと思いました。

Day2

Xcode Preview でUIKitベースのアプリ開発を効率化する

概要

内容

スクリーンショット 2020-09-21 20.14.55.png
Xcode PreviewsはXcode11で導入されたプレビュー機能

スクリーンショット 2020-09-21 20.15.23.png
SwiftUIでの実装ではXcode Previewsが使えるので、リアルタイムにレイアウトを確認することができる

スクリーンショット 2020-09-21 20.16.37.png
UIKitベースの実装ではコード修正のたびにSumilatorを起動する必要があって不便…

スクリーンショット 2020-09-21 20.17.05.png
現状多くのアプリがまだUIKitベースなので、そこにXcode Previewsを組み込んだ話

スクリーンショット 2020-09-21 20.18.53.png
スクリーンショット 2020-09-21 20.19.00.png
Previewコードを書くときに使うプロトタイプたち↑

スクリーンショット 2020-09-21 20.19.11.png
外部から状態を注入できるような、「PreviewableなView」にしておけると良い

スクリーンショット 2020-09-21 20.19.47.png
メルペイでの活用例
短いテキストの場合、長いテキストの場合のレイアウト崩れがないか、一度にチェックできる

スクリーンショット 2020-09-21 20.20.06.png
異なる画面サイズの端末だったり

スクリーンショット 2020-09-21 20.20.21.png
多言語対応している場合は、言語ごとに見たりもできる

スクリーンショット 2020-09-21 20.21.20.png
iOS13未満もサポートしている場合はどうしたらいいのかというと、2つのやり方がある
* マクロを使ってPreviewコードをビルドから除外する
* Preview専用のターゲットを追加する

スクリーンショット 2020-09-21 20.21.51.png
Xcode Previewsを導入して既存アプリをSwiftUIの開発スタイルに寄せておくと、SwiftUIへの移行がよりスムーズになるかも?

ひとこと所感

いろんな状態のViewを一度に表示・確認できるのはめちゃくちゃ便利だなと思いました!毎回Simulator起動するのめんどうなので、ぜひ導入したい…

XCUITestのつらさを乗り越えて、iOSアプリにUITestを導入する

概要

内容

スクリーンショット 2020-09-21 20.37.37.png
UITestのつらさ。いつの間に壊れている(悲しい)

スクリーンショット 2020-09-21 20.37.50.png
UIは変わりゆくものだから…

スクリーンショット 2020-09-21 20.38.22.png
もともと手動テストのみだったが、QAの工数を減らす手段の一つとしてUITestを導入

スクリーンショット 2020-09-21 20.38.51.png
新機能と既存機能、どっちを対象にするか?という話があるが、メルペイでは既存機能に対象を絞った
UITestは繰り返し実施されるテストと、安定稼働している機能に対して行うと効率が良い

スクリーンショット 2020-09-21 20.39.06.png
リグレッションテストに組み込み、効率化を実現した

スクリーンショット 2020-09-21 20.39.50.png
XCUITestに入門する上で押さえたい3つの重要なクラス

スクリーンショット 2020-09-21 20.40.25.png
実際のコードはこんな感じ
テスト対象アプリを定義 → UI要素検索 → UI要素取得 → UI操作 → UI検索 → 表示チェック

スクリーンショット 2020-09-21 20.41.08.png
UITestを書いていると、重複したコードがあることに気づく
まとめたい

スクリーンショット 2020-09-21 20.41.14.png
Page Object Patternというデザインパターンが有効

スクリーンショット 2020-09-21 20.41.39.png
3つの大きなメリットがある

スクリーンショット 2020-09-21 20.41.53.png
PageObjectable プロトタイプを定義して、重複するコードをまとめる

スクリーンショット 2020-09-21 20.43.32.png
UITestは並列化実行しても時間がかかる

スクリーンショット 2020-09-21 20.43.42.png
メルペイでは夜間実行したり、GitHub ActionsのLabelトリガーを使ったりしている

ひとこと所感

苦難に立ち向かった知見が詰まっていて、これからUITestを始める人にとってぴったりのトーク内容でした。具体的なコードがあったのでとてもわかりすかったです。

Webとネイティブアプリの付き合い方を改めて考える

概要

内容

スクリーンショット 2020-09-21 21.03.30.png
スクリーンショット 2020-09-21 21.03.36.png
新しいサービスを作るようになったときのあるある

スクリーンショット 2020-09-21 21.04.19.png
Webとネイティブアプリの比較
* アプリ単体での起動・動作
* オフライン動作
* プッシュ通知
などはネイティブアプリにしかない機能

スクリーンショット 2020-09-21 21.04.46.png
PWAとは?
ネイティブアプリっぽく動くwebアプリケーション

スクリーンショット 2020-09-21 21.05.09.png
PWAも比較
インストールが可能なので、アプリ単体で立ち上げることができる

スクリーンショット 2020-09-21 21.05.25.png
App Clipsも比較

スクリーンショット 2020-09-21 21.05.44.png
(ここから先はあくまで私見によるもの と伝えつつ)
ネイティブアプリと同じ体験をWebでも提供することは可能か?
プッシュ通知や最新のiOS技術のことを考えると難しいのでは

スクリーンショット 2020-09-21 21.06.05.png
現状、プッシュ通知はネイティブアプリ一択

スクリーンショット 2020-09-21 21.06.17.png
どういうときにWebを活用したら良いか?
* まずは広く機能を提供したいとき
* プッシュ通知がいらないとき
* 最新のOS機能がいらないとき

スクリーンショット 2020-09-21 21.06.35.png
専門知識が必要だったり、iOS/Androidセットで作らないとユーザーが減ってしまうことなどがネイティブアプリのデメリット

スクリーンショット 2020-09-21 21.06.45.png
Googleからの検索流入が見込める、両OSに対応できるのはWebのメリット

ひとこと所感

アプリエンジニアなのでネイティブを推したい気持ちはありつつ、Webのメリットも知った上で正しく技術選択をしていく必要がありますよね…。深く考えさせられるトークでした!

テストコードが増えるとバグは減るのだろうか? - 「0% → 60.3%」で見えた世界の話

概要

内容

スクリーンショット 2020-09-21 21.22.37.png
仕様は決まった!と言っても、実は完全には決まっていないことも…(あるある)

スクリーンショット 2020-09-21 21.23.45.png
テストを書くことで仕様の理解が深まる

スクリーンショット 2020-09-21 21.23.55.png
テストには動的テストと静的テストがある

スクリーンショット 2020-09-21 21.24.15.png
仕様書のチーム内レビューは静的テストに分類され、動的・静的を併用することで大きな成果が得られる

スクリーンショット 2020-09-21 21.26.42.png
UI,ロジック、APIなどそれぞれテストが書けていれば、不具合の原因特定も容易になる

スクリーンショット 2020-09-21 21.26.58.png
モックが書けていれば、意図しないレスポンスが返ってきたケースなども再現できる

スクリーンショット 2020-09-21 21.27.47.png
スクリーンショット 2020-09-21 21.27.56.png
しかし、適切なテストを書くには、経験や慣れ、様々な知識が必要

スクリーンショット 2020-09-21 21.29.05.png
長い目で見たら役に立つが、開発時点では間違いなく工数がかかる
それを許容できる開発環境が必要

スクリーンショット 2020-09-21 21.39.32.png
現状の課題をよく見つめ、様々なテスト手法がある中で最適なテスト方法を選択すること
費用対効果があるかを考えるのがポイント

スクリーンショット 2020-09-21 21.29.26.png
スクリーンショット 2020-09-21 21.29.36.png
テストコードが増えるとバグが減るか?
→ △

スクリーンショット 2020-09-21 21.29.46.png
テストコードにバグがないなど複数の前提条件を満たすとき、バグは減らせる

ひとこと所感

△というのがリアルでいいですね。経験が浅いとテストコードのテストコードが必要になってきそうで、テスト難しいなって改めて思いました。

おわりに

(トークの内容が濃すぎて)気づいたら大作になってしまいました!

見たかったけど見れなかったトークのスライドをここに置いておきます。
YouTube動画が配信されたら見るぞ〜:yum:

iOSDC本当に楽しかったです!みなさまお疲れ様でした!!!!:tada:
スクリーンショット 2020-09-21 18.28.17.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xcode Previews入門 〜コードを書かないで画面を作る〜

はじめに

SwiftUIを使うことのメリットとして真っ先に上がるプレビュー機能ですが、実際にプレビュー機能を使いこなしている人は少ないように感じています。(自分含めて)

そこで、WWDC2020のセッションから学んだプレビュー機能をを使いこなせるようにし爆速にアプリの構築ができるようPreviewの基本やショートカットを記事にします。

今回は、Previewの基本的な使い方をご紹介させていただきます。

環境: Xcode12, macOS Catalina ver 10.15.6

:コマンド(Command)キー
:オプション(Option)キー
Ctrl:コントロール(Control)キー

Previewsの基礎

SwiftUIのアプリを起動すると以下のような画面になっています。

プレビューの実行・再実行 ⌘ + ⌥ + P

右上にあるResumeをクリックする、もしくはショートカットキー⌘ + ⌥ + Pでプレビューを実行することができます。
これは頻繁に使い開発効率が上がるので是非覚えておくと良いと思います。
Viewが反映されなくなったらとりあえずエラーが出ていない限り⌘ + ⌥ + Pで再描画されます。

スクリーンショット 2020-09-21 2.06.01.png

ビルドが走りますので、しばらく待つと以下のようにPreviewが表示されます。

スクリーンショット 2020-09-21 2.18.36.png

このままでも良いのですが、実際のViewにのみ注目したい場合はpreviewLayout(_:)を使うと良いです。PreviewProviderのContentViewに.previewLayout(.sizeThatFits) としてあげることでViewのみのpreviewにすることができます。

スクリーンショット 2020-09-21 2.28.00.png

Previewの表示・非表示

Previewは表示・非表示を切り替えることができます。
以下の図の①の箇所をクリックした後、②のCanvasをクリックすることで切り替えることができます。
もしくは⌘ + Shift + Enterでも可能です。

スクリーンショット 2020-09-21 2.39.06.png

Library

ライブラリーにはSwiftUIのViewとModifierがあります。
ライブラリーを起動するにはXcodeの右上にある+ボタンをクリックする。
もしくは⌘ + Shift + Lで表示させることができます。

スクリーンショット 2020-09-21 2.34.51.png

表示されるLibraryはいくつか種類があり、SwiftUIのViewを使いたいならViewsを選択しましょう。
種類は左から、Views, Modifiers, Snippets,Media, Colorとなっております。
状況に応じて使い分けましょう!

スクリーンショット 2020-09-21 2.45.11.png

これらのViewをPreviewの挿入したい箇所にドラッグ&ドロップすることでPreviewを最新の状態に保ちながら修正することができます。

このときドロップする箇所によって、自動的にVStackHStackにしてくれます。

Previewをクリック

previewに表示されたTextの文字列をクリックすると、TextのViewを選択することができます。

スクリーンショット 2020-09-21 11.54.01.png

Previewをダブルクリックするとエディタにフォーカスが移動し、テキストか画像かに関係なくViewの要素を簡単に変更できます。

スクリーンショット 2020-09-21 11.54.23.png

修正された内容も即時反映されます。

スクリーンショット 2020-09-21 12.07.55.png

Viewの複製 ⌘ + D

Preview画面でViewをクリックしている状態で⌘ + Dとすると選択したViewを複製することができます。

スクリーンショット 2020-09-21 13.29.55.png

画像の追加

PreviewとLibraryの機能を使うことで画像の追加がとても簡単に行えるようになります。

Assets.xcassetsファイルにお好きな画像をFinderなどからドラッグ&ドロップで置いておきます。

スクリーンショット 2020-09-21 17.10.16.png

⌘ + Shift + Lでライブラリを起動し、右から二番目のMediaをクリックすると、既に追加されている画像(drink_tapioka_tea_man)が表示されています。

スクリーンショット 2020-09-21 17.11.29.png

この画像をドラッグ&ドロップでHello World!LGTMの間におきます。

スクリーンショット 2020-09-21 17.16.09.png

そうすることで、以下のように表示されます。
SwiftUIでは実際のサイズの画像を表示するので、デカデカと表示されてしまいました。

スクリーンショット 2020-09-21 17.16.17.png

インスペクタの利用 ⌘ + ⌥ + 0

以下の図の箇所をクリックする、もしくは⌘ + ⌥ + 0でインスペクタを開きます。

スクリーンショット 2020-09-21 17.36.03.png

Previewの画像をダブルクリックして選択したのち、一番右の Attributes inspectorを選択します。

スクリーンショット 2020-09-21 17.43.51.png

Add Modifierをクリックすると現在の要素にあった推奨されるModifierが表示されます。

Resizableを選択します。

スクリーンショット 2020-09-21 18.06.30.png

随分と縦長になってしまったので、asaspectRatiopectを設定しましょう!

スクリーンショット 2020-09-21 18.09.56.png

contentModefitにしておきます。

スクリーンショット 2020-09-21 18.10.19.png

サイズを調整していきます。すでに見えているFrameSizeに適当なサイズを入れます。

スクリーンショット 2020-09-21 18.45.16.png

そろそろLGTMも文字が二つあるのは邪魔なので、Viewを選択した状態で⌘ + Deleteで削除します。

そしてTextの代わりに今までの要領で好きな画像を以下のように表示させてみましょう。

スクリーンショット 2020-09-21 19.00.02.png

アクション ⌘ + クリック

選択したViewを様々なコンテナ(VStack, HStack, etc )に埋め込むことができるアクションを利用します。
対象のViewをを押したままクリックでアクションを呼び出すことができます。

スクリーンショット 2020-09-21 19.01.37.png

HStackに格納してみました!

スクリーンショット 2020-09-21 19.03.17.png

これを繰り返し表示させてみます。再び、を押しながらクリックしてRepeat をクリックします。

スクリーンショット 2020-09-21 19.03.17.png

コーヒーが5個に増殖させることができました!便利ですね!

スクリーンショット 2020-09-21 19.05.23.png

テキストを目立たせる

タイトルとしてHello, world!を目立たせていきたいと思います。

インスペクタを表示し、Fontの項目をいじります。

スクリーンショット 2020-09-21 19.13.54.png

Fonttitle, Weightbold, Colorpurpleにしてみました。
お好きな形にしてみましょう!選択した色が即時反映されます。

スクリーンショット 2020-09-21 19.16.44.png

Modifierを無効にする

Modifierをクリアするには、コントロールの隣にある円形のインジケーターをクリックします。

スクリーンショット 2020-09-21 19.20.32.png

色を元に戻すことができました。

スクリーンショット 2020-09-21 19.24.32.png

Previewキャンバス上でインスペクタを呼び出す Ctrl + ⌥ + クリック

Ctrl + ⌥押しながら対象のViewをクリックすることで、右側にインスペクタの領域を取られる事なくModifierの値を変更することが可能です。

スクリーンショット 2020-09-21 19.26.33.png

プレビューの複製

ワンクリックでプレビューの複製が行えます。

スクリーンショット 2020-09-21 19.53.23.png

とても便利です。

スクリーンショット 2020-09-21 19.53.31.png

Previewをダークモードにする

Inspect Previewをクリックし、Color SchemeDarkにします。

スクリーンショット 2020-09-21 19.56.34.png

ダークモードになっていることが確認できます。

スクリーンショット 2020-09-21 20.10.50.png

 おわりに

XCodeのPreview機能の触りを実際に動かしながら解説しました。
次回は、自分で作ったカスタムViewやカスタムModifierを作る記事を書こうと思います。

参考

Visually edit SwiftUI views - WWDC 2020 - Videos - Apple Developer

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】プロビジョニングプロファイル更新手順

前書き

プロビジョニングプロファイルの有効期限(1年)が過ぎた場合は更新が必要になるので、更新手順のメモ書き?

証明書のダウンロード(すでに証明書をキーチェーンに登録している場合は不要な手順)

下記の記事の「④ 証明書(Certificate)の作成」手順通りに証明書を作成して登録しておく
https://qiita.com/Labi/items/3b71b8f5ef065904c1de

プロビジョニングプロファイルの更新

  1. Apple Developer Program へアクセス
    https://developer.apple.com/jp/programs/

  2. Certificates, Identifiers & Profiles を選択
    1.png

  3. Profilesから、更新するアプリを選択してEditを押下
    2.png

  4. Editを押下
    3.png

  5. Certificatesから証明書を選択して、Saveを押下
    4.png

  6. Downloadを押下して、プロビジョニングプロファイルをダウンロード
    5.png

  7. ダウンロードしたプロビジョニングプロファイルをダブルクリックして、Xcodeに登録

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSDC2020初参加して

iOSDC参加しました

今回自分はiOSDC初参加しまして、参加した記事を書くまでがiOSDCだということで

あまりまとまった内容はかけないですが、とりあえず感想だけでもと思い書いています。

iOSDCとは

iOSDCは、iOS周辺技術に関わるエンジニアが、技術的なノウハウを共有するためのイベントです。

2016年から開催されていて今年で4年目のようです。

iOSDC2020公式サイト

今年は初のオンライン開催でした。

ノベルティの量

下世話な話ですがノベルティの量すごい!

スマホスタンドからマスクケースからお水まで。

色々とあってすごいなーと思いました。

参加しての感想

本題の参加しての感想ですが、

いや〜楽しかった!

自分はエンジニアとしてまだまだで、正直聴きながらだと分からずに

タイムシフトで見返してやっと分かる、みたいなものもたくさんありました。

ただ、これは自社の別の現場で使えそうだな〜とか

自分自身試してみたい技術やそれを使って作りたいものも発見というか気づきを得れてとても有意義なものでした!

あとは、スピーカーの皆さんのスライド作るの凝っていたり、真面目過ぎずにクスッとするような作りのものもあって

発表資料としての参考にもなりました。

本当にお疲れ様でした!

もちろん、このカンファレンスを開催してくださった方々もお疲れ様でした!

さいごに

まだまだ、みれていない視聴させていただいたのに、フィードバックできていないトークもありますので

そちらにフィードバックを返したり、発表資料をTwitter等で上げていただけているので資料まとめみたいな記事を

自分用に、備忘録的にまた上げたいなと思います。(トークで気になった記事なども)

勢いで書いているので拙い文章がさらに拙く拙く拙くネストしていっていますが

自分のiOSDC Japan 2020 はいったんこれで区切りとします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

辞書について

1.はじめに


前回配列について説明したので、これまた重要な要素である辞書について説明していこうと思います。前回の配列について解説した記事のURLは以下になります。
(URL:https://qiita.com/0901_yasyun/items/6cc4c0175082e9b63d31)

2.辞書とは


辞書とは、配列と同様に複数のインスタンスを格納できるコレクションの1種です。Swiftの辞書はDictionary型ですが、配列と同様、構造体として実現されており、変数への代入などの動作によって、必要に応じて新しいインスタンスが作成されます。
1つの辞書には、キーと値の組を複数個格納でき、キーから値を検索して参照、格納を行うので、1つの辞書に含まれるキーはすべて異なります。
キーに対応する値は1つですが、異なるキーに同じ値が設定されていても問題はありません。
ただし、キーと値はそれぞれ同一の型で統一されている必要があります。

var y = ["Swift":2014, "Objective-C":1983]

この例ではString型をキーとし、Int型を値とするエントリを2個もった辞書インスタンスを生成しています。

型のみを宣言する場合は、次のように記述します。

var y : [String : Int]

次のように記述すると、String型をキーとし、Int型を値とする辞書のインスタンスが代入されますが、格納されている要素は0個です。

var y = [String : Int]()  // イニシャライザの呼び出し

空の辞書を表すには[:]と記述します。変数を型宣言し、空の辞書を代入するように記述しても、先ほどと同様な初期化が行われます。

var y : [String : Int] = [:]  // 型を指定した変数に空の辞書を代入

なお、辞書型はパラメータ付き型指定で次のように記述することもできます。

var y : Dictionary<String, Int>  // [String:Int]と同じ意味
var y : Dictionary<String, Int>()  // イニシャライザの呼び出し

3.辞書へのアクセス


すでに要素をもつ辞書型の定数や変数に対し、キーを指定して値を取り出すことができます。そのために、配列にアクセスするときと同じように[]を使い、[]の中にキーを指定します。ただし返り値はオプショナル型で、存在しないキーを指定するとnilが返ってきます。
if-let文を使用した次に例を見てください。

var y = ["Swift":2014, "Objective-C":1983]
if let v = d["Swift"] { print(v) }    // 存在するキーなので"2014"と出力
if let v = d["Ruby"] { print(v) }     // 存在しないキーなので出力なし

この例では辞書は[String:Int]型なので、返される値はInt?型です。
辞書に新しい値を追加するには、[]の中にキーを指定して値を代入します。
また、既に存在しているキーの要素を削除するには、そのキーを指定してnilを代入します。以下の例を見てください。

var e = ["Ruby":1995]
print(e)   // ["Ruby": 1995]と出力
e["Java"] = 1995
e["Python"] = 1991
print(e)   // ["Java": 1995, "Ruby": 1995, "Python": 1991]と出力
e["Java"] = nil
print(e)   // ["Ruby": 1995, "Python": 1991]と出力

ここまでの例ではキーが文字列で、値が整数でしたが、他の型でももちろん大丈夫です。
しかし、Swiftの基本的なデータ型の中で辞書のキーとして利用できるものは、各種整数型、実数型、Bool型、文字列、文字などです。実数もキーにできますが、実数値には誤差がつきものなので注意が必要です。

4.辞書の比較


辞書同士を比較する際には、演算子「==」と「!=」が使えます。互いに同じキーの集合を持ち、同じキーに等しい値が割り当てられている辞書を等しいと判断します。以下の例を確認してください。

var a = ["one":"I", "two":"II", "three":"III"]
let b = ["two":"II", "one":"I"]
a == b   // false
a["three"] = nil
a == b   // true

5.辞書から要素を取り出す


辞書は付属型としてのキーを表すKey型、値の型を表すValue型を持ちます。
またキーだけからなるコレクション、値だけからなるコレクションを提供するプロパティがあり、それぞれの型はKeys型、Values型となっています。
辞書自体もコレクションなので、要素を表す型であるElement型、添字の型であるIndex型を持っています。Element型は次のタプルの別名となっています。

(key: Key, value: Value)

for-in文を使って、辞書から要素を1つずつ取り出すことができます。その際、取り出されるのが上記のタプルです。以下の例を確認してください。

var dic = [String: Int]()
var n = 1
for ch in "ありがとう" {
    dic[String(ch)] = n; n += 1
}

print(dic)  
for t in dic {  // タプルで取り出す
    print("\(t.key)=\(t.value)", terminator:" ")
}

print()
for (k, v) in dic {  // キーと値を取り出す
    print("\(k)=\(v)", terminator:" ")
}

print()

これを実行するとこのような結果が得られます。

["が": 3, "と": 4, "り": 2, "う": 5, "あ": 1]
が=3 と=4 り=2 う=5 あ=1
が=3 と=4 り=2 う=5 あ=1

この結果より、辞書の要素が取り出される順番は、実行するたびに異なるのが分かります。
この仕組みについて解説すると少し長くなってしまうので、ひとまず取り出される順番はランダムだということを覚えておいてください。
気になる方は、ハッシュ値などについて調べれば仕組みが分かると思います。

6.おわりに


今回は辞書の基本的な仕組みと主な使い方について解説しました。
今回の記事ではプロパティやメソッドについて、詳しく説明することができなかったので、いつかまた説明する記事が書ければと思います。
読んでくれた方、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】機械学習(=ML)とAIとの違いを学んだのち、Core ML を実装してみる。

機械学習(= ML) とは?

機械学習は英語で、「Machine Learning
簡単に言うと、以下を指します。

  • 『AIが自律的に物事を学ぶための技術
  • 『機械に大量のデータ・パターン・ルールを学習させることにより、判別や予測をする技術』

ML.png

ML は、意外と歴史のある AI 分野のひとつ

機械学習はAIという概念の中の、1つの分野です。

1959年、機械学習の「父」とされている Arthur Samuel は、
機械学習を以下のように定義しています。

「明示的にプログラムしなくても学習する能力」を、コンピュータに与える研究分野。
“Field of study that gives computers the ability to learn without being explicitly programmed”
-- Arthur Samuel --

「AI=機械学習」ではなく、
AI > 機械学習 > ディープラーニングのイメージです。

スクリーンショット 2020-09-21 15.13.26.png

なぜ近年、「機械学習」が大きな話題となっているのか

必要性

これまで人間はデータを分析し、そのデータに基づいてシステムや手順を変更してきました。

しかし、世界のデータ量が増大し、管理できなくなってきており、
データから学習し、それに応じて適応できる自動システムが必要とされています。

技術の進歩

  • AI技術の進歩
  • 大量データの出現
  • コンピューター処理能力の向上

使用例

  • アマゾンエコーは、機械を使用してトレーニングされた音声テキストと音声認識を使用します
  • 疾患の早期発見のために、医学界でも使用されています
  • 自動運転車は、機械学習に依存して自分自身を運転します

AI と ML と DL の違い

人工知能【Artificial Intelligence】

人間のような知能をもつアルゴリズム。

アルゴリズム ...
「何を」「どのような順番で」「何に対して行うのか」を記述したもの。

機械学習【Machine Learning】

AIが自律的に物事を学ぶための技術。

ディープラーニング(= 深層学習)【Deep Learning】

多層のニューラルネットワークを活用し、物事の特徴を抽出する技術。

因みに...

機械学習が「人間が判断・調整する」のに対し、
ディープラーニングは「機械が自動的に行う」ことが特徴。

ディープラーニングで、人間が見つけられない パターンやルールの発見、特徴量の設定が可能になり、

人の認識・判断では限界があった 画像認識・翻訳・自動運転 といった技術が飛躍的に上がった。

ディープラーニングと機械学習の違いとは?

機械学習は、3つに分けられる

機械学習の主な手法には、「教師あり学習」 「教師なし学習」 「強化学習」がある。

教師あり学習 (= Supervised Learning)

正解データを元に、入力データの特徴やルールを学習します。

「過去のデータから、将来起こりそうな事象を予測すること」に使われます。

  • 回帰: 連続する数値を予測する
  • 分類: あるデータがどのクラスに属するかを予測する

例.
【回帰】 "天候"と"お弁当の販売個数" の関係を学習し、お弁当の販売個数を予測する、
不動産価値、商品価格、株価、会社業績 etc

【分類】 果物をサイズ別に分ける、画像や音声を種類別に分ける、
電子メールがスパム(迷惑メール)かどうかを判定する etc

教師なし学習 (= Unsupervised Learning)

正解データなしでデータの特徴やルールを学習します。

「データに潜む傾向を、見つけ出すため」に使われます。

  • クラスタリング: データのグループ分け
  • アソシエーション分析: データ間の関連を発見する
  • 異常検知: 人による指導なく、正常なものと不正常なもの(異常)を検知する

例. 【クラスタリング】 FacebookやInstagramの「あなたの友達かも..?」機能
【アソシエーション分析】 紙おむつを購入する人はビールも購入するetc

強化学習 (= Reinforcement Learning)

失敗や成功を繰り返させ、どの行動が最適か学習します。

✅ 成功に対して「報酬」を与えることで学習効率を上げる方法です。

ロボットの歩行制御

ロボットに「歩けた距離」を報酬として与えます。するとロボットは、
歩行距離を最大化するために、自らさまざまな歩き方を試行錯誤します。
そうすることで、歩行可能距離の長いアルゴリズムが構築されます。

囲碁AIの「Alpha Go」

囲碁は手のパターンが膨大過ぎて、既存の最新のコンピュータでも、手を読み切ることは不可能です。
よって、強化学習により、勝ちまでの手を読み切る代わりに、「どの手を打てば勝ちに近づくか」を学習させます。
試合にて失敗や成功を繰り返すと、最適な行動のみを選択するようになります。こうして「Alpha Go」は強くなっていったのです。

参考サイト

© 2020 データアーティスト株式会社
機械学習をどこよりもわかりやすく解説!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSDC2020に参加しました

どうも、ねこきち(@nekokichi_yos2)です

日本最大のiOSカンファレンス、iOSDC、に初参加しました。

iOSDC2020では、ニコ生上で開催され、地方在住の僕にはとてもありがたかったです。

スクリーンショット 2020-09-21 11.18.32.png

また、アーリーバード枠の参加特典も
- iOSDCのパンフレット
- 有名IT企業のステッカー
- 有名IT企業のパンフレット
と充実してて、ありがたかったです。
スクリーンショット 2020-09-21 17.30.39.png

初めてiOSDCに参加して、色々と感じたことを書き連ねていきます。

新たな知見を得られた

iOSのカンファレンスなので
・Storyboardの活用法
・SwiftUIとUIKitについて
・iPadのマルチウィンドウ
iOSアプリ開発で利用できる技術を知ることができます。

新しい技術を知ろうと思ったら、自分でググる必要がありますが、カンファレンスや勉強会ではスピーカーが発表してくれるので、ググる手間が省けます。

しかも、現役のiOSエンジニアの方々が最新の知見を共有してくれるので、書籍やネット記事よりも勉強になります。

また、スピーカーが丁寧に解説してくれるますから、気軽に新/未知の技術を学ぶことが可能です。

他にも、
・アーキテクチャ
・数学を用いたプログラミング
・CI/CDの知見、検証結果
・ビルド、テストの方法
などの様々なテーマが用意されており、Swift以外の言語を扱う方でも学べるセッションが用意されています。

当日の発表内容は、スライドやYouTube動画で公開されるので、見逃しても、何度でも見返すことができます。

高度なスキルがなくても学べる

iOSDCのセッションを理解できるか不安でしたが、全く心配ありませんでした。

もちろん中には、
・普段は滅多に使用しない処理
・ソースコードが実行される背景
などの難しいセッションもありました。

ですが、 iOSDCの開催中にセッション内容を手元で試さなくても、発表者らが丁寧に解説してくれるので、聞き流すだけでも大体は理解できます。

初心者やベテランの方まで幅広い開発者らが学べるセッションが多く、参加するだけでも得したと言えます。

エンジニアが何を意識して開発してるかがわかった

スピーカーは当然、発表内容を事前に検証して、どうなったかを知っています。

そして、スピーカーが検証した過程を解説するとき、どのようにソースコードを設計したのかを話してくれました。

例えば、
・ViewとModelを分ける
・Delegateでイベントを投げる
・関数をオーバーライドする
など。

つまり、セッション内容だけでなく、セッション内容を実装/検証する際にソースコードで意識したことを学べたのです。

ViewControllerでViewの表示とデータのロジックを一緒に書く僕でしたが、どのスピーカーもViewとModelでViewの表示とデータのロジックを分けることを意識してるんだって知れました。

iOSDCに限らず、エンジニアの話を聴くと、エンジニアの考えを知ることができるので、積極的にカンファレンスに参加しようと思えました。

スピーカーになれる自信を得られた

独自で実装/発見した技術を紹介、の他に、既存技術をわかりやすく解説、するセッションも多数ありました。

スピーカーとして情報を発信することは、高いスキルがなければできないと思いがちです。

しかし、レベルの高い内容でなくても
・仕様、実装方法をわかりやすく解説
・10個のアプリを開発して得たこと
・iOSアプリで〜〜を実装してみた
の内容で全然構いません。

なぜなら、
・とある技術に関するまとまった情報が欲しい
・新しいフレームワークの使い方が知りたい
・iOSアプリで〜〜は機能できるのか
など、様々なニーズがあるからです。

事実、
・まだまだ情報が少ないSwiftUI
・よく使うStoryBoardの活用方法
のセッションは、とても興味深い内容でした。

わかりやすくまとめられたセッションを視聴して、
(俺でもスピーカーとして登壇できるんじゃね?)
と思いました。

人の時間は限られており、尚且つエンジニアは忙しいので、ググる余裕すらありません。

ですから、簡単な技術だろうと、スピーカーとして発信することは意義のあることなのです。

まとめ

Firebase同好会、関西のIT勉強会などに参加してきましたが、iOSDCは大規模なカンファレンスだけでなく、数あるセッションから興味あるセッションを選び、自分でスキルアップを目指せる良いイベントでした。

ネット記事や書籍と違い、リアルタイムで現役のエンジニアと繋がれて、情報を得るためのきっかけとして断然アリです。

まだセッション内容を完璧に理解できませんでしたが、iOSアプリ開発の世界を垣間見れて、もっとスキルアップしたいと思えるようになりました。

来年もまた参加します!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ジョブカンのGPS打刻を拒否する方法(iPhone)

先月、勤務先の会社に 『ジョブカン』 が導入されました。調べてみたところ、ジョブカンには GPS打刻機能 があることがわかりました。こういう個人情報が特定される機能はキライなので、拒否していこうと思います。また、この記事は削除される可能性があるので早めに読んで、共有してください。

結論

さっそくGPSを拒否っていきましょう。
お手元にあるiPhoneを急いで開いてください。ジョブカンはまだGPSの中にあります。
私のiPhoneは iOS14 なので、今回はiOS14の設定を書きます。

  1. iPhoneを開いたら 設定 を開きます IMG_5319.PNG
  2. 次に プライバシー を開きます IMG_5321.PNG
  3. 次に 位置情報サービスオフ にします IMG_5320.PNG

この設定を維持したままジョブカンから勤怠打刻をすることで、iPhoneから位置情報を抜き取ることはできなくなりました。
結果、どこから打刻したのか、ジョブカンには特定できません。

参照
https://support.apple.com/ja-jp/HT207092

そもそもジョブカンとは

勤怠管理とシフト作成が同時に行える、クラウド型の業務支援システムです。 複数拠点の勤怠データをリアルタイムに確認・集計・抽出ができ、給与支払いまでの業務を簡素化、迅速化します。

引用元
ジョブカンホームページ

ジョブカンの主なキモイ機能

参考
https://imitsu.jp/matome/attendance-management/8716491050376361

GPS機能

勤怠打刻した時に、その時の位置情報がバレる

勤怠異常の自動検出

シフトから大幅に外れた時間に勤務していたりすると、バレる

役に立ったと感じたら LGTM を押してください。
ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS14でReact Nativeアプリで画像が表示されない

パッチファイル作成

以下コマンドでルートディレクトリ配下に patches/react-native+0.61.5.patch を作成する。

$ npx patch-package react-native

npx: 150個のパッケージを3.464秒でインストールしました。
patch-package 6.2.2
patch-package: you have both yarn.lock and package-lock.json
Defaulting to using npm
You can override this setting by passing --use-yarn or deleting
package-lock.json if you don't need it

• Creating temporary folder
• Installing react-native@0.61.5 with npm
• Diffing your files with clean files
✔ Created file patches/react-native+0.61.5.patch

以下コードを追加する。

diff --git a/node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m b/node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m
index 01aa75f..4ef8307 100644
--- a/node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m
+++ b/node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m
@@ -269,6 +269,8 @@ - (void)displayLayer:(CALayer *)layer
   if (_currentFrame) {
     layer.contentsScale = self.animatedImageScale;
     layer.contents = (__bridge id)_currentFrame.CGImage;
+  } else {
+    [super displayLayer:layer];
   }
 }

以下のコマンドを叩き、 node_modules/react-native/Libraries/Image/RCTUIImageViewAnimated.m に反映させる。267行目付近。

$ patch -p1 -i patches/react-native+0.61.5.patch
- (void)displayLayer:(CALayer *)layer
{
  if (_currentFrame) {
    layer.contentsScale = self.animatedImageScale;
    layer.contents = (__bridge id)_currentFrame.CGImage;
  } else {
    [super displayLayer:layer]; // 記載されていることを確認
  }
}

キャッシュクリアしてビルド

$ watchman watch-del-all
$ rm -rf ios/build
$ rm -rf node_modules
$ rm -rf ~/Library/Developer/Xcode/DerivedData
$ yarn
$ cd pod; pod install

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【入門】iOS アプリ開発 #9【ゲームの状態遷移とシーケンス動作】

はじめに

今回はゲームとしてプレイできるように、ゲームの開始からゲームオーバーなどの状態遷移や各シーケンスの動作を作成する。以下が完成イメージ。ソースコードは GitHub に公開しているので参照してほしい。

※YouTube動画
IMAGE ALT TEXT HERE

状態遷移に関する仕様書

Image100.png

スタートモードとして、ゲームを開始する時のシーケンスが詳細に定義されている。

プレイモードはゲームプレイ中の状態で、プレイヤーがミスするとそのままのエサの状態からスタートし、残りのパックマンがいなくなるとゲーム・オーバーとなる。

またプレイフィールドのエサを全て食べるとラウンド・クリアとなる。

状態遷移とシーケンス動作の設計

ゲームプレイ中での必要なシーケンスを考慮して、下記の状態遷移図を作成した。

Image41.png

スタートモードは Start, Ready, Go の3つの状態に分けてシーケンスを作成する。

プレイモードは、主に Updating, ReturnToUpdating の2つの状態からなり、
プレイヤーがミスした場合は、PlayerMiss→PlayerDisappeared→PlayerRestartの状態/シーケンスを実行し、残りのパックマンがいれば Ready状態へ、なくなれば GameOver状態となる。

またプレイフィールドのエサを全て食べると、RoundClear→PrepareFlashMaze→FlashMazeの状態/シーケンスを実行しラウンドクリアとなり、Ready状態へ移って次のラウンドが開始される。

状態遷移のソースコード

CgSceneMaze の handleSequenceメソッドに、状態遷移とシーケンス処理を実装していく。

/// Maze scene class for play mode
/// This class has some methods to draw a maze and starting messages.
class CgSceneMaze: CgSceneFrame, ActorDeligate {

    var player: CgPlayer!
    var blinky: CgGhostBlinky!
    var pinky: CgGhostPinky!
    var inky: CgGhostInky!
    var clyde: CgGhostClyde!
    var ptsManager: CgScorePtsManager!
    var specialTarget: CgSpecialTarget!
    var ghosts = CgGhostManager()
    var counter_judgeGhostsWavyChase: Int = 0

    convenience init(object: CgSceneFrame) {
        self.init(binding: object, context: object.context, sprite: object.sprite, background: object.background, sound: object.sound)
        player = CgPlayer(binding: self, deligateActor: self)
        blinky = CgGhostBlinky(binding: self, deligateActor: self)
        pinky  = CgGhostPinky(binding: self, deligateActor: self)
        inky   = CgGhostInky(binding: self, deligateActor: self)
        clyde  = CgGhostClyde(binding: self, deligateActor: self)
        ptsManager = CgScorePtsManager(binding: self, deligateActor: self)
        specialTarget = CgSpecialTarget(binding: self, deligateActor: self)

        ghosts.append(blinky)
        ghosts.append(pinky)
        ghosts.append(inky)
        ghosts.append(clyde)
    }

    /// States of game model
    enum EnGameModelState: Int {
        case Init = 0
        case Start, Ready, Go, Updating, ReturnToUpdating, RoundClear, PrepareFlashMaze, FlashMaze,
             PlayerMiss, PlayerDisappeared, PlayerRestart, GameOver
    }

    /// Handle sequence
    /// To override in a derived class.
    /// - Parameter sequence: Sequence number
    /// - Returns: If true, continue the sequence, if not, end the sequence.
    override func handleSequence(sequence: Int) -> Bool {
        guard let state: EnGameModelState = EnGameModelState(rawValue: sequence) else { return false }

        switch state {
            case .Init: sequenceInit()
            case .Start: sequenceStart()
            case .Ready: sequenceReady()
            case .Go: sequenceGo()
            case .Updating: sequenceUpdating()
            case .ReturnToUpdating: sequenceReturnToUpdating()
            case .RoundClear: sequenceRoundClear()
            case .PrepareFlashMaze: sequencePrepareFlashMaze()
            case .FlashMaze: sequenceFlashMaze()
            case .PlayerMiss: sequencePlayerMiss()
            case .PlayerDisappeared: seauencePlayerDisappeared()
            case .PlayerRestart: sequencePlayerRestart()

            default:
                // Stop and exit running sequence.
                return false
        }

        // Continue running sequence.
        return true
    }

    // ============================================================
    //  Execute sequence in each state.
    // ============================================================
    func sequenceInit() {
        drawBackground()
        goToNextSequence()
    }

    func sequenceStart() {
        context.resetGame()
        context.resetRound()
        context.numberOfFeeds = drawMazeWithSettingValuesAndAttributes()
        printBlinking1Up()
        printPlayers(appearance: false)
        printStateMessage(.PlayerOneReady)
        sound.enableOutput(true)
        sound.playSE(.Beginning)
        goToNextSequence(.Ready, after: 2240)
    }

    func sequenceReady() {
        printStateMessage(.ClearPlayerOne)
        printPlayers(appearance: true)
        player.reset()
        ghosts.reset()
        specialTarget.reset()
        ptsManager.reset()
        goToNextSequence(.Go, after: 1880)
    }

    func sequenceGo() {
        printStateMessage(.ClearReady)
        drawPowerFeed(state: .Blinking)
        player.start()
        ghosts.start()

        // Reset counter for wavy attack of ghosts
        counter_judgeGhostsWavyChase = 0
        goToNextSequence()
    }

    // 〜 以下、省略 〜
}

Start, Ready, Go それぞれの状態に対応するシーケンスを sequenceStart(), sequenceReady(), sequenceGo()メソッドで実装する。

仕様書とメソッドを対応させると以下の通り。

  • ”1UP”表示が点滅 → printBlinking1Up()
  • 巣の上に”PLAYER ONE”表示、巣の下に”READY”表示 → printStateMessage(.PlayerOneReady)
  • 設定パックマンの数だけ”●”がプレイフィールド外の左下に表示される →  printPlayers(appearance: false)
  • スタートミュージック → sound.playSE(.Beginning)
  • ”PLAYER ONE”表示が赤モンスターに代わり → printStateMessage(.ClearPlayerOne), ghosts.reset()
  • 設定パックマン(プレイフィールド外の左下)が1つ減る → printPlayers(appearance: true)
  • パックマンがスタート位置に表示される → player.reset()
  • ”READY!”表示が消えて、プレイモードに移る → printStateMessage(.ClearReady)

プレイモードの仕様書

プレイモードにおいては、モンスターの出現タイミングや波状攻撃、アカモンスターのスパートといった細かい仕様が定義されている。これらによってモンスターは単調な追いかけ動作にならず、またプレイヤーのゲーム進行に合わせて難易度が調整される仕組みになっている。

Image11.png

Image12.png

Image13.png

ラウンドに対して更にモンスター出現タイミングのレベル、波状攻撃のスピードレベル、スパートする残りエサ数が定義されているが、今回は固定のレベル「A」、スパートは「①イ」のみ実装していく。

プレイモードのソースコード

プレイモードの仕様は sequenceUpdating() に動作を実装していく。

    func sequenceUpdating() {
        // Player checks to collide ghost.
        let collisionResult = ghosts.detectCollision(playerPosition: player.position)

        switch collisionResult {
            case .None:
                // When it's no eat time, ghost goes out one by one.
                if player.timer_playerNotToEat.isEventFired()  {
                    player.timer_playerNotToEat.restart()
                    ghosts.setStateToGoOut(numberOfGhosts: 4, forcedOneGhost: true)
                }

                // Appearance Timing of Ghosts
                ghosts.setStateToGoOut(numberOfGhosts: context.getNumberOfGhostsForAppearace(), forcedOneGhost: false)

                // Wavy Attack of ghosts
                // - Do not count timer when Pac-Man has power.
                if !player.timer_playerWithPower.isCounting() {
                    counter_judgeGhostsWavyChase += SYSTEM_FRAME_TIME
                }

                // Select either Scatter or Chase mode.
                let chaseMode = context.judgeGhostsWavyChase(time: counter_judgeGhostsWavyChase)

                if chaseMode {
                    pinky.chase(playerPosition: player.position, playerDirection: player.direction.get())
                    inky.chase(playerPosition: player.position, blinkyPosition: blinky.position)
                    clyde.chase(playerPosition: player.position)

                } else {
                    pinky.setStateToScatter()
                    inky.setStateToScatter()
                    clyde.setStateToScatter()
                }

                // If Blinky becomes spurt or not.
                let blinkySpurt: Bool = context.judgeBlinkySpurt() && !ghosts.isGhostInNest()
                blinky.state.setSpurt(blinkySpurt)

                // Blinky doesn't become scatter mode when he spurts.
                if blinkySpurt || chaseMode {
                    blinky.chase(playerPosition: player.position)
                } else {
                    blinky.setStateToScatter()
                }

                // For debug
                ghosts.drawTargetPosition(show: true)

            case .PlayerEatsGhost:
                let pts = context.ghostPts
                ptsManager.start(kind: pts, position: ghosts.collisionPosition, interval: 1000) //ms
                context.ghostPts = pts.get2times()
                addScore(pts: pts.getScore())
                player.stop()
                player.clear()
                specialTarget.enabled = false
                ghosts.stopWithoutEscaping()
                sound.playSE(.EatGhost)
                sound.stopBGM()  // REMARKS: To change playBGM(.BgmEscaping) immediately.
                goToNextSequence(.ReturnToUpdating, after: 1000)

            case .PlayerMiss:
                goToNextSequence(.PlayerMiss)
        }

        playBGM()
    }

はじめにプレイヤーとモンスターの衝突判定を ghosts.detectCollision(playerPosition: player.position) で行う。

衝突がなければ(.None)、ノーイートタイムによって、ゴーストが巣から出ている処理を行う。4匹の中から必ず1匹は外に出して、ノーイートタイムをリスタートさせる。

次は、食べたエサの数でゴーストを巣から出していく。すでに指定の数が出ていたら何もしない。ミスして新たにスタートするときは、ミスバイパスシーケンスを通す。

CgContextクラスの getNumberOfGhostsForAppearace()メソッドは以下の通り。

    func getNumberOfGhostsForAppearace() -> Int {
        let numberOfGhosts: Int
        // Miss Bypass Sequence
        if playerMiss {
            // Level A
            if numberOfFeedsEatedByMiss < 7 {
                numberOfGhosts = 1
            } else if numberOfFeedsEatedByMiss < 17 {
                numberOfGhosts = 2
            } else if numberOfFeedsEatedByMiss < 32 {
                numberOfGhosts = 3
            } else {
                playerMiss = false
                numberOfGhosts = getNumberOfGhostsForAppearace()
            }
        } else {
            // Level A
            if numberOfFeedsEated < 30 {
                numberOfGhosts = 2
            } else if numberOfFeedsEated < 90 {
                numberOfGhosts = 3
            } else {
                numberOfGhosts = 4
            }
        }
        return numberOfGhosts
    }

sequenceUpdating() メソッドの処理フローに戻る。

次の波状攻撃は、パターンスタートからカウントしている時間 counter_judgeGhostsWavyChase によって Scatter と Chase を切り替える。
ただしプレイヤーが逆転している時は、カウントをストップする。

CgContextクラスの judgeGhostsWavyChase()メソッドは以下の通り。

    func judgeGhostsWavyChase(time: Int) -> Bool {
        let mode: Bool
        // Level A
        if time < 7000 || (time >= 27000 && time < 34000) ||
           (time >= 54000 && time < 59000) || (time >= 79000 && time < 84000) {
            mode = false
        } else {
            mode = true
        }
        return mode
    }

sequenceUpdating() メソッドの最後の処理フローでは、アカモンスター(Blinky)のスパート処理を行う。残りエサ数に達した時かつ全モンスターが巣から出ている時(!ghosts.isGhostInNest())にスパートする。

残りは衝突判定で、パックマンがモンスターを噛み付いた時(.PlayerEatsGhost)
、逆に捕まった時(.PlayerMiss)の処理をそれぞれ実装する。

まとめ

ようやくゲームとしてプレイができるようになってきた。現在のソースコードは約5000行。
次はラウンドによって変化する難易度レベルやスピードレベルの詳細を作り込んでいく。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CATechChallengeで動画を簡単にGIFにできる追加機能をつくってきた

はじめに

サイバーエージェントの3daysインターンに参加してきました。
せっかくなのでこちらで技術的な振り返りをしていきたいと思います!技術的なこと以外の振り返りはnoteにまとめるのでそちらをご覧ください。
noteの記事はあとで書いて頑張って足します。
embwfSW2Q8X4Y0c1600629713_1600629757.png

CATechChallengeとは

今回参加したインターンはCATechChallenge(3daysiOS/Android向け開発型インターン)というもので、AbemaTVに架空の追加機能を実装するハッカソン形式のインターンです。

作成した機能

視聴中の動画を長押しすることでその部分のGIFが作成されツイートされる機能を実装しました(機能名募集中ですw)

使用した技術

言語: Swift
アーキテクチャ: MVC
ライブラリ: Regift, Swifter
を使用しました。アーキテクチャに関しては元々MVCのコードが用意されていたのでそのまま実装しました。
ライブラリはGIF生成のためにRegift, ツイートのためにSwifterを使用しました。

苦戦した箇所

ライブラリがm3u8に対応してなかった

元々用意されていた動画の形式がm3u8だったのですがRegiftがmp4にしか対応していなかったため、危うくアイデア自体が終わるところでしたw
メンターさんがmp4の動画も用意してくださっていたので入れ替えることでこちらは無事解決しました。

長押しした時の秒数の取得

これは完全に僕の勉強不足なのですが、AVFoundationを使ったことがなかったので長押し中の秒数の取得に苦戦しました。
AVFoundationではCMTimeという独自の時間表現をするため、currentTimeがCMTimeで表示されていました。
GIFを作成する際には通常の秒数を取得する必要があったので、親切な方記事をガン見しながらそれっぽく変換しました()

GIFの容量がデカすぎてツイートできない

Twitter的に15MB以上のメディアはアップロードできないらしくGIFがツイートできなくて詰みました。
意図せぬ変更ではあるんですがGIFの画質を落とすことによってこの制限をクリアできたので画質を一番低いものにしました。

Swifterからツイートするとユーザーが文章を足せない

これは使うまで気がつかなかったのですが、アプリからSwifterを使ってツイートすると一瞬safariに飛んでアプリにコールバックするため、ツイート時にユーザーが文章を足すことができません。
個人的に不便だなーと思ったので間に自作のAlertを挟むことでユーザーが文章を足せるようにしました。

工夫したポイント

今回一番のアピールポイントは動画視聴という体験をほぼ中断せずに機能が完結する点です。
ツイートする際にアプリとTwitterを連携させる部分以外は動画を視聴しながら使用できる機能にしました。

実際にツイートすると

ちなみに実際にツイートしたらこんな感じになります!
catweetimg.png

結果

そして気になる結果は...
BestEngineer賞をいただくことができました!!!めちゃ嬉しかったです:clap:

GIFのプレビュー機能や番組へのリンクなどまだまだやりたい機能もあったのでホントはあと1ヶ月ぐらい開発したかったですが、とても楽しめたので参加してよかったです!ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CATechChallengeで動画を簡単にGIFにできる追加機能をつくった

はじめに

サイバーエージェントの3daysインターンに参加してきました。
せっかくなのでこちらで技術的な振り返りをしていきたいと思います!技術的なこと以外の振り返りはnoteにまとめるのでそちらをご覧ください。
https://note.com/lsk4f5/n/nd28c28cfc088
embwfSW2Q8X4Y0c1600629713_1600629757.png

CATechChallengeとは

今回参加したインターンはCATechChallenge(3daysiOS/Android向け開発型インターン)というもので、AbemaTVに架空の追加機能を実装するハッカソン形式のインターンです。

作成した機能

視聴中の動画を長押しすることでその部分のGIFが作成されツイートされる機能を実装しました(機能名募集中ですw)

使用した技術

言語: Swift
アーキテクチャ: MVC
ライブラリ: Regift, Swifter
を使用しました。アーキテクチャに関しては元々MVCのコードが用意されていたのでそのまま実装しました。
ライブラリはGIF生成のためにRegift, ツイートのためにSwifterを使用しました。

苦戦した箇所

ライブラリがm3u8に対応してなかった

元々用意されていた動画の形式がm3u8だったのですがRegiftがmp4にしか対応していなかったため、危うくアイデア自体が終わるところでしたw
メンターさんがmp4の動画も用意してくださっていたので入れ替えることでこちらは無事解決しました。

長押しした時の秒数の取得

これは完全に僕の勉強不足なのですが、AVFoundationを使ったことがなかったので長押し中の秒数の取得に苦戦しました。
AVFoundationではCMTimeという独自の時間表現をするため、currentTimeがCMTimeで表示されていました。
GIFを作成する際には通常の秒数を取得する必要があったので、親切な方記事をガン見しながらそれっぽく変換しました()

GIFの容量がデカすぎてツイートできない

Twitter的に15MB以上のメディアはアップロードできないらしくGIFがツイートできなくて詰みました。
意図せぬ変更ではあるんですがGIFの画質を落とすことによってこの制限をクリアできたので画質を一番低いものにしました。

Swifterからツイートするとユーザーが文章を足せない

これは使うまで気がつかなかったのですが、アプリからSwifterを使ってツイートすると一瞬safariに飛んでアプリにコールバックするため、ツイート時にユーザーが文章を足すことができません。
個人的に不便だなーと思ったので間に自作のAlertを挟むことでユーザーが文章を足せるようにしました。

工夫したポイント

今回一番のアピールポイントは動画視聴という体験をほぼ中断せずに機能が完結する点です。
ツイートする際にアプリとTwitterを連携させる部分以外は動画を視聴しながら使用できる機能にしました。

実際にツイートすると

ちなみに実際にツイートしたらこんな感じになります!
catweetimg.png

結果

そして気になる結果は...
BestEngineer賞をいただくことができました!!!めちゃ嬉しかったです:clap:

GIFのプレビュー機能や番組へのリンクなどまだまだやりたい機能もあったのでホントはあと1ヶ月ぐらい開発したかったですが、とても楽しめたので参加してよかったです!ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む