【问题标题】:Userdefaults can not retrieve custom Struct in Share ExtensionUserdefaults 无法在共享扩展中检索自定义结构
【发布时间】:2020-12-13 14:16:22
【问题描述】:

我有一个应用程序和一个Share Extension。他们之间我通过UserDefaults 共享数据。但它突然停止工作。现在只能在Share Extension 中检索boolsStrings,但是当尝试检索Custom Struct 时,它总是返回nil

UserDefaults 中的自定义结构 getter/setter

//MARK: dataSourceArray
func setDataSourceArray(data: [Wishlist]?){
    set(try? PropertyListEncoder().encode(data), forKey: Keys.dataSourceKey)
    synchronize()
}


func getDataSourceArray() -> [Wishlist]? {
    if let data = self.value(forKey: Keys.dataSourceKey) as? Data {
        do {
            _ = try PropertyListDecoder().decode(Array < Wishlist > .self, from: data) as [Wishlist]
        } catch let error {
            print(error)
        }
        if let dataSourceArray =
            try? PropertyListDecoder().decode(Array < Wishlist > .self, from: data) as[Wishlist] {
                return dataSourceArray
            } 
    }
    return nil
}

我在我的Extension 以及我的主应用程序中这样称呼它:

   if let defaults = UserDefaults(suiteName: UserDefaults.Keys.groupKey) {
        if let data = defaults.getDataSourceArray() {
            print("working")
        } else {
            print("error getting datasourceArray")
        }
    }

这是在主应用程序中打印“工作”,但在我的Extension 中打印“错误获取 datasourceArray”。我不明白这个问题,特别是因为简单的Bool-Getter 也可以从我的共享扩展中工作,所以问题只在于Custom Struct

我在这里错过了什么?

Wishlist Struct:

import UIKit

enum PublicState: String, Codable {
    case PUBLIC
    case PUBLIC_FOR_FRIENDS
    case NOT_PUBLIC
}

struct Wishlist: Codable {
    var id: String
    var name: String
    var image: UIImage
    var wishes: [Wish]
    var color: UIColor
    var textColor: UIColor
    var index: Int
    var publicSate: PublicState

    enum CodingKeys: String, CodingKey {
        case id, name, image, wishData, color, textColor, index, isPublic, isPublicForFriends, publicSate
    }

    init(id: String, name: String, image: UIImage, wishes: [Wish], color: UIColor, textColor: UIColor, index: Int, publicSate: PublicState) {
        self.id = id
        self.name = name
        self.image = image
        self.wishes = wishes
        self.color = color
        self.textColor = textColor
        self.index = index
        self.publicSate = publicSate
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(String.self, forKey: .id)
        name = try values.decode(String.self, forKey: .name)
        wishes = try values.decode([Wish].self, forKey: .wishData)
        color = try values.decode(Color.self, forKey: .color).uiColor
        textColor = try values.decode(Color.self, forKey: .textColor).uiColor
        index = try values.decode(Int.self, forKey: .index)
        publicSate = try values.decode(PublicState.self, forKey: .publicSate)

        let data = try values.decode(Data.self, forKey: .image)
        guard let image = UIImage(data: data) else {
            throw DecodingError.dataCorruptedError(forKey: .image, in: values, debugDescription: "Invalid image data")
        }
        self.image = image
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
        try container.encode(wishes, forKey: .wishData)
        try container.encode(Color(uiColor: color), forKey: .color)
        try container.encode(Color(uiColor: textColor), forKey: .textColor)
        try container.encode(index, forKey: .index)
        try container.encode(image.pngData(), forKey: .image)
        try container.encode(publicSate, forKey: .publicSate)
    }
}

更新

这是失败的部分:

if let data = self.value(forKey: Keys.dataSourceKey) as? Data

有什么办法可以catcherror

我还发现这个功能实际上也适用于其他用户。该应用已上线:https://apps.apple.com/de/app/wishlists-einfach-w%C3%BCnschen/id1503912334

但这对我不起作用?我卸载了应用程序,从 App Store 下载了它,但它仍然无法运行。

【问题讨论】:

  • 不相关但为什么要对属性列表进行两次解码?制作方法throw 那么这就足够了:func getDataSourceArray() throws -&gt; [Wishlist] { guard let data = self.data(forKey: Keys.dataSourceKey) else {return [] } return try PropertyListDecoder().decode([Wishlist].self, from: data) }
  • @vadian 好点谢谢,但就像你说的,与这里的主要问题无关:(

标签: ios swift nsuserdefaults ios8-share-extension


【解决方案1】:

我遇到了同样的问题,但使用了另一种类型的扩展。希望它也对你有用。

  1. 创建一个您在两个目标之间共享的文件,并将以下代码放在那里:
//MARK: - Model
struct WishlistStruct: Codable {
//your wishlist struct, I'll assume you'll have a name and some items
  var name : String
    var items : [String]
}
typealias Wishlist = WishlistStruct

//MARK: - Defaults
let sharedUserdefaults = UserDefaults(suiteName: SharedDefault.suitName)
struct SharedDefault {
    static let suitName = "yourAppGroupHere"
    
    struct Keys{
        static let WishlistKey = "WishlistKey"
       
    }
}

var myWishlist: [Wishlist] {
   get {
    if let data = sharedUserdefaults?.data(forKey: SharedDefault.Keys.WishlistKey) {
            let array = try! PropertyListDecoder().decode([Wishlist].self, from: data)
        return array
    } else{
        //Here you should return an error but I didn't find any way to do that so I put this code which hopefully will never be executed
    return sharedUserdefaults?.array(forKey: SharedDefault.Keys.WishlistKey) as? [Wishlist] ?? [Wishlist]()
    }
   } set {
       
   }
}
  1. 现在,当您需要在应用程序和扩展程序中检索结构时,请使用以下代码:
 var wishlist  : [Wishlist] = []
 var currentWishlist = myWishlist

//In your viewDidLoad call
wishlist.append(contentsOf: myWishlist)

  1. 要编辑心愿单中的数据,请使用以下代码
 wishlist.append(Wishlist(name: "wishlist", items: ["aaa","bbb","ccc"]))
        currentWishlist.append(Wishlist(name: "wishlist", items: items: ["aaa","bbb","ccc"]))
        if let data = try? PropertyListEncoder().encode(currentWishlist) {
            sharedUserdefaults?.set(data, forKey: SharedDefault.Keys.WishlistKey)
        }

如果您需要更多说明,请告诉我

【讨论】:

  • 我会试试看的!但是您知道为什么会出现问题吗???
  • 您检索数据的方式有误。您使用键值,然后将结果转换为数据。尝试使用数据作为键。我在答案中输入的myWishlist 变量中的代码应该可以帮助您
  • 我不这么认为。我尝试将我的 getDataSourceArray 更改为:if let data = data(forKey: Keys.dataSourceKey) { let array = try! PropertyListDecoder().decode([Wishlist].self, from: data) return array } else { print(":(") },但结果相同
  • 还没有,但我会的。目前有点忙于这个项目,我看不出你和我的主要区别是什么?
  • 不同之处在于,使用我的代码,您将能够检索结构。
【解决方案2】:

更新了结构的代码。您应该将某些类型的属性更改为您的(我删除了一些字段进行测试)。

import UIKit

enum PublicState: String, Codable {
    case PUBLIC
    case PUBLIC_FOR_FRIENDS
    case NOT_PUBLIC
}

struct Wishlist: Codable {
    var id: String = ""
    var name: String = ""
    var image: Data = Data()//TODO: use Data type
    var color: String = ""//TODO: change it to your class
//    var wish: //TODO: add this filed, i don't have it
    var textColor: String = "" //TODO: change it to your class
    var index: Int = 0
    var publicSate: PublicState = .PUBLIC

    enum CodingKeys: String, CodingKey {
        case id, name, image, color, textColor, index, publicSate
    }

    init() {}
    
    init(id: String, name: String, image: Data, color: String, textColor: String, index: Int, publicSate: PublicState) {
        self.id = id
        self.name = name
        self.image = image
        self.color = color
        self.textColor = textColor
        self.index = index
        self.publicSate = publicSate
    }
}

struct WishlistContainer: Codable {
    var list: [Wishlist] = []

    enum CodingKeys: String, CodingKey {
        case list
    }
}


class UserDefaultsManager {

//be sure your correctly setup your app groups
private var currentDefaults: UserDefaults = UserDefaults(suiteName: "put here your app group ID")!

private func getFromLocalStorage<T: Codable>(model: T.Type, key: String) -> T? {
    
    if let decoded = currentDefaults.object(forKey: key) as? String {
        
        guard let data = decoded.data(using: .utf8) else { return nil }
        
        if let product = try? JSONDecoder().decode(model.self, from: data) {
            return product
        }
    }
    
    return nil
}

private func saveToLocalStorage(key: String, encodedData: String) {
    currentDefaults.set(encodedData, forKey: key)
}

private func removeObject(key: String) {
    currentDefaults.removeObject(forKey: key)
}

var wishList: WishlistContainer? {
    set {
        guard let value = newValue else {
            removeObject(key: "wishList")
            return
        }
        
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        
        guard let jsonData = try? encoder.encode(value) else { return }
        
        guard let jsonString = String(data: jsonData, encoding: .utf8) else { return }
        
        saveToLocalStorage(key: "wishList", encodedData: jsonString)
    }
    get {
        guard let value = getFromLocalStorage(model: WishlistContainer.self, key: "wishList") else {
            return nil
        }
        
        return value
    }
  }
}

//MARK: - Usage
let list: [Wishlist] = [Wishlist()]
let container: WishlistContainer = WishlistContainer(list: list)
UserDefaultsManager().wishList = container //set
UserDefaultsManager().wishList // get

【讨论】:

  • 你确定吗?为什么它在主应用程序中工作呢?我认为如果它们是 CodableWishlists 确认为 codable ,您可以存储它们。
  • 如果您的共享适用于原始类型,但不适用于自定义结构。这段代码应该会有所帮助。
  • 我会试一试,但我不太明白,因为正如我在问题中所说,它实际上对其他人有用
  • 我想我必须将standard 更改为我的suitName 不是吗?
  • 你能举个例子,我将如何在我的例子中使用代码?
猜你喜欢
  • 2018-01-18
  • 2018-03-05
  • 1970-01-01
  • 1970-01-01
  • 2018-07-12
  • 2021-05-05
  • 2014-11-13
  • 1970-01-01
相关资源
最近更新 更多