Mapping Dictionary keys
该图片由Markus Spiske在Pixabay上发布
问题
在 OC 和 Swift 混编赋值函数参数时,出现类型不匹配的问题:
OC中的方法:
+ (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options;
其中options 的类型为 NSDictionary<NSString*, id>;
桥接到 Swift 中时,options 的类型为 [String: Any]。
Swift中的方法:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool
其中 options 的类型为 [UIApplication.OpenURLOptionsKey : Any]
因为 swift 是强类型语言,因此无法直接把 [UIApplication.OpenURLOptionsKey : Any] 类型的值赋给 [String: Any] 类型
类型不一致时赋值,编译器会报错

解决
需要把 [UIApplication.OpenURLOptionsKey : Any] 类型 转化为 [String: Any] 类型
UIApplication.OpenURLOptionsKey 遵循了 RawRepresentable 协议,所以通过 key.rawValue 即可转换为 String 类型
简单粗暴的方式 for in
var transOptions = [String: Any]()
for x in options {
transOptions[x.key.rawValue] = x.value
}
呃。。。不够优雅,使用 reduce 函数简化
let transOptions = options.reduce(into: [:]) { (result, x) in
result[x.key.rawValue] = x.value
}
呃。。。不够通用,遇到 NSAttributedString.Key、NSNotification.Name 等类型 或者 [Int: Any] 转换 [String: Any] 等 key 需要转换的时候,需要写重复代码。
泛型封装
extension Dictionary {
func compactMapKeys<T>(_ transform: ((Key) throws -> T?)) rethrows -> Dictionary<T, Value> {
return try self.reduce(into: [T: Value](), { (result, x) in
if let key = try transform(x.key) {
result[key] = x.value
}
})
}
}
使用方式如下
let transOptions = options.compactMapKeys {$0.rawValue}