LongCut logo

SwiftUI Architecture - Best Practices and Principles

By AppForce1

Summary

Topics Covered

  • Ditch Per-Screen ViewModels
  • Views Are ViewModels
  • One Store Per Domain
  • Use FetchRequests Directly
  • Test Behavior Not Implementation

Full Transcript

and today we'll be talking about uh Swift UI architecture here is a little bit about myself my name is Muhammad aam I go by

aam I'm an iOS Developer coding boot cam instructor and uh I also run my own website aam sharp.

School author speaker and uh now this talk is going to be interesting because I'll be telling you a lot of things that

you already have been doing in Swift UI architecture that may not be correct so I'm going to tell you my own story and maybe you can learn some patterns and

practices from there it's kind of hard to stand over here with the light all right so it all started with WWDC

2019 and when Apple introduce surui now I was really happy at that point because hey I already know react and I already know

flutter um so sfui was kind of like a walk in the park for me but then I started thinking about okay which pattern should I use I mean in UI kit

applications we were using uh MVC model view controller so what should I use with SF UI and I didn't really thought about it

that much at that time I'm like okay I'll just use what other people are using using so I'll just use mvvm or whatever and after that I went a little

bit crazy start writing books about mvvm uh publishing articles creating videos I was guest in different podcasts and more articles and more videos and

more articles and more videos and yeah I tend to do that and uh you know a lot of stuff a lot of content related to

mvvm most of my applications were client server or core data or Swift data application I think most of you are also developing the same applications and they will consist of

here's a simple view there's different screens over here four or five screens and I say screens and I don't say views because screen over here is a

more of a bigger container that can contain other reusable views but anyway for each screen I will

have a separate view model I mean that's how we're supposed to use mvvm right and each view model will have a dependency on HTTP client because they need to get

the stuff from server or they need to post to the server so for five or six different screens I added five or six different view models pretty normal I mean if you

go read any article or watch any videos this is exactly what you're going to get each of the screen will have a dependency on The View model each of the

view model will have a dependency of HTP client and then this goes on and on and on all right one thing I started to

notice is if I'm using this approach if I'm creating for every single screen I'm creating every single view model a separate view model I can have like 50

screens and I can have 50 view models and in none of those view models I can access things like app storage I cannot access environment object

easily Focus State fetch request you know environment itself query I mean they're all inside available only inside the view

itself so I was like okay maybe I'm doing something wrong I can't access any of these things inside my observable objects inside my view

models and sometime I have views that are deeper down into the hierarchy how do I pass data to them well I can actually use environment object but wait

environment object is only available inside the view I should probably not use that because that's what the iOS Community was telling everyone and then I come across these stack Overflow post

like Hey how do I pass environment object into the view model it's that's 116 upwards 116 likes well you can do that by using dependency injection but

to be really honest it looks horrible I mean by the time I was done passing the environment object in a view model I don't even know what I was doing who was

a source of Truth what was going on things were not really different for core data and s data applications I mean in a very simple application where you

have a one screen that is showing you a list of movies and another sheet or a model that is showing you the ad movie screen how do I add a movie from the add

movie screen and refresh it on the movie list screen how do I do that well I can reload everything on the dismiss I mean dismiss of the view uh dismiss of the

model or the sheet I can definitely do that I can if I'm feeling crazy I can go ahead and Implement for code data NS

fetch results controller that's a lot of code there's no way I can do that I mean it works and it automatically refreshes your screen with all the movies when you

add a new movie but no I I don't want to do that this is a lot of code I don't have time for this BS or if you're feeling completely crazy

then you can pass Flags to refresh the parent so from the ad screen you can or add task view you can pass some flags and I have seen these approaches well I've done these approaches hey let's

just change the accent color to clear and then it will refresh right we have all done those things I can also use closures this is a good technique this is is a fine technique so there's nothing wrong with

this technique it's okay but what about what about just using fetch requests property rapper what about

using query Macro for Swift data what about that can can I just use that oh no the I community no no no no no you cannot use that cannot that's

they're only available inside the view you can do that so so uh this is when I used to have hair but this so this is me now

thinking okay what what just went wrong and I'm like okay you know what forget about it 2019 sfui was introduced 2013 react was introduced react and so

you are very similar why don't I just go and learn from react I already know react and flutter so I looked at react how react was doing things and what

react does is they use a container presenter model so you have a component a container component inside that you have presentation components the job of

the container component is to do all the other stuff like fetching the stuff and then posting it and all that stuff presentation components are used to for reusability and display

purposes so here's a very simple example of react now I know that maybe you don't know react but I can bet all of you $100,000 that you can still understand

what this is right what do component feels like a view right so component in react is kind of like a view and you can see the dog

images container is loading fetch request to this long URL and then right down at the bottom dog images it's just passing the data okay wow this is

actually pretty nice it's so simple in react how can I do that in C UI so I start experimenting

all those view models get them out of here let's just remove all those view models I'm just experimenting don't go crazy all how about the view itself just talk

to the HTTP client can we do that all right now I know people are like what about separation of concerns forget about that right now all right the separation of concerns is the view the

container itself if you don't understand that then go and watch the documentary from react people the people who actually created react the separation of concern is still there the concerns are

just moved into the component itself but that's just one way of doing things this is not the only way okay so you can see on the top I get the HTTP client from

the environment because I just want to make it look fancy I mean you can just pass it that's also fine and then I loaded right there load articles I

just called the function and the load articles just use the HTP client directly and get the stuff and populate

it into articles which is a private State wow so nice so simple I can do that this is the container presenter P pattern that react is has been using

since 2013 so anyone who's thinking you can really make apps on that go and look at react it has been around since 2013 they

are using these patterns to create scalable Enterprise level applications all right because I think the first thing we always say or it's not going to work in a big app well they have been

doing that for big app it's been released since 2013 we have this react has sixe advantage on um s

UI now sometimes you you want to reuse the Sorting Behavior now yeah you can create extensions and all that stuff but I've already created the environment for

the value on the top you can see sort function and I can just use a sort function it's kind of like a hook in react but in sft UI it's more of an environment

value how do I pass the data well you can use closure in this case I mean that's perfectly fine just pass the closure to the add user screen when the user adds the screen then you add it

right there on the left side on the top you can just add it to to the stuff I mean to the array so I was using this pattern and it

was working perfectly fine um but then comes well how do I share data with different screens that are well you know

that are deep into the hierarchy or they are deep in the navigation stack H well how do I do it in react we

use Redux in react anybody has used Redux yeah so we a lot of people all right Redux is basically the global state in react where Redux is already built in

swiftui it's called environment object you don't have to do any fancy stuff with the reducers and all that middleware and all that stuff it's already there it's just called environment

object now in order to use environment object we have to use observable or observable object so we come to this diagram and I'm like okay so if I want

to use Redux if I want to use some sort of a global object maybe I can just put something in the middle and I can call

it store or aggregate model or something and apple actually discussed this particular technique in WWDC 2020 data Essentials in sfui you can see this

orange rectangle or the rounded rectangle that's a surface area meaning all your views those blue things are going to talk to that surface area and get access ACC to all of these entities

and you can look at several different examples that apple have also put out those examples are like fruta app and the food truck app and the backyard birds app all of those things are

actually there for you to check it out now granted they are not like Enterprise level millions of lines of code but you can still learn from those actually I would say food truck is a pretty decent

size app I mean I would say it's like a medium siiz app here's a direct screenshot from Apple use excode for server side development that's the name of the WWDC session and

you can see that Apple what apple is doing is they're creating a food truck model I mean you can call it food truck store that's fine and in there they're

using the donut server client to get all the donuts from a HTTP request now in a small application you probably may need only one observable

object if you only have one source of truth but in larger applications you can add more here's the code from my own application hello Market where I'm using

product store and all the things that are responsible for products like saving the product loading all the product loading my products updating products

everything is in that single product store I can call it product view model or whatever it doesn't matter I just call it product store and the good thing about this product

store is that now I can go ahead and inject it I can create a product store and I can injected as an environment so it's available to everyone and now everyone can just use

it from the environment object the same goes to for the add product screen because it's adding the product we can just use it directly we

just get the product store and we start using it wherever we want now at this particular Point people usually say well hold on a second isn't

product store just a view model well the answer is no the product store is not the view model if we go back to the diagram you see in the view

model approach when we were doing we were creating view model for every single screen do you really think think about this do you really think your every single screen has a different

source of truth no no you don't then why creating three different observable objects we don't

have a reason right because we were just following somebody said something and we just start following it here's what the it will look like when you actually use

a single source of Truth movie store now this is only for the movies it's not like oh I'm just going to put all my code in movies no no not all the code only for the movies adding the movies

deleting the movies updating the movies movie details listing movies sorting movies filtering movies movie store or you can say movie view model whatever

you want to say for products it's going to be product store all right so in this example where you have

the view models removing all the view models over here and then replacing it with some

sort of a product store as you can see there we go or movie store but wait if that contains all the business Logic

the movie store what's in the view so this is example from the Food Truck application the and this is the actual view it's called orders View and you can

see what's in the view itself orders order section so in the view you have the code for transformation logic

validation logic mapping logic and these kind of things so at that point it hit me like a brick of a back

of my head that the view itself is the view model in sfui all right and that was the turning point that when I was like okay hold on

a second now I get it view is like a component in react view is the view model I don't have to start creating uh separate view model for every single

screen and that is how apple is doing it that is uh the way to do it to make your life much simpler so you can see that how we have

reduced the code all of these things are about movies instead of creating five different sources of Truth we created one single source of Truth for that particular bounded context for that

thing movies but what about about bigger apps I mean that's definitely not going to work for bigger apps right well for bigger apps you can

create multiple sources of truth but it should not be based on the number of screens that you're adding I mean just think about it if you're working in an

e-commerce app you may have 50 60 100 screens do you really think you will have 100 view models would it be easier to manage few

sources of Truth rather than 100 so whenever we are working on uh you know these applications like larger e-commerce applications we can create

sources of truth based on the context catalog fulfillment you know others things again the same diagram all the movie screens are talking to the movie

store all the product screens are talking to the product store and this is what it kind of looks like in a large context you can see all on the top you have catalog UI these can be simply

folder so catalog UI will contain all the screen for the catalog and catalog itself will be catalog model or Catalog Store catalog view model whatever you want to call it I wouldn't really use

the term view model because the view model if you go back in 2004 it was known as presentation model by Martin Fowler you can read about it and view model was or mvvm was originated with

WPA Windows presentation foundation and the main idea was one view model per screen so I would refrain from calling it view model I can call it catalog

model Catalog Store something like that all right catalog controller whatever and then they all access my store kit so this can be some sort of a you know hdb

client or whatever store kit that you have but you can think about is if catalog has 50 screens inventory has 50 screens ordering has 50 screens and shipping has 50 that's like 200 would

you actually create 200 view model for every screen that's going to be tough all right good luck with that so what we have done again is

reduce every single screen we were using this approach and we have now made sure that we are creating one observable

object that can give us that particular data that we want then I went to core data and sft data don't fight the framework all right

I mean Apple has provided you with fetch request section fetch request query and all of those different things these are highly optimized property wrappers and

macro they write a lot of code they do a lot of things for you all right so use this they also work with tracking meaning if you add something it will

automatically refresh now you can use Query inside the view you can see over here that I'm using query inside the view all right

there's nothing wrong with that nothing wrong with that you can use that but if you want to use the same query inside a different view you can extract it see

that premium at the bottom just extract the query out you can write test for it if you want to and you know you can return all of that

stuff and now you can see that over here I've extracted it out and if I want to use it I can use it like that it's nice and

simple for Swift data one thing to remember is that your logic the domain logic not the presentation logic or validation logic or the transformation L

the domain logic goes inside the the model you can see the model is called the recipe model and I have the domain logic called is ingredient valid for

course that's the actual domain logic that's a rule that's a business rule it goes right inside the model there's no need for creating view model in this

case actually I would even say that if you're using um Swift data and you use view models you're going to get into a lot of problems all right and then you

can write test for it here's another example where I have budget category and I am trying to find out if the budget category exists or not

so that's the actual domain Rule and I can put it right inside the model itself and then I can write a unit test for it since we're talking about

testing let's start with testing what testing is because everything I uh what has happened over the course of decades and

decades is we have put testing on the pedestral and testing is now right here everything else architecture we don't really care that's garbage nobody cares

about if I can test it it's just better that's dhh David Hansel Hansen hinam uh who the creator of Ruby on Rails he did a very good talk

in 2014 I think and he's also talking about the same exact thing that we as developers oh we can test it it's just better there is no other question and I

think we're we're getting into this uh phase where everything has become about testing and I'm not saying testing say is not important I'm just saying that

test the important parts here is Ken Beck the the creator of tdd test development and here has a very nice code I get paid for code that works but not for tests so My Philosophy is to

test as little as possible to reach a given uh level of confidence now obviously kbak over here is not saying that don't test but kbak is saying that test are things that actually provide

value you must have some sort of a return on your investment here's a very simple code that's how I write my login view you can see I have a

property call is form valid and whenever some people see this they first thing is that you're not using view model how are you going to test it that's the first question they ask right they don't care

about the Simplicity the beauty the clarity of this they're like well how do you tested well oh boy okay if you want to test it then you're actually testing the

actual property right this is the actual property you're testing it nobody's uh telling you to not to write unit test you can go ahead and write these unit tests if you want to all right that's

perfectly fine same thing over here I have in the view I have filtered products now you can move filtered products into the observable object

that's perfectly fine but if you're using container presenter part pattern and if you want to how do you test filtered products well you can write a

UI test for it or you can extract it out into a product filter form and then write test for it all right one thing to note over here is

that just because if you're using mvvm or any kind of a model just because you're testing the model it does not

test your user interface I have seen people who are trying to recreate the Swift UI view life cycle so that they

can test it I'm like what how about I don't know write a freaking UI test jeez got to be kidding me right and if

you're using um you know those sort environment objects and all that you can actually test it out test the behavior not the implementation details this also means

that if you're writing a test and you're refactoring it and your test is breaking that means you're not a good test it's a bad test here's a here's an example of when you're actually testing the

implementation details you should test that what the fetch products actually returns but not when it was called or not um also for the mocking don't use

mock for manage dependencies I've seen so much code where people are just mocking the mocks and the mo those mocks have its own mocks and nobody knows what's going on why are you mocking

things that you own you should only mock things that you don't own like payment Gateway is one uh maybe a currency exchange because you don't have access

to those things so mocks should be very limited there's a really great talk uh by a Cooper and he talks about tdd where did it go wrong and he talks about the

same things that we have gone absolutely crazy with everything has to be tested everything has to be tested uh I mean nobody over here or maybe there is someone over here who's uh sending a

person to moon or Mars but if you are then yeah sure go ahead and write all the tests you can but most of the time we are not really sending people to Mars or sending people to the Moon we're just

creating I don't know to-do list apps right I mean that's what most of the apps are right but no in all seriousness I mean if you're working for a financial

application like bank make sure that you find out where the money is made in the bank debit credit charging fees no likes that but it makes money test that out

all right you're not going to get a call in the middle of the night that oh you can't believe it aam the the color of the button is purple not pink oh okay

hang up jeez all right test the isolation or test is the isolation not the thing under test there another misinformation

about testing is well I'm writing a test I need to completely isolate it from the file system from the database it shouldn't touch anything that's completely wrong completely wrong 100%

wrong if your test wants to access the database go ahead I mean if your it's own database locally then go ahead do that just make sure that when the test

is finished you clean up the database so that the other test is not passing or failing because of the residue that is Left Behind with the previous

test and for testing make sure that you're writing end to endend test those are the best test and quite frankly most of the developers do not write end to

end tests by saying that well it's just too slow right well of course it's slow it's doing the work I can actually write a test that will run in zero seconds if

it doesn't have to do anything that make sure that you're writing n to end test from other experimentation I also looked at navigation inui um you can

create a very basic navigation for customers you click on the customer or tap on the customer you go to the customer details screen and that's pretty basic you can use navigation

destination whatnot that's fine um how can I do programmatic navigation using enums okay so this is kind of like a programmatic navigation you can see that

in the content view I have navigation destination and depending on where you click I can just add something to the routes and it based on the the route itself but then I looked at react see

there's so much things to learn from react and I looked at this line number 10 Navigator I'm like whoa this looks really nice I wonder if I can get my S

UI to work like that like navigate well you can I mean it's just an environment value so you can work through something

and create a navigate you know navigate environment values it's not updated for iOS 18 you can use entry macro I think but

um and then you can use it you know you can set it up in some sort of uh root view so you don't have to do it again and again and then here's the

usage on the left side you can see enum and that is kind of like a nested enum so you can even go to Patient or doctor but you can say I'm going to go to the

patient but list patient and I want to go to the doctor but to create a doctor so over here navigate patient list so nice so simple right over there

environment navigate and it's basically exactly like uh you know react now over here and I'm My Own

biggest critic over here even though it works the amount of work that was needed to make it happen maybe it was not worth it for small or medium siiz app maybe if

you're working for a very large application maybe this will make sense because over here I'm like playing around with I'm kind of like like forcing or going against uh you know

surf UI itself I'm trying to do things like twisting it in a wrong way so the current navigate hook method requires extra steps if you want to pass binding which is a very common operation so it

will not work with binding unless you do something extra Global sheets I also wanted to see how we can look at Global sheets meaning

how do we display sheets well if you want display sheets in s UI you just use a sheet view modifier as you have seen already over here okay can I display

multiple sheets yes you can I mean just use multiple sheet modifier okay how about sheets based on enum yeah you can use enums and now the

sheet is based on enums but what if we can do this again with the environment value maybe there's an environment value

called show sheet and you can see in the button clicks I just say show sheet and it just displays the sheet well you can

so again you can create an environment value you can create all of those enums um and then injected into the environment

itself injected into your actual application route so that everything is manage in one place and then you can actually display

sheets now now since this is a global sheet there's only one sheet anyways so this means that you cannot display sheet on top of another sheet if you use that particular Behavior if you want that

particular Behavior then you still can use the normal sheets all right what about displaying messages you know those toast messages error something bad has happened or success

you have added a new customer the same technique can be used over there also all right same exact technique you will create create an environment value and then it will look something

like this so with all of these approaches you see that what I'm trying to do is to I I'm trying to minimize the work that I need to do if I want to show a message

then I can just say show message and I'm done I don't have to do anything other fancy stuff now whenever you're trying to create reusable

views you can either pass in The Binding just like this one reminder cell view or you can pass in closure so over here I'm passing in on checked and on delete and

the reason is that I want the parent to be the person responsible for implementing on checked and on delete because I want to reuse that for this example it's fine I mean there's only

two closures on checked and on delete but if I go to three or four or five then it becomes messy both on this side and on the calling side so what we can

do is how about we take those and put it into um the reminder cell events like an

enum and then the reminded cell view can just have the enum and then it can just use that so this makes it a little bit

simpler for the caller to call this and for the consumer to consume this and here's the calling side you can see reminder cell view now we are switching

the vent on checked on delete a little bit more cleaner because you know we're not passing like 30 different closures or five different closures and finally

formatting all right now there are so many different uh variations that Apple has provided to you this is just for date formatting and you can see there's so many different variations that you

can use right inside the view and all of these will work with the local so whatever local that you're using it's automatically going to pick up that local and it's automatically going to

work so try to use whenever you're trying to format something especially dates uh this also goes for list formatting um so you can use list

formatting also like John Mary and you can you know separate them with and and or and all those things so all of these things are available to

you and finally one last thing in 2022 um when I was learning these

things I started publishing a lot of Articles all right maybe you have read some of these articles and every article was built and it was improving hopefully

right so I was building I was writing these articles these were long articles that I was publishing and I wrote a lot of articles I created a lot of

videos um you know lot of YouTube stuff especially this article building large application with so youi a guide to modular architecture it's a pretty long article U you can read it on aam

sharp.com and what happened was 2022 right so it was like two years ago more than two years ago

probably what happens is that a lot of people have invested time in learning things in a certain way whether they're using mvvm whether they're using blah

blah blah Redux and all that stuff and they didn't like what I posted all right so iOS Community went

crazy I was receiving direct messages from people I don't even know like take down these articles you're ruining the community you're ruining everything take

it down our Junior developers are reading this oh my God all right but I didn't I mean it's still there so

I got a thick skin all right so a lot of uh a lot of people were saying like some weird stuff right I mean they were on Reddit which is usually a very nice

place to hang out um but uh yeah so a lot of video a lot of things were going on so I didn't really do much I mean I just treated more content and more stuff and more

courses I just like you know what I'm patient I'm I'm I'm just going to wait so now we're in 2024 probably a little bit over 2

years and what what I see happening now is people are experimenting people are writing their own articles this is not me obviously

but people are looking at different ways of building apps all right whether they are using whatever I discuss whether they're

using their own techniques I'm not interested in that I'm just interested wow they are actually experimenting they are out of their comfort zone and at

least trying out things you know I think it all started with this post anybody seen this post on uh so if you have Farms on Apple this was the actual post

right if you read the comments to this post they are horrible what are people doing that's that's not how to behave

right so I read this post in 2022 or something and instead of writing like you suck or something like that I actually contacted the guy who wrote the

post it's not me by the way all right I contacted the guy who wrote the post and I'm like I'm interested I'm H invested

in mvvm let's talk let's just talk so we had a zoom meeting and he talked uh and we talked you know discuss ideas uh I agree with some things and I didn't

agree with other things but but that was a nice discussion a way to discuss these things all right and now I'm seeing a lot of people

are experimenting you know it works for their apps I mean I'm not saying that these techniques should work for every single app but it works and that's I'm happy to see this

because they're experimenting all right so what was the whole idea the whole idea was just to spark a discussion right I mean this is why you

all are here this is why I'm here I'm sure I have much to learn from you right so if you don't follow any of

these patterns if you don't follow any of the principles any of the design pattern architecture choices just follow one be kind right thank you so much

everyone so

Loading...

Loading video analysis...