Кодирование универсального объекта String дает ноль Swift - PullRequest
0 голосов
/ 02 июня 2019

У меня есть класс 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
    }
}

Ответы [ 2 ]

5 голосов
/ 02 июня 2019

Подумайте, как будет выглядеть строка "hello", закодированная в JSON.Это выглядело бы так:

"hello"

не так ли?

Это неверный JSON (согласно здесь )!Вы не можете кодировать строку непосредственно в 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 {
    ...
0 голосов
/ 02 июня 2019

Хотя комментарий Sweeper полезен и должен быть ответом (поскольку я не смогу найти свой собственный ответ без его ответа), я все же продолжу и поделюсь тем, что я сделал, с моим классом по умолчанию, чтобы сделать он обрабатывает объекты не кодирования 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
    }
}

А вот тест (спецификация), который я написал для тестирования методов по умолчанию. Все прошло! ✅

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