SwiftUI Firebase Chat 16: Removing Snapshot Listeners

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey hello what's up everybody welcome back to the let's build that app.com channel hope you guys are doing well out there and i hope you're ready for episode number 16 of the swift ui firebase chat series in the very last video i showed you guys how to integrate the firestore codable objects inside of our project let me quickly show you what that looks like here inside of the recent message model so you see we have our firebase firestore kind of dependency up here we have our document id and all of our objects are now conforming to codable like so and then if you look at chat message i also provided this change in the downloaded source code solution so make sure to go download that and take a look as to how this is implemented uh if you launch your project now i can well let me just hit the play on the top left it's going to launch our app right inside of our pro max simulator uh if you take a look at it we are logged in as steve right here and i added some helpers to my recent message rose there you'll see that the pikachu leaves waterfall 1 and the fields those user names are coming from this quick little getter function there and then take a look all the way on the right side in the blue 28 minutes ago 31 44 and 45 minutes that's being achieved by using swift 5.1 there's now this thing called the relative daytime formatter that's available and you know basically you set up your properties here you can make it as complicated as you want but essentially i'm returning a string called time ago using this guy and if you look at the main message view and just search for time and go in the project you should see this guy right over here and if you want to give this the foreground color of maybe color dot label you'll see this will turn into black like that okay so hopefully you guys are able to you know pick up on these changes so far uh in today's video i want to discuss something else related to firestore and firebase and you know listening to data changes and basically let me illustrate a problem with my application now first i'm going to load up the iphone 13 pro simulator and let me just bring this guy back up in the pro max and then it's going to build for the pro right now so let me just tap into the 13 pro and it'll launch right here for everyone to see uh so let me illustrate this problem here and some of you guys might have noticed this already but if you are logged in as let's say waterfall one here if you log out right if you sign out and you sign back into the application as this steve user you should see these four messages here instead of these two right in other words the application should automatically update itself with the proper messages but if you log out and sign in you're not going to see this but you'll see this kind of obsolete list of messages here let me show you what i mean let's perform the actual sign out here and you know quickly log back in steve gmail.com one two three one two three log back in again you won't see this entire list because this is a stale listener for your firestore they so let me show you how to fix this issue here and then we'll talk about another screen where the listening isn't exactly foolproof uh let me go ahead and slide this bad boy out of the way and the quick fix to all this is to go back into main message view right which is this screen here and all you have to do is this so let me close out of that and whenever you sign out right so you can look for the sign out function here uh you're handling the sign out and whenever that happens you want to make sure you are kind of resetting the data and the way everything works is whenever we go into this login view right here uh you know we actually complete the login process through this callback and then we fetch our current user again so all that's left is to call self.vm self.vm.fetch recent messages and just use it like that this is a private function so we are going to make sure that it is accessible this is pretty easy we are going to go up to here remove the private modifier for that function all right now what i'm going to do is i'm going to launch this in the simulator again it's just a little easier to illustrate this process with a simulator um so we see that we're now logged into sd and we you know reloaded our app so everything looks just the same here and what i'm going to do is i'm going to sign out of my application one more time uh hit the sign out and i will log back in as let's just use the pikachu user so i have a new user called pikachu and i'll log back in so once you log back in right you'll see that pikachu is showing up on the top left and this list right here is somewhat refreshed correctly somewhat we have five messages here and i believe these four rows at the very bottom are no longer correct we should be seeing just this one row there and the way i'm going to go ahead and fix this issue is to go back to the sign out section so sign out right here and you just want to make sure that you empty out the messages so you can go back to fetch your recent messages and whenever you execute this function you can go somewhere down here and say self.recentmessages.removeall that's going to remove all the messages that are obsolete okay um that looks pretty good uh i will try to log in as a kind of a new user again so pikachu has one message here i'm gonna log out and log back in steve and you should only see four messages so pretty good and i'll log back in here let's make sure everything is working and everything should be a okay so log back in i have four messages that look just like that all right so nothing too bad with this solution here right uh one thing that you might not notice right off the bat here is that there's actually somewhat of a memory leak inside of your application right now and the way to illustrate this is a little bit tricky because i have two simulators up and let me go ahead and kind of let me just go in the code here and show you where this memory leak is so you know whenever we start up our application and we have our you know main message view here right we initialize our main messages view model inside of this initializer we call the fetch recent messages and further down below we're adding something called a a snapshot listener to this firestore collection and essentially it's listening for all of these document changes below inside of the enclosure now whenever this you know whenever you add this listener you actually have to remove it inside of your code somewhere and like the problem with not removing it is that whenever you log out of your application like let's say you log out of steve right you are still listening for the messages that steve is interested in but you really shouldn't it's actually going to make the cost of your firebase database just more expensive for really no reason so the fix to this is to make sure that you add a listener to this guy or you make sure you capture a reference to the listener which you can stop listening to uh might sound a little bit complicated but here is the solution i'm gonna go right here use a private variable and you can just say listener and maybe uh what is it firestore listener that sounds like a better name and we will set this as the listener registration so i'll just initialize that as a optional nil object like so all right i want to build and you should see everything is going to be a-okay and that looks fine no error so far okay um one question you might be that you might be having is that how do you know it's a listener registration right well if you command click into this right here it tells you or not command but option click you'll bring up this little documentation helper window and then you'll see that it is a listener registration here it's a kind of returns a fire listener registration instead of swift it's called this right here objective c has that fir okay now having this reference you can set this listener to this guy just by doing this like that in other words your listener will actually be set to something and this listener you can disable it by calling this right here so firestore listener dot i believe it's remove and it tells you that it removes this listener from you know tracking the firestore data changes so that's what we will actually call in the sign out process so you can i guess you can call it here if you really wanted to uh yeah so i guess we'll do that here the firestore listener and remove all or remove for that and remove all for the messages all right so that is pretty good i'm going to re-run this one more time and you'll see everything still works just as it did before but you know we're not listening for unnecessary messages anymore okay so here is my simulator the iphone 13 pro i will go ahead and hit the sign out here just want to make sure things actually do work so let's use the pikachu user gmail.com and one two three one two three there and that looks pretty good we still get our messages there i'm going to message this pikachu user using the steve person here so let's message the pikachu user i hope you are able to still see this message and hit send there it's going to show up here and it also shows up right here as the the steve message below and that looks pretty good um one other issue that you'll have to fix as well is also related to uh the listening of messages so for example inside of this screen right this screen with all the messages it's actually the view inside of i believe we have chat log view somewhere where are you chat log view it's right here somewhere so where are you i believe i moved everything into this utils folder and not that helpful so i'll just move it back to somewhere right here i think that should be okay all right nothing too bad and you can kind of try again everything should work itself out so maybe moving your folders around isn't the best thing to do but i'm going to just build my project this should be fine there okay so quickly let me show you a bug with our application right now that occurs inside of this fetch messages you'll see that this screen right we're also fetching all of these messages using this snapshot listener just like we did in this screen here so we have this issue where whenever we kind of not log out but whenever we get back to the screen let's say we are messaging this pikachu user right let me just look for pikachu here i'm listening for all the messages that are inserted inside of this collection the moment that i hit back right here i really shouldn't be listening to those messages anymore right i'm not using them for anything so i should really stop listening so let me just illustrate how to track an issue like this by using some print statements inside of my chat log view uh i'm going to go ahead and go inside of fetch messages for catalog view and whenever a new messages or a new message comes in we're inside of this block of code for document changes and whenever we insert a new message we're actually here inside the append call so i'm going to say print appending chat message in chat log view all right so make sure you're writing something descriptive and you know where this message is coming from i'm just simply using a print statement if you're using a logger that might be much more helpful for determining where these messages are coming from but for simplicity i'll just use a print statement like that okay i'm going to go ahead and let me uh let's see what i want to do here i'm going to rerun this inside of the iphone pro simulator and this should be relatively easy to follow okay so here we go we have pikachu logged in as that person there i'm going to move this just slightly below behind me and then i'm going to go ahead and launch the application again inside of the iphone pro max and i'll show you what happens inside of the console and you'll see kind of the bug that i'm trying to illustrate so we are now for this uh pro max in the top right corner where the we are currently the steve user i'm going to open up the pikachu chat area which is this right here this is the pikachu chat area right so that looks pretty good i'm going to load the actual user here and i'm going to start messaging the steve user on that corner there so steve where are you over here and you'll see in the back console area as i type messages here is a message and hit the send here you'll see i'm appending all these messages to the kind of this area inside of this you know blue and white message block right and another another another all right so send you'll see a bunch of log messages up here like that so there are currently you know about 10 messages right if i now go back inside of this view and if i message the steve user again you'll see that it'll still print out these long messages but it really shouldn't be printing out anything so i'll just say one two three and hit the send here so again there are these kind of log messages being printed out which means that i'm actually i'm still listening for these messages inside of this snapshot listener but i don't really care about these messages anymore right i'm no longer inside of that pikachu screen which is this right here so the question is now how do i fix this issue right well let me go ahead and apply a very similar fix to this screen so here is my chat log view and you know we are going to declare a private var uh let's you know call it the firestore listener again and we will use the listener registration obviously let it be nil and then whenever we start off this fetch process we'll just uh call the remove function and uh that looks okay oh is that okay so maybe we don't need this i think what i need to do is i need to track when this back button is being pressed uh one easy way of doing that is to just use the on disappear so whenever i click that right that whole view will disappear which is this chat log view we are inside of the body view and you can just use on disappear like so and you can do vm.firestore listener and just call remove there now this is private so i'll have to fix that real quickly i just remove this right here and now i'm going to re run this inside of the simulator there uh maybe i will append the date to that guy okay so it looks pretty good and let's see this is the optional type right here and i think i'm ready to rock and roll with my simulator yet again uh so this is the pro max we're currently looking at the pro max back over there i'm going to click in the pikachu user just click into this and we're loaded up with this screen right so if i go one two three and hit the send right there you'll see that i'm listening for the messages back here uh that looks pretty good if i message myself again with something like abc send you'll see these messages appear like so if i now go back on this screen right here and if i message that user again right you'll see that i've removed the listener on the disappear function and no longer see uh console messages alright so stand right here and that should fix the issue but uh for whatever reason it's not working just yet so i believe one final fix is to make sure that this firestart listener is actually non-nil but let me just fix that bug right here go back to fetch messages all we got to do is to make sure that this is actually set to the snapshot listener all right a quick little bug and uh we'll fire off this guy one more time to make sure that the messages do not appear so we are listening to pikachu gmail.com we are going to say one two three messages are coming in i hit the back there and let's see this looks okay uh let's go with this right here so test one more time to send and these messages are still showing up so one thing that i will try to do is this here uh let's go with the firestore listener and i'm going to make sure that whenever i hit the on disappear where are we on disappear this should fire correctly to hit that we are going to hit the pikachu user here and i'm gonna head back right here so whatever that fire is the firestore listener should remove itself and let's go with the vm is right here and then the wrapped value we see that our firestore listener should be removing itself and not exactly sure why this bug is still happening alrighty everyone so i just spent about 30 minutes looking at this issue here with the chat log view and also the fetch messages function and it looks like the real problem that we're experiencing with our application now is that the fetch message function this right here is being called a lot more than we would like so let me illustrate this whole problem again by launching our pro max on the top right i'm going to put a breakpoint here and some of you should try to guess how many times this guy is being called right so you're you know you're fetching your messages through this initializer here and this catalog view model is only being created every time you navigate to this screen right there right so whenever you click on there you should navigate to the chat log view and whenever that happens you are fetching your messages like so and the real issue is that whenever i go back to another simulator so this person is the pikachu user right so let me find our steve jobs again whenever i message the user you'll see this this fetch message on line 23 will get invoked again so another invocation of fetch messages something like that so the send and you'll see that we're initializing this listener guy yet again and the real reason why that happens is because when you're creating this view here you're updating this view with the new messages and whenever that happens it'll redraw the entire kind of view hierarchy and when that happens you get some listeners you get some view models kind of just disappearing and being recreated just to make sure your views are correct so long story short this view model object it's being initialized in a way that we can't really predict using swift ui so whenever i run into this issue and i've actually fixed this issue before in another project whenever this happens right you really want to control where this initializer is being called so currently we would expect this initializer to be called somewhere down below so let me go back to my chat log view which is right here and we are initializing this chat user every time our view is being initialized or every time our chat log review is being initialized we are initializing our view model with this on 170. so you would assume that works but the chat log review this view right here is being created like really really often and it's not really predictable when it's being created as well so whenever this happens i would almost prefer to create this view model this chat log view model here i prefer to create it inside of the actual view right here instead this way i can control it a little better in other words i don't really want to use this initializer so this guy here is not really that helpful for me and what i'll do is i'm going to show you how to fix this issue i'm going to comment all that out and that should be okay now once you comment that out you'll run into this problem where the chat user is no longer accessible so it should be accessible on the the view model so that should be uh you know that error should go away now whenever you you know remove this initializer you'll see that the areas in which you have the chat log view you can no longer use that initializer so the question is well what should the fix really be right well the chat log view you can see if you open this guy up again you have the default initializer of the view model that you can pass in so you can create the the view model in this area instead so you know you can do this but you are going to run into the same issue where this navigation link it might be called pretty often so what i mean is this if you put a breakpoint here and you're messing messaging your users this view model is being initialized a lot so you're still going to be listening to a lot of messages so you know one better way to fix all of these problems is to uh you see this view model object right i'm going to create it as a private reference here so private var and i'll call this the chat log view model and i will declare it as the chat log view model like this here and you can even construct it using this i believe so let me see if this works so i'm going to use this right here and the chat log can be a nil user at the very beginning once you have that you can pass in your view model to this right here and i will cancel out of that so i'm going to build one more time with command r and launch inside of this view there and you should be able to see some different behavior in the console now i'm going to launch into this view here and you see no messages are showing up which is something different so no messages in the console either just at the same you'll see nothing is being printed out because currently messages are not being fetched inside of our chat log view and view model so the fix to all this is actually pretty easy let me just remove that commented line don't need that anymore and whenever we you know we're inside of this view here uh whenever we you know click a new user whenever you know somewhere right here we click pikachu or we're here you click on pikachu whenever we do that we're simply executing uh one line of code and let me go ahead and see where we are at here so i believe we have a view called a new message view or create new message so let me look for this guy right create new message whenever we select one of these users here we're just simply executing this code and whenever you know we're here right you can simply say self.chat log viewmodel.chatur equals the you know the user that you're selecting here and whenever that happens you can then call self dot chat log view model dot fetch messages like so and this i believe might be a private function so we are here i think we need an optional to fix this i think that's an optional there and this guy is a private function so we are here we've removed that and let's see what is this top guy uh complaining about so the chat user on this is a let so i'll fix those two issues right now by going back into the chat log view very top we have our view model this guy right here you want to make sure it's a var so you can reset it whatever you want and then we want to make sure that the fetch messages call is public like so all righty i think that's kind of what i want to do now okay hopefully this should start working again i believe i need to go in this view to you know make sure everything works so i'll go into that right here and you'll see all my messages are showing up again right so abcdef you'll see send and we'll see a pending chat message and we'll only see it once because you know now we have this better solution where our view model is being controlled somewhat more manually and if i go back into this view right i'll say abcdef one more time hit the send and whenever you know another message here you have to send the previous issue where where we have all those console messages being printed out that's no longer showing up in here because we're not invoking fetch messages over and over sort of unpredictably right so that's kind of the fix that i have for everyone uh if i click into this you'll see this guy which i'm not sure if that's what we want click into the leaves we have all the pikachu messages here so i will fix one last issue and i'll let you go all right i'm going to go into my uh let's see where is my view code these swift files are pretty long so we have our messages view here which is uh somewhere right here now sort of kind of on my own time i implemented this bit of code and the scroll view of messages are now they're all a button so these guys are now buttons and the button is just what you saw before it's your h stack with your messages and your icons right and whenever you click on these these rows or these buttons now we are figuring out what our chat user needs to be right here using this and it's just using the the email and then the profile image url and the uid from your actual user and it's nothing too complicated but you'll have to look at it on your own to truly understand what's going on and uh we just want to do the same thing with that we had before chat log view model and uh chat user equals self dot you know just you can do that now and then you can perhaps invoke the fetch message call again so what this is gonna do is every time you click on the message row it's simply going to uh sort of refresh everything and hopefully that's going to work i am going to go inside of the pikachu user here we have pikachu gmail.com we have leaves at gmail.com i think these messages are not correct or are they correct i can't really tell so waterfall and there's a lot of messages here that i don't want to see so i'll go back into the chat log review model again and whenever i'm fetching messages i am also going to do one more additional check which is chat log messages i'll uh i'll remove everything from the array and i think that's the final fix that i need so uh keeping track of everything is always a little tricky in your ios apps click against the pikachu we have this long list liens we have two messages there we have waterfall looks like that and then the final final user we just have simply three message messages like so alrighty so that is how you fix this issue with the listener registration right again whenever you have uh your swift ui application and when it contains a navigation view type of hierarchy right so let me go back to main messages view and we have our our main body which is a navigation view whenever you have this hierarchy and whenever you're navigating into you know a different view that's being pushed onto the stack you'll run into these issues where this entire review itself will just redraw and you might not even see it and the reason for that is whatever these navigation links right here whenever they get activated uh it's very hard to predict what is being recreated in the previous case we were we were pretty much recreating our chat log view for a lot of these navigation areas and whenever that happens you can't really guess what's going to occur with your viewmodel objects so the fix that we introduced for today's video was to introduce a reference to the actual chat log view model inside of our main view here and what this really means is that we can control what is being fetched when it needs to be fetched for whichever user that we're clicking on uh again you'll try to use this solution whenever this situation arises and you can imagine that if you log out of this user of steve right here you might see a problem with this chat log view model you might need to do some further uh resetting i suppose but uh i guess so far everything is kind of good all right so if you guys have any questions about the you know questions about any of this stuff i i can see that it does get confusing uh rather quickly if you are not the author of the code so uh make sure to leave a question down below and i'll provide this entire project uh down below as well i'll see you guys next time
Info
Channel: Lets Build That App
Views: 955
Rating: undefined out of 5
Keywords: ios, swift, development, tutorial, learn, xcode, programming, code
Id: 3TSYCPE1UXI
Channel Id: undefined
Length: 37min 11sec (2231 seconds)
Published: Sat Dec 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.