How to use Combine with MVVM for UIKit and SwiftUI - fetching tweets example project

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Great video I appreciate you showing both UIKit and SwiftUI

👍︎︎ 9 👤︎︎ u/MacMeDan 📅︎︎ Feb 20 2021 🗫︎ replies

Any plans to do a video about writing tests and how to write testable code?

👍︎︎ 6 👤︎︎ u/AnotherThrowAway_9 📅︎︎ Feb 20 2021 🗫︎ replies

Looks good I’ll watch this later!

👍︎︎ 1 👤︎︎ u/_HailSatin_ 📅︎︎ Feb 20 2021 🗫︎ replies

Great video!

👍︎︎ 1 👤︎︎ u/itsabc 📅︎︎ Feb 21 2021 🗫︎ replies

Just yesterday, I finished watching your Combine intro!

👍︎︎ 1 👤︎︎ u/anymbryne 📅︎︎ Feb 21 2021 🗫︎ replies
Captions
welcome to another coding review and this one is going to be a little bit different because i would like to show you more about the combined framework and we're going to have a look at the project of another tutorial on youtube which is from let's build that app and this one is tied to the advanced swift combined framework the balance suit optimization and there's a couple of things i would like to demonstrate so first of all it's a ui kit but it doesn't it's not even if you're not interested in ui kit i will show the second part how to do it in swift ui because part of what i want to do is i want to refactor the structure of this code because right now he's using not the best pattern which is actually massive view controller i wanted to demonstrate to use mvvm also for uicad because as soon as we have a nice view model we can very easily use that for our swift ui code too especially since there or table view is going to be so much easier to handle so this is the first one to re-architecture then there's colorful things box that i would like to talk about and the big one is that what i see a lot is people people don't go full in combine you can use a lot more combine in this code i want to show you why and what advantage it can bring you in this case it's nice because you can see the functional code and the combine code so you directly see what's the advantages like is it easier to read can you do some things for combiners it's much harder to do for functional programming i did link the other tutorial in the description box and the good thing about this one is you can also get the source code because this is where we want to start from so in this project we're going to work with twitter and we're going to just fetch a couple of tweets and up here you also have the possibility to search which doesn't work right now because there is something not connected i'm not really sure if i it was me or if it was already there but i will show you what's missing in a second if you don't want to get the project from there you can create you can just redo the project with me and i'm using the same frameworks as he did and the most important one is this sd twitter don't really like it but this is just where we start off so in the um in his file he only has one view controller where everything is in so the ui elements and also the business logic and the model so that's why i'm saying this is he's practicing a massive view controller here and i did also take out um the secret and the key here if you want to do that you have to go to twitter and get your own so i just wanted to make sure that you really do that and we're going to start by refactoring so i this is the first thing i want to take out you shouldn't have the secret and the key in your view controller on your view because apparently also for swift ui people start to have the massive 50y views design pattern so i start by creating you another group which is my model and then a new group which is my viewmodel so i'm just going to put in here this three things so this is my swift file and this is my twitter api so what i need to do in here is i need to use this flame this st twitter framework and i already know that i'm going to use here a couple of combined things some convenient stuff talk about in a minute so this is the struct twitter api pi so and i'm going to move stuff from here maybe i don't really need this right now i'm moving these three out and in here so then if we go back to our view controller let's have a look at what kind of code i want to take in my view model so we have here on the view that load the search bar listener this is a data stream from the text field from the third text field to start here this search it is view related because we are trying to get a data stream for our view to our view model so it will stay in here with a slight modification then the next thing is we have here this view setup which is fine it can stay in the view in the view controller but we also have here this api verified credentials that's definitely nothing to do it should not be in the view controller so i cut this out i mean it's already complaining because it wants to have this api so i need to have a view model swift file so this is a tweet view model i'm only right now moving things that i don't want to have in the view controller in the view model combine import st twitter so this is a class because it's a view model tweet view model so i'm just going to comment this out and we discuss everything in a minute then if i go back to my view controller and i go further the next function call here is here this search tweets and you see here again this gets completion handler this ethercallback then the do catch block so this is definitely this is functional programming there's no combine in here but this part we are going to re-write in combine so we have quite a nice comparison it's going to cut this all out and i'm just copying this in my defeat view model and combing this out because it's not working correctly yet and i have to here in my setup listener uncomment this one line because we're not doing it directly here then we have just two model definitions in the view controller this really hurts i'm going to create this two files now my model group which one for my tweet i'm just going to move these two over there do i need everything else no this is already decodable weiss is only decodable so usually i i wouldn't do it just decodable but again okay for this project you we only need it as decodable but why not make it codable you might at some point want to also send some tweets then i have here the declaration of my tweets which i really don't want to have i prefer to have my view model the owner of this data because it's also the one that is going to fetch the data so i'm going to move this one in my view model then now we have these two places where it's complaining because now my tweets are not in the view controller anymore and i did i have here my tweets in my view model as a property but i prefer because i need to also hook this up with my table view to update it i need to have here a publisher or i want to have a publisher with this one and in this case in ui kit you shouldn't use at published because this is going to give you only half the behavior in this case you can use a current value subject value subject and then you have to in this angular brackets define what type they are so in this case the actual value is its feet a array of tweets then the error which is never and here's the current value subject you have to give it an initial a current value for the initial value so we start with an empty array of tweets so now i'm going to show you how to use this in the viewcontroller so i have here two places where i use my tweets one when the table view wants to know how many cells for it has in the table view it's now asking the view model which i actually didn't add a instance yet so this is a led view model this is a tweet view model so now down here when i need to give the amount of cells and for the table view i can ask my view model for the tweets and this is the subject and i want to know the fd value in this subject so it's the value.count and then down here when i have to give the data for the cell i have to not directly ask for the tweets but my viewmodel has noted feeds in the tweets subjects values and this index path dot row if i run this it's going to there's no errors right now but i did not take care of the data fetching the data anymore so the first thing if you want to search for tweets for a certain term we need to also store this term and right now i didn't do that i just have here this publisher that has the right value for the text and the text field but i didn't put it anywhere so in my tweet view model i like to have now a storage place for this so this is a var search text and similar to this because it's a value there is always a value for the search term it can be empty i'm using again a current value subject not a password subject and in this case the type what i'm sending in this data from this publisher is a string and again no failure so it's coming from my text field and here i can give an initial value so the one that i want to start searching with and i did use here before as a search term you see your search tweets with query paul hudson because everybody likes paul hudson so i'm just going to set this as the initial search term and now we need to set up our data stream in the initializer of this viewmodel i have here these two blocks that did this before but they are not combined friendly i mean they're not they're not using combine at all so we're going to make this now work with combined and this thing that we start with is you have here this ad hoc callbacks with this completion and in order to trans make this to and we are going to wrap this in a future publisher and we are going to write a function that creates us a publisher um that does this so i'm going to take these first and we are going to do this in our twitter api because i anyway this belongs together this kind of publisher's stuff so this is a so i'm just going to copy this code in here because we're going to very easily refactor this now so this is a function that is just going to call this the same as this one here we don't need to have any arguments to create this and we are going to create a future publisher that returns us so now you can decide what kind of data values are you want to return and what kind of error since i already see here callback here might give me an error i'm going to say yes this this publisher might give an error and i have here the username and the user id that i can return these are both strings and i'm going to return them as tuples or the string string and they're actually optional not really sure if they're always optional or not so now we have the client what we want to generate so i'm going to have a future a future has to have a promise i'm going to close this after this add her callback because now i can use it so i uncomment this i'm not doing this search term here i'm just going to do take this out so you see it if i'm in my error block i'm pretty sure i have an error and i'm going to tell my promise that the result is a failure with this error and oh yes and the api is here already declared as this lazy var if i am in my success block i can tell my promise that here my result is a success and i'm returning the username and the user id as a success and actually oops i need to use this in the initializer here because of this api is equal to this so now we did this with our first callback here for verify credentials but i also want to do this for the second block because i really don't like this guard if and this catch blocks here we can make this a lot nicer looking i'm just going to copy this part in our twitter api so again i'm creating here a function that creates me a publisher so this is get search tweet with query and in this case i do want to have a input value have query and this one is going to return me any publisher so you see it just trying to show you another one so we are going to return here directly in our little data stream a array of tweets with an error so we start with you see here we again have a call back which we do the same you're going to wrap this in the future so this is a future with a promise in so i would use this part and it has here this arrow completion handler again like this so if the first error it gives me here the types don't match um this is because we have to continuously work on this one so the first thing is i need to give something back in my promise so if i have an error i'm going to return my error here and if i have data then i'm going to return here a success with this result and now i have here my data stream any and you see here he also did use a couple of options to transform this adjacent decoder and this serialize decoder so after my future publisher i'm going to continue my data stream by transforming this with tri-map because i want to use here this tri question mark and so it's not just a map it's a dry map so in here using this part don't need this and this is the value so we are using adjacent serializer to we are changing this area of any into a json file that i can now use with decode and i'm going to have here a tweet by itself because our tweets are decodable and i'm just going to use you the same decoder with these properties so var json decoder this is a json decoder led decoder is json decoder because i need to use this decoding strategy here and then i am using returning this decoder so now i can use here my json decoder here you see don't worry about this errors this is just a because we didn't finish it with this any publisher here and this is not try question mark it's just try because i have here try map so now the newest arrow it gives me is that i have here this nested publisher which doesn't work as and i'm supposed to give here any publisher so right now i'm like maybe i'm already at the right type so we are going to use erase to any publisher and this last error is just these optional that i didn't handle handled my optionals if you have a optional values in your stream and you just want to ignore it if it's null you can use a compact map so this compact map and i'm actually not going to do anything i'm just going to call it so we're not transforming it you just can make a basically this compact map makes unwraps optionally it's like a guard statement with an optional so now we have everything i guess um i did not hear catch my arrows so now i have everything i have now here a publisher that i can give a query term too and that returns me an array of tweets i do have here errors it depends if you want to have them in the stream you can also do a catch here already but if you compare this to this code here where you have this arrow block at the end and this do catch inside it's really hard to see what is done in which order whereas this reads really nicely we first check our tweets api what they give us and then we decide okay we have no some data value we're doing decoding here so it's nicer to understand i'm just going to take this part out because we don't need this anymore this was just for me to know what i'm supposed to do so now i have here these two features and we can build our view model with this go back to our tweet view model the first thing that i wanted to do is i want to verify the credentials and we wanted to use the future for this so i want to have this for my twitter api it's feature api and since we are going to create here a couple of subscriptions i'm creating here a var subscript where i can store my subscriptions is a set of any cancelable now on my initialize i can build the data streams and the first one was the verified credentials now so now i have here a stream and we can use a we need to have a subscriber at least one so i use sync and you see i have here a completion handle with this arrow that we might have so this is a completion and is it enum so we can use switch completion case failure now we have here this error that i passed in the futures promise as a failure type so we for now can print this error if you want it ideally you would put it on the ui as a hint so you could create create you another publisher that you can hook up to a ui element but since i don't have anything in this view controller i won't do that for now so ah this is not exhausted quiz dot finished and if it's finished and i know i'm fine the same is if i received here in value i'm just going to ignore this t values that i have here and this is a success if you are here and now we have a data stream which we need to store somewhere otherwise it's not going to keep it after we leave the initializer not really sure if we have to do this verify um credentials but we're just going to do it then i need to create another data stream to fetch my tweets to replace this blog and i'm going to create another function where we set this up set up fetch tweet and i'm going to call this when we know we are successfully logged in otherwise i'm going to get here more arrows so in here i'm going to call my set up tweets this sync is going to capture self which is my viewmodel so i need to use here's self. which means that you have to use your unknown self to otherwise you have a reference cycle and i've just let me check yes he actually used here a reference cycle so you see here in the setup search listener when he hooked up a publisher with this text field and here in distinct he gets back this value and he just uses self so this is a reference cycle so just make sure that with the sync you always usually have to use weak cells or unknown self so if i go back to my view model we have now our first stream so i can go back to create my fetch tweets and the one i want to the publisher that gives me values to go through the stream is this current value subject here your search text so i'm going to type my search text and we're going to create a publisher subscribe to this what we have to do now is we have to have a publisher in a publisher stream a sub publisher stream because i need to go fetch my request i want to use this other future publisher here that i created this one gets such tweets so we have a publisher and a publisher stream and you have two possibilities is either flat map but in this case it's much better that we use switch to latest so first we're going to have here a map and i have here my text in so now i'm going to call my ap twitter api to get searched feeds with this text and i have to use yourself again and unknown self self so now i have here a stream in a stream and in order to go back to only having values of the sub stream we using here switch relates and now we have a array of tweets in there and the other thing is i want to make sure i'm here handling my arrows so i'm just going to show you a possibility to use a catch block so you get here the error i'm not going to look at the error right now but if you have a catch block you need to replace the error with a value and in this case i just want to have an empty array so we're using it just publisher and we're going to use here a array of tweets we are having an empty one i just need to resolve this right now so what i'm going to return here is a any publisher that has an array of tweets and that does not fail so never and i because in any publisher i have to again use erase to any publisher if you don't have an array but just a simple struct that you won't have here you also can use a empty publisher and say complete immediately too in this case this one wouldn't pass any value or just complete the stream that would be another option so now we have something in the stream and now it's complaining you haven't used it that's true so i can use here a sync to receive my tweets and what i want to do is i want to use this value and set it to my tweets here so this is again self so we have to use again your unown self and self so this is my tweets don't send so since i have a core values subject i have to use send i'm sending my new tweets and i'm still not done because i need to store this in my subscription okay so now the last thing i wanted to do with this stream is i haven't taken advantage of the fact that i can decide how many values i want to pass here so if you look at the view controller he used here this d-bounce in order to kick off this search but actually don't need to do this directly here i'm going to use this d-bones in my tweet view model to decide for my search text field when there's a new value how much values do i want to pass so in this case he's like if the user stops typing for half a second then i'm going to let one value pass but you can also use here remove duplicates so and now i did i already saw it here i did not finish hooking this one up in this sink i actually need to tell my viewmodel the search textfield to send a new value which is this string and this is again a self so this is again a big soft you see here again there's optional values in this data stream so you can use again a compact map and you can also use here and optional if there's a little value over here then it's just not going to pass this value and yes this is actually now you see the main reason why in the beginning i didn't see my search results because i didn't keep this subscription in the beginning so i need to also store the subscription description oh i don't have this in the year so this is my subscriptions any of type any can syllable and for this to work i need to help to have import combine here so now i have a set of a nike and silver start with an empty one and i can store my subs i can store the subscription that i created here in this subscription set of any consoles soon we are using here in our view controller this view model when the text field changes we are changing the property in our viewmodel or the we are having a new value in this subject we have a data stream from this subject from the search text field that goes and fetches new texts tweets and stores them in this current value subject is tweet so now in our view controller we need to react to two updates to it here and if you did load i can also tell my view model sweets to create a subscription and we're going to use your sync in this case i just want to be called so i can say myself to a table view to reload the data and i have to use your unknown self again self dot store in subscriptions so now we have the text field talks the view model the view multi fetches and we have another data stream back to our view controller to update the view review controller and before i do this i just need to change my api key so when i run this i should be able to see and yes we have you see here this one print statement success because i have here this verify credentials and i go and fetch my tweets so the table view does work unfortunately it's not the scrolling um infinite scrolling because i'm not re-fetching within the end of this table of this list of 10 tweets and i couldn't figure out how to use this sd twitter thingy but the good thing about this i could demonstrate how to it's a good example for using here switch to latest because before you see here this one function call that was there before and every time you do that you call another you fetch another tweet set of tweets and they might come back especially if i'm here searching i might start this process for multiple of my of these and they might come back in a different order but if you have here the switch to latest whatever is fetched in between if i update this it's going to only keep the latest fetch stream substream so i can make sure that whatever comes back is the newest one so i wouldn't need to even checked which is quite nice so you can try to search for swift here for example i see a lot of people who have when they have functions and every time they call this function they create a new data stream because actually they're creating this part of the data stream with the twitter api with this fetch request if you do this you give up half of your of the combine because and that moment you have separate data streams every time you call this you have separate data streams where you don't know how many values you have in this case you cannot use this removed booklets bounds or the switch to latest so it's much much nicer to have these data streams and data streams but i understand because it's unusual to use current value subjects if you have instead of this map and switch to latest a flat map this is basically instead of switching to the latest you would keep all fetch requests so that would be much more useful if we had here this infinite scroll behavior i would send in the data stream in the stream to fetch me more values which could create this sub fetches here and then you have new values coming back which you can just append to your array of tweets so that would be an example for flat map get confused with all this map so it's flat map or switch to latest and since we are here i just wanted to show you also why i did not use for this tweets a at published okay you don't need to do this i'm just going to show you published bar tweets which is a empty array of tweets so i'm uncommenting this which is going to give me a lot of errors because we need to you have to call and publish differently than current value subject but the most important thing is that ed published does add you in the background the current value subject so it's from the baseline is the same publisher it just looks different in the way you use it so how many errors so the first one here in this when we want to change the value of of this tweet because the tweets themselves are not a publisher anymore so i can say self dot tweet this is like behaving like a normal one self.tweet is the tweets so then we have another one i think in our view controller because here when you want to create a subscription with this from this property now the street is the property but i want to have this is dollar tweets so down here when we have to tell the table view how many cells there are this is not subject so it's directly the tweets and it's the same known here when we have to give the value for this cell or for this index path so the first thing you see is that you don't see anything and it's not because you don't have data the problem is that here or data stream you expect yeah this is called when the tweets are changed but these api this ad published creates you a publisher that sends the value in the will set of this property your data stream has the value before the actual value before it tweets your data stream the publisher sends an update before the value itself changes which means that you're trying to reload the table view before this tweet has a value so if you here you swift you see now that we have some updates but this is because i have the previous one it's not the newest one it's a bit difficult to see right now there's probably examples where you can see this better but that's why i wouldn't recommend to use it published with ui kit it's made for swift ui so i'm just going to undo this and the next thing is i'm going to rework this into s50y project because now i have everything separated i have here my tweet model my view models and my model in an extra file so i can just change a couple of smaller things and we can directly use this with ui you're now going to continue by building the same project with swift ui and i created a new project and i did install the cocoapods this three frameworks again actually i don't need to have this lbta tools because this is for hdx and just some stack which we don't need to suit you are this sb web image helps you to load lazy load images in views that is still convenient okay we start if i just go so from our old project i want to have the view models and i want to have the models and the view models so i'm just going to include them in the project so we don't need to change anything in our twitter api because this is very um much related to combine and the back end so it's enough since you're a kitten if you idea more related to views but um in our view model here i'm going to change a little bit because of this of the way i'm going to hook it up so now i can use here this ad published for my tweets and i'm also doing the same for my search text because i can easily hook this up to a text field in swift ui this is it published var and we start also with paul hudson comment this out so you see both of them and then down here when i create a data stream for my search text i need to use dollar to get the publisher i can go back to my set on tweets and in this case we can actually use assigned to depending on so you have a sign assigned to on the problem with this one here this on if you use self this is going to create you another memory cycle because it's assigned to keeps a strong reference to this on and that's why you shouldn't use self for [Music] ios 14 you have here this two we can use here our tweets publisher just take this out so this works otherwise you again do the same here with the sync and the good thing about the assign 2 is you don't need to keep the subscription so your code looks a lot nicer and shorter so this is it i don't need to do anything else in here this is another advantage of having a view model and nicely separating everything because obviously this should not be related to our view not like previously with this massive view controller problem okay so now i have everything but we need to show it so in my content view here you could also rename this to tweet list but yeah i'm not going to make this a even longer so since i have a class i want to use i need to have some some something something object and i'm using your state object well this is my tweet view model and because it's a state object i can create it here so this is a tweet view model and ah yeah i forgot to make my viewmodel conform to observable object so object then now my content view i wanted to have the same as before so we need to have a text field unfortunately they don't have a search text field yet so you need to have a little bit more yourself search so this is the placeholder which is search tweet and this one we are going to hook to our video model search text so the dollar sign here is a binding this is not a publisher then although it uses publisher streams to get updates so previously in uikit we had to do the um we had to ourselves connect our ui text view to our data but in this case it's automatically done for us just going to add here text field style around it for the text field style you can also create a little button to cancel everything again if you want to so now i want to have a list of all my tweets this is not with children this i mean the children is actually nice but i don't want to have it it is this one the idea and raw content so this is my tweet view model the tweets and i don't think it's identifiable yet yeah we don't store anything here but i can make it identifiable and var id is a string return text so i assume that you shouldn't have tweets with the same text hopefully but i'm guessing there's something in the api that has a id anyway that is probably better suited for this i don't want to work more on the model so in the content view i don't need to have this id here and now i can use the content so now i have here my tweet maybe display dot text and run this so it's not the most amazing ui but it's pretty fast for this so previously which i still have here we had a cell with an image of the user and then the username and the actual tweet so i'm going to create here a tweet cell so this is a new file so dry view this is a tweet raw and this has a tweet now we can just give here a tweet in the preview text [Music] ipsum this is a user form so this is a age stack so we want to have this image and then on the right side we have the text defeats user's name in bold and the text tweets text and this was in foreground color gray and maybe the alignment should be leading probably should not make this more complicated than it is so now here i can use the image i'm not going to do this because it's going to take a little bit longer than i want to but you could load the image in here so now i have my tweet raw so in my content view i can use notice tweet raw with this tweet and maybe i should just add here padding so now you see here our nice tweets and if you change this to swift it's going to re-fetch them this actually reminds me that i right now if i change this to an empty it's going to fetch it so we probably shouldn't do that so in our tweet view model when you're here in this setup tweet search instead here you can decide if the text field is if text dot count is smaller then if it's at least three letter two three letters i'm going to do this else i'm just going to reset my array so and so it's the same as this one just publish an empty one and again i need to use the rest any publisher and return so it's basically not if you could also have your filter and not proceed but if i have less letters i want to make sure my ui updates so and in this case it's updating and just resetting it to an empty array an empty list of things here so if i have just this it's going to be empty and now we'll start as soon as i have more than three letters so in this case you make sure that uri is always empty so it's a nice little way of here deciding which publisher i want to use or where i want to get my data from do i just want to publish an empty array or do i actually want to fetch something so with this i'm going to wrap this up i know i did is it did get a little bit longer than i wanted but i want to demonstrate how much more combine you can use and how much nicer it can be because now in this case for example you have a clear path of what to do when and what what things happen after which one so it's nicer to read and obviously my main concern for this review for this coding refactoring was to um have a better architecture or a better structure you can use mvvm and you see it's also nice to use it for swift ui which isn't expected but it also works really nice for ui kit because of combine of this easy to hook up with these data streams before it's not so easy to update and call on each other but right now you just need to create a subscription on this side or in a subscription on this side and just get the data flow going so i hope you enjoyed this and did get some information can some valuable information give this video a like if you want to see more of these let me know if you actually like the kind of combine i used and what kind of combined tool you haven't seen before that you might actually want to use in the future too if you want to see a lot more of combine i also have a new course you can find a link to this course in description i'm going to post more free stuff on youtube too so if you don't want to miss more combined stuff subscribe to this channel until next time happy coding
Info
Channel: Karin Prater
Views: 6,387
Rating: undefined out of 5
Keywords: combine api call, combine framework, combine framework example, combine framework tutorial, combine mvvm, combine mvvm uikit, combine swiftui and uikit, combine switchtolatest, combine uitableview, currentvaluesubject, getting started with combine, how to use combine, swift combine example, swift combine mvvm, swift combine tutorial, swift combine uikit, swiftui combine mvvm, Karin Prater, ios development swift, iosdevelopment, ios development 2021, how to write an ios app
Id: O8vY5LUDagY
Channel Id: undefined
Length: 43min 57sec (2637 seconds)
Published: Sat Feb 20 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.