
Outline
仕事でAPI・CMSサーバーをそれぞれVaporで作る機会がありました。
その際に蓄積されたノウハウや、VaporやSwiftでサーバーを書くことについて感じたことをまとめて記事にしました。
前提
本記事の想定読者
本記事の想定する読者はマニュアルを読んでサンプルを触ってみて、じゃあもう少し規模の大きいものを……と思っているような方です。特にマニュアルに載っていないが実用上では必要になるような知識を埋めることを目指します。
基本的な事項はマニュアルのリンクを貼っていきますので、適宜参照してください。
Vaporの基本的な特徴や長所をお求めの方は公式を御覧ください(Vapor3かつ適当にまとまった記事が見つからなかったので)。
https://vapor.codes/
案件の概要
- 受託スマートフォンアプリのためのJSONベースのAPIサービスとHTMLベースのCMSサービス 機能数的には小規模
- デプロイ環境は一般公開向けの本番用、お客様向けの確認用、内部の開発者用と複数ある
- デプロイ先のサービスバージョンに従ってデータベース定義がずれているので、その更新作業は自動化したい
- 今回の着手以前に作られたプロトタイプ版が稼働していたが、メンテナンス性を考慮してVaporへの移行となった
- プロトタイプ版のテーブル定義やデータを破損することなく移行したい
着手以前の私のスキル
Swift歴は4年ありますが、サーバーサイドはプライベートでPHPやNode.jsを触っていたくらいで、仕事で取り組むのは初めてでした。
SwiftNIOの早期導入等に感化されてVaporありきで始めたため、Kituraや他のメジャーなフレームワークとの比較等はできませんので記事でも触れません。
TL;DR
項目が多岐に渡るため、それぞれは各論として先に総評を提示します。
Vapor開発に興味を持つ人の多くは、Swiftで開発できることに魅力を感じていると思います。この点については完全に当たりでした。
今回はiOSアプリと連携するため、APIの型やユーティリティを共有するようにしたのですが、これによりサーバー・クライアント間で定義の食い違いによって起こる問題を排除できました。またVaporのソースコードを読みに行く必要がしばしばあったのですが、その際もSwiftの可読性の高さに助けられました。型が明確なので今自分のしたいことのためにどの機能を調べれば良いかということが容易に分かります。
逆にもともとSwiftに慣れていない人にとっては、必要となる知識が少々多いかもしれません。Vapor自体の知識の他に、言語自体の知識、SwiftPMの知識が必要になり、特に後者は踏み込んでいくと厄介です。
Vapor自体も2系から大きく変わった3系のリリースが最近なので、古い情報が参考にならないことが多いです。そのためソースコードを読みに行くのが一番良いという事になりがちなので、ある程度Swift慣れしていないと開発効率が落ちてしまいます。
デプロイ先はLinux環境が多いと思いますが、Appleデバイスではいつも使っているフレームワークがLinux環境では使えなかったり、クロスプラットフォームのFoundationでもLinuxでは未実装の関数があったりしました。
Vapor導入を決める前に必要な機能の洗い出しを十分に行い、開発着手後はデプロイ先と同じ環境で随時テストできる環境を早いうちから用意するのが望ましいです。
以上、短所のほうが多いのでは?という気がしますが、Swiftユーザー的にはSwiftを使えることによる開発効率向上が目覚ましいので、少々の短所は塗りつぶしてしまいます。
Swiftを使ったことがないという人もサンプルから入ってみて、Swiftの魅力に取り憑かれたら苦よりも楽が多い状態で開発を進めていけると思います。
では各機能については以下各論に譲ります。
コア機能
Vaporは数多くのSwiftパッケージで構成されています。最初にvapor update
で依存関係を取得する際にはその量に圧倒されるかもしれませんが、直接触る必要がある部分はそこまで多くないです。
この項ではどの機能を使うにしても必ず出てくるVapor及びCoreの内容を扱います。
Future
Vaporはネットワーク機能の実装のため、SwiftNIOを下請けとして用いています。
SwiftNIOはNon-Blocking IOを扱うためにFutureパターンを採用しており、VaporのAPIもほとんどがFuture
を通して扱うようになっています。
基本的な使い方はドキュメントに書いてあるので、そこで学ぶのが良いです。
ドキュメントに書いてあること以外で、出現頻度が高いのに記述が難しかったり、毎度書き下すには長かったりするような用途については、いくつかユーティリティ関数が用意されていますので以下に紹介します。
複数のFuture
の結果を待ち合わせて何かしたい
トップレベル関数のmap
/flatMap
が使えます。
以下HTTPリクエストを送るclient.get(_ url: URL) -> Future<Response>
を例にとって説明します。
map(to: Void.self, client.get(url1), client.get(url2)) { res1, res2 in
// 両方が完了した段階で実行
print("\(res1), \(res2)") // それぞれの結果出力
}
2つ以上のFuture
を引数にとり、全ての終了を待って、それらの結果を引数にとるクロージャを実行します。
Future
の返す型はそれぞれ自由になっているので、Future<Int>
とFuture<String>
を渡すようなこともできます。
Colelction<Future<T>>
の結果を待ってFuture<Colelction<T>>
にしたい
flatten
及びmap
/flatMap
が使えます。
let urls: [URL] = ...
// 全URLに対しリクエストを発火する
let requests: [Future<Response>] = urls.map { client.get($0) }
// 全リクエストの終了を待ち、`[Response]`に変換する
let flat: Future<[Response]> = requests.flatten(on: worker)
[Future<T>]
を使うため型が統一されている必要があります。
[Response]
として得られる順番はもとのurls
の順番通りになるのですが、Future
の処理順は分からないような実装になっているため、呼び出し時にworker
を渡す必要があります。worker
はEventLoop
への参照を持っており、最終的に得られるFuture
がどのEventLoop
に所属するかを指定するのに使います。
その他注意点として、Future
は作られた瞬間処理を開始するため、urls
が100件のURL
を持っていたら100件のリクエストが同時に始まってしまいます。
直列に実行したい場合については次の項で説明します。
コレクションの各要素について直列に非同期処理を行いたい
typealias LazyFuture<T> = () throws -> Future<T>
が用意されており、[LazyFuture<Void>
]に対してsyncFlatten
が使えます。
let urls: [URL] = ...
// 全URLに対し`LazyFuture`を作成する
let requests: [LazyFuture<Void>] = urls.map { url in { client.get(url).transform(to: ()) } }
let allEnd: Future<Void> = requests.syncFlatten(on: worker)
LazyFuture
は評価されるまでFuture
が作られないので、実行開始が遅延されます。
この例だとrequests[0]
を評価して得られたFuture
を待ち、終わったらrequests[1]
を評価して得られたFuture
を待ち……というように続いていきます。
シグネチャを見れば分かりますが、なぜか[LazyFuture<Void>]
にしか用意されていないので、[LazyFuture<T>]
で使いたい場合は自分で書く必要があります。
基本的な部品はCore/Asyncにまとまっており、量も少ないので目を通しておくと良いかもしれません。私は読まずに突貫したのでこれらの再発明をしてしまいました。
Futureパターンによる記述は、部品が揃っているとはいえ同期的なコードと比べるとどうしても複雑になりがちです
現在Swiftにasync/await
を導入しようという議論が進んでおり、これが導入されるとFutureパターンを同期的なコードと同じような構文で書けるため、Vapor開発においては恩恵が大きいです。
Swiftにおけるasync/await
については以下の資料が詳しいです。
サービスとコンテナ
VaporフレームワークではDependency InjectionのためにDIコンテナを用いており、Service
プロトコルに準拠したインスタンスをContainer
プロトコルに準拠したコンテナから取り出すというコードが頻出します。
コンテナとして一番頻繁に使うのはRequest
です。Request
はHTTPリクエストが届くたびに生成されるオブジェクトですが、実際のコンテナアクセスは共有のものに委譲されています。
取り出すサービスはコンテナにキャッシュされるため、次に別のリクエストが来て同じサービスを取り出す場合にも効率的に動作します。
またこの仕組みによってDatabaseConnectionPool
等複数のリクエストに跨って作用するタイプのサービスも上手く共有できるようになっています。
コンテナアクセスの委譲はContainerAlias
というプロトコルで実現されており、Request
の場合はEventLoop
単位で存在するコンテナに委譲されています。
一方で、認証情報等のようにリクエスト固有のものの扱いには注意が必要です。
例えば後述するミドルウェアで認証情報を確認し、それをRequest
に付加して続きの処理に進むというコードを書きました。
この認証情報をrequest
コンテナに入れると他人のリクエストの認証情報と混ざってしまうことになります。
そのような個々のリクエスト固有のものについてはRequest.privateContainer
というのがあるので、こちらに入れるようにします。
自分で作ったサービスを使いたい場合、configure.swiftにあるようにServices
に登録しておく必要があります。
ミドルウェア
ミドルウェアのドキュメントは現在のところComing soonとなっています。
ミドルウェアは個別のリクエストハンドラの前後で何か処理をしたいときに用います。
Middleware
プロトコルは以下です。
public protocol Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response>
}
Request
を使った処理を行う場合引数のrequest
を、Response
を使う場合next.respond(to: Request)
で得られるFuture<Response>
を用います。またnext.respond
はthrows
で、各ルートで発生するエラーを一括で捉えるのにも使えます。
Middleware
はアプリケーション全体か、特定のルート群に対して登録できます。
まず全体への登録ですが、configure.swift内でMiddlewares
に登録していきます。
第一にサンプルでも用いられているMiddlewareConfig.use(Middleware.Type)
で登録する方法があります。この方法で登録する場合、必要になった時点でミドルウェアはインスタンス化されますが、そのインスタンス化はサービスの流儀に則って行われるため、ミドルウェアをService
に準拠させてServices
にも登録する必要があります。
テンプレートに入っているErrorMiddleware
などは別の場所で登録されているので、真似して書いて動かなくて少し詰まりました。
インスタンスを直接渡すMiddlewareConfig.use(Middleware)
もあって、こちらはService
への準拠が不要なのですが、コメントにあるようにインスタンスがスレッド間で共有されるので、スレッドセーフであることを確認する必要があります。
特定のルート群のみに適用したい場合、Router
がgrouped(Middleware.Type) -> Router
やgrouped(Middleware...) -> Router
というメソッドを持っており、これらで得られるRouter
に登録してやったルートではミドルウェアが適用されます。
例えばログインAPIとログイン後にだけ使えるAPIを分けたい場合に、AuthUserMiddleware
を使うとすると、routes.swift
の記述は以下のようになります。
public func routes(_ router: Router) throws {
// 認証なし
router.post("login", use: loginHandler)
// ミドルウェアで認証要求
let authed = router.grouped(AuthUserMiddleware.self)
authed.get("user/info", user: userInfoHandler)
authed.post("user/edit", user: userEditHandler)
...
}
DB周り
Vaporはデータベースを簡単に扱うためFluentというライブラリを提供しています。
Fluentはデータベースエンジンごとに実装が別れていますが、今回利用するのはMySQLだったのでFluentMySQLを使いました。
マニュアルのGetting Startedに従って、必要なパッケージをPackage.swiftに追加することで使用できるようになります。
モデル
Fluentでデータを読み書きするため、各テーブルに対応するModel
プロトコルに適合した型を定義します。
Model
プロトコルに適合することによって対応するテーブル名やID用のフィールドの実装が要求され、後述するQueryBuilder
と組み合わせることによって、自分でSQLを書かずにクエリを発行することができます。
ドキュメントではモデルをclass
で書いてありますが、私は全部struct
で書きました。値型を用いることによりオブジェクトの共有で発生しがちな問題を回避することができるので、私はSwiftを使う上では積極的にstruct
を採用するようにしています。
struct
を使う場合注意点があり、ドキュメントのConformanceの項に以下のように書いてあります。
however you must pay special attention to Fluent’s return types if you use a struct. Since Fluent works asynchronously, any mutations to a value-type (struct) model must return a new copy of the model as a future result.
Fluentは非同期に働くので、struct
だとクエリを呼び出す元のモデルインスタンスを書き換えることができないため、代わりにコピーを返すと書いてあります。
一例としては、FluentではINSERT
したいときはid
をnil
に設定したモデルインスタンスのsave
を呼び出すのですが、INSERT
してどのid
が振られたかはsave
の結果返ってくるモデルに設定されています。struct
の場合save
を呼び出すモデルインスタンスとsave
の結果返ってくるモデルインスタンスが別物になるので、save
後にid
を使った処理をしたい場合は結果のほうのインスタンスを使う必要があります。
とはいえstruct
はそういうものなので、日頃からstruct
を活用しているSwiftユーザーなら別に気にならないかと思います。
クエリビルダ
モデル型自体に用意されたメソッド群で単純なSELECT
, INSERT
などはできますが、更に複雑なクエリを発行したい場合QueryBuilder
を用いることでタイプセーフに記述することができます。
https://docs.vapor.codes/3.0/fluent/querying/
生クエリを書くと、単純にクエリの構文を間違って動かないことがあるだけでなく、テーブル定義を更新した際にはそれに伴う修正を目視で確認する必要があります。
一方モデルとQueryBuilder
を使う場合、テーブル更新に従ってモデルだけ更新すれば修正が必要な箇所はコンパイルエラーとして現れるので、こちらを用いていくことでメンテナンス性が向上します。
ドキュメントで書かれている分で多くのケースは事足りるのですが、更に複雑なクエリを発行したいとなると対応しきれないということが出てきました。
GROUP BYとCOUNT
次のクエリはあるusers
テーブルに登録されたユーザー全員と、各ユーザーが投稿したposts
テーブルのレコード数を合わせて取得するものです。
このようなクエリはどうもQueryBuilder
では実現できないようでした。
SELECT users.*, IFNULL(posts.count, 0) FROM users
LEFT JOIN (SELECT user_id, COUNT(*) AS count FROM posts GROUP BY user_id) AS posts ON users.id = posts.user_id;
上クエリではGROUP BY
及びCOUNT
が必要になるのですが、QueryBuilder
のメソッドgroupBy
を呼び出した後にどうやって積算させるようにするのかがわかりませんでした。
(マニュアルにはgroupBy
自体が載っていないので、恐らくGROUP BY
クエリの部分だけ実装されてその先は未実装という状態なのだと思っています。)
各ユーザーの投稿数を知りたいだけならcount
というメソッドがあるので、それをユーザーIDごとに呼び出せば得られます。
しかしユーザー数だけクエリを叩く必要があるので、いわゆるN+1問題になってしまいます。
このようなクエリをQueryBuilder
で実行するため、データベース側にビューを作成するようにしました。
CREATE VIEW
文で作成するビューはあるクエリの結果をテーブルのように扱う機能です。
https://dev.mysql.com/doc/refman/5.6/ja/create-view.html
ビューからデータを取得するのにはテーブルと同じクエリを用いることができるため、ここで作ったビューに対応するモデルを定義してやれば、QueryBuilder
が発行できるクエリの範囲で必要なデータを取得できます。
ビューに対するINSERT
やDELETE
はできないのですが、モデルがテーブルを前提としている都合上save
やdelete
が生えてしまうのがちょっと気になるところです。
HAVING
HAVING句を用いたフィルタリング処理が必要になりました。
SELECT *, ABS(weight - ?) AS weight_diff FROM users HAVING weight_diff > 10 ORDER BY weight_diff;
これはユーザーの体重weight
とある値(プレースホルダの?)の差weight_diff
を計算し、その差が10を超えるユーザーを、差の昇順で取得するというクエリです。
weight_diff
は再利用されるためカラムにすることで計算の記述を一度にしていますが、その場合WHERE
句を用いたフィルタリングができず、代わりにHAVING
句を使うことができます。
https://stackoverflow.com/questions/16068662/calculated-column-in-where-clause-performance/16068737#16068737
このクエリで問題になるのはweight_diff
の計算に外部からの入力値が必要なことで、これによりビューを作って対応ということができません。
このケースはどうしようもなさそうだったので、仕方なく生クエリで書くことにしました。
生クエリからのデコードは以下のように簡単にできるようになっています。
let conn: MySQLConnection
return conn.raw("SELECT @@version as version")
.all(decoding: MySQLVersion.self)
https://docs.vapor.codes/3.0/mysql/getting-started/#query
デコード部分のカスタマイズ
自分の話ではないですが、Discordで以下のような話を見かけました。
- 同じテーブルを二重でJOINしたい
-
LEFT JOIN
を使っていて対応する行が存在しない可能性がある
モデルにデコードする前の状態の行を取得する方法として、QueryBuilder.decodeRaw()
や、生クエリを発行するMySQLConnection.raw(_:)
というのがあります。MySQL
の場合だと[[MySQLColumn: MySQLData]]
という形で取得できます。
これらをMySQLConnection
のdecode
メソッドを用いて自分でデコードするという手段が取れます。
以下の例はMessage
に、その送信元、送信先のPerson
を二重にJOIN
して取得するコードです。
let conn: MySQLConnection
conn.raw("""
SELECT * FROM messages
JOIN persons AS from_persons ON messages.from_person_id = from_persons.id
JOIN persons AS to_persons ON messages.to_person_id = to_persons.id
""").all().map { rows in
try rows.map { row -> (Message, Person, Person) in
let msg = try conn.decode(Message.self, from: row, table: "messages")
let from = try conn.decode(Person.self, from: row, table: "from_persons")
let to = try conn.decode(Person.self, from: row, table: "to_persons")
return (msg, from, to)
}
}
https://github.com/t-ae/fluent-example/blob/sqlite-decoder-bug/Tests/AppTests/AppTests.swift#L96-L107
(上リポジトリはSQLiteとMySQLでふるまいが違うことを報告するために作ったもので、SQLiteでは同じコードが正しく動かないです。)
もちろんビューを作っても対応できますが、必要な箇所のたびに作成しているとビューとモデルが増えて管理が煩雑になるので、こういう方法もあるんだと覚えておくと役立つと思います。
マイグレーション
データベースマイグレーションとはデータベースのセットアップやアップデートを自動で管理できるようにする機能です。私は今回はじめて使いました。
データベースの変更をパッチとして蓄積していき、サーバーの立ち上げ時に現在のDBのバージョンから必要なパッチを順に適用してくれます。
マイグレーションについてはドキュメントが揃ってます。
https://docs.vapor.codes/3.0/fluent/migrations/
ここに書いてあるのはテーブル定義の変更までですが、以下のようにやりたいことがいろいろ出てきました。
- NullableなカラムをNOT NULLにしつつNULLだったものには特定値を埋める
- UNIQUE制約の追加
- Viewを作る
単純にテーブルにデフォルトのデータをインサートしたいというような場合にも、モデルの型を使ってsave
するのは不適ではないかと思っています。
モデル定義の方は常に最新のものになって行くので、インサート後にテーブル定義変更のマイグレーションが続く場合、インサートするマイグレーションの時点ではモデルとテーブルの定義が不一致になってしまうためです。またモデルのシグネチャが変化するとそれを以前から使っているマイグレーションにも変化が及び、デプロイのタイミングによってテーブル構造が変化してしますことも考えられます。
Fluentのモデルとマイグレーションを協調させるのは相性が悪そうだったので、今回はマイグレーションについては全て生クエリで管理することにしました。
ドキュメントの記述は単一のデプロイ環境で作業することを想定しているようで、私のようにデプロイ環境が複数あり、テーブル定義のバージョンがずれている場合については考慮されていないような印象を感じました。テストコードも各fluent-*リポジトリにあるのですが、ドキュメントと同じような内容しかないので、もっと様々なケースについてのドキュメントやサンプルがほしいところです。
ちなみにマイグレーションの実行管理は、管理用にfluent
というテーブルが作られて、そこに実行済みマイグレーションの行が記録されていきます。
マイグレーションが間違っていたときは手で変更を戻して、fluent
テーブルに記録された行を削除して再度立ち上げという手順を取っていました。
既成DBの移行
私の着手時点で(Vaporとは関係なくマイグレーションもない)プロトタイプ版のテーブルが存在し、データもいくらか入っていました。
これらのデータを消さずに引き継ぐ必要がありました。
一方でプロトタイプ版が動いていないまっさらなサーバーにおいても、起動時にはデータベースを構築するようにしなければなりません。
このように開始点が2種類あり、どちらもVaporサーバーを起動した段階では同じ状態になっているように構築する必要があります。
これを実現するのには少し工夫が必要でした。
今の問題はプロトタイプ版のDBと新規のDBで定義が異なっていることなので、この差分を埋めるマイグレーションを用意します。一旦同一の状態に持っていければ、以降のマイグレーションは共通のものとして書いていけます。
具体的には、プロトタイプ版のテーブル構造をSQLとしてダンプし、それをそのまま実行するマイグレーションを作りました。これを実行することにより、新規DBをプロトタイプ版とおなじ状態に持っていけます。
一方で、この差分を埋めるマイグレーションはプロトタイプ版が構築済みのDBには作用しないようにする必要があります。
私は前述のfluent
テーブルを手動で編集し、このマイグレーションは実行済みであるという扱いにしました。これを行うことでプロトタイプ版が動いていた環境では該当のマイグレーションがスキップされ、結果としてまっさらな状態から始めたのとおなじ状態になります。
このスキップの設定はプロトタイプ版が動いているサーバー全てに行う必要がありますが、動いているサーバーが少数でありこれ以上増える予定もなかったため、今回は手動で済ませました。
大量にある場合はテーブルの存在判定を入れて、マイグレーションが自身を実行するかどうか分岐するようなコードを書くという手段も取れそうです。
ところで、MySQLには複数のクエリを一度の通信で行わせるために、CLIENT_MULTI_STATEMENTS
という機能が存在します。
最初はこれを使って、ダンプしたSQLを一つのString
として実行させようとしていたのですが、実際にやってみたところデコーダー周りが対応していないようでクラッシュしてしまいました。この件についてはIssueを立てており、将来的にサポート予定とのことです。
https://github.com/vapor/mysql/issues/178
現在のところクエリを一つ一つ分割して、順に実行するという手順が必要になります。
Automatic Model Migration
Vaporにはマイグレーションをモデル自身に適合させるAutomatic Model Migrationという機能もあります。
これはモデルの定義から自動的に適切なテーブルを作成するマイグレーションを生成してくれる機能です。
一見便利な機能ですが、ドキュメントには以下のように書かれています。
This method is especially useful for quick prototyping and simple setups. For most other situations you should consider creating a normal, custom migration.
プロトタイピングに便利だが、多くの場合マイグレーションを自分で書くCustom migrationsを使うことを考えるべきだと示されています。
開発の上でモデル定義が変化していくので、Automatic Model Migrationで生成されるマイグレーションもそれに合わせて変化していきます。そしてその変更差分を埋めてくれる機能があるわけでもないので、これをベースに始めてしまうとバージョン管理の煩雑化が危惧されます。
Vaporを始める際に最初にvapor new
で作るであろうAPI Templateではこれが使われていますが、特に説明も無く使われているので、テンプレートを見ながら始める人にとってはちょっとミスリーディングかなと思いました。
タイムゾーン
前記のプロトタイプ版の事情で着手以前からデータがいくらか入っていたのですが、それらはJSTで格納されていました。
一方Fluentにはタイムスタンプを設定する仕組みが入っており、この仕組みに乗っかってcreated_at
, updated_at
を自動設定するようにできます。しかしこの機能でDBに記録する際のタイムゾーンはUTC固定となっており、JSTのデータをどうしても上手く扱えないという状態になってしまいました。
この対応のため、JSTだった全データをUTCに変換するという手をとりました。これについてもマイグレーションにすることでうまく取り扱うことができました。
UTC以外のタイムゾーンの取扱についてのIssueは上がっているのですが、特に進捗がないので、新規開発で選択の余地がある場合は最初からUTCを選択するのが良いと思います。
https://github.com/vapor/fluent/issues/464
DB周り総評
基本的なことはQueryBuilder
経由でできるのですが、よくありそうなケースでも生クエリが必要になることが少なからずあるのがちょっと気になりました。
特にマイグレーションを全て生クエリにしたため、クエリが間違っているのに気付かないまま実行してあとでやり直す作業が数回発生してしまいました。
ユニットテスト用のDBを用意してマイグレーションを流し、情報が正しく取得できるようになっているかまでテストするのが良いです。
プロトタイプ版時代は手作業でテーブル構造を管理していましたが、マイグレーションの整備が終わると必要な手作業はほとんど排除でき、結果目覚ましい発展となったので結構満足しています。
マイグレーション以外で生クエリを書く必要が出てきたのはHAVING
で説明した1パターンのみだったので、メンテナンス性はそこまで悪くなっていないと思っています。
Leaf
VaporはテンプレートエンジンとしてLeafを提供しています。CMSについてはこれを用いて開発することにしました。
が微妙な点ばかり目についてしまいました。
- テンプレート内で整数演算するとレンダリング結果が浮動小数点数になる #101で報告され解決済み
-
Dictionary
のsubscript
が使えない - enumの条件分岐ができない
#if(someEnum == .someCase)
が常にfalseと判定されていました
Leafテンプレートの中でロジックを書こうとすると問題が多いようだったので、それらはできるだけ使わないようにしました。
データの加工はSwift側で行い、テンプレート内ではドキュメントで明示されている条件式だけを用いて、あとは渡ってきたデータをそのまま出力する形にしたので、それ以降はLeafの問題を踏むことはありませんでした。
Leafテンプレートの編集は、ファイルツリーに出ているのでXcodeでやりたくなるところですが、Syntax Coloring:HTMLを選ばないとインデントが酷いことになります。さらにその設定はファイル個々に必要だったり、xcodeprojの再生成で設定したのが消えたりということがあるようです。設定を永続化できる手順もドキュメントのリンクに記載されていますが、なかなか面倒そうなので外部エディタを使うのが楽だと思います。
いくつかのエディタでシンタックスハイライトが提供されているのでこれらから選ぶと良いでしょう。
テスト
ミドルウェアを含めたテストを行うため、Request
を渡してResponse
を得るという形のテストを書きたくなりました。
Application
とRequest
を作成し、Application
から取り出したResponder
にRequest
を渡すことで、実際に外からRequest
が来たのと同じ条件でテストできます。
これは別件で書いたものですが、以下のコードがまさにそれをやっています。
...
let app = try Application(
config: config,
environment: env,
services: services
)
let req = Request(http: HTTPRequest(method: .POST, url: "/path/to/api"), using: app)
let res: Future<Response> = try app.make(Responder.self).respond(to: req)
https://github.com/t-ae/TwoRequestsOneConnection/blob/master/Tests/AppTests/AppTests.swift
以上によりネットワーク通信を行わずに、自作のミドルウェアも含めた結合テストが書けました。
Swiftをつかうことについて
今回の案件はスマートフォンアプリのバックエンドでした。
アプリ側は先輩社員の @omochimetaru が担当しており、こちらもSwiftで書いていました。
双方をSwiftで書くことのメリットとして、APIを全て型で定義し、共通して使えることがあります。
もともとはAPI仕様書を管理していましたが、API型の定義を共有する手順が固まったことでAPI型=仕様という状態になり、仕様書は廃止しました。APIに変更を加えた場合はAPIリポジトリのプルリクエストで通知しあうという形にすることで、更新に気付かないミスを防ぐことができました。
またシグネチャが変わる類の更新は、チェックアウトした段階で使用箇所全てでコンパイルエラーになるため、アップデート対応漏れを確実に無くすことができます。
(このあたりの話をVapor meetup 2ndのLTで発表されていました。そのときに言及があったサンプルのリポジトリを見たら雰囲気が掴めるかと思います。)
もちろんAPI型にかかわらず値の計算、バリデーション等のユーティリティも共有できるので、サーバー・クライアントで処理が食い違って壊れるというような問題も防げます。
コードの共有でミスしてしまった点としては、iOS開発側で共有モジュールに入れた型が、Linuxでは使えないがmacOSでは使えたのでデプロイまで気付かなかったことがありました。macOSで開発を進めてLinuxにデプロイするタイミングが遅かったため、発覚が大分遅れてしまいました。
例えばCoreGraphicsのCGRect
や、GLKitのGLKVector2
のように、iOS/macOS開発ではよく使う型が使えないので気をつける必要があります。
今回はCIを導入していなかったのですが、導入していれば早期に気付けた問題だったと思います。
サーバー・クライアント間で共有するための型を作っていくのは良い方法ですが、GLKVector2
のようなシンプルな型まで全部再定義していくというのも大変なので、必要なものはサーバーサイドにも対応した同名の型を用意するという手順を取りました。
同時にmacOSでも自前で用意した型を用いるようにすることで、Linuxデプロイ時にしか起こらない問題を減らすことにしました。
vapor toolboxの機能として、vapor.jsonというファイルに設定を書いておくことで、vapor build
やvapor xcode
の時にビルドフラグを渡してくれます。
vapor.jsonについてはドキュメントに記述が見つからなかったのですが、以下のようなファイルをプロジェクトのルートディレクトリに置くことでVaporフラグを渡してくれるようになります。
{
"name": "server-app-name",
"flags": {
"build": {
"mac": [
"-Xswiftc", "-DVapor"
],
"linux": [
"-Xswiftc", "-DVapor"
]
},
"test": {
"mac": [
"-Xswiftc", "-DVapor"
],
"linux": [
"-Xswiftc", "-DVapor"
]
}
}
}
このフラグを使って以下のように分岐させることで、iOSではGLKitの定義を、macOS/Linuxでは自前の定義を使うようにできました。
#if Vapor
public struct GLKVector2 {
public var x: Float
public var y: Float
public init(_ x: Float, _ y: Float) {
self.x = x
self.y = y
}
}
# else
import GLKit
extension GLKVector2 {
public init(_ x: Float, _ y: Float) {
self = GLKVector2Make(x, y)
}
}
#endif
なおvapor.jsonはあくまでvapor toolboxの機能なので、SwiftPM側のswift pacakge ~
系のコマンドでは働きません。
常にvapor ~
で行うようにしましょう。
SwiftPM
VaporプロジェクトはSwift Package Managerで管理します。
基本的なコマンド操作はvapor toolboxがラップしていますが、Package.swiftの記述などSwiftPM側の知識が必要になることも少なからずあります。
一例として、私のプロジェクトではAPIリポジトリを分離して共有していたこともあり、そちらの編集とVaporアプリ本体の編集を並行して行いたいという状況になりました。
そのような状況のためにSwiftPMにはpackage editという機能があり、これを利用して並行して作業を行っていました。
https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#editable-packages
Codable
Vaporを使う上でCodable
が様々な場所に出てきます。
VaporからSwiftを始めるという人は以下の公式のドキュメントを読んで、どういうものなのか知っておくのが良いと思います。
Encoding and Decoding Custom Types
Codable
は型宣言に対して適合させた場合、必要なメソッドを自動で実装してくれる機能があります。
extension
で後づけする場合は必要なメソッドを自分で定義する必要があり、これは避けたいところです。
さて、今回はAPI型を別のパッケージに分け、クライアントサイドとサーバーサイドで共有していたのでした。
APIパッケージには各APIのリクエスト型とレスポンス型が定義されています。
リクエスト型はクライアントサイドからは送信するのでEncodable
に、サーバーサイドでは受信するのでDecodable
にする必要があります。同様にレスポンス型についてもそれぞれDecodable
、Encodable
に適合させる必要があります。
理想的には必要なプロトコルにだけ準拠するのが好ましいですが、以上のようにクライアントサイドとサーバーサイドで必要なものが分かれ、かつ個別に実装する場合は必然的にextension
で書くことになるため、自動実装が使えないという問題に行き当たります。
そのため今回は無駄なプロトコル適合に目を瞑り、APIパッケージ内でCodable
に適合させることとしました。
その他
Docker
サーバー周りはdocker-composeを用いて管理するようにしました。
デプロイ環境固有の設定やDBパスワード等のセンシティブな情報はコミットに含めたくない情報なので、環境変数としてコンテナに渡すようにしました。
サーバーアプリ側ではそれを読み取って使用するように記述することで、リポジトリに各種設定を書かずに済むようにできました。
SwiftPM絡みの対応
Vaporのプロジェクトをビルドする際、SwiftPMの依存を解決できるようにしておく必要があります。
パブリックなGitHubのリポジトリをhttps
経由で指定していれば認証が不要ですが、プライベートリポジトリ等認証が必要になる場所が依存に組み込まれている場合、適切に設定しておく必要があります。
Docker上で動かす構成は、ホスト側にリポジトリをクローンし、それをコンテナにマウントしてビルド&実行するという形にしました。
この場合vapor build
時に依存解決が行われるため、コンテナ内部からプライベートリポジトリへのアクセス手段を用意する必要があります。
私はGitHubのデプロイキーを用いてアクセスさせることにしました。
プライベートリポジトリが複数ある場合の注意点として、同じデプロイキーを複数のリポジトリで使い回すことができないということがあります。
そのため同じGitHubに置かれたリポジトリでも、個別にキーとエイリアスを用意する必要があります。
例として、以下のようなconfigをdeploy-keyと共にコンテナの.sshディレクトリにマウントします。
Host vapor-private-module
HostName github.com
User git
IdentityFile ~/.ssh/deploy-key
StrictHostKeyChecking no
これによってvapor-private-moduleというエイリアスでアクセスする際にはIdentityFileで設定されたdeploy-keyが使われることになります。
vapor-private-moduleというホスト名は本来のリポジトリには出てこないため、対象のプライベートリポジトリのURLをこのホスト名を使ったものに置き換えるという手を取ります。
具体的には以下のようにDockerfileの中でgit configのinsteadOf
という設定項目を追加しました。
FROM ubuntu:16.04
WORKDIR /root
RUN apt-get update && \
apt-get install -y wget && \
wget -q https://apt.vapor.sh -O apt.vapor.sh && \
/bin/bash -c "$(cat apt.vapor.sh)" && \
apt-get update && \
apt-get install -y \
swift=4.1.0 \
vapor \
imagemagick \
libpython2.7 && \
git config --global url."git@vapor-private-module:hogehoge/private-module.git".insteadOf https://github.com/hogehoge/private-module.git && \
mkdir -p .ssh && \
chmod 700 /root && \
chmod 700 .ssh
WORKDIR /var/vapor
CMD vapor build --verbose && vapor run
git config --global url."git@vapor-private-module:hogehoge/private-module.git".insteadOf https://github.com/hogehoge/private-module.git
によって、プライベートリポジトリhttps://github.com/hogehoge/private-module.git
へのアクセスはgit@vapor-private-module:hogehoge/private-module.git
に置き換えられます。
置き換えたもののホスト名は.ssh/configに設定したものなので、これによってdeploy-keyを用いるようになり、プライベートリポジトリの取得ができます。
自動再起動
サーバーサイド開発ではプロセスがクラッシュするとサービスが停止してしまいます。
普段開発しているiOSアプリの場合、万が一クラッシュしても個々のユーザーが再起動できるため被害は軽微ですが、サーバーが停止していると全ユーザーがサービスを利用できないことになり被害は桁違いです。
とはいえ万が一にもクラッシュしないようにOptional
のアンラップや配列アクセスなど、プログラミングミスでクラッシュしうる箇所を全てチェックし、条件に外れたらthrowしていくというのはコーディングの手間が不必要にかかりますし、何より本当に処理すべきエラーの存在が霞んでしまい、余計にバグが入り込みやすいコードになってしまいかねません。
やはりクラッシュすべきところはクラッシュするようにしておくほうが、開発効率は高く保てると思います。
今回はクラッシュした場合には自動で再起動するように設定しました。
Dockerコンテナはvaporプロセスを起動しており、クラッシュ時にはコンテナも終了するため、restartオプションを付けておくことで自動でコンテナの再起動=Vaporプロセスの再起動を行うようにできます。
しかし、自動再起動ができるとはいえ、プロセス開始からサービスの動作まではそれなりに時間がかかりますし、クラッシュする箇所があるということは何らかのバグでサービスが不完全なものとなってしまっていることになります。
クラッシュが発生したら原因を追跡し、解消していくことは必須です。
画像処理
今回は画像アップロード機能があり、EXIFを消すなどの処理が必要でした。Linux環境ではCoreGraphicsを使えないので別の方法が必要です。
今回はProcess
を用いてImageMagickのコマンドを叩くという手段に落ち着きました。
画像処理に限らない話ですが、他言語だとライブラリが豊富に存在する機能についても、Swift向けのライブラリはあまり提供されていなかったりするので、フレームワーク選定の際の検討事項に入れるべきです。
なおC APIでいいならSwiftPMのsystem moduleを使うという選択肢もあります。
一応ImageMagickのC APIであるMagickWandで使う方法も模索したのですが、SwiftPMのpkg-configサポート機能が不足していて問題が生じたため諦めました。その機能不足については公式にissueが立っています。
https://bugs.swift.org/browse/SR-7689
コミュニティ・ドキュメント
Discord
Vapor公式のコミュニティは最近SlackからDiscordに移りました。
Slackに入ったその日にDiscordへの移行がアナウンスされたのでそれ以前の話は知らないですが、Discordはけっこう盛り上がっています。
質問も気軽にできる雰囲気ではあるのですが、回答がないまま流れているのもよく見ます。
簡単な質問には比較的回答がついていますが、私が出したドキュメント化されていないVaporの仕様やバグに関わるトピックなどには反応が鈍かったです。
そのため結局ソースコードを読んで調べるほうが手っ取り早いという印象でした。
あとPenny Botなるものが導入されています。質問に答えてもらったら@someone ++
と送りましょう。
GitHub
開発中に遭遇したバグ等について何本かIssue/PRを上げましたが、レスポンスがあるまで結構時間がかかる印象でした。
他のIssueを見渡してみても開発者のTannerさんのワンマン運営に近い状態で、素早い対応ができる状況ではなさそうなので、クリティカルなバグにあたったら自分で調査対応するくらいの気概を持って取り組むことが必要そうです。
ドキュメント
案件の着手時期が5月頃で、そのころはまだVapor3がベータだったこともあり、ドキュメントが全然整っていなくて苦労しました。現在は大方ページも埋まってきていますが、開発中にでてくるいろいろを網羅するには大分足りていないので、必要な機能が用意されているかどうかソースを当たりに行くことが必要そうです。
上ドキュメント以外ではコードのドキュメンテーションコメントが豊富な情報を含んでいるので、それらも読むようにするとより理解が深まると思います。
総括
最近3.0がリリースされたばかりということもあり、ネットを調べて見つかる情報が古くて役に立たないケースがままあるので、自分で調べながら進めていく覚悟と時間が必要そうです。それが受け入れられるなら、ベータが外れて安定してきたところなので、始めるにはちょうどいいタイミングだと思います。
iOSアプリとの連携については上述したとおりですが、そうでなくともSwiftで書けるというだけでモチベーションを高く保てました。
総評としては以下のような人にはおすすめできると思います。
- サーバーサイドでSwiftを使うことにメリットを感じている
- iOSアプリとコードを共有したい
- 少々のバグは耐えられる・むしろバグ追跡を楽しめる
個人的には次回サーバーサイドの案件が回ってきたらまたVaporを採用しようと思っています。
- FirebaseのデータをObjectMapperを用いてデータ構造を管理する方法
- Metal逆引きレシピ
- 1年遅れのWWDC 2015 Tour
- Swiftの静的コード解析ツールTailorの使い方
- Swift3でFacebook SDKを使う
- UITableViewのデリゲートメソッドまとめ
- iOS コーディングガイドライン
- Notification的にRxSwiftをクラス間の通知に使う
- React NativeでObjective-C/SwiftのAPIを扱う(Native Component編)
- Firebase Notificationを使用してiOS端末にPush通知を送信する
- Metalの恩恵は受けつつCore Imageで「手軽に」画像処理
- Swiftでのローカル通知・リモート通知の実装メモ
- UINavigationControllerと座標ズレの小ネタ
- [swift] Firebase Realtime Databaseのリスナーについて
- モジュール結合度について
- FacebookやTwitterのアプリで気になった表現を自分なりにトレースした際の実装ポイントまとめ(タイルレイアウトがサムネイル画像の枚数に応じて変わる表現)
- Widgets(Today Extension)のまとめ
- iOSアプリに審査なしでパッチを当てるライブラリを作ってみた
- Swift Evolution の Commonly Proposed がすごい勉強になる
- AndroidとiOSの実装を徹底比較する
- CALayer概要
- [Swift]0から作るXmasカウントダウンアプリ
- 猿でもできるKituraでJSONを生成する方法
- Visual Format Languageを使う【Swift3.0】
- User登録画面で活用できそうなProtocolを活用したDesignPattern3選
- RxSwift スレッドクイズ (解答・解説編)
- Kickstarter-iOSのViewModelの作り方がウマかった
- Swift で 2つの線分の交点を求める
- Metalでカメラからの動画入力をリアルタイム処理する
- iOSアプリ開発でアニメーションするなら押さえておきたい基礎
- iOS標準の写真アプリのように画像をズームしながら遷移させる
- プロトコル指向言語としてのSwift – OOPからPOPへのパラダイムシフトと注意点
- Actionを使って快適なViewModel生活を🏄
- 【Swift】最前面のUIViewControllerを取得する方法
- UIFeedbackGeneratorをiOS10未満対応アプリで楽に書けるUtility
- ViewControllerをObservableとして考える
- 【swift】イラストで分かる!具体的なDelegateの使い方。
- iPod20台を通信/制御/連携してみた【Max × Swift × OSC】
- iOSのMPSCNNによる手書き数字認識のサンプルを読む – 前編
- swift3でCGFloat.min, CGFloat.maxを利用したい
- Objective-CからSwiftへの移行でバグりやすいポイント
- コードレビューをチームにリクエストするSlack BotをSwift実装したお話🚢
- 誰でもわかるプログラミング入門を目指し隊〜Swift編〜
- Metal で 三角形を組み合わせて 2D の線を描く
- Swift中間言語の、ひとまず入り口手前まで
- MetalでiOSアプリに宿る生命
- Xcode 8.2, Swift 3.0でTwitterの認証を通してタイムラインを取得するまで
- Swift 3 以降の NotificationCenter の正しい使い方
- Windows 10でSwift開発環境構築 with Atom
- Observable.just()からはじめるRxSwift
- `as AnyObject` で何が起こるのか
- まだiOS Clean Architecture で消耗してるの? 爆速開発ツールを作ったのでご紹介
- Swiftにおけるアニメーションの世界観を掴む
- [Swift] [iOS] Watson Visual Recognitionを使って顔解析アプリを作ってみた
- 【Swift】いまさらですがiOS10でプッシュ通知を実装したサンプルアプリを作ってみた
- 【Swift3】日本語を含むURLをNSURLに
- スワイプすると出てくるメニューをSwiftで作る
- 脱・文字列ハードコーディング
- Objective-C の循環的複雑度を計測する
- 続:いろんなところでsushiを流す話
- iOSのMetalで畳み込みニューラルネットワーク – MPSCNNを用いた手書き数字認識の実装
- [iOS10] カメラを触ってみる
- ビルドで待たないための、Simulator 上で実行中の view をいじる方法
- Swift での UI テストの雑なまとめ
- iOSの新規プロジェクトでClean Architecture + Application Coordinatorの構成にした理由と感触
- [Swift] バリデーションチェックは、正規表現で!
- MkMapViewの使い方(swift版)
- UIKitにある機能でWebで見かけるようなUI達を作る
- Swift 3でSwiftBondを使ってMVVMしちゃおう
- [Swift] サンプルを見ながら、リファクタリングを勉強する
- Swift製のコマンドラインツールをbrewでインストールできるようにする
- 【Swift】アプリからメーラーを起動する方法
- Swiftで日記アプリを作ろう 〜その1 カレンダー表示編〜
- Carthageを使っていて”$(SRCROOT)/Carthage/Build/iOS/*.framework”を打ち飽きた方へ
- AWS + Nginx + Node.js + iOS(Swift) でリアルタイムチャットアプリを作ろう
- 【Xcode 8対応版】Xcodeプラグイン Alcatrazを導入しよう
- SwiftのAddress/Thread Sanitizer
- iOSで指先またはペン先の動きを記録
- Swiftで日記アプリを作ろう 〜その2 Realm導入編〜
- IBM OpenWhiskというサーバーレス実行環境を用いて、Swiftでおみくじ🎍
- 初心者のためのSprite Kit入門
- 【Swift】Realm BrowserでRealm Mobile Databaseの中身を確認する
- Swiftで日記アプリを作ろう 〜その3 Realm活用編〜
- iOSプロジェクトのテストバンドルに存在するファイルを取得する
- Swift3でDocumentディレクトリのファイルにアクセスする
- RxSwiftでUITextFiledのtextのオブザーバーがSwift3から変わっていた
- 【Swift】UITextFieldのカーソル非表示&コピー・ペーストを不可にする方法
- Alamofire, URLSessionの通信処理をMethod Swizzlingでスタブに置き換える
- 冬休みの8日間を使って、ダンス動画アプリを作った
- Swift で計算して Bezier 曲線を描く
- Swiftで小学生の頃ハマった定規でぐるぐるするやつを描く
- iOSアプリが何回もリジェクトされた後にAppleからフォローアップされる件
- ジェスチャーやカスタムトランジションを利用して入力時やコンテンツ表示時に一工夫を加えたUIの実装ポイントまとめ
- Xcodeの旧バージョンをインストールする方法
- Share Extensionでデータを共有する
- 【iOS10】Firebaseでサイレント通知を行う
- iOSで音声/ビデオ通話をWebRTCを使って実現する(調査編)
- iOSアプリ向け通信スタブの使い方
- Swift3.0対応 CoreGraphicsでPieを描く
- 【iOS】Tinder的アニメーション
- Instagramライクな画像フィルタライブラリをCocoaPodsで公開してみた
- SceneKitで扱える3Dモデルのフォーマット/アニメーションつき3DモデルをSceneKitで使う
- UITabBarにcustomなバッジをつける
- SwiftとJavascriptの変数宣言の比較
- RxSwiftをサクッと勉強してみた
- そういえばMacでAndroidアプリつくるために必要な準備ってなんだっけ?
- 吹き出しのようなViewを作ってみる
- Swiftのエラー4分類が素晴らしすぎるのでみんなに知ってほしい
- Swiftの型の限界を超える
- SwiftのString(文字列) APIとの付き合い方
- 恐怖!忍び寄るライブラリへのロックイン
- 関数型プログラミングの実際のところ
- Never GiveUp というタイトルで Swift Tweets で発表しました。 #swtws
- Clean Architecture 開発ツールの話
- iOSにおけるクリーンアーキテクチャよもやま話 #swtws
- 型推論のビルドが遅いらしいから調べてみる
- iOSエンジニアの正規表現入門
- UIViewPropertyAnimatorの基本まとめ①
- Swift3での日時に関する処理
- プッシュ通知を究める!その①〜普通のプッシュ通知の実装の仕方〜
- プッシュ通知を究める!その②〜リッチ通知(メディア付き)の実装の仕方〜
- KickStarter iOSアプリのStoryboardのenumでの管理のしかたに感動した
- iOSアプリからFirebase Cloud Messaging経由でプッシュ通知を送る
- Swift で Elixir のパイプ演算子を実装してみる
- Swiftの値型と参照型、値渡しと参照渡し
- Swiftのクロージャにおける循環参照
- [Swift] Validatorというライブラリを使ってValidationする
- KituraでWebSocketを動かす
- iOSアプリ開発基礎ハンズオン
- 素晴らしいSwiftのポインタ型の解説
- Swiftのメモリレイアウトを調べる
- Storyboard を使わずコードだけで画面を生成、遷移をしてみる
- DispatchQueueでthrottle/debounceを実現する
- Swiftのenumのメモリレイアウトの最適化が凄い
- RxSwiftでの実装練習の記録ノート(前編:Observerパターンの例とUITableViewの例)
- [Swift3]SegmentedControlでViewを切り替える
- SwiftLintのバージョンを固定する方法
- iOS 10.3で追加されたアプリレビューを投稿できるSKStoreReviewControllerを試してみた
- UIViewControllerAnimatedTransitioning によるカスタムトランジション
- CFSocket を使って iOS で socket 通信した話
- 画像をダブルタップとピンチイン・ピンチアウトで拡大・縮小する Swift3編
-
iOS 10.3からアプリ内レーティングが可能に!- SKStoreReviewController – - Swift3による自動更新購読のアプリ内課金(In-App Purchase)の実装 for 月額課金
- 【Swift】AppStoreのレビューフォームを開くパラメーターが新登場 – action=write-review –
- Swift EvolutionのAcceptedステータスまとめ (2017.1.27) – Protocol-oriented integers, Permit where clauses to constrain associated types, …etc
- 【Swift3.0】xibを使ったカスタムビューの作り方
- iOSの審査に関するまとめ
- Apple Watch に Core Motion を使って色々なデータを取得して表示させてみる
- [macOS]macOS Cocoaプログラミングをはじめる
- 値渡しと参照渡しと参照の値渡しと
- 【iOS】UILabelにPaddingをつける(Swift3対応)
- 【AutoLayout】UILabelの幅を文字列の長さに合わせて可変にする方法
- 【Swift3】ToDoアプリを作る【CoreData】
- Cloud9で始めるServer Side Swift (Vapor)
- UIViewPropertyAnimatorの基本まとめ②
- #Swift fastlaneの次に来る? Sourcery🔮でメタプログラミングする
- iOSのMetal Performance Shadersでニューラルネットを実行する際のモデルの渡し方
- iOSアプリ開発時に使う小ネタ
- 2点間の距離計算 (C, Clojure, Go, Haskell, Java, LOGO, OCaml, Ruby, Rust, Scratch, Swift)
- SwiftでBase64 Encode / Decodeする
- Today Extensionについて
- [Swift 3.0] ViewController単位で画面の向きを制御する
- RxSwiftでの実装練習の記録ノート(後編:DriverパターンとAPIへの通信を伴うMVVM構成のサンプル例)
- swiftのguardを使ってみる
- 乾電池型IoT”MaBeee”を使って簡単なおもちゃ制御アプリを作ってみる
- ReactiveSwift 基本要素の使い方 Signal / SignalProducer編
- [Swift|Kotlin]+Rxで作ったネイティブアプリのコードの違い
- 1行で全てのUIButton(UIView)の同時押しを無効にする
- ありそうでなかったSwiftでのPostリクエストの投げ方
- Swiftによるシリーズアプリの共通部分を切り出して一括管理する
- [iOS] Google VR SDK の代替案としての MetalScope
- iOS アプリの Unit Test – Swift 編
- Swift で Quadratic Bezier 曲線の長さを計算する
- 脳波でスカートをめくるP-WAVEを開発した話
- 今更聞けない?Struct と Class の使い分け方(補足)
- わーい、すごーい
- Swiftで「サクッと使える」通信ライブラリ【RapidFire】
- Swift GPUImage2で画像の平均色を抽出する
- 【Swift x PHP】iOS端末とPHPサーバでHTTP通信
- Auto Layout を使わずにわかりやすいコードでレイアウトが組めるフレームワーク NotAutoLayout
- Carthageで管理しているライブラリのライセンスをまとめてくれるスクリプト
- 【Swift 3】処理の完了を待ってから後続処理を行う
- 【Swift x Ruby】iOS端末とRuby on RailsサーバでHTTP通信
- Swift:UIColorを16進数カラーコードで初期化する
- SwiftLintのRules全まとめ
- Xcode 不使用リソースを抽出する
- ios10 シャッタースピードやホワイトバランスなどiPhoneカメラに特殊機能を実装する方法
- RxSwiftとAlamofireとObject Mapperで
- UITextViewにタップ可能なリンクを挿入する
- web制作者にもわかる、Swift 3が++と–を削除した理由
- iOSのMPSCNNに渡すモデルパラメータのフォーマット / TensorFlowからの書き出し
- [Swift] 三角形のボタンを作る
- [Swift]segueを使った画面遷移、segueを使わない画面遷移
- SwiftにおけるMethod Dispatchについて
- ZIG SIMが送信するセンサの種類とデータ構造について
- TwitterAPIとSwiftを使ってiOSアプリを作ろう! – 前編 – #dotsgirls
- TwitterAPIとSwiftを使ってiOSアプリを作ろう! – 後編 – #dotsgirls
- TwitterAPIとSwiftを使ってiOSアプリを作ろう! – カスタマイズ編 – #dotsgirls
- Google画像検索APIをiOSで利用する
- Apple公式のiPhoneアプリチュートリアルやってみた①(Build a Basic UI)
- Swift3対応をこれからする人が役立ちそうなこと!
- tableView(_:didSelectRowAt:)にて、modalなSegueをperformSegueすると描画が遅い件は、DispatchQueue.main.asyncで解決できる
- Custom URL Schemeでアプリ内の任意のページを表示する
- Swiftの強力な機能であるstaticメソッド制約の紹介と、Kotlin, TypeScript, Java, Scala, C++との比較
- [iOS] Swift + Kannaでtableをスクレイピング
- 激安NSAttributedString
- lazy var の遅延参照?という面白い動き
- UIButtonの画像をUIButtonに対してAspectFitする方法
- iOS の時間関数の精度
- [Swift] count == 0 より isEmpty を使うべき理由
- [swift] InterfaceBuilderで多角形のボタンを作る
- Swift3で消費型アプリ内課金(In-App-Purchase)を実装してみた
- UIStackViewを使って詳細ページの実装をシンプルにする
- Swift 3のUIScrollViewでカルーセルUI(ページング/画像などをスワイプで行き来できるView)をつくる
- iOS(Swift)でアラート画面のようにViewの背景透過をする
- Concourse CIでiOSアプリのCIを行う
- VO, DTO, POSO, DAO, Entity の違い
- Swift の文字列の長さ
- モックオブジェクトをより便利にする (Try! Swift2017)
- よくわかるUIGestureRecognizerDelegate
- Swift,iOS,Xcodeの設計についてメモ
- サーバサイドSwiftの現状考察 #tryswiftconf
- Try! Swift 2017のまとめ
- TableViewで複数セルを一気に複数削除する
- 新しいアプリを作るときによく使うSwift Extension集
- ユニットテストにも可読性を持たせる (Four Phase Test)
- try! Swift 2017 Tokyo 参加レポート
- UIImageのリサイズ方法と注意点
- 始めて見よう! SwiftでWebアプリ開発(基礎編)
- try! Swift Tokyo 2017 に参加した&リンク集
- 2017年におけるObjective-Cコミュニティの動向
- SpriteKitの衝突処理について(categoryBitMask collisionBitMask contactTestBitMask 使い方)
- 猿がようやく理解したSwiftのdeferの使いどころ
- [Swift3.0][iOS][SpriteKit]SpriteKitでテキストを落としたり、ぶっ飛ばしたりした!
- SwiftはどのようにJavaの検査例外を改善したか
- iOS 10以降のNotificationの基本
- 猿がついに理解できたSwiftのthrow・do・try・catchの意味
- print()やNSLogを書かずにconsoleにメッセージを出力する方法
- 猿がもがきまくって理解したSwiftのデリゲート(Delegate)という仕組み
- iOS Test Night_#3に参加できず悲しいのでまとめた
- iOS Test Night #3
- Firebase Cloud Messagingで画像つきプッシュ通知を送信する
- TypeというiOS用のMarkdownエディタを作った
- iOSのCIにモバイル向けCI、Bitriseを導入する(CocoaPods, GitHub Privateレポジトリ対応版)
- RxSwift 3.3.0で追加された3つのUnit(Single, Maybe, Completable)
- CAMPFIRE iOS #1 参加レポート
- 【iOS】ViewControllerを汚さずにUITableViewの下部からCellを追加する実装
- [Swift3]文字列が人名かどうかをバリデーションする方法
- [Swift] シンプルなカウントアップでSequenceに強くなる
- LINQライブラリまとめ
- iOSのBackground Transferがよくわからない人用に整理した
- [iOS][Swift]UITableViewのヘッダーとフッターを設定する方法
- 【Git】 project.pbxprojのコンフリクトの直し方
- Swiftのmutatingキーワードについて
- アプリにApple Payを導入する – 商品購入のハードルを下げる –
- Swiftでasync/awaitな書き方もできるPromiseライブラリHydra
- Playground で Carthage ライブラリを import する
- キリスト教の牧師(見習い)からWebエンジニアに転職した話
- [Swift 3.0]NSLayoutAnchorを用いたコードによるAuto Layout
- 俺の俺による俺のための iOS プログラム設計
- 和暦に関するメモ
- Interface Builderとソースコードで共通のカラーパターンを利用する方法
- SwiftyBeaver でアプリケーションログをメール送信しデバッグしやすい環境を作る
- [MVVM] kickstarter/ios-oss での画面遷移のやり方
-
図解 MemoryLayout
で解き明かす型のメモリー構成 - iOSのビルド高速化 7つの方法
- LINE NEWS風のUI実装
- カメラロールのようなシンプルなイメージピッカーの作成
- swiftLint 導入
- Instagramライクなパン/ピンチ操作できるイメージビューの作成
- 【iOS】アプリ内からレビューを依頼する 10.3未満も対応
- How to write basic UnitTestsを発表した際の参考資料
- Swiftのstatic、classキーワードの違いについて
- 心がHomebrewで旧バージョンのパッケージを入れたがってるんだ
- ただ画像を右側に表示したいだけなんだ・・・ – UIButtonの画像を右寄せにする
- 制約の修正なしで縦方向に要素を追加可能なビューの作成
- Storyboard上で置いてるUIVisualEffectViewのブラーのかかり具合を調節、アニメーションさせる [iOS10]
- DI(Dependency Injection)の概要
- SwiftRater を使って好きなタイミングで SKStoreReviewController を表示する
- Swift 3.1で死んだコードまとめ
- クラスや変数の名前とかを安易に英語にしたい
- Swiftのprint()をファイル名、行数、関数名を出力して分かりやすくする
- iPhoneのSFSpeechRecognizerとAVSpeechSynthesizerと発泡スチロールでボスっぽいなにかを作る
- [Swift] 順序付き辞書、DictionaryLiteral
- iOS/Androidアプリを突貫工事で開発するときにやって良かった!悪かった!こと まとめ
- APIKitでRequestのイニシャライザに渡すのはValueObjectやEntityじゃないほうが良い
- iOS 実装サンプルアプリ集
- iOSアプリを新規開発するときに最低限守っていること
- 日付関連クラスのまとめ(Swift3)
- Foundation.Operationの並列オペレーションがよくわからない人向けの説明
- Swiftで複数のフラグを管理するためにOptionSetを使うと便利だった
- Swiftで連番画像のコマ送りアニメーション
- Swift3でJSONパースを行う
- Swift3.0 NavigationBarをhidden(隠す)方法
- iOS 10 での通知処理について
- 【Swift3】RxSwift + APIKit + Himotokiで作るAPIクライアント
- Swiftでいい感じのViewModelを作るためのメモ
- IGListKitとRxSwiftを掛け合わせてみた🤔
- UIFeedbackGeneratorの使い方と便利に使えるライブラリ
- カスタムViewをNibから初期化の最新版
- 【iOS】ステータスバーのカスタマイズ【Swift3.0】
- carthageのバージョンを今すぐ上げよう
- SwiftでContainerViewを使ってみる
- Swift の struct の stored property は var にしよう
- アイコンを申請なしで変える(iOS10.3)
- SwiftのOptionalのベストプラクティス
- Vapor as a web framework
- モックを「差し込む方法」を考える
- Swift4で何がかわりそうなのか
- Swift Standard Library相関図を作ってみた・眺めてみた #swtws
- iOS(Swift)から3ステップでMastodonに投稿を行う
- なぜDelegateをプロパティに持つとweakを指定しなければいけないの?
- SlideMenuControllerSwiftの使い方とカスタマイズ
- あとで読むQiitaリーダーアプリをリリースしました
- null安全な言語でも、バグ検知を怠れば安心ではない
- 【ネタ】Swift で Sleep Sort
- [Swift] enumerated()はindexを返さない
- [SiriKit]Siriから料金の支払いをする
- 2017年版Realmのエレガントな使い方
- Swiftのプロパティとinout参照を組み合わせたときの挙動が面白い
- swiftのenumでStringがカスタムなときでもenumを諦めない
- 【iOS】Fluxを利用して画面遷移を制御する
- Swift3.0 で CoreGraphicsを使って見る
- Debug Memory Graph で Memory Leak を調査する
- Apple公式のiOS開発チュートリアルをやってみた(Andoridエンジニア視点)
- Swift 4の新しいreduceが素晴らしいので紹介する
- Alert,ActionSheetの表示処理がスッキリかけるUIAlertControllerのラッパーライブラリを作りました
- 体系的なSwift言語学習
- AppのIconからVersionやCommitを判別して混乱を防ぐ
- IB上で範囲の確認ができるUIButtonのタップエリアを拡大する実装
- RxSwift 用語解説
- 【Swift3.0】Alamofireで画像&パラメータを送信
- Swift Package Manager(SwiftPM)で作ったコマンドラインツールをHomebrewに登録する方法
- SwiftではバージョンはStructにして演算子オーバーロードで比較したらどうでしょう
- Swift3ではKVOにkeyPath()式を使っていくのが便利
- やはりお前らのboundingRectWithSizeは間違っている
- SwiftでTestコード(on Xcode)
- `typealias My` をススメようと思ったけどやめた件 あるいはstatic methodをどうやって呼ぶか
- Firebase iOS SDKが刷新されましたよっていう話し
- [2017年版]RxSwift + Alamofire + ObjectMapper + RealmのSwift実装について
- BluetoothをもちいたiOS同士の通信
- Rails 5 Action CableチャットアプリのiOSクライアント側を作る
- SpeakerDeckのスライドをPDF形式で表示できるiOSアプリを作った
- SwiftでArrayがnilか0件の場合に共通の処理を動作させたい時はnil結合演算子を使う
- fastlaneを導入してビルドを楽にする
- 10年間の iOS 機能のまとめ with WWDC
- 書評:Swiftの各機能が「なぜ」存在し「いつ」使うべきかを解説した技術書 – Swift実践入門
- iOS SwiftでBLEのサンプルを動かしてみる
- Swift3で日付(Date)の比較が超簡単になっていた件
- AppleWatchKitとSpriteKitでスペースシューティングゲームを作ってみた
- 様々な言語でMap, Filter, Reduceを実現してみた(1)
- アニメーション付きのボタンを実装するためのテクニック
- Setを使いこなしたい(願望)
- AVFoundationで動画のリアルタイム合成
- Swiftガイドライン的な
- 位置情報アプリ開発者必見!Energy Efficiency Guide for iOS AppsのReduce Location Accuracy and Durationを読んでみた
- Instagramのような画面UIを簡単に作れるPastelViewを試してみた
- Swift API デザインガイドライン
- インタフェースと型クラス、どちらでもできること・どちらかでしかできないこと
- サーバーレスサーバーサイドSwiftとHexaville
- RxSwiftのExamplesにしれっと入ってる双方向データバインディングの演算子がイケてた
- Swift3対応をしてハマった不具合
- iOSでアプリ間でデータをやり取りするためのNの試行
- Embedded framework使用時の肥大化問題
- Swiftならメモ化も最高にスッキリ書けます
- 詳解! ios-Charts
- Instagramのログイン画面みたいなグラデーションのアニメーションを自分で作る
- Swiftのmap, filter, reduce, etc…は(あたりまえだけど)for, if, switch, whileからできている
- iOS11で新しく導入されたFramework
- pageViewController(_: viewControllerBefore:) および pageViewController(_: viewControllerAfter:) が呼び出されるタイミングについて(UIPageViewController)
- What’s New in iOS11まとめ (Metal2以外)
- Xcode による iOS 開発で秘匿したい情報をどう管理するか
- iOS11のCoreNFCを使う
- 公式ドキュメントを追いながらARKitを試してみよう
- Swiftでクラス名や関数名等をログ出力する
- 【iOS 11】開発者ドキュメントから見る iOS 11 の新機能 #WWDC17
- WWDC2017で更新されたサンプルコードまとめ
- Xcode8のDebug Memory GraphでCFArrayのメモリリークの原因を探る
- ARKitを触ってみよう 〜第1話〜
- Swift4.0 で追加される Codable
- 引っ張って閉じることができるモーダルを実装する (UINavigationControllerの場合)
- iOS11 Swipe Actions
- [iOS][Swift3] ニュース系アプリのユーザインタフェース PageMenuKit の実装
- Swift4 CodableでJSONが扱いやすくなる?
- Swift4のCodableでフラットなJSONからネストしたオブジェクトにデコードする
- Swift4のCodableでISO8601の日付をデコードする
- 【iOS 11】【Core ML】pip install coremltools でエラーになった場合の対処法
- Swift4のJSONDecorderは、Date等のパース方法をカスタマイズできるみたい
- ReactorKit(Flux + Reactive Programming)を学ぶ1 入門編
- ReactorKit(Flux + Reactive Programming)を学ぶ2 基礎編
- Swift4のCodableに対応した、独自のDecoder(CSVDecoder)を実装してみよう
- 個人開発アプリでSwift 4に一足先に対応しました
- [Xcode 8] Swiftのドキュメントコメントについての簡潔なまとめ
- init の名は。
- iOS11で追加されたDeviceCheckについて
- SwiftでWebRTC実装ハンズオン 事前準備編
- iOS11で発表されたMusicKitについて
- iOS 11 WKWebView 3大新機能 (WWDC 2017)
- NSURLSessionがメモリリークしてしまうのをなんとかした
- Array や Dictionary にもモダンでオシャレな extension を実装する
- 99%くらいのSwiftプログラマーが使わないであろう演算子の話
- UIKitのView表示ライフサイクルを理解する
- 【まとめ】What’s New in Testing【WWDC 2017】
- Swift PlaygroundsでXcode projectのコードを動かしてみよう
- WWDC17で新しく発表された画像フォーマットHEIFを使ってみた
- iOSでの各回転検知方法とその結果
- SwiftでWebRTC実装ハンズオン 本編
- [Swift4] privateにextensionからアクセスできる
- iOS – CrashlyticsのrecordErrorでカスタムログを送る
- コードを一行も書かずにHello Worldする方法
- 画像を表示する際にモアレ(干渉縞)を抑制する方法
- APIKitとCodableでAPIクライアントを作る
- CodingKeyで、case名のcamelCase ⇄ stringValueのsnake_case を自動で変換する
- 【まとめ】Engineering for Testability (前半)【WWDC 2017】
- 【まとめ】Engineering for Testability (後半)【WWDC 2017】
- 【Swift】iOSで放置型育成ゲームを作りたい(0)
- [Swift]初心者のためのSwiftチートシート
- Swift Package Manager (SwiftPM) Version 4 概要
- ARKitで豆腐作り
- Swiftで複数の非同期処理の完了時に処理を行う
- 【Swift】hogehoge.delegate = self は何をしているのか。
- cmdshelfによるスクリプト運用のご提案
- mbed × BLE × iOSでとりあえず通信したい人のための記事
- iOS LINEでログイン(Line SDK)
- Swift:UserDefaultsで初期値を設定する方法
- ios, androidのローカライズファイルを共通管理して幸せになった話
- 型システムの理論からみるSwiftの存在型(Existential Type)
- Swift4.0でDictionaryが大幅にパワーアップした
- Swift4で変更されたStringAPIをいくつか試してみた
- NO MORE ビルド時間泥棒 ☕️❌【RxSwift編】
- 新規アプリサービスのためのログ実装とサービス選定
- SwiftのSelfキーワードの使い方まとめ
- [Swift]WinでやるC#erの為のSwift基本文法覚書
- Decoder, DecodingContainerの、デコード先の型を推論させたい!明示的に指定したくない!
- XCode9で追加されたAsset catalogsのNamed colors supportについて
- Swiftのattributeまとめ[Swift4対応]
- SwiftでもKotlinのif式を使いたい
- iOSでBeaconの振る舞いを確認する
- はじめてのSwiftアプリ制作4: StoryboardとAuto Layoutその1
- Swift4でのSingletonを用いた共通データの値渡し
- ルートが配列のJSONをCoadableでカスタムモデルにマッピングする
- iOSで楽にデバッグメニューをつける
- 【Swift】TextFieldのキーボードを閉じる方法3選
- SwiftとKotlinの文法を比較してみた(基礎パート)
- [コピペで使える]swift3/swift4でリアルタイム顔認識をする方法
- Swift4 [SE-160 Limiting @objc inference] 概要
- SwiftのExtensionによるクラス分割
- Xcodeのビルド待ちで消耗してたので見直したら50%以上削減できた話
- Swiftコンパイラ開発環境構築
- (初心者向け)Flickr Apiを使って画像を引っ張ってくる(1)
- Equatableとは?(swift) ~ object同士を比較できるようにしよう〜
- Swift4のCodableが内部で何をやっているか確認する
- SwiftでアプリのCPU使用率とメモリ使用量を取得する
- できるだけプログラムっぽくないプログラミングへの挑戦(Swift編)
- MVVMをベースにしつつCleanArchitectureを取り入れてみた
- GCD(Grand Central Dispatch)でキューの順番とスピードを制御する方法(1/2)
- GCD(Grand Central Dispatch)でキューの順番とスピードを制御する方法(2/2)
- Swiftコンパイラのテスト環境
- できるだけプログラムっぽくないプログラミングへの挑戦(Objective-C編)
- 特定のアプリがインストール済みかチェックする
- 「PythonとSwiftは結構似ている」説の検証
- ARKitでタップした座標を検出する方法
- Aspect Fill, Aspect Fit, Scale to Fillの違い
- APIKitとCodableとの連携
- ストーリーボードでUIを綺麗にレイアウトするネタ集
- Swift コンパイラのアーキテクチャ
- 最近Swift書いていて可読性を上げるために意識していること
- Swiftで静的DI(Mixin-Injection)
- UIImageView で cornerRadius と Shadow を同時に使いたい
- Enigmaの実装
- iOS開発でClean Architectureを採用した際のイイ感じのディレクトリ構成とは
- iOS開発で導入しているライブラリの一言説明
- 【メモ】Xcode9ビルドでCarthage経由で導入したライブラリに関してswift version errorが発生
- インクリメンタルサーチ【RxSwift/RxCocoa編】
- CotEditor を Swift に移行する
- Swift3.0でアニメーション1 ~ Animate()メソッド編~
- Swiftに息づくstructural types(構造的型)
- メソッドのhookは正しいタイミングで行おう【RxSwift/RxCocoa】
- 実践Swiftコンパイラ #swtws
- 純粋値型Swift
- Mac: 開発向け厳選ツール群(18/6/23更新)
- Swift でアニメーションの連続実行をしてみる話
- SwiftでiOS脱獄チェック
- iOSの機械学習フレームワークの比較 – Core ML / Metal Performance Shaders (MPSCNN) / BNNS
- 【初心者向け】Core Dataの使い方と説明swift3.0
- RxSwiftのshare*の早見表
- Swiftの有名画像キャッシュライブラリを比較してみた
- indexがArrayの範囲内かチェックする色々な書き方
- 【iOS】fastlaneでipaファイルを作成して、fabric crashlyticsでベータ版を配布し、Slackで完了通知を行う
- iOSアプリでMockを使ってUnitTestを書く
- iOSアプリでCIを始めようとサービスを調べた
- StubとSpyを使ってiOSのUnitテストを書いてみた(Clean architecture)
- RSKImageCropperの使い方とカスタマイズ
- アプリ内での Touch ID を利用したユーザ認証
- Automatically manage signingとxcconigで超効率化
- Instagram APIでOAuth認証する (Swift3版)
- [Swift] MainThreadで処理を実行する
- Carthage updateとCarthage bootstrapの違い
- iOSのクラッシュログをSymbolicate(復元)して解析する
- やさしいSwift単体テスト~テスト可能なクラス設計・前編~
- やさしいSwift単体テスト~テスト可能なクラス設計・後編~
- [iOS][Swift 4] CodableでJSONのパース
- [Swift] Dictionaryをこねくり回すネタ集
- Screen Recordingの録画開始・停止を取得する
- TDD ✕ Property-based Testing (SwiftCheck) で数学パズルを検証してみる
- 超効率化外伝: xcconfigの便利なところ&設定例
- Swiftで花火を作った話
- SwiftでHigher Kinded Polymorphismを実現する
- 絶対にやってはいけない「Apple IDをテストで13歳未満にすること・・」
- Spajam2017優秀賞「嫌われAIの命名」の発想プロセスからiOSアプリ実装まで
- RxSwift `a.withLatestFrom(a)` 同じ上流元の同期的合流問題
- Range が Codable に適合してなかったので後付けで適合させてみる話と、Codable のエラーハンドリングについて
- Swiftのfinalについて
- 【Swift】Dateの王道 【日付】
- サーバーレスとiOSアプリの連携 〜IBM Cloud Functionsを使ってサーバーサイドSwiftで試してみる
- ARKitで立方体の6面それぞれに異なるテクスチャを貼る方法
- iOSでlottie-iosを使ってリッチなアニメーションを簡単に実現してみる
- Swift の class の mutating func とは何か
- 余計なOptionalはやめてくれ
- 【プッシュ通知】Y Combinatorも投資するOneSignalがFirebaseより便利で素晴らしかった
- 【swiftエラー】clang: error: linker command failed with exit code 1 (use -vto see invocation)
- RxSwiftでwithLatestFromが最新じゃなくなることがあるorz
- RxSwiftについてようやく理解できてきたのでまとめることにした(1)
- RxSwiftについてようやく理解できてきたのでまとめることにした(2)
- RxSwiftについてようやく理解できてきたのでまとめることにした(3)
- RxSwiftについてようやく理解できてきたのでまとめることにした(4)
- iOSの消耗型課金のサーバーサイドTipsまとめ
- iOS 11 WKWebViewで広告などのコンテンツブロックをする
- タブスワイプで画面を切り替えるメニューUI
- 【MacOS】スクリーンレコーディング 【Swift】
- Codableについて色々まとめた[Swift4.x]
- FirebaseStorageの画像をSDWebImageで表示しようとして詰まった話
- [Swift] classにEquatableを実装するのは一筋縄ではいかない(ことがある)点に注意。
- ニュースアプリでAPIの記事をRealmにキャッシュして有効期限内だったらそれを表示する
- Swiftで参照型の値から生ポインタを作る方法
- Swift の protocol における Interface と Method の違いを理解しよう
- ARKitを扱う際の心構えとTips
- 脱Storyboardのすすめ
- Firebaseでアプリを開発するならClient Side Joinを前提にすること
- UILabelの文字色をグラデーションさせる
- [Swift3]ローカル通知の実装方法
- [Swift]iOSのデフォルトの関数を活用した Validation String Extension集
- Closureって美味しいの?
- [Swift3]アプリ内でレビューを依頼する
- プッシュ通知設定画面へ遷移させる為の実装
- iOS9, 10 WKWebView – Cookie操作
- iPad対応アプリを開発するときに、UI周りで気をつけることをまとめてみた
- Swift 4 で「プラマイ」範囲を作る
- ぼくのやっているVIPER(のようなもの)
- MVVMを勉強するときに参考になったリンク集 & 概要まとめ
- Xcode8でiOS11beta端末の動作確認がしたい(iOS11実機ビルドしたい)Could not locate device support files.エラー
- iOS11のバグ修正を行うに当たって気になったレイアウト関連の変更点(contentInsetAdjustmentBehavior, SafeAreaLayoutGuide)
- iOS 11 UITableViewでcontentOffsetを使ったスクロールが上手くいかない
- iOS 11 の Safe Area は Auto Layout だけでなくコードベースでも取れる
- iOS向けfastlaneアクションまとめ
- iPhone Xをネイティブ解像度から判定する
- Swift4 Stringのsubstring周りが変わっていた
- iPhone X Human Interface Guidelinesの要点
- iOSDC 2017 まとめ
- RxSwift の Observable とは何か
- Swaggerで始めるAPI定義管理とコードジェネレート
- iOS11で追加されるScreen Recordingについて
- iOS11 カメラとCoreML(Vision)で画像検出
- Swift初心者が3ヶ月でiOSアプリを公開するまでにやったこと、ハマったこと。
- iOSアプリ開発の全体像
- ARKitのコードによく出てくる4次元行列transformについて
- Swiftの @escaping と weak/unowned の理解
- iOSDC Japan 2017で「Auto Layoutのアルゴリズム」について発表しました
- Setは遅いのか
- iOSDC 2017 でさらっと出てきた Phantom Type さらっとやった話
- 超朗報。Xcode 9でやっとSwiftのリファクタリングが可能に
- iOS11から搭載されるスクリーンレコーディングでの録画を検知する方法
- Swift 4 マイグレーション、またはXcode9対応 メモ
- iOSと人工知能(AI) -GPU並列演算の仕組みと機械学習- というタイトルで、iOSDC2017に登壇しました
- 今度のiPhone Xは我々開発者をどれほど苦しめるのか #okamoba
- ARKitのサンプルコード集「ARKit-Sampler」
- iOS11のTwitter投稿対応(Social.framework → TwitterKit)
- iOS11で Grouped UITableView のセクションヘッダーに余分な高さが出る問題について
- [Swift] UserDefaults に画像を保存するとフリーズした
- iOS11 + Xcode9.0でedgesForExtendedLayoutの値を空にしていると、UITableViewのドリルダウンでアニメーションが崩れる
- 【iOSDC2017】MVC→MVP→MVVM→Fluxの実装の違いを比較してみる
- [Swift4]ARKitで球体をランダムに描画する
- 【Xcode9】ファイルヘッダーコメントをカスタマイズする
- [Swift] CharacterSetはCharacterのsetではありませんよ?
- iOS11のVision.frameworkを使ってみる
- iOS11からViewの一部だけを角丸にすることが簡単になった
- Swift 4の魅力の一面を3行で表す
- [Swift] 読み上げ機能、簡単に使えるライブラリつくったよ
- iOS11.0でUINavigationControllerのTitleViewのタッチイベントが呼ばれない現象について
- PDFKit を使ってみた
- Xcode の Debug Memory Graph が便利
- iOSアプリで紙吹雪を降らして画面を賑やかにする
- iOS 11ファイルAppにDocumentsフォルダを表示して他のアプリと共有する方法
- 【Swift】 画像を3種類も書き出したくないでござる
- Swift4 全予約語 (98語) の解説
- ライブラリを使わずにMV*の話(iOS)~ViewとModelの役割〜
- ライブラリを使わずにMV*の話(iOS)〜MVC, MVP, MVVM〜
- .ipa file を実機にインストールする方法(iTunes 12.7)
- UILabelをNSAttributedStringで文字装飾(Swift 4対応)
- AutoLayoutでiPhoneXのedge-to-edge対応

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。