【问题标题】:Encoding generic String object gives nil Swift编码通用 String 对象给出 nil Swift
【发布时间】:2019-06-02 08:03:46
【问题描述】:

我有一个UserDefaults 类,用于处理存储、删除和获取存储对象的默认值。这是完整的课程,简洁明了,我相信:

现在问题在于存储功能。我似乎无法编码 Encodable String 对象。我知道我可以将该对象存储为默认值,但这会破坏处理通用对象的 MVDefaults 的目的。

我在这里遗漏了什么?

import Foundation

enum MVDefaultsKey: String {
    case requestToken = "defaultsRequestToken"
}

/// The class that has multiple class functions for handling defaults.
/// Also has the helper class functions for handling auth tokens.
class MVDefaults {

    // MARK: - Functions

    /// Stores token.
    class func store<T: Encodable>(_ object: T, key: MVDefaultsKey) {
        let encoder = JSONEncoder()
        let encoded = try? encoder.encode(object)
        UserDefaults.standard.set(encoded, forKey: key.rawValue)
    }

    /// Removes the stored token
    class func removeDefaultsWithKey(_ key: MVDefaultsKey) {
        UserDefaults.standard.removeObject(forKey: key.rawValue)
    }

    /// Returns stored token (optional) if any.
    class func getObjectWithKey<T: Decodable>(_ key: MVDefaultsKey, type: T.Type) -> T? {
        guard let savedData = UserDefaults.standard.data(forKey: key.rawValue) else {
            return nil
        }

        let object = try? JSONDecoder().decode(type, from: savedData)

        return object
    }
}

【问题讨论】:

  • 不要 try?,永远不要将 try?Codable 结合使用。 捕捉错误并print 它,或者——甚至更好——创建方法throw 并将错误交给调用者。并请说明MVDefaults 类的用法。

标签: ios swift generics codable


【解决方案1】:

想想编码为 JSON 的字符串 "hello" 会是什么样子。它看起来像:

"hello"

不会吗?

这不是一个有效的 JSON(根据here)!不能直接将字符串编码成 JSON,也不能直接解码字符串。

比如这段代码

let string = try! JSONDecoder().decode(String.self, from: "\"hello\"".data(using: .utf8)!)

会产生错误

JSON 文本未以数组或对象开头,并且允许未设置片段的选项。

还有

let data = try! JSONEncoder().encode("Hello")

会产生错误:

顶级字符串编码为字符串 JSON 片段。

这里的工作只是使用UserDefaults 提供的专用set 方法来存储您的字符串。不过,您仍然可以使用泛型方法,您只需要检查类型并强制转换:

if let str = object as? String {
    UserDefaults.standard.set(str, forKey: key)
} else if let int = object as? Int {
    ...

【讨论】:

  • 谢谢!知道了。我也发布了自己的答案!
  • 看起来 Swift 的 Codable 不符合 RFC 8259,其中单个通用字符串(例如“hello”)被视为有效的 json 对象
【解决方案2】:

虽然 Sweeper 的评论很有帮助并且应该是答案(因为没有他我将无法得出自己的答案),但我仍然会继续分享我在 Defaults 课程中所做的事情,以使它处理非 JSON 编码对象(例如 String、Int、Array 等)。

这里是:

MVDefaults.swift

import Foundation

enum MVDefaultsKey: String {
    case requestToken = "defaultsRequestToken"
    case someOtherKey = "defaultsKeyForTests"
}

/// The class that has multiple class functions for handling defaults.
/// Also has the helper class functions for handling auth tokens.
class MVDefaults {

    // MARK: - Functions

    /// Stores object.
    class func store<T: Encodable>(_ object: T, key: MVDefaultsKey) {
        let encoder = JSONEncoder()
        let encoded = try? encoder.encode(object)

        if encoded == nil {
            UserDefaults.standard.set(object, forKey: key.rawValue)
            return
        }

        UserDefaults.standard.set(encoded, forKey: key.rawValue)
    }

    /// Removes the stored object
    class func removeDefaultsWithKey(_ key: MVDefaultsKey) {
        UserDefaults.standard.removeObject(forKey: key.rawValue)
    }

    /// Returns stored object (optional) if any.
    class func getObjectWithKey<T: Decodable>(_ key: MVDefaultsKey, type: T.Type) -> T? {
        if let savedData = UserDefaults.standard.data(forKey: key.rawValue) {
            let object = try? JSONDecoder().decode(type, from: savedData)
            return object
        }

        return UserDefaults.standard.object(forKey: key.rawValue) as? T
    }
}

这是我为测试 Defaults 方法而编写的测试(规范)。都过去了! ✅

MVDefaultsSpec.swift

@testable import Movieee
import Foundation
import Quick
import Nimble

class MVDefaultsSpec: QuickSpec {
    override func spec() {
        describe("Tests for MVDefaults") {

            context("Tests the whole MVDefaults.") {

                it("tests the store and retrieve function for any Codable object") {

                    let data = stubbedResponse("MovieResult")
                    expect(data).notTo(beNil())
                    let newMovieResult = try? JSONDecoder().decode(MovieResult.self, from: data!)

                    MVDefaults.store(newMovieResult, key: .someOtherKey)

                    let retrievedObject = MVDefaults.getObjectWithKey(.someOtherKey, type: MovieResult.self)

                    // Assert
                    expect(retrievedObject).notTo(beNil())

                    // Assert
                    expect(retrievedObject?.movies?.first?.id).to(equal(newMovieResult?.movies?.first?.id))
                }

                it("tests the store and retrieve function for String") {

                    let string = "Ich bin ein Berliner"

                    MVDefaults.store(string, key: .someOtherKey)

                    let retrievedObject = MVDefaults.getObjectWithKey(.someOtherKey, type: String.self)

                    // Assert
                    expect(retrievedObject).notTo(beNil())

                    // Assert
                    expect(retrievedObject).to(equal(string))
                }

                it("tests the removal function") {

                    MVDefaults.removeDefaultsWithKey(.someOtherKey)

                    let anyRetrievedObject = UserDefaults.standard.object(forKey: MVDefaultsKey.someOtherKey.rawValue)
                    let stringRetrievedObject = MVDefaults.getObjectWithKey(.someOtherKey, type: String.self)

                    // Assert
                    expect(anyRetrievedObject).to(beNil())

                    // Assert
                    expect(stringRetrievedObject).to(beNil())
                }

                it("tests the store and retrieve function for Bool") {

                    let someBool: Bool = true

                    MVDefaults.store(someBool, key: .someOtherKey)

                    let retrievedObject = MVDefaults.getObjectWithKey(.someOtherKey, type: Bool.self)

                    // Assert
                    expect(retrievedObject).to(equal(someBool))

                    // Assert
                    expect(retrievedObject).notTo(beNil())
                }
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 2016-09-27
    • 1970-01-01
    • 2018-12-03
    • 2020-05-31
    相关资源
    最近更新 更多