Class or struct for hierarchy model? - swift

I understand difference between class and struct in Swift. Now I'm wondering what to use for hierarchy model.
To define a class is pretty simple (setting connections on properties set is now irrelevant).
class XYClass {
var title: String
var subinstances: [XYClass]
weak var superinstance: XYClass?
}
But it looks like pretty fine model for struct. Especially if I need to instantiate a lots of these and frequently. But I'm wondering if I can somehow safely point to superinstance or I need to store whole object graph to every instance on every change... Should I use class or struct and if struct, how to define it?

You are making a linked list. If you were to try to form a linked list of structs of a single type, memory management would not be feasible, and the compiler would stop you dead in your tracks. This won't compile:
struct XYClass {
var title: String
var subinstances: [XYClass]
var superinstance: XYClass?
}
The compiler has spotted the problem. You cannot refer to an instance of a struct as a property of that struct. (The compiler calls this a "recursive value type".)
Thus, for your situation, you must use a class, because only then can you get a weak reference and avoid a retain cycle. Only a reference to a class can be weak (and only if the reference is typed as an Optional).
This will compile, and will give your linked list coherent memory management:
class XYClass {
var title: String = ""
var subinstances: [XYClass] = []
weak var superinstance: XYClass?
}

Related

Swift Codable struct recursively containing itself as property

I have a rather large struct that conforms to Codable, and one of its properties needs to be of the same type as itself. A shortened sample of what I'm trying to do is shown below:
struct Message: Codable {
let content: String
// ...other values
let reference: Message // <-- Error: Value type 'Message' cannot have a stored property that recursively contains it
}
Swift doesn't seem to allow a struct to recursively contain itself as one of its values. Is there any way to get this working besides creating a complete duplicate Message struct (which turns this into a chicken and egg problem where the duplicate struct cannot contain itself etc). Not creating a duplicate struct also allows me to reuse SwiftUI code that takes in and renders a Message struct.
A simple way is to just change the struct into a class:
class Message: Codable {
let content: String
// ...other values
let reference: Message? // optional - the recursion has to end somewhere right?
}
But this could break other parts of your code, since structs and classes have vastly different semantics.
An alternative would be to make a reference type Box:
class Box<T: Codable>: Codable {
let wrappedValue: T
required init(from decoder: Decoder) throws {
wrappedValue = try T(from: decoder)
}
func encode(to encoder: Encoder) throws {
try wrappedValue.encode(to: encoder)
}
}
Then,
struct Message: Codable {
let content: String
// ...other values
let boxedReference: Box<Message>?
// you can still refer to 'reference' as 'reference' in your swift code
var reference: Message? { boxedReference?.wrappedValue }
enum CodingKeys: String, CodingKey {
case content, boxedReference = "reference"
}
}
Sweeper's approach provides two good solutions, both of which involve injecting a class. But there are a several other things you might try, some that work and some that don't, and it's worth understanding the underlying issue. It's not just a quirk; it's an important part of how Swift works.
// FAIL (for abstract reasons)
var manager: Manager
var manager: CollectionOfOne<Manager>
// FAIL (for swift-specific reasons)
var manager: Manager? // when manager is a struct
var manager: Result<Manager, Error> // when manager is a struct
// PASS
var manager: Manager? // when Manager is a class
var manager: Result<Manager, Error> // when manager is a class
var manager: Box<Manager> // our custom class Box
var manager: [Manager]
var manager: EmptyCollection<Manager>
The following example also passes:
protocol ManagerLike {
var id: String { get set }
var mail: String { get set }
var manager: ManagerLike? { get set }
}
struct Manager: ManagerLike {
var id: String
var mail: String
var manager: any ManagerLike?
}
The first two cases fail for abstract, logical reasons. It's impossible for any "eager" language (like Swift) to construct a type that must contain a value of its own type. That's infinitely recursive. And an eager language needs to construct the entire value before it can progress. Even in a lazy language, you'd need some kind of generator to construct this. This is true no matter whether Manager is a struct or a class (or an enum or anything else).
The next two issues are related to how Swift implements structs. A struct stores each of its fields in sequence in memory; there's no indirection. So the size of the struct is based on the size of its properties, and Swift must know its final size at compile time. (In Rust, you can explicitly mark types as Sized to express this explicitly, but in Swift, it's an implicit part of being a struct.)
Optional and Result are themselves just structs, so they inline their contents just like any other struct, and their final size is dependent on the size of their content. Since there is no way to know at compile time how deep this chain goes, there's no way to know how large this struct is.
Classes, on the other hand, store their data on the heap, and are implemented as a pointer to that storage. They are always exactly the same size, no matter what they contain. So a nested Manager? works. It is logically possible, since it can eventually terminate (unlike a nested Manager). And it is sizable because it is always the size of an object pointer. Result works the same way.
Box is just a specific case of a class, which is why it works.
But what about [Manager]? Certainly we don't know how many elements are in it, so how does Swift know how large it is? Because Array is always the same size. It generally stores its contents on the heap, and just stores a pointer internally. There are cases where it can inline its contents if they're small enough. But the key point is that Array itself is a fixed size. It just has a pointer to variable sized storage.
EmptyCollection works for the same reason. It's always the same size, empty.
The final example, using a protocol, is an example of an existential container, which is what the new any points out. An existential container is very similar to the Box type, except that it's made by the compiler and you can't access it directly through the language. It moves the contents to the heap (unless the contents are very small), and so is itself fixed-size.
This brings up one more example that seems to work, but doesn't:
protocol ManagerLike {
var id: String { get set }
var mail: String { get set }
var manager: ManagerLike? { get set }
}
struct Manager<M: ManagerLike> {
var id: String
var mail: String
var manager: M?
}
Now, rather than using an any Manager, it is replaced with a generic. This will compile. So you might think "I'll just pass Manager as the type of M and get my original goal!" And you'll find the rule is...self-enforcing:
let m = Manager<Manager<Manager<Manager<...
The type itself is now infinitely recursive. Oh well. Hard to sneak past algebra.
The key take away is that a struct must know its size, and that means everything within it must know its size, and that's impossible if any of its properties' sizes rely on the size of the struct type itself.
I'm answering my own question after a tip-off by #Sweeper.
By converting the Message struct into a class and changing several extensions, Message recursively containing itself as a property is possible. This is possible since class is a reference type, which are allowed to recursively contain themselves. So, the code below will compile:
class Message: Codable { // <-- Message is now a class
let content: String
// ...other values
let reference: Message
}

Do all #Published variables need to have an initial value in a view model for SwiftUI?

Do all #Published variables need to have an initial value in a view model (I am conforming to MVVM) for SwiftUI?
Why can't I just say that I want the #Published variable of type string with no initial value?
So does that mean that I need to have:
If not how can I get around this?
I was thinking about making an init() for the class, but I would still need to input default values when I initialize the class.
Unlike SwiftUI views, which are Structs, Observable Objects are always classes, and in Swift, all classes must have initializers.
A) Consider making your #Published variable an optional
#Published var title: String?
B) Add an init method
init() { self.title = "" }
Else, there's way to not have an initial value for a class' property.
You may find that force unwrapping with "!" will "solve" your problem, but that's a bad practice, don't do it; if you don't have an initial value for your variable, then it must be optional in your case.
But why are you designing a Model, as an observable object, for SwiftUI, consider using simple Structs if you are not intending on persisting (saving to disk), your data, else use Core Data and it's NSManagedObject class, that is already conforming to ObservableObject.
All stored properties should be initialised somehow. You can delay initialization till construction, like
final class ViewModel: ObservableObject {
#Published var title: String
init(title: String) {
self.title = title
}
}
and later, somewhere in SwiftUI View, create it with value needed in context
ViewModel(title: "some value")

Is there a way to reuse model within itself?

I have an app that stores User (UserModel) Friend list. if a friend clicks one user, its type is the same type (UserModel). In Swift it wouldnt allow using the model recursively, giving me this error:
"Value type 'OwnerModel' cannot have a stored property that recursively contains it"
import Foundation
struct OwnerModel: Codable {
var ownerId: Int
var ownerEmail: String
var ownerUserName: String
var ownerCommonName: String
var ownerBirthDate: String
var ownerCountry: String
var ownerBdayReminderId: Int
var ownerIsVerified: Bool
var ownerIsOnline: Bool
var ownerIsEventGreeted: Bool
var ownerIsBirthdayGreeted: Bool
var ownerAllowGreeting: Bool
var ownerFriends: OwnerModel
}
Is there a way I can reuse the OwnerModel under ownerFriends?
This can't work because structs are value types. So each OwnerModel would have to have a OwnerModel inside it, which would have to have an OwnerModel inside it, which would have to have an OwnerModel inside it.... This can never resolve. Since you've marked this Codable, try to write the JSON you expect to encode this to.
That said, ownerFriends seems plural, which would suggest [OwnerModel], and that's not a problem, since you could have zero of them:
struct OwnerModel: Codable {
...
var ownerFriends: [OwnerModel]
}
Remember again, however, that structs are value types. So each OwnerModel is just a value. It's not a reference to any other object. If you want to refer to other owners, you may want to store IDs rather than the actual object (or use classes in order to create references).

Swift Protocols causing Invalid Redeclaration and cluttering function table

TLDR: Using many Swift protocols in a large project is great for testing and SOLID coding, but I’m getting function clutter and invalid redeclaration clashes. What’s the best practice to avoid these problems in Swift while making heavy use of protocols?
Concretely, I want to use protocols to separate responsibilities from view classes such that they don’t need to know anything about the data models used to “decorate” them. But this is creating a lot of functions for my data model classes that are exposed throughout the app, and that are starting to clash with other protocols.
As an example, let’s say I want to set up my custom tableview cell from a certain data model in my project. Let’s call it MyDataModel. I create a decorating protocol like so:
protocol MyCellDecorator {
var headingText: String?
var descriptionText: String?
}
And then my cell is like
class MyCell: UITableViewCell {
#IBOutlet weak var headingLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
func setup(fromDecorator decorator: MyCellDecorator) {
headingLabel.text = decorator.headingText
descriptionLabel.text = decorator.descriptionText
}
}
Now all I need to do is provide an extension from my data model class implementing MyCellDecorator, providing headingText and descriptionText, and I can plug my data model object into setup(fromDecorator:).
extension MyDataClass: MyCellDecorator {
var headingText: String {
return “Some Heading“
}
var descriptionText: String {
return “Some Description“
}
}
This makes the cell easy to test; it clearly separates responsibilities, MyCell and the UIViewController driving it now need to know nothing about MyDataModel..
BUT now MyDataModel has two extra properties, headingText, and descriptionText - available everywhere. But MyDataModel already extends 10 other decorator protocols for specific UI throughout my project, and it so happens that another protocol already defines headingText, so I get the compilation error “invalid redeclaration of ‘headingText’”.
With all of this headache, I decide to quit, go ahead and just pass MyDataModel into MyCell, it all compiles but I lose all the aforementioned advantages.
What are good ways, in such a big project as this, to score those sweet sweet protocol wins, without cluttering up my class’s function tables and having redeclaration clashes between different extensions?
I agree with where Andrey is going, but I believe it's even simpler. You just need decorator types, and the way you've described them, they should be able to be simple structs, with no inherent need for protocols.
struct MyCellDecorator {
let headingText: String
let descriptionText: String
}
(I've made these non-optional, and I strongly recommend that unless you have a UI distinction between "empty string" and "none.")
Extensions work almost exactly as you've done before:
extension MyDataClass {
func makeMyCellDecorator() -> MyCellDecorator {
return MyCellDecorator(headingText: "Some Heading",
description: "Some Description")
}
}
In some cases, you may find that model objects have very consistent ways that they generate a decorator. That's a place where protocols will allow you to extract code such as:
protocol MyCellDecoratorConvertible {
var headingText: String { get }
var descriptionText: String { get }
}
extension MyCellDecoratorConvertible {
func makeMyCellDecorator() -> MyCellDecorator {
return MyCellDecorator(headingText: headingText,
description: description)
}
}
This example captures the case where the cell happens to have exactly the right names already. Then you just have to add MyCellDecoratorConvertible and the property comes for free.
The key point to all of this is that rather than have model.headingText you'll have model.makeMyCellDecorator().headingText, which will address your explosion of properties.
Note this will generate a new Decorator every time you access it, which is why I'm using a make (factory) naming convention. There are other approaches you might consider, such as an AnyMyCellDecorator type eraser (but I'd start simple; these are likely very small types and copying them is not expensive).
You can split the UI into modules and use internal extensions. Those will not appear in other modules, which will prevent myCellDecorator from showing up everywhere. If more convenient, you can put the myCellDecorator extensions in the same file with MyCell and mark them private.
Since this is a large, existing code-base, I highly recommend allowing any existing code duplication to drive your design. There is no one pattern that is ideal for all systems. It's not even necessary to have every decorator follow the exact same pattern (in some cases it may make more sense to use a protocol; in others a struct; in others you might want both). You can create a pattern "language" without boxing yourself into a world where you're creating extra protocols just because "that's the pattern."
But MyDataModel already extends 10 other decorator protocols for specific UI throughout my project, and it so happens that another protocol already defines headingText, so I get the compilation error “invalid redeclaration of ‘headingText’”.
I think this is the main pitfall here, that you use single model to provide data for different parts of the application. If we are talking about the MVC pattern, then the single model should only provide data for corresponding controller. I think in this case there will be much less protocol adoptions in the model.
On other hand you can try to split functionality inside of the model:
For instance, if we have
protocol CellDecorator {
var headingText: String?
var descriptionText: String?
init(withSomeData data: ...) {}
}
we could create something like this
class MyCellDecorator: CellDecorator {
var headingText: String?
var descriptionText: String?
}
class MyDataClass {
lazy var cellDecorator: CellDecorator = {
return CellDecorator(withSomeData: ...)
}
}
One struct-based way I've played with is this:
Instead of extending MyDataClass, I create a simple struct (which can be fileprivate to the view controller class, or not) that looks like:
struct MyDataClassCellDecorator: MyCellDecorator {
var headingText: String? {
return "Some heading with \(data.someText)"
}
var descriptionText: String? {
return data.someOtherText
}
let data: MyDataClass
}
This way MyCell can still use the protocol to decorate itself, MyDataClass doesn't need any extension at all, and in whatever access scope I want it I get a struct that does the decorating logic for MyDataClass + MyCellDecorator.

Weak reference to closure in Swift

I have the following code to create an observable property for data binding. It's in the works so I'm not sure what the final implementation is going to be and I'm still pretty new to Swift.
class Observable<T> {
typealias Observer = T -> Void
var value: T {
didSet {
for observer in self.observers {
observer?(self.value)
}
}
}
var observers: [Observer?] = []
init(_ val: T) {
self.value = val
}
}
I would like to keep weak references to the Observer closures. I don't want to rely on the client to ensure that the closure is weak/unowned before passing it in, via the capture list. Especially because there can be many observable properties on a given class.
Is it possible to make the closure references weak in my Observable class?
UPDATE:
I found a couple of resources that I think will help me accomplish what I want:
Make self weak in methods in Swift
specifically,
func methodPointer<T: AnyObject>(obj: T, method: (T) -> () -> Void) -> (() -> Void) {
return { [unowned obj] in method(obj)() }
}
The following link refers to the above stackoverflow answer and goes into some more detail:
http://blog.xebia.com/2014/10/09/function-references-in-swift-and-retain-cycles/
and this is a two-way binding example:
http://five.agency/solving-the-binding-problem-with-swift/
specifically,
class BondBox<T> {
weak var bond: Bond<T>?
init(_ b: Bond<T>) { bond = b }
}
where the listener is wrapped in a class called Bond, which is weakly referenced in the BondBox.
Is it possible to make the closure references weak in my Observable class
No. Only class instances can be referred to via weak references in Swift, and a function is not a class instance. (And not only must they be class instances, they must be an Optional wrapping a class instance.)
There are some pretty obvious ways around this, or course - the simplest being a wrapper class. But I do not actually recommend that in this situation, because you have not convinced me that weak references to functions are needed here in the first place. Remember, a weak reference to an object to which there is no strong reference will instantly lose the reference and will be pointing at nil. I can't believe that is what you want. I think you're barking up a wrong tree here.
Weak/strong are for memory management of objects, so only apply for variables of reference types (i.e. types which point to objects). Function types in Swift are not reference types, and therefore it does not make sense to talk about weak/strong for their variables.
Plus, you do not actually have variables of function type in your code (except in the middle of the iteration). You simply have a variable of array type. Even in Objective-C, you can only mark variables as weak or strong, not values that are stored inside other things.
And if you were to write the thing you are writing in Objective-C, you would want the "Observable" to have strong references to the closures. Otherwise, who else would have a strong reference to the closure?