【问题标题】:Failable initializer always returns nil even though requirements are met即使满足要求,可失败初始化程序也始终返回 nil
【发布时间】:2020-01-21 13:25:05
【问题描述】:

如果有以下代码,应该将 [String: any] 文档从 Firestore 转换为结构。

当时我调试时所有要求都满足,但返回值是nil

我尝试将init? 更改为常规initguard 上的else { fatalError() }。如果数据有效,这将有效并返回一个有效的结构。

我在使用可失败初始化程序时做错了什么?

这不起作用(总是返回 nil,即使有有效数据):

 struct Banner {
        let destinationUrl: URL
        let imageUrl: URL
        let endTime: Date
        let startTime: Date
        let priority: Int
        let trackingKeyClicked: String
        let trackingKeyDismissed: String

        init?(document: [String: Any]) {
            guard
                let destinationUrlString = document["destinationUrl"] as? String,
                let destinationUrl = URL(string: destinationUrlString),
                let imageUrlString = document["imageUrl"] as? String,
                let imageUrl = URL(string: imageUrlString),
                let priority = document["priority"] as? Int,
                let trackingKeyClicked = document["trackingKeyClicked"] as? String,
                let trackingKeyDismissed = document["trackingKeyDismissed"] as? String,
                let startTime = document["startTime"] as? Date,
                let endTime = document["endTime"] as? Date
                else { return nil }
            self.destinationUrl = destinationUrl
            self.imageUrl = imageUrl
            self.priority = priority
            self.trackingKeyClicked = trackingKeyClicked
            self.trackingKeyDismissed = trackingKeyDismissed
            self.endTime = endTime
            self.startTime = startTime
        }
    }

// using it like this
let bannerStructs = querySnapshot.documents.map { Banner(document: $0.data()) }

这适用于有效数据(但在错误数据上崩溃而不是返回 nil):

 struct Banner {
        let destinationUrl: URL
        // ...
        let endTime: Date

        init(document: [String: Any]) {
            guard
                let destinationUrlString = document["destinationUrl"] as? String,
                let destinationUrl = URL(string: destinationUrlString),
                // ....
                let endTime = document["endTime"] as? Date
                else { fatalError() }
            self.destinationUrl = destinationUrl
            // ...
            self.endTime = endTime
        }
    }

【问题讨论】:

  • 如果它在非可选初始化时不起作用并且崩溃,那么初始化失败,您需要调试您所做的字段和强制转换。在你的 else return nil 语句中放置一个断点,它会命中它。使用 Codable 可能会更好
  • 看来您正在处理 JSON。为什么你的结构不应该实现 Codable ?
  • “当我调试时,我可以看到所有要求都得到满足” 但是我们看不到,因为您没有显示任何数据。如果失败,返回 nil,那是因为守卫语句失败。我相信运行时,而不是你。
  • 你可以使用可选链来代替守卫。您将能够找到确切的问题。
  • 我怀疑startTimeendTime 是否会被转换为Date。你可以在你的保护语句的开头设置一个断点,然后点击Step over 并检查你的哪些变量没有被强制转换吗?我假设是您的 startTimeendTime 变量失败,因此返回 nil

标签: ios swift


【解决方案1】:

如果可失败初始化程序返回 nil 并且正常初始化程序由于错误数据而崩溃,那么这将我指向可失败初始化程序中的保护语句失败导致它返回 nil。在 guard 语句中的 return nil 行上放置一个断点,看看它是否被命中。

【讨论】:

  • 如前所述,满足所有要求,并且守卫没有失败。但是如果 init 是可选的,我仍然得到 nil 的回报......
【解决方案2】:

如果您的guard let 条件都不满足,它将失败并最终触发 fatalError 或返回 nil,具体取决于您使用的实现。 请调试好哪些数据没有正确解析/转换。

我设置了一个很好的例子来说明你可能期望它如何工作和一个不好的例子让你知道一个在那种格式中不期望的 attribute 如何使 initialiser 返回nil:

import Foundation

struct Banner {
    let destinationUrl: URL
    let imageUrl: URL
    let endTime: Date
    let startTime: Date
    let priority: Int
    let trackingKeyClicked: String
    let trackingKeyDismissed: String

    init?(document: [String: Any]) {
        guard
            let destinationUrlString = document["destinationUrl"] as? String,
            let destinationUrl = URL(string: destinationUrlString),
            let imageUrlString = document["imageUrl"] as? String,
            let imageUrl = URL(string: imageUrlString),
            let priority = document["priority"] as? Int,
            let trackingKeyClicked = document["trackingKeyClicked"] as? String,
            let trackingKeyDismissed = document["trackingKeyDismissed"] as? String,
            let startTime = document["startTime"] as? Date,
            let endTime = document["endTime"] as? Date
            else { return nil }
        self.destinationUrl = destinationUrl
        self.imageUrl = imageUrl
        self.priority = priority
        self.trackingKeyClicked = trackingKeyClicked
        self.trackingKeyDismissed = trackingKeyDismissed
        self.endTime = endTime
        self.startTime = startTime
    }
}

// using it like this
let goodData:[String:Any] = [
    "destinationUrl": "http://destination--url",
    "imageUrl": "http://image-url",
    "priority": 17,
    "trackingKeyClicked": "Tracking Key Clicked",
    "trackingKeyDismissed": "Tracking Key Dismissed",
    "startTime": Date(),
    "endTime": Date()
    ]
let goodBannerStructs = Banner(document: goodData)

let badData:[String:Any] = [
    "destinationUrl": "http://destination--url",
    "imageUrl": "http://image-url",
    "priority": 17,
    "trackingKeyClicked": "Tracking Key Clicked",
    "trackingKeyDismissed": "Tracking Key Dismissed",
    "startTime": "17 December",
    "endTime": Date()
    ]
let badBannerStructs = Banner(document: badData)

print("Good banner: \(goodBannerStructs)")
print("Bad banner: \(badBannerStructs)")

这是打印出来的:

Good banner: Optional(Banner(destinationUrl: http://destination--url, imageUrl: http://image-url, endTime: 2020-01-21 17:45:27 +0000, startTime: 2020-01-21 17:45:27 +0000, priority: 17, trackingKeyClicked: "Tracking Key Clicked", trackingKeyDismissed: "Tracking Key Dismissed"))
Bad banner: nil

你可以试试这个代码:http://online.swiftplayground.run/

它可能是 document 的字典键之一,可能与来自查询的内容不正确,可能是 priority 可能不是 Int 或日期可能是 String。你必须调试它。

【讨论】:

  • 当然。但你能告诉我为什么它返回 nil 并返回有效数据吗?
  • 尝试在单独的 if 语句中转换那个守卫 let..,let...,let... 以便您在调试时更好地了解情况。并尝试检查它不起作用的地方。否则,您可以尝试使用调试器和控制台po 命令,如果您熟悉它并且不需要临时更改您的保护语句。
  • 请重新检查我的答案,我提供了一个适合您的结构初始化的示例,一个坏的和一个好的示例。确保使用调试器或打印出值来检查错误,以检查为什么一个或多个转换/解析行为不正确。
【解决方案3】:

这不起作用(总是返回 nil,即使有有效数据)

由于您的guard 总是失败,因此数据似乎不正确。我猜startDateendDate 不是那么容易转换成Date。能否请您发布一个json数据的示例?

如果这是原因,here 是描述如何使用DateFormatter 从字符串创建日期的人。如果您的日期遵循 ISO8601,您可以使用 Apples ISO8601DateFormatter 来执行此操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多