Swift 有没有办法更简洁地使用守卫声明?
我正在使用它进行JSON实例化。下面是一个示例类:Swift 有没有办法更简洁地使用守卫声明?,swift,Swift,我正在使用它进行JSON实例化。下面是一个示例类: public class MyObj: Decodable { let id_user : String let contact_addr1 : String let contact_addr2 : String? let contact_city : String let contact_state : String let contact_zip :
public class MyObj: Decodable
{
let id_user : String
let contact_addr1 : String
let contact_addr2 : String?
let contact_city : String
let contact_state : String
let contact_zip : String
let points : Int
// Deserialization
required public init?(json: JSON)
{
guard let id_user : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let contact_addr1 : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let contact_city : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let contact_state : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let contact_zip : String = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
guard let points : Int = "somekey" <~~ json else {
assertionFailure("MyObj - invalid JSON. Missing key: wouldbenicetonotwritethisforeachmember")
return nil
}
self.id_user = id_user
self.contact_addr1 = contact_addr1
self.contact_addr2 = "somekey" <~~ json
self.contact_city = contact_city
self.contact_state = contact_state
self.contact_zip = contact_zip
self.contact_points = points
}
}
也许有一种方法可以防止字符串键数组的出现?实现这一点的方法有很多种。它们都归结为编写一个包装器函数来将键映射到值。下面是我想到的几个快速示例,但正如我所说的,根据您的目标,有很多方法可以做到这一点:
enum JSONError: Error {
case keyNotFound(String)
}
extension JSON {
func values<T>(for keys: [String]) throws -> [T] {
var values = [T]()
for key in keys {
guard let value: T = key <~~ self else {
throw JSONError.keyNotFound(key)
}
values.append(value)
}
return values
}
func values<T>(for keys: [String], closure: ((_ key: String, _ value: T) -> Void)) throws {
for key in keys {
guard let value: T = key <~~ self else {
throw JSONError.keyNotFound(key)
}
closure(key, value)
}
}
}
第二个将把键、值对传回到一个闭包,因为它们出现在您的键数组中,并将抛出它发现的第一个不存在的闭包
do {
let keys = ["foo", "bar"]
// The type of the closure's value argument is important.
// In this example we're saying look for values of type String.
try json.values(for: keys) { (key, value: String) in
print("value for key \(key) is \(value)")
}
} catch JSONError.keyNotFound(let key) {
assertionFailure("key not found \(key)")
}
在类的init?()
函数中使用第一个版本,我们有如下内容:
public struct MyObj: Decodable {
public let id_user : String
public let contact_addr1 : String
public let contact_addr2 : String?
public let points : Int
public init?(json: S) {
do {
let stringKeys = ["id_user", "contact_addr1"]
let stringValues: [String] = try json.values(for: stringKeys)
id_user = stringValues[0]
contact_addr1 = stringValues[1]
// this isn't required, so just extract with no error if it fails
contact_addr2 = "contact_addr2" <~~ json
let intKeys = ["points"]
let intValues: [Int] = try json.values(for: intKeys)
points = intValues[0]
} catch JSONError.keyNotFound(let key) {
assertionFailure("key \(key) not found in JSON")
return nil
} catch {
return nil
}
}
}
公共结构MyObj:可解码{
公共let id\u用户:字符串
public let contact_addr1:字符串
public let contact_addr2:字符串?
公共出租点:Int
public init?(json:S){
做{
让stringKeys=[“id\u user”,“contact\u addr1”]
让stringValues:[String]=尝试json.values(for:stringKeys)
id\u user=stringValues[0]
联系人地址1=stringValues[1]
//这不是必需的,所以如果失败,只需无错误地提取即可
contact_addr2=“contact_addr2”我没有使用Gloss,而且考虑到它足够简单,可以安全地解析JSON,而不需要额外的库,也不需要使用不熟悉的语法,所以它似乎没有必要
选项1:
您可以在单个guard
语句中对可选展开进行分组
例如:
public struct MyObj {
let id_user : String
let contact_addr1 : String
let contact_addr2 : String?
let points : Int
public init?(json: Any) {
guard
let entities = json as? [String : Any],
let id_user = entities["some key"] as? String,
let contact_addr1 = entities["some key"] as? String,
let points = entities["some key"] as? Int
else {
assertionFailure("...")
return nil
}
self.id_user = id_user
self.contact_addr1 = contact_addr1
self.contact_addr2 = entities["some key"] as? String
self.contact_points = points
}
}
// Helper object for parsing values from a dictionary.
// A similar pattern could be used for arrays. i.e. array.stringAt(10)
struct JSONDictionary {
let values: [String : Any]
init(_ json: Any) throws {
guard let values = json as? [String : Any] else {
throw MyError.expectedDictionary
}
self.values = values
}
func string(_ key: String) throws -> String {
guard let value = values[key] as? String else {
throw MyError.expectedString(key)
}
return value
}
func integer(_ key: String) throws -> Int {
guard let value = values[key] as? Int else {
throw MyError.expectedInteger(key)
}
return value
}
}
选项2:
另一种方法是完全消除guard
语句,让解析器在解析过程中抛出错误,并使用可选的try
将结果转换为nil
例如:
public struct MyObj {
let id_user : String
let contact_addr1 : String
let contact_addr2 : String?
let points : Int
public init?(json: Any) {
guard
let entities = json as? [String : Any],
let id_user = entities["some key"] as? String,
let contact_addr1 = entities["some key"] as? String,
let points = entities["some key"] as? Int
else {
assertionFailure("...")
return nil
}
self.id_user = id_user
self.contact_addr1 = contact_addr1
self.contact_addr2 = entities["some key"] as? String
self.contact_points = points
}
}
// Helper object for parsing values from a dictionary.
// A similar pattern could be used for arrays. i.e. array.stringAt(10)
struct JSONDictionary {
let values: [String : Any]
init(_ json: Any) throws {
guard let values = json as? [String : Any] else {
throw MyError.expectedDictionary
}
self.values = values
}
func string(_ key: String) throws -> String {
guard let value = values[key] as? String else {
throw MyError.expectedString(key)
}
return value
}
func integer(_ key: String) throws -> Int {
guard let value = values[key] as? Int else {
throw MyError.expectedInteger(key)
}
return value
}
}
解析器:
public struct MyObj {
let id_user : String
let contact_addr1 : String
let contact_addr2 : String?
let points : Int
public init(json: Any) throws {
// Instantiate the helper object.
// Ideally the JSONDictionary would be passed by the caller.
let dictionary = try JSONDictionary(json),
self.id_user = try dictionary.string("some key"),
self.contact_addr1 = try dictionary.string("some key"),
self.points = try dictionary.integer("some key")
// Results in an optional if the string call throws an exception
self.contact_addr2 = try? dictionary.string("some key")
}
}
用法:
// Instantiate MyObj from myJSON.
// myObject will be nil if parsing fails.
let myObject = try? MyObj(json: myJSON)
关于使用gloss。你应该阅读本文的最后一段,应该使用结构而不是类,并花一些时间为你的结构创建自定义初始值设定项,而不是使用gloss。你应该看看这一点。很多人喜欢对Swift过于冗长但几乎不可读的默认实现进行一些抽象。我想t以避免十亿个x作为?[String:Any]保护语句。空合并运算符(??)允许我非常简洁地执行我想要的操作,但它不允许我在缺少密钥时执行任何错误处理(这在开发过程中会很有帮助)。“…更喜欢一点抽象,而不是Swift过于冗长的…”.对不起,这引起了我的注意。你和Objective-C合作过很多吗?有些东西在Swift上不是“开箱即用”的,无论是版本2、版本3,甚至版本4。据说,这可能是我第一次听到“Swift”和“冗长”在同一句话中::-)是的,尽管Swift的使命声明,Obj-C实际上不那么冗长,也更容易阅读,因为选项太乱了。如果你想辩论,请在适当的论坛上发表文章。你必须在这里回答我原来的问题。现在这就是我要说的。请给我一分钟时间回顾一下。类型是凝灰岩对我来说是新事物。当示例1中的guard
出现故障时,您无法判断哪个键是故障点,这有点限制。在示例2中,您的string()
和integer()
函数是很好的例子,但不应隐式返回未包装的选项。只需分别返回String
和Int
。