didSet не вызывается Array.append () в Swift - PullRequest
0 голосов
/ 29 марта 2020

Я слежу за 100 Днями SwiftUI и достиг Дня 37 . При выполнении Внесение изменений постоянно с UserDefaults , я сталкиваюсь с проблемой с didSet.

(я использую Swift 5 с iOS 13.4)

В примере код, он пишет

.navigationBarItems(trailing: Button("Save") {
    if let actualAmount = Int(self.amount) {
        let item = ExpenseItem(name: self.name, type: self.type, amount: actualAmount)
        self.expenses.items.append(item)
    }
})

, где didSet должен вызываться .append().

Однако на практике didSet не вызывается, если я не изменю приведенный выше код на

.navigationBarItems(trailing: Button("Save") {
    if let actualAmount = Int(self.amount) {
        let item = ExpenseItem(name: self.name, type: self.type, amount: actualAmount)
        let newItems = self.expenses.items + [item]
        self.expenses.items = newItems
    }
})

Я также пишу небольшой тест (см. Ниже) в Playground, который показывает, что .append() работает очень хорошо с didSet

struct Count {
    var array: [Int] {
        didSet {
            print("struct Count - didSet() called")
        }
    }
}
class CountClass {
    var array: [Int] {
        didSet {
            print("class CountClass - didSet() called")
        }
    }
    init() {
        array = [1, 2, 3]
    }
}
struct Test {
    var countA = Count(array: [1, 2, 3])
    var countB = CountClass()

    mutating func testDidSet() {
        countA.array.append(4)
        countB.array.append(4)
    }
}

var t = Test()
t.testDidSet()

Это странное поведение действительно заставляет меня задуматься как работает didSet Или эта проблема связана с использованием @ObservedObject (как в случае примера проекта)?

PS: я скачал готовую версию с Project7 , и она также имеет проблема.

Ответы [ 4 ]

0 голосов
/ 04 апреля 2020

Это известная ошибка Swift 5.2: наблюдатели обернутых свойств не вызывают после модификации (https://bugs.swift.org/browse/SR-12089). Они знали об этом с января и все же выпустили обновление, ломающее кучу производственного кода ¯ \ _ (ツ) _ / ¯

. В вопросе уже представлен временный обходной путь - переназначение свойства вместо модификации .

0 голосов
/ 31 марта 2020

Я закончил этот проект на прошлой неделе, я думаю, но, проверяя страницу Swift GitHub , кажется, что обновление было 24-го.

Когда я делал этот проект, все работало нормально, но сейчас у меня 47 день, и у меня возникла эта проблема. Возможно, это связано с обновлением Swift 5.2.

0 голосов
/ 31 марта 2020

То, что происходит, - то, что Swift не распознает .append() как установку переменной. Вы можете обойти это, скопировав ваш массив в новый временный массив, добавив новое значение, а затем установите для вашего массива классов этот временный массив.

var newActivityList = [Activity]() //new temporary array
for activity in self.activityList.activities {
    newActivityList.append(activity) //copy class array to temp array
}
newActivityList.append(newActivity) //append new value

self.activityList.activities = newActivityList //set class array to temp array

PS: пример выше для 47-го дня, но логика c такая же.

0 голосов
/ 29 марта 2020

Я также прошел этот проект. Убедитесь, что ваше свойство items в классе Expenses помечено как @Published. Как показано ниже:

import SwiftUI

struct ExpenseItem: Identifiable, Codable {
let id = UUID()
let name: String
let type: String
let amount: Int
}

class Expenses: ObservableObject {
init() {
    if let items = UserDefaults.standard.data(forKey: "Items") {
        let decoder = JSONDecoder()
        if let decoded = try? decoder.decode([ExpenseItem].self, from: items)     {
            self.items = decoded
            return
        }
    }
    self.items = []
}


@Published var items: [ExpenseItem] {
    didSet {
        let encoder = JSONEncoder()
        if let encoded = try? encoder.encode(items) {
            UserDefaults.standard.set(encoded, forKey: "Items")
        }
    }
}
}

и добавьте self.presentationMode.wrappedValue.dismiss() на панели навигации в кнопку Сохранить.

.navigationBarItems(trailing: Button("Save") {
            if let actualAmount = Int(self.amount) {
                let item = ExpenseItem(name: self.name, type: self.type,   amount: actualAmount)
                self.expenses.items.append(item)
                self.presentationMode.wrappedValue.dismiss()
            } else {
                self.isShowingAlert = true
            }

        })
...