Swift URLからパラメーターを取り出す
ディープリンク対応する際にこちらのリポジトリを参考に実装しました。
urlのパラメーター(具体的にはuser?id=0
user/:id
的なやつ)はpathParameterとqueryParameterと呼ばれているらしいです。
使用例
URLの文字列をpattern文字列を指定してパースします。 パース結果のオブジェクトはqueryParamsとpathParamsプロパティーを持ちます。 これらはSwift4.2で追加されたDynamicMemberLookUpを使ったPrameterオブジェクトとして定義しています。
例えば通常のパラメーターの場合はqueryParamsで取得できます
let pattern: "myapp://user" let actual: "myapp://user?id=123" ParsedObject(url: actual, pattern: pattern).queryParams.id // "123"
パターン文字列:id
のように指定すると
pathParamsの値にマップされてpathParams.id
として取得できます。
let pattern: "myapp://user/:id" let actual: "myapp://user/123" ParsedObject(url: actual, pattern: pattern).pathParams.id // "123"
先述した通り内部でDynamicMemberLookUpを利用することで、 パターンの文字列がそのまま変数名になります。
let pattern: "myapp://user/:userId" let actual: "myapp://user/123" ParsedObject(url: actual, pattern: pattern).pathParams.userId // "123"
どちらもある場合はこんな感じです。
let pattern: "myapp://user/:id/" let actual: "myapp://user/xxx?id=yyy" ParsedObject(url: actual, pattern: pattern).queryParams.id // "yyyy" ParsedObject(url: actual, pattern: pattern).pathParams.id // "xxxx"
ソース
Ap
struct ParsedURL { let openUrlOption: OpenURLOption let pathParams: Parameter let queryParams: Parameter init?(_ patternUrl: URL, url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) { guard url.scheme == patternUrl.scheme, url.host == patternUrl.host , patternUrl.pathComponents.count == url.pathComponents.count else { return nil } let keywordPrefix = ":" // init pathParams do { var params: [String: String] = [:] // compare host if patternHost.hasPrefix(keywordPrefix) { let key = String(patternHost[keywordPrefix.endIndex...]) params[key] = url.host } // compare path components for (component, pathCompoent) in zip(patternUrl.pathComponents, url.pathComponents) { if component.hasPrefix(keywordPrefix) { let key = String(component[keywordPrefix.endIndex...]) params[key] = pathCompoent } else if component != pathCompoent { return nil } } self.pathParams = Parameter(values: params) } // init queryParams do { if let components = URLComponents(url: url, resolvingAgainstBaseURL: true) { var params: [String: String] = [:] components.queryItems?.forEach { params[$0.name] = $0.value } self.queryParams = Parameter(values: params) } else { self.queryParams = Parameter(values: [:]) } } self.openUrlOption = OpenURLOption(options: options) } } extension Scheme { @dynamicMemberLookup struct Parameter { let values: [String: String] subscript(dynamicMember name: String) -> String? { return values[name] } } struct OpenURLOption { let sourceApplication: String? let annotation: UIDocumentInteractionController? let openInPlace: Bool init(options: [UIApplication.OpenURLOptionsKey: Any]) { self.sourceApplication = options[.sourceApplication] as? String self.annotation = options[.annotation] as? UIDocumentInteractionController self.openInPlace = options[.openInPlace] as? Bool ?? false } } }