Swift中UserDefaults存[String:Any]后String变__NSCFString的Mixpanel适配问题
解决方案
一、获取与存入时一致的Swift原生类型
UserDefaults在存储和读取Any类型时,会自动做Objective-C和Swift的类型桥接,所以你存的Swift String会变成NSString的私有子类__NSCFString。要拿到原生Swift类型,只需要在读取时手动把桥接后的类型转回去就行。
修改你的计算属性的getter方法,对每个值做针对性类型转换:
var mixpanelProperties: [String: Any] { get { guard let storedDict = defaults.dictionary(forKey: "\(#function)") else { return [:] } var convertedDict = [String: Any]() for (key, value) in storedDict { // 把NSString/__NSCFString转成Swift String if let nsString = value as? NSString { convertedDict[key] = String(nsString) } // 针对数字、布尔值做额外处理(UserDefaults会把这些存成NSNumber) else if let nsNumber = value as? NSNumber { // 区分布尔值和普通数字 if nsNumber.isBool { convertedDict[key] = nsNumber.boolValue } // 整数类型转回Swift Int else if nsNumber.intValue == nsNumber.doubleValue { convertedDict[key] = nsNumber.intValue } // 其他数字转回Swift Double else { convertedDict[key] = nsNumber.doubleValue } } // 其他未处理的类型直接保留 else { convertedDict[key] = value } } return convertedDict } set { defaults.set(newValue, forKey: "\(#function)") } }
这样你读取mixpanelProperties时,里面的字符串就是标准的Swift String类型,和存入时完全一致。
二、让Mixpanel兼容桥接后的类型
如果你不想手动转换类型,也可以通过扩展让Mixpanel接受__NSCFString(本质是NSString)。Mixpanel的事件属性要求遵循MixpanelType协议,我们可以给NSString扩展这个协议:
import Mixpanel extension NSString: MixpanelType { public func mixpanelJSONString() -> String { return self as String } }
不过这种方法不如第一种稳妥,因为Mixpanel官方文档推荐使用Swift原生类型,手动转换能避免潜在的类型兼容问题,更适合长期维护。
内容的提问来源于stack exchange,提问作者Dawood Mujib
相关产品推荐
相关产品推荐

