代码之家  ›  专栏  ›  技术社区  ›  Glenn Posadas

对通用字符串对象进行编码会得到nil Swift

  •  0
  • Glenn Posadas  · 技术社区  · 5 年前

    我有一个 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
        }
    }
    
    1 回复  |  直到 5 年前
        1
  •  5
  •   Sweeper    5 年前

    想一想绳子会是什么 "hello" 编码为JSON,看起来像。它看起来就像:

    "hello"
    

    不是吗?

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

    例如,这个代码

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

    将产生错误

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

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

    将产生错误:

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

    这里的工作就是用专用的 set 方法由 UserDefaults 。您仍然可以使用泛型方法,但只需检查类型和强制转换:

    if let str = object as? String {
        UserDefaults.standard.set(str, forKey: key)
    } else if let int = object as? Int {
        ...
    
        2
  •  0
  •   Glenn Posadas    5 年前

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

    这是:

    默认值。敏捷的

    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
        }
    }
    

    下面是我为测试默认方法而编写的测试(规范)。都过去了!

    @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())
                    }
                }
            }
        }
    }