I used to develop web apps using React for a long time and as recently I had to work with IOS Mobile development team inside my company helping to port web application to IOS and I noticed the Apple SwiftUI framework, indeed it is heavily inspired by React.js as data and state driven rendering technique, and it does apply MVVM architecture that i find a very clean approach, therefor I decided to cheat 🙂
So let's first look at simple plain SwiftU implementation of MVVM and see how clean it look
// Model
struct Task {
var id: UUID = UUID()
var title: String
var complete: Bool
}
// ViewModel
class TodoManagerViewModel: ObservableObject
{
// value publishers (publish change to view state and force view render)
@Published var tasks: [Task] = []
func addTask(title: String)
{
tasks.append(Task(title: title, complete: false))
}
func complete(id: UUID)
{
for i in tasks.indices
{
if tasks[i].id == id
{
tasks[i].complete = true
}
}
}
func clear()
{
tasks.removeAll()
}
}
struct ContentView: View {
// Observe ModelView and request render
// if any @Published variables changed
@StateObject var todoManger = TodoManagerViewModel()
var body: some View {
// Render logic same as
// React Components composition style
return AnyView()
}
}
and by compiling this code we get this app
So the key design points is:
- ViewModel with
@Published
properties - attach the ViewModel to the View to receive published changes.
Now let us construct the same design using Reactjs
I built this typescript module to help us construct a ViewModel and publish it state .
now let us see how we can build MVVM react TodoManager application with this module the same way as Apple SwiftUI do.
1) install react-mvvm-like
2) creating Task Model with autoDoneAfter property so task gonna flip to done after a few seconds if not done by user.
export type Task = { | |
id: string; | |
title: string; | |
done: boolean; | |
autoDoneAfter: number; // seconds | |
interval: number; | |
}; |
3) construct the ViewModel class extending ObservableObject
and define published properties
import { Published, ObservableObject } from "react-mvvm-like"; | |
export class TodoViewModel extends ObservableObject { | |
@Published tasks: Task[] = []; | |
async fetch() { | |
... | |
} | |
intervalToggle() { | |
... | |
} | |
removeTask(task: Task) { | |
... | |
} | |
addTask(title: string, done: boolean = false) { | |
... | |
} | |
toogle(task: Task, compelete?: boolean) { | |
... | |
} | |
doneAll() { | |
... | |
} | |
pushToServer() {} | |
} |
4) receive the changes inside React Component.
import { useObservedObject } from "react-mvvm-like"; | |
const todoViewModel = new TodoViewModel() | |
export default function Component() { | |
const todoManager = useObservedObject(todoViewModel); | |
useEffect(() => { | |
todoManager.fetch(); | |
}, []); | |
return (<ul> | |
{todoManager.tasks.map(t => { | |
return <li key={t.id}>{t.title}</li> | |
})} | |
</ul>) | |
} |
thats it, we are done, please check full example below:
If you have any feedback on this article, please let me know!
Top comments (0)