0

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)
    }
}
1

1 Answer 1

0

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 like a memory leak because a new object will be init (and any of its expensive init work will be done too) 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.

Sign up to request clarification or add additional context in comments.

3 Comments

Pretty certain you're wrong with "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." - it's an extra overhead but there is no leaking of the object vars, they will be cleaned up by reference counting. The real problem is possibly missing on copying values to new objects. Struct copying will normally just copy the REFERENCE to the object. Same thing happens passing structs down through function chains as params, when the struct owns object pointers.
It's the overhead I'm talking about and it's worse if the object init does something.
"Memory leak" is a very specific term. If you mean "memory update cost" or similar then say so. It is not a leak!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.