Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Custom URL Schemeを支える技術

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Custom URL Schemeを支える技術

Avatar for giginet

giginet PRO

July 26, 2018
Tweet

More Decks by giginet

Other Decks in Programming

Transcript

  1. let url = URL(string: "pokedex://pokemons/25")! let components = url.pathComponents if

    url.scheme == "pokedex" { if url.host == "pokemons" { if components.count == 2, let pokedexID: Int = Int(components[1]) { presentPokemonDetailViewController(of: pokedexID) } else if ( ... ) { } else { // ... } } }
  2. let url = URL(string: "pokedex://pokemons/25")! let components = url.pathComponents if

    url.scheme == "pokedex" { if url.host == "pokemons" { if components.count == 2, let pokedexID: Int = Int(components[1]) { presentPokemonDetailViewController(of: pokedexID) } else if ( ... ) { } else { // ... } } }
  3. import Crossroad class AppDelegate: UIApplicationDelegate { let router: DefaultRouter =

    { let router = DefaultRouter(scheme: "scheme") router.register([ ("scheme://search", { _ in return true }), // ... ]) return router }() func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any]) -> Bool { return router.openIfPossible(url, options: options) } }
  4. let router = DefaultRouter(scheme: "pokedex") router.register([ ("pokedex://pokemons", { context in

    let type: Type? = context.parameter(for: "type") presentPokedexListViewController(for: type) return true }), ("pokedex://pokemons/:pokedexID", { context in guard let pokedexID: Int = try? context.arguments(for: "pokedexID") else { return false } guard let pokemon = Pokedex.find(by: pokedexID) else { return false } presentPokemonDetailViewController(of: pokedexID) return true }), ]) router.openIfPossible(url)
  5. let router = DefaultRouter(scheme: "pokedex") router.register([ ("pokedex://pokemons", { context in

    let type: Type? = context.parameter(for: "type") presentPokedexListViewController(for: type) return true }), ("pokedex://pokemons/:pokedexID", { context in guard let pokedexID: Int = try? context.arguments(for: "pokedexID") else { return false } guard let pokemon = Pokedex.find(by: pokedexID) else { return false } presentPokemonDetailViewController(of: pokedexID) return true }), ]) router.openIfPossible(url) pokedex://pokemons/25 25
  6. public protocol Extractable { static func extract(from: String) -> Self?

    } extension Int: Extractable { public static func extract(from string: String) -> Int? { return Int(string) } } let pokedexID: Int? = try? context.arguments(for: "pokedexID")
  7. enum Type: String, Extractable { case normal // ϊʔϚϧλΠϓ case

    fire // ΄ͷ͓λΠϓ case water // ΈͣλΠϓ case grass // ͘͞λΠϓ // ... }
  8. public extension RawRepresentable where Self: Extractable, Self.RawValue == String {

    public static func extract(from string: String) -> Self? { return Self(rawValue: string) } }
  9. // match pokedex://pokemons?type=fire ("pokedex://pokemons", { context in let type: Type?

    = context.parameters(for: "type") // ϙέϞϯҰཡը໘Λදࣔ͢Δ presentPokemonListViewController(of: type) return true })
  10. extension Array: Extractable where Array.Element: Extractable { static func extract(from

    string: String) -> [Element]? { let components = string.split(separator: ",") return components .map { String($0) } .compactMap(Element.extract(from:)) } }
  11. @dynamicMemberLookup struct Container { let values: [String: Any] subscript<T>(dynamicMember member:

    String) -> T? { if let value = values[member] { return value as? T } } } let container = Container(values: ["name": "Pikachu"]) let name: String = container.name // Pikachu
  12. // match pokedex://pokemons/:pokedexID // before let pokedexID: Int? = try?

    context.arguments(for: "pokedexID") // after let pokedexID: Int? = context.arguments.pokedexID
  13. @dynamicMemberLookup public struct ArgumentContainer { private let arguments: [String: String]

    public func fetch<T: Extractable>(for key: String) throws -> T { if let argument = arguments[key] { if let value = T.extract(from: argument) { return value } } throw Error.parsingArgumentFailed } public subscript<T: Extractable>(dynamicMember member: String) -> T? { return try? fetch(for: member) } }