简体   繁体   English

编码通用String对象给出nil Swift

[英]Encoding generic String object gives nil Swift

I have a UserDefaults class that handles storing, deleting, and fetching of stored objects to defaults. 我有一个UserDefaults类,用于处理存储,删除和获取存储对象为默认值的类。 Here's the complete class, neat and simple, I believe: 我相信,这是完整的课程,简洁明了:

Now the problem lies in storing function. 现在的问题在于存储功能。 I couldn't seem to encode an Encodable String object. 我似乎不能编码Encodable String对象。 I know I could just store that object to the defaults, but that would defeat the purpose of this MVDefaults that deals with generic objects. 我知道我可以将对象存储为默认值,但这会MVDefaults处理通用对象的目的。

Anything I'm missing here? 我在这里想念什么吗?

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

Think about what would the string "hello" encoded to JSON, look like. 考虑一下将字符串"hello"编码为JSON的样子。 It would just look like: 它看起来像:

"hello"

wouldn't it? 不是吗

That is not a valid JSON (according to here )! 那不是有效的JSON(根据here )! You can't encode a string directly to JSON, and you can't decode a string directly either. 您不能直接将字符串编码为JSON,也不能直接解码字符串。

For example, this code 例如,此代码

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

will produce the error 会产生错误

JSON text did not start with array or object and option to allow fragments not set. JSON文本不是以数组或对象开头,并且没有允许片段设置的选项。

And

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

will produce the error: 会产生错误:

Top-level String encoded as string JSON fragment. 顶级字符串编码为字符串JSON片段。

The work around here is just to store your strings with the dedicated set methods provided by UserDefaults . 此处的解决方法只是使用UserDefaults提供的专用set方法存储字符串。 You can still have your generic method, though, you just need to check the type and cast: 不过,您仍然可以使用通用方法,只需检查类型并进行强制转换:

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

Whilst Sweeper's comment is helpful and should be the answer (since I won't be able to come up to my own answer without his), I'll still go ahead and share what I did to my Defaults class, to make it handle the non JSON encoding objects (eg String, Int, Array, and whatnot). 尽管Sweeper的评论很有帮助,应该是答案(因为没有他,我将无法提出自己的答案),但我仍然会继续分享我对Defaults类所做的事情,以使其能够处理非JSON编码对象(例如String,Int,Array和whatnot)。

Here it is: 这里是:

MVDefaults.swift 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
    }
}

And here's the test (spec) that I wrote for testing the Defaults methods. 这是我为测试Defaults方法编写的测试(规范)。 All passed! 全部通过!

MVDefaultsSpec.swift 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())
                }
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM