Last active
March 17, 2020 09:09
-
-
Save ryotapoi/2c6fce5cf79b39bf49157b8fa886664f to your computer and use it in GitHub Desktop.
URLQueryItemをカッコよく使いたい
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// MARK: - URLQueryItem Key | |
class URLQueryItemKeys { | |
init() {} | |
} | |
class URLQueryItemKey<Value : URLQueryItemValueCompatible> : URLQueryItemKeys { | |
let name: String | |
init(_ name: String) { | |
self.name = name | |
super.init() | |
} | |
} | |
// MARK: - URLQueryItem Value | |
enum URLQueryItemValueCompatibleError : Error { | |
case mustNotBeNil | |
case convert | |
} | |
protocol URLQueryItemValueCompatible { | |
init(urlQueryItemValue: String?) throws | |
func toURLQueryItemValue() -> String? | |
} | |
// MARK: - URLQueryItem Compatible Value | |
extension Int : URLQueryItemValueCompatible { | |
init(urlQueryItemValue: String?) throws { | |
guard let value = urlQueryItemValue else { throw URLQueryItemValueCompatibleError.mustNotBeNil } | |
guard let int = Int(value) else { throw URLQueryItemValueCompatibleError.convert } | |
self = int | |
} | |
func toURLQueryItemValue() -> String? { | |
String(self) | |
} | |
} | |
extension Double : URLQueryItemValueCompatible { | |
init(urlQueryItemValue: String?) throws { | |
guard let value = urlQueryItemValue else { throw URLQueryItemValueCompatibleError.mustNotBeNil } | |
guard let double = Double(value) else { throw URLQueryItemValueCompatibleError.convert } | |
self = double | |
} | |
func toURLQueryItemValue() -> String? { | |
String(self) | |
} | |
} | |
extension Float : URLQueryItemValueCompatible { | |
init(urlQueryItemValue: String?) throws { | |
guard let value = urlQueryItemValue else { throw URLQueryItemValueCompatibleError.mustNotBeNil } | |
guard let float = Float(value) else { throw URLQueryItemValueCompatibleError.convert } | |
self = float | |
} | |
func toURLQueryItemValue() -> String? { | |
String(self) | |
} | |
} | |
extension Bool : URLQueryItemValueCompatible { | |
init(urlQueryItemValue: String?) throws { | |
guard let value = urlQueryItemValue else { throw URLQueryItemValueCompatibleError.mustNotBeNil } | |
guard let bool = Bool(value) else { throw URLQueryItemValueCompatibleError.convert } | |
self = bool | |
} | |
func toURLQueryItemValue() -> String? { | |
String(self) | |
} | |
} | |
extension String : URLQueryItemValueCompatible { | |
init(urlQueryItemValue: String?) throws { | |
guard let value = urlQueryItemValue else { throw URLQueryItemValueCompatibleError.mustNotBeNil } | |
self = value | |
} | |
func toURLQueryItemValue() -> String? { | |
self | |
} | |
} | |
extension Optional : URLQueryItemValueCompatible where Wrapped : URLQueryItemValueCompatible { | |
init(urlQueryItemValue: String?) throws { | |
if let value = urlQueryItemValue { | |
self = .some(try Wrapped(urlQueryItemValue: value)) | |
} else { | |
self = nil | |
} | |
} | |
func toURLQueryItemValue() -> String? { | |
switch self { | |
case .some(let wrapped): | |
return wrapped.toURLQueryItemValue() | |
case .none: | |
return nil | |
} | |
} | |
} | |
// MARK: - URLQueryItem Extension | |
extension Array where Element == URLQueryItem { | |
func value<Value>(for key: URLQueryItemKey<Value>) throws -> Value { | |
let value = first(where: { $0.name == key.name })?.value | |
return try Value(urlQueryItemValue: value) | |
} | |
} | |
extension Optional where Wrapped == Array<URLQueryItem> { | |
func value<Value>(for key: URLQueryItemKey<Value>) throws -> Value { | |
switch self { | |
case .some(let array): | |
return try array.value(for: key) | |
case .none: | |
return try Value(urlQueryItemValue: nil) | |
} | |
} | |
} | |
extension URLQueryItem { | |
init<Key, Value: URLQueryItemValueCompatible>(key: Key, value: Value) where Key : URLQueryItemKey<Value> { | |
self.init(name: key.name, value: value.toURLQueryItemValue()) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// type safe QueryItem key | |
extension URLQueryItemKeys { | |
static let string = URLQueryItemKey<String>("string") | |
static let int = URLQueryItemKey<Int>("int") | |
static let bool = URLQueryItemKey<Bool>("bool") | |
static let floatOptional = URLQueryItemKey<Float?>("floatOptional") | |
static let doubleOptional = URLQueryItemKey<Double?>("doubleOptional") | |
static let notOptionalButNothing = URLQueryItemKey<String>("notOptionalButNothing") | |
} | |
// access QueryItem | |
let url = URL(string: "https://foobar.com/path?string=stringValue&int=238&bool=true&floatOptional=123.789")! | |
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)! | |
do { | |
try urlComponents.queryItems.value(for: .string) | |
try urlComponents.queryItems.value(for: .int) | |
try urlComponents.queryItems.value(for: .bool) | |
try urlComponents.queryItems.value(for: .floatOptional) | |
try urlComponents.queryItems.value(for: .doubleOptional) | |
try urlComponents.queryItems.value(for: .notOptionalButNothing) | |
} catch { | |
print(error) | |
} | |
// create URL with QueryItem | |
var schemeComponents = URLComponents() | |
schemeComponents.scheme = "myapp" | |
schemeComponents.host = "edit" | |
schemeComponents.path = "/add" | |
schemeComponents.queryItems = [ | |
.init(key: .string, value: "abc"), | |
.init(key: .int, value: 5), | |
.init(key: .bool, value: false), | |
.init(key: .floatOptional, value: 32.55), | |
.init(key: .doubleOptional, value: nil), | |
].compactMap { $0 } | |
let scheme = schemeComponents.url | |
print(scheme) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment