Giter Club home page Giter Club logo

swiftdata's Introduction

There is no view

There is an article on hacking with swift about SwiftData. This article shows you the wrong way of separating SwiftData from the SwiftUI.View. It proves to be a failure because it does not work completely and there is a conclusion:

a number of people have said outright that they think MVVM is dead with SwiftData

MVVM is dead with SwiftUI

Sadly most of iOS dev community started pushing MVVM into SwiftUI, probably influenced by past experiences. General MVVM approach is abusing observable objects to decouple business logic from the SwiftUI.View. That way of decoupling is just moving code in a circle and breaking basic principles of SwiftUI.

Decoupling is (kinda) good

Decoupling is good if plan to reuse components and test them in isolation. But why bother testing the code that is broken by design? And when its broken by design then we can not even reuse it properly. It makes a mess inside the project once again.

SwiftUI.View is not a real view

We can not use any of the MV patterns because we dont have a view. In general we have have a model (struct) and a body (function). Model conforms to SwiftUI.View and body returns a SwiftUI.View.

Apple killed the view

SwiftUI.View has no properties of a view, no frame, no colors, no nothing. Its just a protocol. Apple never uses the term viewModel because they know there is no view, they just use a term model instead.

Business logic and values

Model represents a state and then entire body is the business logic. SwiftUI.View is required to be a value type. Only from value types you can access environment. Thats why using observable object is breaking the basics of SwiftUI and you should not move business logic code into a class.

Decoupling correctly

Decoupled is not always the best. But if we still decide to decouple business logic from the SwiftUI.View then we have to make components that are a value type, a struct. This can be achieved using DynamicProperty like this:

@ViewModelify
@propertyWrapper struct SwiftDataModel: DynamicProperty {
    @Environment(\.modelContext) private var modelContext
    @Query var items: [Item]
    func addItem() throws {
        let newItem = Item(timestamp: Date())
        modelContext.insert(newItem)
        try modelContext.save()
    }
    func deleteItems(offsets: IndexSet) throws {
        for index in offsets {
            modelContext.delete(items[index])
        }
        try modelContext.save()
    }
}

@ViewModelify is just a small macro that implements boilerplate code for us:

  • wrappedValue required for DynamicProperty
  • inspect property required by ViewInspector
  • dummy view protocol extension required for testing

Usage

Here is example usage with a "clean view":

struct CleanView: View {
    @SwiftDataModel var model
    var body: some View {
         List(model.items) { ... }
         Button("Add item") { model.addItem() }
    }
}

Testing

I use a great package ViewInspector for unit testing. Basically there is no other way to properly test models with @State inside. Here is a test:

var model = SwiftDataModel()
let exp = model.on(\.inspect) { view in
    let model = try view.actualView() // conforms to View
    XCTAssertEqual(model.items.count, 0)
    try model.addItem()
    XCTAssertEqual(model.items.count, 1)
    try model.deleteItems(offsets: .init(integer: 0))
    XCTAssertEqual(model.items.count, 0)
}
ViewHosting.host(
    view: model.environment(\.modelContext, modelContainer.mainContext) // environment works
)
wait(for: [exp], timeout: 1)

Special thanks to Apple for providing SwiftData!

swiftdata's People

Contributors

sisoje avatar

Stargazers

Anas Alhasani avatar AmyF avatar  avatar Sebastian Urban avatar Narcis avatar keaton avatar

Watchers

 avatar

Forkers

abbas-hameed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.