8 Common SwiftUI Mistakes – and how to fix them!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] swifty y is a big and complex framework and although it's lots of fun to work with it's also got a lot of scope for making mistakes in this video i'm walking through eight common mistakes folks make when they learn swift ui some of these things are simple misunderstandings and again swift ui is big and complex these are easy to make some of these are about getting a deeper understanding of how swift ui works and some are signs of legacy thinking this is the way you solved it in ui kit they try and solve it the same way in swift ui or perhaps you've written lots of views lots of modifiers and haven't taken the time to slim it down when you've got your desired result anyway not to keep you in suspense but what my eight are i'll summarize them really fast you can just skip through to the ones you care about uh the first one is adding views and modifiers when they aren't needed second one is using observed object when you really mean state object the third one is putting modifiers in the wrong order the fourth one is attaching property observers to property wrappers the fifth one is stroking shapes when you really need to stroke borders of shapes the sixth one is using alerts and sheets with optionals uh seventh one is trying to get behind swifty wire views and the eighth one is creating dynamic views using invaded ranges we'll go into lots more detail those in just a second but first i want to thank instabug for sponsoring my site this week they've got an amazing new application performance monitoring solution so you can look out for network hiccups or ui hangs or more on production devices on users devices directly it's an incredible way of working thanks instabug anyway let's get into it adding views and modifiers where they aren't needed this is our first problem and this is one of the most common in fact because uh you're basically writing more swifty ui code than you actually need to write and it's common partly because we kind of write code as we're thinking and we'll figure out a problem and it's easy to forget to go back and rethink your code and slim down and simplify your code afterwards and again sometimes it's a sign of going back into old habits you're thinking like a ui kit developer an app get developer and similar as an example if i said to you please fill our view here with a red rectangle you might write code like this there's a rectangle and it's filled with color dot red and you'll see a red rectangle fill the screen and honestly that works great it gets the exact result you want but half that code isn't needed right you can literally just say i want color.red and get the same result this is because all of swiftywise shapes all their colors automatically already conformed to the view protocol so you can use colors and shapes and more as views in swift ui which is great and you'll also see this with clip shapes you can say to someone you know i want to have a a rounded rectangle here they might say okay i'll do coloured up red i'm thinking ahead here but i'll then do a clip shape with a rounded rectangle corner radius of 50. so round the edges of my uh rounded rect and it's complaining for some random reason uh corner radius there we go not corner size boom my mistake and around the edges like that and that's fine but again it's not required you can literally simplify your code dramatically by just saying i want a corner radius of 50 and get the same result or you know 150. it works identically and obviously removing this redundant code takes time because you're trying to shift your mindset a little bit it's also harder to do when you're learning swift ui because you're still figuring things out so don't feel bad if you're using the longer versions while you're learning but when you're more advanced perhaps think about simplifying your code a little bit to get the easier results the second problem people hit is using observed object when they mean state object and of course 50y provides us all sorts of property wrappers like uh at state at object at environment object and more and knowing when to use each of these matters if you get it wrong you'll have unexpected side effects in your program the first one straightforward at state that's value types only uh it's owned by the current view owned by somebody else so that's integers strings arrays of stuff and more they're all great candidates for at state but the other two observed object and state object that are very commonly used course confusion which one should you use and you'll often see code like this some sort of data model that conforms to observable object with inside there i'll do at published var username i'll just default to two straws save some time and they'll have that in their content view like this at observed object var model equals the data model okay it's a model like that then go ahead and use that in your body somehow i'll do text model dot username boom and that is gonna work most of the time but will occasionally randomly fail and i can tell you now this is categorically the wrong modifier wrapper to use here you should absolutely across the board by using at state object when you create your data in the view like this because this state part like at state and at state object means this view owns the data this view creates and manages the data and when you say at observed object you're not getting that ownership someone else should create and pass it over to you and they will keep it alive for you like i said it's gonna be really annoying because this code is gonna work most of the time but you know suddenly fine when you push views onto navigation view whatever you want to and come back to the root view your observed object's gone because 50y has destroyed your view and did not keep the model alive so watch out for that when you see at state it means this view owns the data so that's that object when you create it observe the object should be used if you aren't creating it if you're just passing in from somewhere else like that the third mistake folks hit is putting modifiers in the wrong order let's clear up our code just slightly um this is sort of a it's a problem that will cause your layouts to look wrong but might also cause them to behave poorly too if you've really got your gestures in the wrong order for example who knows what will happen and we can say the sort of canonical example of this is unlike uh text hello world uh with a nice large font so you can see what i'm doing and a background color i'll do green and some padding and this is all the canonical example of modify order mattering very much in thrift ui because here we are applying the padding after the background color and so the color will not be applied to the padded part of the text it'll be applied directly to the textview itself so we're applying the padding after the background and if you want both to be green of course you should swatch them around switch them around there we go put the background after the padding like that so add the padding first then color the resulting view and that's the result you'll get and this comes particularly interesting when you try to adjust the position of views for example if you use the offset modifier this changes the location of view as rendered but doesn't change the underlying view geometry it still is where it was originally it's just drawn somewhere else and this means any modifiers applied after the offset act as if the offset never happened we can try this out let's keep hello world um and large title i'll then say there's an offset x of 15 y of 15 and then our background color green again and look at the end result you can see it's offset the text here but the background color hasn't moved because this thing is backgrounding the original geometry of our text where it was originally the offset here is only changing the rendered position of the text not the geometry and so if you swap offset and background now the whole thing gets offset but notice there's still this sort of line around the geometry of my text it's still any other modifiers we placed there and not in the offset location alternatively the position modifier changes the location of view is rendered in its parent but it can only do that if the parent is very very large so it places your view inside a flexibly sized box and it positions it inside that flexibly sized box and again we can see this with our background color we can say background color green and then position and i'll say 150 150 and now you can see there's our text with background color and it is 150 150 from the edge sort of centered here like that so it's working correctly you'd think but it's actually made a flexible frame around it and you can see a little hint of that up here this purple line just about see the top here um if you were to switch position and background you'd see what's really going on boom so you've now got a full screen green view with a black hello world uh there and this is exactly what's supposed to happen again saying position x 150 y150 has to create a flexibly sized view which will fill all variable space at our purple line around the text we can put a text inside that view correctly and then we're saying background color green to the flexibly sized frame which is why the whole screen appears green now although this happens you know regularly it's important to understand why it's happening because every modifier you apply like position and background and others padding will wrap your current view in a new view a modified content view some don't you know changing the foreground color or font in some places will bubble through as a preference rather than a new view um but the vast majority will wrap your content in a new view offset for example position background they'll all uh place your views in new views each time and this is actually a powerful feature and yes it means it's painful sometimes because your modifier order isn't quite right but it allows you to do interesting effects that would otherwise have been very hard because each modifier is applied to a new view every single time we can say font large title then add some padding then add a background of color.green and then add some more padding wrap that in a new view and then give that the background of color dot blue so you can stack up modifiers again and again and again which you wouldn't be able to do if these were simple method calls modifying the same thing directly whereas they create new views you can do this you can also say i want to deepen certain effects i want to have a hello world here but i want a foreground color of white and apply a shadow to this using color oops color lowercase c color uh dot black and radius 10. you'll see a shadow like that but because every time you call shadow you get a new view back we can say shadow shadow shadow and get a super deep shadow like that coming back much thicker okay the fourth thing i see folks trying to do in attaching property observers to property wrappers let's clean up our code a little bit so there are some situations where able to attach a property observer did set will set uh two property wrappers but often this just does not work as you'd expect for example if you had a slider let's say we have uh at state private val rating there's no point naught and then a slider here with oh sorry value dollar rating um you know your slide down it'll change the binding that's what it's doing here but if you want to watch this changing and run some code as it changes you might try and say uh did set print rating changed to rating like that but if you run that code back you're gonna find nothing happens yes the slider will work but nothing is being printed out into my debug console down here it is not doing what you expect and the reason is because the values being changed directly inside the state property you're not actually changing the state struct wrapper around it changing the internal value only and the swift ui native approach to this to fix this is to use the on change modifier so you'd sort of scrap this code here no more did set i instead say that my slider has an change of rating bring the value in and then just print out rating change to value like that and that will now watch the rating value for changes and when it changes it'll print our code out so here you go boom so that's the way swift ui wants us to do it i personally prefer a slightly different approach i use an extension on binding the binding struct itself that gets called when the wrap value changes but also calls a handler function of my choosing and i'll show you how that works really fast you can say there's an extension on binding with funk on change it takes a handler which is escaping uh over whatever the value of the binding is returns void and returns a new binding over that value and inside here we can say return a binding where the getter is self.wrap value that's easy enough and the setter takes a new value coming in we'll assign that to our wrapped value and then call our handler function on the new value like that and i find that easier to work with because we can now isolate our code more clearly or writing we could say uh there's a method down here slider changed to value double i'll just go ahead and take this thing here into there and get rid of the unchanged entirely and said now say slider value dollar rating with dot on change slider changed so i attach my method directly to the binding being used in the slider i find that significantly easier to think about and it'll do the same thing as you're dragging around but it's just there's no more on change on change on change as modifiers it's directly in the binding this thing this changes run this and so forth so you could say when the slider changes do this when something else changes do something else as opposed to all going through the same rating on change modifier use whichever one works best for you number five is stroking shapes when really you mean to use stroke border let's uh clear up some of this junk from here so uh when you're drawing shapes it's really common to want to uh stroke around them but these can cause all sorts of problems if you said i want a circle with a stroke of color.red and a line width of 20. when that runs back in my canvas you're going to see this circle here but notice how the left and right edges of our circle here and here are clipped you can't see the edge of the circle and this is because when you ask for a stroke to happen in this case it's 20 points that struggle happens centered on the edge of your shape which in this case of course is our circle it goes to the very edge of the screen to the very left and the very right here and when it draws the line it'll draw half of it outside that edge and half foot inside that edge so it'll be centered on the circle itself and sometimes that's what you want as you can see here it's causing problems a better solution most of the time is to say i want a stroke border which means stroke the full amount inside the shape and so our circle's still the same size still goes edge to edge here on the left and right sides but now the stroke is drawn inside the circle which is much much nicer there is one time when stroke versus stroke border is it strokes a better choice here um and that is when using a stroke style if you look at stroke you'll see there's a lot of options here that are there and so many of these return a view view or sorry a view here view here loads of views come back but a handful are stroke only return shapes the stroke does stroke style does and stroke line width returns new shapes not views which means you can create certain effects that would otherwise be impossible such as stroking a shape twice for example if i said i want to do a stroke with a style and i have my stroke style was a line width of 20 and a dash of i'll do an array of just 10. you get that uh if i then said you've got a frame with a width of 280 and a height of 280 boom i can actually apply this stroke twice because using a stroke style modifier so we get back a new shape so i'll just copy and paste that stroke like that and we get an interesting sort of railroad effect it's great and you couldn't do that with stroke border because you get a regular view back not a shape so you couldn't call stroke twice most of the time though stroke border number six is using alerts and sheets with optionals this is the recommended solution to a problem folks have many times when they're learning to present alerts and sheets it is very common in fact very normal to take a simple approach the easiest possible approach which is to bind your data to a boolean so let's say something like this there's a struct called user which identifiable there's an id for that and inside our content view there's one at state property which is called selected user which is an optional user and another at state property which is showing alert and that is false and then in our v stack and our body zone down here sorry we'll make a v stack and i'll make this v stack have a single button saying show alert and when that's pressed i'll make our user equal to a new user with the id of at two straws and i'll make showing alert equal to true so we'll show the alert and we can now bind that to a simple alert by saying dot alert modifier using is presented and this will be dollar showing alert and the content is going to be alert with the title of text hello selected user force and wrap dot id like that so we've got this boolean is the alert showing or not plus a user to set when the button is pressed make a user show the alert and here show the alert show their user id i'll press command r to build and run that code i press this button here boom hello to straws so that works great and honestly it's the easiest way i think to learn alerts is it showing true or false but once you're past the basics you should consider if possible moving across to a different form of alert which is it takes optionals instead same for sheets and these let you remove the extra boolean entirely plus remove the force unwrap but it has a single requirement which is whatever item you're presenting must conform to identifiable which our thing helpfully does not entirely by coincidence for example we can show the same alert by ditching this boolean down here go away ditching it being set down here and just set to select the user directly and instead say item dollar selected user watch that for a value and pass in that user when it's set and now here we can just say user.id so we're saying watch this value here whenever it changes and as soon as it changes becomes not nil or changes from one value to another value whenever you want to call our alert pass in the unwrapped version of that optional and use it inside our alert so it makes your code simpler to read and write about obviously it still works great but it also removes the extra worry of the force unwrap if you've done things slightly wrongly things weren't quite as you thought that's no longer an issue number seven is trying to get behind your swifty y view and this is a really common problem people hit when they're learning they want to change what's behind this 50yv view and it usually starts with code let me get rid of this junk again code that looks something like this uh text hello world dot background color dot red and they expect this thing to fill the screen with a red background color and it doesn't as you can see it's a white background color uh down here and it's it's gonna fit tightly around the text which is very annoying you want a red screen you actually get a white screen and so they start thinking well what's behind my content view how can i set that how can i give a background color to the rest of the screen and they might you know go into the app area here and try and dig around with the window group for example uh other folks might uh go into ui kit territory and dig around there and yes of course you can do that you know behind our view if you run it right back in the simulator um you can dig through and have a look and see what's there and yes there is a ui kit view controller behind this boom this hosting view controller a ui hosting controller and it's a regular view controller like any other it absolutely works you can dig into that and find it and modify it you want to but you might find your swift ui code starts behaving strangely or you might try and move your cross-platform swift ui code to mac os or to watch os or something else where ui kit doesn't exist and that's going to cause problems in the long term so try and think like swift ui if that is the only part of your content view then make it fill the screen say this thing should have a frame with a max width of dot infinity and a max height of dot infinity let's even add ignores safe area so it really fills a complete screen if that's what you mean say it remember your content view itself doesn't really exist there are no properties here as a zero size at run time it just calls the body property and put this onto the screen inside the hosting controller that's all that's happening and the last one is creating dynamic views using invalid ranges and this is actually quite common because so if several several of swifty wise initializers have these slightly easier versions that let us pass ranges which is really cool it makes all sorts of layouts easy to create for example if you want to show a list with four items you might say something like this var row count is four and then i have a v stack list zero two row count row in text uh row row and that works great honestly that kind of code works fine do that use the easy adapter thing here but it becomes a problem if you try and change that range at runtime for example if i had made row count at state private valve row count it can now be changed i can now go inside our v stack and say there's a button here called add row and when that's pressed uh row count plus equals one add a little bit of padding to keep it away from the top bar like that and if you run the code now you'll see xcode issues warnings to you i'll press add row boom big error don't do this it's only for constant data and in fact it hasn't even added a row it just does not work so don't do that to fix this you should always make sure your list or your 4-h is bound to identifiable objects or provide a specific id even if it's the same idea what it had before it's a i a specific idea to work with and this makes it clear to swiftly why this is going to change so here we might say zero to row count with id of backslash dot self use those numbers zero one two three in this case plus whatever we add in the future as unique identifiers for our views and now adding row will work correctly so it actually is a much better idea so i hope you found this video useful uh no matter how much suitability experience you have as i've said several times honestly some of the alternative code i've shown you this old way of doing things works great and although yes you can improve it's okay there are always things you can improve and if you're just learning swift ui then don't get hung up too much on whatever code helps you get stuff done once again i want to thank instabug for sponsoring this go and check out the map at instabug.com but also have a look at the application performance monitoring solution it's very impressive if you enjoyed this video subscribe hit the like button leave a comment tell me the kind of mistakes you made when you were learning swifty you
Info
Channel: Paul Hudson
Views: 28,243
Rating: undefined out of 5
Keywords: swift, xcode, ios, swiftui, ui, list, identifiable, property, wrapper, observer, shape, stroke, dynamic, state, stateobject, observedobject, modifier, alert, sheet, optional
Id: qkcKTJhDyLs
Channel Id: undefined
Length: 28min 4sec (1684 seconds)
Published: Fri Oct 09 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.