Point Free製のURL-ROUTINGを使ってディープリンクのルーティング書くとめちゃくちゃ便利でした
https://github.com/pointfreeco/swift-url-routing
動機
使用してみて便利だったので記事にします。
DeepLinkで渡っていたURLをどう処理するかを、文字列をゴニョゴニョすることなく、
うまくルーティングすることができたので備忘録として残しておきます。
SwiftでiOSアプリ開発だけでなく、サーバーサイドにも使用できるそうです。
ルートをenumで定義する
ルーティングをenumで定義していきます。
enum AppRoute {
case books
case book(id: Int)
case searchBooks(query: String, count: Int = 10)
}
URLごとに、どういうPathで渡ってきた場合、
どのcaseで返せばよいかを記述します。
パラメータとして渡される場合、どんな型で渡されるかも記述します。
例えば以下の2つ目の例では、数字が渡ってくることを
Digits()を通じてparseしています。
import URLRouting
let appRouter = OneOf {
// GET /books
Route(.case(AppRoute.books))) {
Path { "books" }
}
// GET /books/:id
Route(.case(AppRoute.books(id:))) {
Path { "books"; Digits() }
}
// GET /books/search?query=:query&count=:count
Route(.case(AppRoute.searchBooks(query:count:))) {
Path { "books"; "search" }
Query {
Field("query")
Field("count", default: 10) { Digits() }
}
}
}
パラメータとして渡される値をどのようにParseするかは、
https://github.com/pointfreeco/swift-parsing
このPoint Free製のSwift-Parsingを使用しています。
渡ってきたURLをどのように処理するかを書く
func handleDeepLink(url: URL) throws {
switch try appRouter.match(url: url) {
case .books:
// navigate to books screen
case let .book(id: id):
// navigate to book with id
case let .searchBooks(query: query, count: count):
// navigate to search screen with query and count
}
}
appRouterの関数で
func match(url: URL)
にURLを渡すとenum AppRouteが返さるので、
ルートごとにどのような処理をするか書きます。
Deeplinkの場合は、
ディープリンクの場合で例えば以下のようなURLがあるとき
mydeeplink://foo/bar/3
Route(.case(AppRoute.fooBar(id:))) {
Path { "foo"; "bar"; Digits() }
}
このようなRouterだと機能しません。
/bar/3
しか見ていないので。
つまり、hostを判定する必要があります。
以下、Swift-Parsing https://github.com/pointfreeco/swift-parsing
を拡張してHost名を判定するParseを作りました↓
import Foundation
import URLRouting
import Parsing
struct Host: ParserPrinter {
@usableFromInline
let name: String
@inlinable
public init(_ name: String) {
self.name = name
}
@inlinable
public func parse(_ input: inout URLRequestData) throws {
guard let host = input.host else { throw RoutingError() }
try self.name.parse(host)
input.host = nil
}
@inlinable
public func print(_ output: (), into input: inout URLRequestData) {
input.host = self.name
}
}
@usableFromInline
struct RoutingError: Error {
@usableFromInline
init() {}
}
mydeeplink://foo/bar/3
Route(.case(AppRoute.fooBar(id:))) {
Host("foo")
Path {"bar"; Digits() }
}
追加したHostを使って上記のように記述すればよいです。
おわり
これのおかげで、文字列でゴニョゴニョする手間を省けるので便利でした!
指定した型に変換して返すことも、Swift Parse https://github.com/pointfreeco/swift-parsing を利用してParserを作れば良いみたいです。
余談
-
URLRoutingを使ってjsonでリクエストとかも送れるみたいです。
https://github.com/pointfreeco/isowords/blob/154141a0c83f8e1ce3cb922e47a8a95fa2d77f2c/Sources/ServerRouter/Router.swift -
フリーランスでSwiftUI, ComposableArchitecture, Firestoreの組み合わせのお仕事あれば、ご紹介お願いします!