#ios #swift #nsuserdefaults #ios8-share-extension
#iOS #swift #nsuserdefaults #ios8-share-extension
Вопрос:
У меня есть приложение, а также Share Extension
. Между ними я обмениваюсь данными через UserDefaults
. Но он внезапно перестал работать. bools
Strings
Теперь внутри можно получить только or Share Extension
, но при попытке получить a Custom Struct
он всегда возвращается nil
.
Пользовательская структура getter/setter
в UserDefaults
:
//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")
}
}
Это печать «работает» в основном приложении, но «ошибка получения datasourceArray» в моем Extension
. Я не понимаю проблемы, особенно потому, что simple 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
Есть ли какой-либо способ catch
error
?
Я также обнаружил, что эта функция действительно работает для других пользователей. Приложение работает: https://apps.apple.com/de/app/wishlists-einfach-wünschen/id1503912334
Но у меня это не работает? Я деинсталлировал приложение, загрузил его из App Store, но оно все еще не работает.
Комментарии:
1. Не связано, но почему вы дважды расшифровываете список свойств? Создайте метод
throw
, тогда этого будет достаточно:func getDataSourceArray() throws -> [Wishlist] { guard let data = self.data(forKey: Keys.dataSourceKey) else {return [] } return try PropertyListDecoder().decode([Wishlist].self, from: data) }
2. @vadian хороший момент, спасибо, но, как вы сказали, не имеет отношения к основной проблеме здесь: (
Ответ №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 {
}
}
- Теперь всякий раз, когда вам нужно получить структуру, как в приложении, так и в расширении, используйте следующий код:
var wishlist : [Wishlist] = []
var currentWishlist = myWishlist
//In your viewDidLoad call
wishlist.append(contentsOf: myWishlist)
- Чтобы отредактировать данные внутри вашего списка пожеланий, используйте следующий код
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)
}
Дайте мне знать, если вам нужны дополнительные разъяснения
Комментарии:
1. Я попробую! Но есть ли у вас какие-либо идеи, почему возникает проблема???
2. Ошибка в способе извлечения данных. Вы используете значение для ключа, а затем преобразуете результат в данные. Попробуйте вместо этого использовать данные для ключа. Код внутри
myWishlist
переменной, которую я ввел в свой ответ, должен вам помочь3. Я не думаю, что это все. Я попытался изменить свой
getDataSourceArray
на это:if let data = data(forKey: Keys.dataSourceKey) { let array = try! PropertyListDecoder().decode([Wishlist].self, from: data) return array } else { print(":(") }
, но тот же результат4. пока нет, но я это сделаю. В данный момент я занят проектом, и я не вижу основной разницы между вашим и моим?
5. Разница в том, что с моим кодом вы сможете получить структуру.
Ответ №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
Комментарии:
1. вы уверены? тогда почему это работает внутри основного приложения? Я думаю, вы можете сохранить их, если они есть
Codable
, иWishlists
подтверждаетcodable
.2. Если ваш общий ресурс работает правильно для примитивных типов, но не работает для пользовательских структур. Этот код должен помочь.
3. Я попробую, но я не совсем понимаю, потому что, как я уже сказал в вопросе, это действительно работает для других
4. Я думаю, что мне нужно перейти
standard
на мойsuitName
, не так ли?5. и не могли бы вы привести пример того, как я буду использовать код в моем примере?