Swift Codable struct recursively containing itself as property - swift

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
}

Related

SwiftUI FileDocument using Class instead of Struct

I've started a document-base macOS app in SwiftUI and am using a FileDocument (not a Reference FileDocument) as the type of document. In every tutorial I've seen, even in Apple's own WWDC video discussing it (https://developer.apple.com/wwdc20/10039), a struct is always used to define the FileDocument.
My question is: is there an issue with using a class in the struct defining the document. Doing so doesn't result in any Xcode warnings but I wanted to be sure I'm not creating any issues for my app before going down this path.
Below is some example code for what I'm talking about: declaring TestProjectData as a class for use within the DocumentDataAsClassInsteadOfStructDocument - struct as a FileDocument?
public class TestProjectData: Codable{
var anotherString: String
init(){
anotherString = "Hello world!"
}
}
struct DocumentDataAsClassInsteadOfStructDocument: FileDocument, Codable {
var project: TestProjectData
init() {
project = TestProjectData()
}
static var readableContentTypes: [UTType] { [.exampleText] }
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let _ = String(data: data, encoding: .utf8)
else {
throw CocoaError(.fileReadCorruptFile)
}
let fileContents = try JSONDecoder().decode(Self.self, from: data)
self = fileContents
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
let data = try JSONEncoder().encode(self)
return .init(regularFileWithContents: data)
}
}
It appears that yes, we need to use a struct for documents. See this post for a thorough example of the issues you can run into if you use a class instead of a struct.
SwiftUI View doesn't update
SwiftUI's architecture is all about using value types for speed and consistency. E.g. on a state change we create all the View structs and then SwiftUI diffs and uses the result to init/update/deinit UIView objects.
I believe the same thing happens with FileDocument. The struct is diffed on a change and the difference is used to init/update/deinit a UIDocument object.
If you init object vars inside these structs then basically it is a memory leak because a new object will be init every time the struct is created which is every time something changes. Also chances are you'll end up using the wrong instance of the object because there will be so many. You can see this type of problem surface when blocks are used inside body, the callback usually happens on an older version of the View struct, which isn't a problem when everything is value types but it is a big problem if referencing old objects.
Try to stick to value types in SwiftUI if you can, if you use objects you'll run into all kinds of headaches. And I don't think ReferenceFileDocument even works yet - I seem to remember it needs some kind of undo manager workaround.

How to store properties in generic struct?

I have a Codable struct that is part of my app, RemoteData. I’m building a reusable package that will fetch the data and store it in UserDefaults. The data fetching, DataFetcher class has a Codable generic parameter. I am subclassing DataFetcher to pass in RemoteData as the generic param.
// in my app
struct RemoteData: Codable {
var experimentOne: [Variant<[Page]>]
var experimentTwo: [Variant<Bool>]
var experimentThree: [Variant<String>]
}
All of the properties in RemoteData will be arrays of type Variant<T> where T is Codable:
// in my package
public struct Variant<T: Codable>: Codable, VariantProtocol {
public var experimentName: String
public var variantName: String
public var percent: Int
public var value: T
}
I’d like to be able to save this data in UserDefaults. I’d like to perform some filtering on the Variant array to see if this user should see that configuration. I’d like to save the data so that each experiment name is the key and the single variant the user should see is the value rather than the whole array. Although if the whole array is the only option, I’d be ok with that too.
However, since my DataFetcher doesn’t know what the properties are since it is just taking in a generic I don’t think I can do that. My first thought was to create a protocol that RemoteConfig confirms to and that the DataFetcher generic also conforms to.
// in my package, but subclassing in my app to provide url
open class DataFetcher<T: Decodable> {
var remoteConfig: T?
var url: URL
public init(url: String) {
self.url = url
}
func fetchAndSaveData() { ... }
}
That doesn’t work because I then need to specify T in Variant and I will only be able to have Variant arrays of one type.
I’m stuck here and not sure how to move forward.

How does the encoded data end up in the Encoder, when it has no mutating functions?

I am trying to code a custom Coder and I am having trouble understanding something, particularly about the Encoder part.
The Encoder is only required to have three functions and two variables :
struct MyEncoder : Encoder {
public var codingPath : [CodingKey]
public var userInfo : [CodingUserInfoKey : Any]
public func container<Key>(keyedBy type: Key.Type -> KeyedEncodingContainer<Key> where Key : CodingKey {}
public func unkeyedContainer() -> UnkeyedEncodingContainer {}
public func singleValueContainer() -> SingleValueEncodingContainer {}
}
You will notice, none of them are mutating. They all return something and that's it.
The containers themselves, to generalise, have sub functions to encode specific types into themselves :
struct MySingleValueContainer : SingleValueEncodingContainer {
var codingPath: [CodingKey]
mutating func encode(_ value : someType) throws {}
/// etc with a lot of different types
}
If a user wants to customise how their class is encoded, they may do this :
func encode(to: Encoder) throws {}
And here is my problem ! The encoder persists after the call to encode(to: Encoder) but it does not mutate so how does the encoded data end up inside it ? Reading the JSON Encoder's source I can see that their containers have a variable containing an Encoder, which is passed down. But it should be copy-on-write as everything else in Swift, which means the data shouldn't be able to make its way back up the chain !
Is the Encoder somehow secretly passed as a reference instead of as a value ? That seems like the only logical conclusion but it seems very weird to me... If not, what is going on ?
I have also taken a look at this question and its answer, and although they have helped me they display the same behaviour of magically bringing data up the chain, except it passes down a custom type instead of the Encoder itself.
Is the Encoder somehow secretly passed as a reference instead of as a value?
Close, but not quite. JSONEncoder and its underlying containers are relatively thin veneer interfaces for reference-based storage. In the swift-corelibs-foundation implementation used on non-Darwin platforms, this is done using RefArray and RefObject to store keyed objects; on Darwin platforms, the same is done using NSMutableArray and NSMutableDictionary. It's these reference objects that are passed around and shared, so while the individual Encoders and containers can be passed around, they're writing to shared storage.
But it should be copy-on-write as everything else in Swift, which means the data shouldn't be able to make its way back up the chain!
One more note on this: all of the relevant types internal to this implementation are all classes (e.g. JSONEncoderImpl in swift-corelibs-foundation, _JSONEncoder on Darwin), which means that they wouldn't be copy-on-write, but rather, passed by reference anyway.
Regarding your edit: this is the same approach that the linked answer uses with its fileprivate final class Data — it's that class which is shared between all of the internal types and passed around.
JSONEncoder.encode uses JSONEncoderImpl as the concrete implementation of Encoder (source):
open func encode<T: Encodable>(_ value: T) throws -> Data {
let value: JSONValue = try encodeAsJSONValue(value)
...
}
func encodeAsJSONValue<T: Encodable>(_ value: T) throws -> JSONValue {
let encoder = JSONEncoderImpl(options: self.options, codingPath: [])
guard let topLevel = try encoder.wrapEncodable(value, for: nil) else {
...
}
return topLevel
}
wrapEncodable would then eventually call encode(to: Encoder) on your Codable type with self as the argument.
Is the Encoder somehow secretly passed as a reference instead of as a value ?
Notice that JSONEncoderImpl is a class, so there is nothing "secret" about this at all. The encoder is passed as a reference in this particular case.
In general though, the Encoder implementation could also be a struct, and be passed by value. After all, all it needs to do is to provide mutable containers into which data can be encoded. As long as you have a class somewhere down the line, it is possible to "seemingly" mutate a struct without mutating or making the struct a var. Consider:
private class Bar {
var magic: Int = -1
}
struct Foo: CustomStringConvertible {
private let bar = Bar()
var description: String { "\(bar.magic)" }
func magicallyMutateMyself() {
bar.magic = Int.random(in: 0..<100)
}
}
let foo = Foo()
print(foo) // -1
foo.magicallyMutateMyself()
print(foo) // some other number

Alternatives to an array of T swift

In my Quiz app I initialize quizzes, and the providing class does not know the format of the questions before being provided them (although they are constrained by a QuestionProtocol):
public protocol QuestionProtocol {
init?(fields: [String] )
var description: String {get}
var question: String {get}
var solution: String {get}
var explainAnswer: String {get}
var answered: Int {get set}
var qa: String {get}
var qb: String {get}
var qc: String {get}
var qd: String {get}
}
And I can initialize the quizzes and return them easily enough through a method with the signature
public func initializeQuizzes<T: QuestionProtocol>(with type: T.Type, withCompletionHandler completion: ((Result<[Quiz<T>], Error>) -> Void)?)
However to provide these quizzes is expensive (an API call or a SQL retrieval) so I want to store these quizzes and retrieve them separately from a suitable function with signature
public func getNextQFromSet<T: QuestionProtocol>(with type: T.Type) -> (question: T, answers: [String])?
The problem I have is storing these questions which are of type T.
They are linked to a Quiz object:
public class Quiz<T> {
private let questions : [T]
private let name : String
init(name: String, questions: [T]) {
self.name = name
self.questions = questions
}
public func getQuestions() -> [T] {
return questions
}
func getName() -> String {
return name
}
}
So I'm able to store them as quizzes that conform to the QuestionProtocol
private var quizzes = [Quiz<QuestionProtocol>]()
But then I lose the extra information I want to store in the question.
I can store Any, but I believe that is bad practice
private var anyquizzes = [Quiz<Any>]()
Ideally I would like to store T i.e.
Quiz<T>
but that seems to be impossible in Swift.
Because these classes are in a pod they have no way of knowing about the internal workings of a Question, and are provided these at runtime hence the use of generics and the difficulties in storing these questions.
I can't think of a way to improve the design of the App (more specifically the Pod) - I want to initialize the quizzes once and once only and then run functions like getNextQFromSet() to retrieve a relevant question - which obviously depends on me knowing the type of the question (which I do not know before runtime).
For clarity here is a link to the Pod: https://github.com/stevencurtis/QuizManager
How can I store an array containing these questions without knowing the type?
To be short, I think it makes sense to remove QuestionProtocol and replace it with plain data structure struct Question.
Before I explain my point of view, I want to note that even though I looked at the pod, I still do not know all the requirements, so I might be wrong.
Let's try to have a look at the problem from design perspective instead of programming language perspective.
What is the reason of having QuestionProtocol? Could it be replaced with, let's say, object instead? Why do those properties should be polymorphic? Of course implementation details should be hidden, but hiding data is not about protocols or additional function layers, is about abstractions.
Let's convert QuestionProtocol to Question object for now and think about an abstraction. If there is a real abstraction, then there should an object that hides the data (details) and expose functions that manipulate that data. But there is no functions in Question object and it means that there is no real abstraction behind.
Finally, It means that Question entity most likely is a plain data structure with public properties and could be defined as struct Question.
Having this Question struct now, you can define quizzes as Quiz<Question> and use it to save and retrieve the data.
In addition, I think it worth to point out two things which could simplify and potentially improve design and implementation:
Why does SQLiteManager knows something about concrete question (depends on QuestionProtocol)? I think it makes sense to introduce some generic DBObject or at least plain dictionary [String: Any] which SQLiteManager would know how process and then insert. Then Repository could transform Question data structure into DBObject on some level of composition and pass it to SQLiteManager.
When using generics, in most cases there is no need to define additional type: T.Type parameter. Once generic is defined you can use it as [T], T.init, etc. If you still need a metatype (T.Type) you can get by T.self.
Hope this helps!
Update:
There is great example of Quiz app created with TDD and modular design: Quiz App. There is also a video series explaining design and creation process step by step.
How can I store an array containing these questions without knowing
the type?
To my knowledge you can't. As rraphael pointed out in his comment generics aren't resolved at runtime. Furthermore Arrays in swift are designed to hold a single type:
Specifically, you use the Array type to hold elements of a single type, the array’s Element type.
So whatever you do you'll have either an array of Any or maybe QuestionProtocol but nothing more dynamic than that : the type will be resolved at compilation time
You may be able to redesign your QuestionProtocol to suit your needs but without any information on the different types of question it's a bit difficult to help you more since it is an architecture matter.
You can use enum with associated values for describing types. For example:
struct QuestionTypeA { }
struct QuestionTypeB { }
struct QuestionTypeC { }
enum Question {
case typeA(question: QuestionTypeA)
case typeB(question: QuestionTypeB)
case typeC(question: QuestionTypeC)
}
And then:
public class Quiz {
private let questions : Question
private let name : String
...
And store an array of Quiz without generic
private var anyquizzes = [Quiz]()
You wouldn't be able to store a Quiz<T> and a Quiz<U> in the same array using either of those types. They're just not the same type.
If you have an Array<QuizProtocol>, you can just match against your known types in a switch-case statement:
var quizzes: [QuizProtocol] = ...
for quiz in quizzes {
switch quiz {
case let someQuiz as SomeQuiz:
...
case let someOtherQuiz as SomeOtherQuiz:
...
default:
... // couldn't cast to any known type; do some fallback logic
....
}
}
where SomeQuiz and SomeOtherQuiz conform to QuizProtocol (though strictly speaking, you could match against any type).

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.