I have an application in which I use SwiftUI and Core Data. In the Core Data datamodel, I have an Entity, Record
, which has various attributes, two of which are of a custom class type, who's purpose is to act as a wrapper for an array. These classes both contain a property which is an array of another custom class type (Array<[Class]>
). Some of properties in the definitions of the aforementioned class types are prefixed with the @Published
property wrapper, so that the view will update when these properties change, along with the context. Rather unfortunately, this does not seem to be the case — and I can't work out why! I have tried to work around this by having a custom delegate to manually call objectWillChange.send()
, but this doesn't feel very clean, nor does it register changes with the context.
The application can be found here, the relevant data model classes are in this directory, and the relevant view classes are ContentView.swift and RecordDetailView.swift (I have commented the [SwiftUIView]_Previews
structs because I'm not quite sure how to get the previews to work with the Core Data container). I won't post many code-snippets here, as they are somewhat dependent on each other and there is too much to form a concise question.
Below is the definition of one of these "wrapper" classes:
import Foundation
@objc(Nodes)
public class Nodes: NSObject, NSSecureCoding, ObservableObject {
public static var supportsSecureCoding: Bool = true
@Published public var nodes = [Node]()
public enum Keys: String {
case nodes = "nodes"
}
override init() {
super.init()
}
init(nodes: [Node]) {
super.init()
self.nodes = nodes
for node in nodes {
node.delegates.append(self)
}
}
public func encode(with coder: NSCoder) {
coder.encode(nodes, forKey: Keys.nodes.rawValue)
}
public required convenience init?(coder: NSCoder) {
let nodes = coder.decodeObject(forKey: Keys.nodes.rawValue) as! [Node]
self.init(nodes: nodes)
}
}
extension Nodes: NodeDelegate {
func didUpdateNode(node: Node) {
objectWillChange.send()
}
}
And below is the definition of the Array type:
import Foundation
@objc(Node)
public class Node: NSObject, NSSecureCoding, ObservableObject, Identifiable {
public static var supportsSecureCoding: Bool = true
public var id = UUID()
var delegates: [NodeDelegate] = [NodeDelegate]() {
didSet {
delegates.forEach { $0.didUpdateNode(node: self) }
}
}
@Published private var x = "0" {
didSet {
delegates.forEach { $0.didUpdateNode(node: self) }
}
}
@Published private var y = "0" {
didSet {
delegates.forEach { $0.didUpdateNode(node: self) }
}
}
var xDouble: Double {
set {
x = String(newValue)
}
get {
if let x = Double(x) {
return x
} else {
return 0
}
}
}
var yDouble: Double {
set {
y = String(newValue)
}
get {
if let y = Double(y) {
return y
} else {
return 0
}
}
}
var xString: String {
get {
x
}
set {
x = newValue
}
}
var yString: String {
get {
y
}
set {
y = newValue
}
}
public enum Keys: String {
case x = "x"
case y = "y"
case delegates = "delegates"
}
override init() {
super.init()
}
convenience init(x: String, y: String) {
self.init()
xString = x
yString = y
}
convenience init(x: Double, y: Double) {
self.init()
self.xDouble = x
self.yDouble = y
}
public func encode(with coder: NSCoder) {
coder.encode(x, forKey: Keys.x.rawValue)
coder.encode(y, forKey: Keys.y.rawValue)
coder.encode(delegates, forKey: Keys.delegates.rawValue)
}
public required convenience init?(coder: NSCoder) {
let x = coder.decodeObject(forKey: Keys.x.rawValue) as! String
let y = coder.decodeObject(forKey: Keys.y.rawValue) as! String
let delegates = coder.decodeObject(forKey: Keys.delegates.rawValue) as! [NodeDelegate]
self.init(x: x, y: y)
self.delegates = delegates
}
}
On another note, there are also several really horrible UI glitches which I do not understand, for example, sometimes when tapping on a RecordThumbnailView
from a ContentView
it will do nothing, but then when tapping on a different RecordThumbnailView
it will suddenly push all of the RecordDetailView
s which correspond to all of the unresponsive RecordThumbnailView
s. In addition, when performing this same segue, the ChartViewControllerView
will jolt upwards very quickly, and will be hidden behind the navigation bar.
If you know of any ways to overcome these issues, I'd appreciate the assistance — I've been staring at these problems for several days!