Advanced Combine Publishers and Subscribers in SwiftUI | Advanced Learning #19

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] all right welcome back everyone i'm nick this is swiffle thinking it is like 5 a.m here so i know i look really tired but we're gonna get past that in this video we're gonna look at advanced combine pipelines so most of the time when we've used combine so far we've just fetched some data and came back with an array and published that array into our app but one of the most important parts of combine is that these publishers and subscribers can stay alive even past the first publish so in this video we're going to create a fake publisher that's going to publish values every couple seconds and then we're going to manipulate that data with some really cool unique subscribers and we are back uh i don't know if you can tell this just yet but i did get a new microphone so i'm hoping that the sound quality in this video and all the videos going forward will be a little bit better as most of you guys know i live in the middle of manhattan which is definitely one of the loudest places on earth and i've been struggling with some of the background noise in my recent videos so i do apologize for that guys i am working on it i have some other soundproofing stuff coming on the way but hopefully this is a little bit better already with that said let's start doing some of the code for this video so in the last two videos we opened up separate targets for our unit tests and our ui tests we're not going to use those anymore so i'm just going to close up those folders let's right click the navigator create a new file and we're back to creating swift ui views this time let's just call this advanced combine boot camp and we will click create let's make sure now when we're creating our files that we are only in our main target we don't want to create more files in our testing targets and let's click create and jump on in here and again this is the advanced combine video so if you don't know what combine is this is not the video for you i have other videos on my channel in the swift ui continued learning playlist where we learn the fundamentals of combine the normal use cases of combine in this video i'm going to assume that you already know the basics of combine and we're just going to jump in to some of the more advanced flows and some of the really cool unique things that we can do with combine pipelines so before we actually get started we got to do some setup of course so let's create a viewmodel for this view it'll be a class advanced combine combine boot camp view model let's make it conform to observable object and then we will observe it from our view with an at state object and my autocomplete is not working today i'm not sure why state object private var vm and we'll set it equal to the advanced combine bootcamp viewmodel inside the view model we're going to hold a very simple array we're going to call it at published var let's let's call this data and it will be a type and it will be of type array of string and we'll set it equal to an empty array to start for the majority of this video i'm just going to use strings we're not going to use any custom data types but any kind of custom data type is going to work the exact same way coming through a publisher and we're going to simulate like we also have another file in our project that is maybe our data service that is going to do all of the downloading functions for our app so in this project and probably pretty realistic in a real project we're not going to actually download in the view model but download into that data service so let's create another class i'm going to call it advanced combine let's call it data service and we will open the brackets here and i just want you guys to notice again that this class is not going to be an observable object because this class is not being viewed is not being observed with any kind of property wrapper from the view we make this an observable object again because we are observing it from the viewing and we want those constant updates here we don't need that we don't need the v model to actually observe this entire class instead we're going to add some subscribers in a second to observe specific variables within this class and i think i misspelled combine here inside our data service we're going to have a init here then we have a function that says let's make it a private funk and we'll call it uh publish fake data bump and close parentheses open brackets let's call that in our init here and we're just going to basically mock like an api call because in this video we're focused on the combine uh pipeline and not actually focused on the api request so just to simulate that quickly let's add a dispatch queue dot main dot async after let's do it after now plus one second and then we're just going to publish some fake data and and when we publish the data let's publish it through an at publish var and let's call this maybe let's just call it basic publisher for now and this will be of type array of string and we'll set it equal to a blank array to start and then when we publish our fake data we will say self dot basic publisher and we'll set it equal to a new array this would be like the array that you actually downloaded from the internet i'm just going to say maybe one two three just for one second we're going to change this up in a minute so we're going to publish fake data it's going to wait one second and then publish that data into this basic publisher and i know this bit of code is not actually combined but we're not focused on this publishing fake data in this video we're going to put we're going to be focused on the combine subscribers that are going to be in our view model so uh basically we want our view model then to observe this basic publisher and i've done this in a bunch of videos if you follow my crypto course this is pretty much the same setup that we had there uh so down here in our knit of the view model we will add some subscribers let's create a private funk here called add subscribers open close brackets open parentheses let's call that inside our init so we add those subscribers and then let's of course subscribe so we want a reference to this combine data service and let's initialize it inside our view model so we'll say let data service and we'll set it equal to a new advanced combine data service again if you're following this playlist you're probably well aware now that we wouldn't probably initialize this inside the view model we would instead dependency inject it into this view model because this data service might be shared with a bunch of different view models a bunch of different classes so we would inject it through the inits all the way down to the entrance of our app but again we're not focused on that right now so let's initialize it here and it'll subscribe to it so we'll call data service dot and we're gonna subscribe to the publisher with the money sign here the basic publisher when we get these values through this publisher let's just sync them let's do the completion and received value i'll press enter on the completion and then we will switch on the completion there are two cases here we have case finished in k and if it's finished obviously it's successful so we'll just break we also have a case failure let error so we can get an error if we fail and for now i'm just going to break i think we'll use that in a second if we start failing some of these but let's leave that there and then here's the received value that we're actually going to get so this will be an array right now of returned let's just call it return value and in here we're going to set uh the data that's on the screen the data that we're going to put on the screen in our view model equal to those return values so we'll set self.data equals the returned value and and of course as you guys probably were just thinking uh this is asynchronous code so this is returning uh not immediately but whenever these values come back and because of that we have to add this self and this is a strong reference to self which is of the class but being the great developers we are we're going to make that a weak reference so we'll say weak self and then self will be optional and if you do not know what week is i have an entire video on it go check out that video on my channel moving on we have these quick error messages let's get rid of them um this error let's actually just print it out we'll print out um let's put error and then a colon and then we'll just print out the actual error dot the localized description that should go away and then we need to store our subscriber somewhere so let's import combine of course it's a video all about combine we haven't imported it yet and in our view model here let's create a var call it cancelables we'll set it equal to a set of any cancelable and we will open and close the parentheses there and this subscriber let's store it in our cancelables finally on the screen here let's actually put the this data array so coming down here let's go to the text i think we can do a scroll view open the brackets and then maybe a v stack open the brackets and in here we'll do a for each open the parentheses we are going to do this is an array of strings and strings are hashable so we need the one with an id so we can reference the hash value so the vm dot data we're looping on and we're going to loop on the backslash dot self which is the hashable id for each string and then we have our content here and in xcode13 i guess they got rid of that like four x in code here usually it says like for item in and we can type that in and have it still work but just to for brevity's sake let's make it let's not include that and here we'll just put in a text and because we don't have that for item in instead here we have to put in the money sign zero which is going to be the that value that we are looping on let's give it a font let's just do maybe large title and we'll give it a font weight of black i'm gonna click resume on the canvas and get this v stack on the screen and let's press play here and should wait one second and then publish our values so again just to follow up with what we did we have our view our view has a view model in our view model we have a reference to the data service and then in the view model we are subscribing to a publisher inside that data service and when that publisher gets values we'll receive them here and then update the data on our screen which is this published variable and then if we look back in the data service obviously this is just a fake data service now this is not combine code but this could have been combine code that downloaded from an api came back and then updated this publisher that part is irrelevant right now but but i wanted to just start out this video with this simple setup because this is a setup that you've probably seen many times before and this is very common in our apps because a lot of times when you go and you download data from an api it's going to return back an array of data and then we can take that data and we can put it on the screen and that's great for your regular get api calls if you're going to just go fetch some data and then once it returns you're done you have all of your data that's the most common kind of fetch request but part of the magic of combine is that these publishers can stay alive after they get a single publish so in this example we just get one publish and then we're finished but this publisher is still alive this publisher is still alive we are still subscribed so if anything ever were to publish here again our whole flow would then update which is great so what i'm going to do now is convert this instead of holding it right here let's just publish individual values so this publisher i'm just going to make a string and instead of giving it a blank array as a starting value we have to give it a default value so i'm just going to put a blank string here so in this function i'm just going to take this array and let's just put it up here we'll say let items equals one two three and then let's create a simple like four x in items a little loop here then we'll put our dispatch queue inside that loop and instead of looping on the items let's loop on the items dot indices and so this x will dispatch q after now plus whatever the x seconds are so uh the first ones happen in the first one we'll have an index of zero so be now plus zero seconds now plus 2 one second now plus 2 seconds i think we just have to make this a double and then in here let's just set self.basic publisher equal and we're going to set it equal to items at the x index all right and now this is just a regular string we have to come down edit our view model so the return value here is no longer an array but it's actually just a single string so when we have our data array that's on the screen instead of updating the entire array we're just going to call self.data.append and we'll add in that new element which is the return value so one more time i'm gonna just play the simulator here and after one second it's gonna add two and then add three so here is just a quick example of how combine can start to become really powerful because the publishers stay alive until we cancel them so if there's multiple things getting published obviously we can observe those and continually update our screen right so we got one we got two and we got three and these are all separate publishes and the first thing i want to go over in this video is actually this published variable so so this at published wrapper is probably is definitely the most common uh publisher that you guys probably see uh in tutorials on youtube channels but what really is this at published property wrapper doing for this variable not many people actually dive into the underlying publisher that we have here so really in combine there are two types of publishers and the first one is going to be called a current value publisher so if we didn't have this at published property wrapper the way to write this would be we can create a constant and we can call this let's call this current value publisher we could have called it basic publisher 2 but i just want to be explicit here this is the current value publisher and we'll set it equal to a current value subject and we can see here a subject that wraps a single value and then publishes the new element whenever that value changes so it is taking a value whatever type we want to give it and we'll publish it when we send it through this publisher so it also will become a publisher so current value subject and then we need to give this a type because this is a generic function and we can give it any kind of type whatever kind of value we want to publish so here we'll add string and then we need to give it a possible error because all combine flows possibly have errors we've dealt with this before um this publisher right here does not ever have an error because it's always going to be in a string so we can make this never if there's never an error and then we'll initialize it with the open close parentheses here and as we do that we're going to get a quick error message here that we're missing argument for parameter number one and call insert a string and i'm going to click fix and basically wants us to put a string inside this open close parenthesis when we initialize this current value publisher and the reason for that is because the current value subject is holding a current value so the so the starting value we have in this basic bubbler is a blank array string and i'm just going to use that here as well uh let's actually even just prove that we're starting with that so i'm going to make the first value that we get here let's just put it um first publish let's make that the starting value and i'll put it here as well i'm going to resume i'm going to resume the simulator one more time before we're actually using this uh and the first and you're going to see the first publish is the first thing that comes through the pipeline so when we set up this basic publisher we give it a starting value and then when we subscribe to it the first thing that's going to get published is that starting value that very first publish so we have the first publish and then here we are updating for one for two and for three there's actually four publishers going on through this and this is going to be the same thing with the current value publisher this is the same thing as this pretty much here we have a publisher with a current value which is going to be it's going to start as first publish so let's use this publisher now instead of this one so i'm going to comment this out come on down here and when we subscribe instead of subscribing to the basic publisher let's subscribe to the current value publisher and we don't need the money sign here because it works a little bit differently but we have the current value publisher here and then when we are up here and we are publishing our fake data here instead of updating the value for the basic publisher we want to send publishes through our current value subject so what we do is we'll call self.currentvaluepublisher.send and here we can either send a completion or an actual input and if we send a completion it's going to come through this completion sync and we can send finished or failure but what we want of course is to send an actual value so we'll send sorry an output is what we want and it's going to be the items at that x index so now let's click resume one more time and we are now doing the exact same thing this is nothing magic but we have learned that we can use a current value subject so basically if we didn't have this at published wrapper this is just a convenience wrapper this is basically what we would be setting up and this current value subject is going to hold the current value at all times so it will always be holding a value in memory and then we can publish through it and usually when you set these up when you go to an api call it's gonna it might have an error when it comes back so a lot of times this is actually going to publish with an error here and it's not going to be never and the next one we're going to do is a pass-through value so we'll say let pass through publisher and we'll set it equal to a not a current value subject but a pass through subject and again we need to give it a type so we'll do string error and then open and close the parentheses and you'll notice when we initialize this we don't give it a starting value and that's because our pass-through subject or pass-through publisher works the exact same way as the current value publisher except it does not actually hold a current value so this pass-through subject can be a little bit more memory efficient if the value you were holding here so if this was like an array of maybe let's say an array of images and we had all of our array of images as the current value that could be sto taking up a lot of memory space in your app so if you didn't actually need to hold the current value in this publisher because again after we publish we're holding the values in this data around down here so a lot of times you might not need to actually hold the reference to that array here as well instead you're just trying to publish them to the rest of your app so if you don't need that current value to be be held we can use a passthrough subject which is going to work the same way we can still publish values it's just not going to hold on to the current value so here let's use the passthrough publisher instead and coming down here let's subscribe to the pass through publisher click resume one more time and we're gonna see that it works the exact same way and from the screen the only way we can tell that there is a difference is because we don't have that initial publish so we don't start with that first publish instead it's just going to wait for whenever it gets that first published so this could also be a great solution if you don't want those initial publishes to occur awesome and now that we're getting a bunch of different publishes we can do some really cool things in our pipeline we can subscribe and manipulate our data in some really cool and efficient ways but before we actually go into that instead of publishing strings let's publish integers because i think um publishing the integers on the screen will just be a little bit easier to visualize the publishes because we can then see like as they come in order we can see publish one publish two publish three so instead of an array of strings here let's do an array of integers i'm just going to go from let's create an array and let's go from 0 let's do dot less than 11. so this array will be from 0 to 10 and these are of course integers i'm going to make this an array of int and let's publish these so our pass-through subject is going to be of type int now we can make this int as well although we are not going to use this for a little while so i'm just going to comment it out and our pass-through publisher let's send items at x and now coming down here we have the error because uh our data is an array of strings so what we're going to do in this flow before we actually get into the sync let's just convert those integers back to strings so we can put those strings on the screen so up here i'm just going to call dot map and open the brackets here and we'll do a we'll create a string with the from money sign 0 which is that the value that's being published i have gone over how to use these basic functions before so i assume that you you know this already and let's run this one more time just so we can see what's going to happen on our screen we're going to get our numbers our numbers should be publishing through 1 through 0 through 10. so every second we're going to get another publish this is not rocket science um i just want to create this streamlined publisher flow that we can then manipulate so here are all of the publishers that we're getting one after another so now we are set up for the bulk of this video and i'm not going to really touch much of the data service anymore what we're going to be focused on is we're subscribing to this pass-through publisher and we're getting publishes which are which are the integers as they come through and right now we're just converting them to strings and then putting them on the screen that's great but let's manipulate this pipeline a little bit so i'm going to add some space here and again if you add space between here this is not going to affect anything or our code is still going to work the exact same but let's look at some of the operations we can add to our publisher pipeline here so we're going to start with something called sequence operations and the first thing that comes to mind is that we can call dot first this is going to publish only the first element of the stream and then finish so if i call first on this pipeline we will press play and even though we're getting all those other publishers obviously the only one that's going to come through the rest of our pipeline is the first publish let's comment that out we can also do dot first where and we'll get the integer that we are publishing so this will be the int and we can set some logic for if we only want maybe the first let's say the first where let's return where int is greater than four so now it's going to listen to that publisher array but it's only going to publish the first value that is greater than four now we only get five so this is hin so this starts to come in handy if if maybe we're sending like a whole lot of data through this publisher but we only want to pick out certain publishes to actually show on this screen we can do something like this now we're going to do a lot of stuff like this in the rest of this video and when we create these and when we create these modifiers obviously um this is the long-form way to write it we can break out to this closure and we can and we can put this code in here but as i've reviewed in a bunch of my videos already the faster way to write this is actually to just open the brackets and then in here we could do money sign zero to reference the integer and we can say is greater than four so i'm going to delete this first method here and we're going to use this shorthand instead because because it is uh cleaner and a little bit faster the one exception for that the one exception for our shorthand rule is going to be when we use uh modifiers that might throw errors so so we can also do try first and in here we're going to publish the first item that satisfies the closure so here we're going to try first i'll click enter on the closure and we don't need this modifier here so this will be for the int and this is a try first the try notifies us that this closure can throw errors so if there's a reason that maybe we get a certain publish here if we want to throw an error onto this screen or something like that we can add some logic in here so in here i can add if int maybe if integer is equal to 3 we want to throw an error and i'm not going to create custom errors but let's just do a url error and we'll do maybe not bad server response just as an example and then down here we can return maybe if integer is greater than 4. so here we are going to try to publish the first value where the integer is greater than four however we have a little closure here that if the integer is equal to three instead of publishing the first value as whatever this is we're going to publish a value that is actually an error so if i run this now so i run this now we are actually not going to even get to this point of the screen because obviously three becomes before four and when we hit three we're going to throw an error and that's going to be the first publish so we never are going to actually publish this value and just to prove that let's put the error on the screen so up here let's so in our view model let's just create maybe an add published var error of type string and we'll make it and we'll set it equal to a blank or a blank string for one second and let's just put that on the screen so i'm going to put it in the v stack but below or for each and let's just put if um vm.error dot is empty so if it is not empty use the exclamation point there if it's not empty it's not a blank string we'll put a text on the screen that says the vm dot error so when we run this publisher right now we should actually see our bad server response and and finally we need to of course when we get the error we're going to set self dot error equal to and then let's let's actually put this string here and then we don't and then when we run our publisher we should actually see the error that comes through so we're getting this url error that's going to publish and not this actual value so here we have the error operation couldn't be completed this is not very uh specific we can focus on our error messages later if maybe we take out the localized description it'll be a little more specific and yeah here so now we have nsurl domain error which is i guess the bad server response error um but what i want to show you guys is that we can throw errors if we're getting specific publishes uh alternatively if we change this code so that maybe the first integer is is so maybe we do look for the first integer that is greater than the number one so this will actually succeed before we throw an error right because the number two is going to be is going to get published before our number three so if i rerun this now we still have our try first except it's actually getting that first value that is acceptable and we don't ever throw this error i'm all right i'm going to comment this out we can also do a dot last and we call last of course it's looking for the last item that gets published through the publisher and if we run this you're probably thinking well the last item that gets published should be our number 10 but we don't see it on the screen and that's because when we're doing the last publisher we then need to know somehow when this publisher is finished when is the last publisher because when we're subscribing to the publisher it it basically is going to be alive forever until we get some sort of completion if we never get a completion this publisher is just going to think there's always a possibility that something else could get published so when we what we're going to do is come up to our publish fake data function and we know that when we hit the last item in this index we are done publishing so what we're going to do is in here we're going to say if we'll say if x is equal to items in dot indices dot last so if it's equal to the last index uh then we will call self.pass through publisher and we're gonna send not an item but a completion and this completion here is exactly what we are going to get in our sync so we can we can either send a finish if it's finished successful or we can force some sort of error so here i'm going to press the period and again we can do dot failure if we want to force an error we can add our error here but if we get to the last one we're actually going to succeed which is not finished so now once this completion gets published the subscriber would then know that the last thing that got published was the value right before we got that completion so if i press play and wait 10 seconds we're going to see the last value now gets published which is the number 10 the last thing to get published before the completion came through similarly we can also do dot last where and i'll do i'll open the brackets here and we'll do money sign zero maybe greater than four so just like the first where is greater than four except this time we are again waiting for the publisher to finish and then it's going to give us the last item where zero was greater than four so the last item that zero is greater than four is actually 10 right the it's not the first item where 0 is greater than 4 which would be 5. it's the last item that got published that was that fit this mark if i made this maybe that is less than 4 the value that we should get here is going to be three and again it's not going to actually publish and come through our pipeline until the publisher finishes so i really want to point that out the number three here got published three seconds in right because we did our pipeline one two three but we didn't actually get it in as a received value until our entire pipeline until our publisher finished and we got that completion so although three published three seconds in we still had to wait for all the publishes which was 10 seconds and then the only thing that got published was the last that fit this criteria and of course just like just like the first we can always try to get the last and here we'll get the int and same thing we can say here we'll say if let's take the code from up here if int is equal to three let's throw url error dot bad server response otherwise let's return the let's return the last one where int is greater than one and here we are trying to get the last value that fits this but if we do throw an error we're not going to actually return the rest of this so here the integer greater than 1 so 2 got published before we hit 3 but during the lifetime of this publisher we ended up throwing an error and when we throw an error that actually goes through and becomes a completion because we throw the error to finish up our pipeline it'll pretty much end the publisher so in this situation right now although this is succeeding at number two although we're getting a value at number two when we once we hit three uh we throw the error and that's gonna be the last thing to publish so the numbers 4 5 6 7 8 9 10 are not actually coming through here but if i change this maybe to like 13 and we come through here um we can then we're checking if we get this so if there's maybe like a bad value that we don't want to get here and if if we get that value we want to throw an error then we can do something like this but otherwise it will return the last item that that fit this criteria which of course is our number 10. i'm gonna keep moving here we have a lot to cover so i'm going to comment a bit of this out let's put our publisher back to normal so we have zero one two three four five and the first thing that's getting published here is the number zero but we can also call dot drop first and here we will drop the first publish so literally same thing that our normal flow will do except we don't get that first publish coming through and this could be handy this drop first if we were using maybe like these this basic publisher or current value publisher and we gave it a starting value that was maybe a blank string and you didn't want to publish that blank string so you could drop the first publish because you know that that first publish is going to be that starting value which the blank string probably is not relevant to the rest of your app but for in this case i just wanted to show you guys that we can drop the first publish we can also call dot drop first and add in a number here so maybe we want to drop the first three publishes so same concept except 0 1 and 2 are not going to get published now and and it actually makes more sense now to actually start at the number one so i'm going to change the array up here to 1 to less than 10. so the 3 that we're going to drop is 1 2 and three and then we're gonna start four five six and keep going from there comment this out and we can also dot drop while you'll notice here in the definition that this omits elements until a given publisher returns false before republishing all remaining elements so if i drop while and i'm going to do money sign 0 dot less than three so while it's less than three we're going to continue to drop uh these values and i could change this mate let's do maybe five so while it's less than five we will continue to drop and then eventually we get to 5 6 7 8 9 10. what i want to point out here is that if we do the greater than sign we're going to actually get all of the values because the drop because the drop as it said is omitting elements from the publisher until the closure returns false so the first thing that got published here is the number one and we know that the number one is less than five so the argument in here looking for where the value is greater than five is returning false on that first publish so once it returns false we're done dropping so this actually makes more sense really if we want to drop at the beginning of our publish and not at the end so let's keep this as a less than sign and of course we can try to drop because sometimes we just want to try to drop and we can say here if int is equal to five we can then maybe throw our url error otherwise otherwise here let's return where int is less than six and again when we get this when we throw an error it's forcing our completion to come through so once that completion comes through nothing else is going to get published so we are trying to drop while the integer is less than six so we're continually dropping for zero one two three four five however once we hit the number five we're going to throw an error and because we throw the error the numbers 6 7 8 9 10 are never getting published however if we want maybe made this like 15 of course we're never going to hit the number 15 we're never going to throw the error and now we can continue to drop while the integer is less than six and then publish when the integer is equal to or greater than six so we can then try to throw an error here if we again want to do maybe some data validation uh to make sure that maybe there is good data or we didn't get a certain value something like that moving on we can also call dot prefix and this will give us a max length this will republish elements up to the specified amount so we can put in here prefix of number let's do four and we will get the first four publishes in this stream so up here i just want to point out when we did the first where it was it was doing validation on the actual items on the actual values so this was giving us the first value where the value is greater than four but here but here there is no check this is just give us the first four things that were published through this publisher alternatively we can prefix while let's open the brackets and we'll do while the value is greater than five so looking at this again we can read a little bit of the documents here and we're going to use prefix while to emit values from the upstream publisher that meets a condition and we can see the publisher finishes when the closure returns false they give us a little example here but so here we're looking for a value that is greater than five right so the first value that's getting published the number one we know number one is not greater than five so the closure is returning false immediately and then our prefix is actually done and we can't really do much here but we couldn't but alternatively we could say well a value is less than five and here we're going to continue to get those first publishes while this is still true and now this seems a little anti-pattern because we could always just do prefix the number five but this would come in handy if you had custom data types here so instead of looking for an actual number of less than five because then you know it would be the first five elements in our array but when you actually get regular data that's in your app you might want to prefix while a certain condition is true so maybe while i don't know the the messages are new to the current user or something like that um some sort of condition that you want all the first you want all the first publishes while a condition is true and just like above we can always.try prefix and i'm not actually going to do this one because there's a lot to cover in this video these are some of these are a little less common to actually use in your app but you guys can figure out how to use them on your own time and the last couple i want to go through is dot output and we can do dot output at and you'll see here that it's giving us an integer and i don't want you to confuse this integer with the actual integer that we were publishing if we were publishing strings or some custom data type this would still be integer this is looking for the item that gets published at this index so if five things get published they would have in they would have basically indexes of uh 0 1 2 3 4 and we could get the output at one of those publishes so if we want the second to publish the in the index of the second item in an array is actually number one so what we should get here is just the number two so we're outputting at the first index and obviously we can do any index we do the fifth index which should then give us the number six and alternatively we can call dot output in and then we can give it a range so the range i'm going to do is from 2 dot dot and let's do maybe less than 4. so for the indexes between 2 and three so index two is going to be number three and index three is gonna be number four and that's that's why we're getting these outputs now so this is where we can start to really get unique if we have these really custom flows coming through all right i'm going to comment this out and then i'm going to create a multi-line comment here with these asterisks uh forward slash and up here i'm just going to um forward slash asterix and then let's command option left arrow to fold up a little bit of that code just so that we don't see it anymore just so that we can continue working without this crazy long comment section if you are confused about the folding it you can always go to the editor and click code folding and we can fold and unfold parts of our code below the sequence operations we're also going to do some mathematic operations the first thing we can call here is dot max so we can only call the max because the system knows that the values getting published are integers which are capable of having mathematic operations but the max of course that we're going to get published is the number 10. and again i want to point out that if we don't throw in this completion and we're trying to get the max value it's never going to know when to calculate that max value because it doesn't know when the publisher finishes and you need to obviously know the entire range before you can calculate which is the maximum so we again need to always have these completions coming through um and this is probably pretty important if you've been trying to use some of these and maybe they weren't working it might be because uh your publisher that you're subscribing to is never actually finishing can also call here dot max buy and i will press enter here and if i click the hold the option button and click on this we can do a little bit of reading here that this is going to determine the maximum value of elements received by the publisher based on an ordering closure you specify so here let's have int one and then into two and let's compare the two so we'll say we'll return here where int one is greater than int two and we should actually be using less than here so the maximum where the first thing that gets published is less than the second thing that gets published and of course our publishers are going in order from 1 up to 10 so the maximum where the maximum is going to be 10 and let's go up to our items right here so i have this array of one to ten i'm gonna actually just make it an actual array for a second let's do a one two three four five six seven eight nine ten so in this function we're comparing um into one the first published to in two which is the second publish so if i look here this is gonna be the first publish and then this is the second publish and then the first publish and then the second published in the first publish and then the second publish then the first publish and so on and so forth until we get to the end first publish the second publish when we look at 9 verse 10 9 is less than 10. so the so where 9 is less than 10 is successful and that's why the max is going to be the maximum that fit this criteria but if theoretically just for example say maybe the value that got published here was 11. so if we run the same thing now when we get to this point where we start comparing 8 is less than 11 that's true but 11 not less than 10. so the maximum that fit this criteria is actually going to be the number 11. all right i'm gonna put this back one two three four five six seven eight nine ten coming on down and let's comment this out we can also try max same kind of concept i'm not going to go through that because there's so much to cover um but we can alternatively do dot min so here of course you guessed it this is going to publish the minimum value which should have been the number one and of course we gotta wait till the end until we get that completion before we can calculate the min and of course it's number one just like in max we can also do dot min buy so this is where the min fits that criteria which i'm not going to do right now and we also can do tri-min same kind of concept where we can throw an error if we need to and if we don't throw the error we can get them in i'm going to also fold some of this up so let's use the asterisks and the forward slash and then in here and then in here let's uh forward slash asterisks and command option left arrow fold that up we got our sequence operations we got our mathematic operations i think we are and we are cruising through combine all right all right all right moving on here guys we uh let's do the next section which i will call filtering slash reducing operations what is the first uh that we want to do here is map which we already are doing i'm just going to put it up here so we have a reference to it we can obviously map the values and i'm not going to go dive deep into this but basically we can transform a certain type which we're getting an integer and we are mapping it to a string right here i'm going to comment that out and we can also of course try to map it so when we try map it possibly can throw an error and here we can have the integer and in here maybe we are going to return a string of the integer but we could also have some errors so if uh int is equal to the number 5 let's throw an error instead so we'll do our url we'll throw a url error bad server response so now if we watch this we can as we publish these we are mapping them all so of course we can map zero one two three four but once we hit number five we're gonna throw an error and because we throw the error and we go into that completion we're not gonna actually publish anything after so even though number six seven eight nine ten uh do not fit this criteria and would actually convert to a string uh we're gonna stop once we throw that error so here so here we can basically just make sure that anything that's getting published to our stream um can correctly get mapped to whatever this is now alternatively you might want to try to map but if something maybe doesn't fit your criteria maybe you want to keep getting the rest of the publishes and just ignore the number five so what we can do and i will do another let's comment this out here we can do a dot compact map and here i'll put the integer and here we can we can add some logic for compact mapping and and i've covered it before compact mapping is basically the same thing as mapping x but if something doesn't work it we can just ignore that value so here i'll do if int is equal to five just like we did up here instead of throwing an error because of course we're not trying and possibly throwing an error here if equals five let's just return nil so if n equals five we'll return nothing and this should be int and then otherwise we're going to return our string with the integer so now again we get our publishes and we can get four and then six so the only thing that got avoided here is something that maybe could not convert to whatever type we want to convert it to so this is handy if we had maybe some raw type from the api coming through and we wanted to try to map it to our local type but if it failed we just want to ignore that value and we want to continue to try to map all the rest of the publishes so perfect for that situation and i don't want to confuse you guys i'm bringing a string to converting it into a string we can do it like this we can also do it like this we put the integer it's the exact same thing well let's keep moving on here i'm going to comment this out and we can also try to compact map so same concept here i'm not going to actually do that but if we were doing this and we wanted to potentially throw an error so we could have some logic to throw nil we could have some logic for success we could also have logic for throwing an error we could try to compact map it and that could be useful if maybe you have the same setup here but if there was a certain item that got published that would maybe throw everything else off we could then throw the error in that situation something like tri compact map is definitely less common in apps it's there it's good to know that we can do that but i would say when you're developing i don't go out of my way to use some of these it's more like if i come to a situation where the standard publishers are not working and we need to do a little bit of extra manipulation on the pipeline then we can dive into some of these special unique functions uh there's a couple more you guys probably already know let's put our regular mapping back in just to get our our values publishing we can also dot filter and filters uh pretty self-explanatory and we can add some logic in our filter maybe um maybe we're gonna do filter where the value is greater than three uh and maybe the value is less than seven and now we can get publishes and of course we're gonna only we're gonna filter for the values between three and seven i'm not going to dive deep into filtering filters obviously super important for whatever logic that you have in your app and just like everything else we can always dot try filter where we can then throw an error if we run into one but we're not going to go through that we can call dot remove duplicate and this is a really useful one so if two values get published back to back that are the exact same value then we can remove one of those because it's a duplicate so this is really so this comes in handy if you have a really complex app where a bunch of different things might be forcing publishers into this pipeline you might have situations you probably will have situations where maybe two things cause publishers to emit but the value that's getting published is the exact same so if it's the exact same value and it's gonna update and we know that would then end up updating our screen in the exact same way we then don't want to reload the screen twice so we could remove so then we can remove the duplicate publish and we'll just add a little bit more efficiency into our app so as an example i just want to show you guys let's do the number four and then we'll do another publish of the number four and if we didn't have a remove duplicates it would publish both but right now obviously it's only going to publish uh one of those fours and then keep moving forward additionally though if we put the four over here so it was not back to back we're gonna see we're gonna get this first four i'm gonna skip this one we're gonna have five and then we're gonna get this next four because the remove duplicates it has to be back to back publishes and if it's not back-to-back publishes then it doesn't consider it a duplicate and this is actually a good thing because if it's not the same back to back then something changed in your app and if it goes back to four maybe something changed back in your app whereas here maybe it was actually just the same exact state in your app that got published twice so we can see four gets published two separate times here because it's not back to back i'm gonna undo some of this i'm gonna leave that for one second come down here we can also we can also remove duplicates by so here we get the first publish we'll call it in one the second publish in two and the typical return here would be int one is equal to int two so this is going to be the exact same situation that we have the regular remove duplicates the difference here which we can't really tell in our example i'm not going to do it but i want you guys to know that you can and finally of course we can try to remove the duplicates and if there's an error and we can throw an error if we want to but again i am not going to do that some of these are really not i don't think i've ever actually used that in a in an app at least not yet all right so for the next one i want to show you that we can also uh do some replace function so we can replace nil so if a value that gets published is nil we can then replace it with this specified value so here i can put in whatever type we want to then publish instead of nil and we're going to get an error because the password publisher right now is just pushing out integers but if this was maybe but if this was maybe an optional integer and we have a potential for publishing nil so let's put our array back to one two three four five six seven eight nine ten and maybe for the number two we want to publish nil instead and let's make this of course an array of optional integers and when we come back down to our app now we can replace the nil values with specific values so and don't forget that we can stack a bunch of these so if maybe we had some compact mapping maybe we had some other mapping and filtering uh and maybe some other sequence operations and at some point in the pipeline maybe a value converted to nil or maybe we just published a value as nil like we did right now we can replace any nil value with specified uh objects so this could come in handy if maybe instead of displaying nothing you wanted to give it maybe like a default value on the screen so maybe you were displaying some sort of message to the user and you wanted to download that from your server but if the value that got returned from the server was nil so you didn't actually get the message to display maybe you want to replace the nil with uh some sort of default message that would be a good situation for this let's move this back to a regular integer just wanted to show you guys that let's put this as an integer as well uh similarly we can also replace empty now it doesn't make much sense right now we're never going to publish an empty value but this comes in handy if we were dealing with a raise if if we were publishing a raise and maybe one of those arrays was an empty array we could then replace that with some default values so oftentimes you'll see like an array here or reference to another array that's stored somewhere so give it a default value if we get empty data back and of course we can also dot replace error now it says integer right now but let's actually force an error in our pipeline quickly so back up here we have the try map so let's uncomment that so if the integer is equal to 5 we're going to throw the error otherwise we're going to convert it to a string so if i run that quickly and we don't need the map at the bottom here if i run that quickly we should go zero we should go one two three four error beautiful and what we can do is call dot replace error so if somewhere in the pipeline we're getting an error we can then replace it with the default value so whatever you want to replace your errors with and this might be a better user experience rather than showing an error on the screen you give it a default value but i do want to point out once we hit that error it does stop the publisher still so we still hit the error here and then 6 7 8 9 10 never got published all right coming back up here i'm going to comment this out again let's get rid of our replace error and let's put our mapping function back in down here all right guys we can also scan of course and scanning when we scan we'll add an initial result the starting value is going to be zero and then we can click enter on here and this first t is going to be the existing value that was being scanned for and this will be the new value that got published and here we can return the existing value plus the new value all right and the scanning value is really unique here we can see the values that got published i'm going to hold the option button and click on scan and we're going to read a little bit about it use scan to accumulate all previously published values to a single value and then publish that new value so here we are scanning and we're here we are scanning all previously published values so at the very beginning of our publisher we're going to start at the very beginning of our publisher we don't have a previous value right so what we gave it was a starting previous value of zero and we could have started at maybe let's say the number five so the first thing that gets published is going to start at number five plus that first new value which is one and that's why we have six here but generally scans will start at zero and then they will accumulate so at the first publish first publish is published number one so the existing value is zero and then the new value is one so we're going to return existing value zero plus one and that's why we get one then on the second publish we're publishing the number two the existing value is the value that's being accumulated before that so 1 plus the new value 2 which is actually 3 and then same thing when we get to the when we get to the next publish existing value is three and the new value that's getting published is also number three so we go to six the next one existing value is six new value is four and we get ten so on and so forth this is definitely less common but this is really really powerful stuff guys i mean we can get some really cool things especially if we are doing maybe some kind of algorithms with complex numbers and things like that we can get some sort of almost like an exponential number system going on here and just to show you there's another the quicker way of writing this because it's a little it looks a little different we can also just scan zero open the brackets and we can do money sign zero plus money sign one so same concept this is the exact same code as this uh but a little shorter maybe a little harder to read but but it does look a little more professional this might be something you put in your code where junior developers will scratch their head but but you being the expert that you are knows exactly what it's doing and there's even a shorter way to write this which is dot scan zero and because we're just going to add the first and then the second we can just put a plus sign so look at that literally like this is like what 10 letters this is like 10 characters and we have this magic happening in our code we can also try scan if we want to possibly throw an error but we're not going to do that and just like scanning we can also reduce so this works the exact same way as the scans we'll start with zero and then here we can do the existing value uh versus the new value and we can maybe add these together so we'll return existing value plus new value and it looks the same as our scan except it's not going to publish until we are done because the reduce is reducing all of these publishers into one single ending value so here instead of aggregating and then publishing at each publish we are aggregating across the lifetime of the publisher and once the publisher is done then we get that completion we're going to reduce all of those published values into one final value so the number 55 we're getting here is the aggregate of one plus two plus three plus four plus five six seven eight nine ten and we add all those publishers up together to reduce it into one single value and that becomes in handy uh a lot of times when we're working with integers or if we're working with some sort of duplicates that maybe remove duplicates doesn't work with we can reduce those into a single value just like the scan we can also just call dot reduce zero plus and it will work the same exact way alternatively we can dot collect this is going to collect all of the publishes and then publish them all at once so here uh let's call let's so here let's actually let's map them to strings and then collect them and the value that we're going to get is an array because once we collect each publish it's going to collect them and then you can prove it here if we try to map we can see that the value that's going to come through is an array of strings so although we are publishing individual strings we're going to collect all of them into one single array and then publish that so here we can set self dot data self dot data equals uh the return value i'll comment this out for one second so we're publishing the same thing and our first publisher is going to publish individually 10 times but this publisher is just going to collect all of those and then once it's done publish them all on the screen and this could come in handy if maybe you were doing a lot of like loading type stuff and you don't want to actually display it to the user until the publisher is done until you collected all the data from maybe different data sources or different apis collect it all and then we can publish it at once and it'll be a really nice user experience because instead of having things load as they come in we could have it all load once it's all there let's comment that out similarly we can also dot collect with an amount with a count and i can put in the number three here and now it's going to collect in batches of three so once it hits three publishes it'll then produce the value and it's changing because we're setting the data equal to the return value if we did data dot uh maybe append the contents of the return value we can see that they will all stay on the screen so sets of three we're collecting and then publishing in sets of three and this could come in handy if uh maybe you had a lot of stuff coming in a lot of different api calls but but maybe you don't want to actually update the screen and change things unless you get collect unless you collect maybe three new values so if you think about if you think about like an instagram feed now they have a button where you can like refresh to get new posts but if they wanted to automatically do it they could maybe have some sort of collect on a publisher that says once there is five new posts then go ahead and reload the screen or something like that moving on here i'm going to comment out this let's move this mapping back down to the bottom we can also dot all satisfy so here i'm going to return all that satisfy where money sign zero is maybe equal to five and we gotta put our original append back in here i'll comment this out because we are again back to publishing single values so here uh we are checking if and with all satisfy we are checking if all of the items that get published satisfy this criteria so here we know we do have an item that satisfies it with the number five but all of the publishers one two three four are not fitting that criteria right because they don't equal 5. so the value we're getting here is going to be false but if let's do maybe if all satisfy maybe all are less than the number 50 then once we come once we hit that completion and we check we should then have true because we know everything they got published in our subscriber right now satisfies the condition that is less than 50. so we get the number true here so this one i would say is probably less relevant for actually downloading the raw data so maybe more common if you have child subscribers maybe if you could check if a bunch of different values all satisfy a certain criteria maybe you show a certain button on the screen something like that so the the value gets produced is true but then we can use that value the true to actually do something a little more complicated and similarly we can uh dot try all satisfy same concept except we can throw an error if we need to all right that's enough for this section so i'm going to again asterix forward slash and up here let's put in a little uh forward slash asterix and then command option left arrow to fold all this up nice and neat and now let's look at some timing operations uh the first one we're gonna do uh you guys have probably already done before because i did it in my crypto course uh it is called the bounce this is super common for when working with text fields i'll tell you why in a second we can debounce we give it a specified amount of time so for one for right now let's just put one for one second the scheduler is basically which thread we want to publish these values on which we will do the dispatch queue dot main thread definitely the most common and then we don't need any options right now the d-bound is watching all of the publishers that get that come through the publisher and it's only going to publish a value through here if there's at least one second between each of the publishes so right now we don't see anything because we don't have a one second between each of our publishes but let's come up here for a second and i'm gonna actually just comment out our our regular for statement uh just for a minute and what we're gonna do is put in a different dis a custom dispatch queue just to simulate this let's do this one after let's do this after zero seconds and we'll call self.passthroughpublisher.send send the number one here i'm just going to copy this paste it twice this will be after 0.5 seconds and this will then be after 1.5 seconds and here we'll send the number two and here also the number three so when we run this now uh there will be zeros at zero seconds we're going to send the number one at 0.5 seconds we'll send two and at 1.5 seconds we'll send three so the difference between these two publishes is going to be 0.5 seconds and the difference between these two publishes is going to be one full second so if i come down here and i make my debounce let's make it 0.75 seconds we're gonna notice here that the number one never gets published i'm gonna press play and we'll notice here the number one never gets published that's because we are debouncing for 0.75 seconds so we get this publish and that's going through the pipeline but then that publisher is it's going to wait for 0.75 seconds to see if there's another publish because this way if there's another publish we're going to use that one instead of the first one so within 0.5 within 0.75 seconds we actually get this publish which happens within which happens 0.5 seconds after so because of that the system is basically thinking oh we got this published instead and let's use this and ignore that first one because the two of these happen within 0.75 seconds of each other and we're going to use the last item that got published in between that time frame now this might sound a little anti-pattern like you might be thinking when you're going to use this but this is really common for text fields is if a user is typing in a text field every time they type a letter it's probably going to publish a value and you can imagine if users are typing really quickly if you type in 10 letters quickly back to back to back you might then run your entire pipeline 10 times for each value so instead what you could do is add a little debounce maybe 0.5 seconds or something like that to wait basically for the user to stop typing so that we wait so that the publishes stop for a second and then we will do the rest of our functions and that could save us a lot especially if the rest of those functions were maybe making api calls or doing maybe some heavy lifting in our app so here we have these two publishers back to back but we're de-bouncing so we only get the second one and then the difference between this one and this one is one full second which is more than our d-bounce so both of these would then publish all right i'm gonna comment this out and put our for loop back in just to get back to our starting state it's coming out the d-bounce next we can add a dot delay and we can delay first delay for two seconds and we don't need a tolerance the scheduler let's do dispatch main again we also don't need options now we can delay for two seconds when we receive a publish so so these still look like they're happening one second to each other but they're all actually delayed by two seconds so let's try that one more time and if i click play here you'll see that it's one second two second and then all these start publishing so even this number three it actually got published two seconds it actually got to this point in our pipeline two seconds before we saw it on the screen because then we delayed for two seconds and then put it out and the delay could come in handy if maybe this loaded immediately and you wanted to add like a loading indicator for your content you could add maybe a little delay or something like that and then publish all your values let's comment that out next let's call dot measure interval this is going to be a weird one we're going to do this on the main thread again and we don't need any options and let's just see what gets published through here and if i call dot map here we can see that the value that's coming through the publisher after this measure interval is of type dispatch queue scheduler schedulertimetype.stride so let's actually just transform that we'll say here's the stride and we want to convert it to a string to put on the screen so we're going to say here return and let's convert our stride dot time interval and get rid of this map for a second and it's giving us in a nanosecond so we can go ahead and convert these to actual seconds if we want this but this is giving us the exact time between each of our publishes so nanoseconds here we have like what is this one two three one two three we have one trillion nanoseconds which i think is pretty much one second so we can see that the actual publishers are just slightly off we know these loops are happening about every second because it's now plus whatever that integer is in seconds but there is some nanoseconds between each of them because we're running through that for loop so it's not exactly one second but these if we rounded these to one second i think they would all turn out to be exactly one second unless maybe down here it looks like maybe the simulator had a little bit of a lag but we can use this measure interval i think primarily for debugging purposes but we can see the time between each of the publishers in our pipeline let's comment that back out and i'm going to let's also comment out this map here put back in our regular string mapping and the next thing we're going to do is dot throttle which is one of my favorites i'm gonna throttle for maybe let's do two seconds let's do it on the dispatch q dot main again and then latest we will do true for now sorry let's actually do number 10 for a second so let's throttle so we're gonna open and close the throttle so we can think about this as opening and closing throttle and we can open and close it every 10 seconds so after we get our first publish we are going to throttle and we're not going to open that throttle back up for 10 seconds and then at the end of those 10 seconds whatever is published between in those 10 seconds we are going to either publish the latest value which is true or the first value so i'll do false here and after those 10 seconds it's then going to publish the number 2 which was the first value so we can open and close the throttle here if maybe we don't want it to just constantly publish as soon as we get values but maybe we want to just you know every five seconds every 10 seconds we'll open the throttle and we'll we'll let whatever new values came in within the last five seconds the last 30 seconds and then we'll publish them at once and this is a good way to kind of batch your publishers if you need to so here we have 10 but we could also do something shorter maybe like five so every five seconds we'll get a publish and let's do the latest as true and then let's check this out so we're gonna get that first publish and then we're gonna close the throttle and wait five seconds and then we'll see whatever is published in those five seconds we'll get the latest which is number six we're going to wait five more seconds for the next publish and then number 10 is the latest there so now we are not so much worried about every single publish but if there were new publishes we can open up that throttle every minute and publish them and i think um you know at this low level 5-10 seconds is is not super is not super common but i've seen people do this even for like maybe minutes if you're on the screen maybe you don't want to reload all the data immediately but maybe if the user's been on the screen for like two whole minutes or something uh maybe it's time to give them a reload or something like that and then you can open up throttle push out all those publishes and then close it back up for another 30 seconds or whatever you do all right couple quick more things here we can also call dot retry which i think i also did on the um in the crypto course which we can't really test right now but basically if this was like an api call and we probably wouldn't do it on subscriber we would probably do it like directly on the the publisher that's fetching our data but if that publisher was coming back with an error we could retry it so instead of just going straight to the completion and then putting the error on the screen we could say let's actually retry it let's try maybe to redownload the data and we'll try that three times so if we try three times and we get the error all three times then let's go to the completion publish out an error but otherwise if we get the error one time let's try to make let's retry and make that request one more time to see maybe if it comes back with a successful result and lastly on the same note here we have a timeout which we give it an amount of time to wait so we can wait five seconds for a value and we don't need any of these options you can come out the retry let's do a timeout maybe 0.75 so we know these are actually publishing every one second and we can see here that if we don't get a publisher within these 0.75 seconds we're going to consider this publisher to basically fail and we will stop listening and if i hold the option one and click on timeout we can see that it terminates publishing if we exceed that time interval so so if the time between the publishes is more than 7.75 seconds then we will basically terminate that publisher we know our publishers are every one second so of course we are terminating right here almost immediately this really becomes in handy again if you are doing like external api calls maybe you're trying to download somewhere on the internet and that download is taking a really long time well you don't want the app to just wait forever for the fetch request to return instead you would put this as maybe a lot of people do like five or ten seconds so if the api goes out and it doesn't return in five ten seconds let's just time out and not continue waiting forever all right that was a quick little section uh i'm going to uh i'm going to again do a multi-line comment let's do the asterix and the forward slash in here let's do the forward slash asterisks and then let's command option left arrow to fold this up all right so you guys are becoming combine masters we did a sequence operations mathematic operations filter operations timing operations now i just want to wrap up this section of the course with with a little bit on how we can deal with multiple publishers slash subscribers all right and the first thing we're going to do which i also use in the crypto cores a bunch of times one of my favorite things that we can do with combine is combine latest and here we combine the latest publish from multiple publishers now in our example before we do that we need to update our example because uh our example right now we only have one publisher up here but let's so let's create another pass-through publisher just so we can play around with a little bit of data so i'm gonna create another one let's call this let's make this let let's call this maybe bull publisher i will set this equal to a pass through subject same thing except this one will publish booleans um and we'll give it a possible error even though we're not going to throw any errors in when we are running our code here i and let's see and when we are running our code down here i want to not only publish to the passthrough publisher let's also publish to the bull publisher so in here obviously we can only publish bools so in here we'll say if and then we'll add a little bit of logic so i'm going to say if x x is going to be our index we'll say if x is greater than four and x is less than eight maybe open the brackets and if that's true we'll self dot boolpublisher.send and we're gonna send a value with maybe true and then we'll say else self.publisher send false so every time we get a publish here we're also going to get a publish on the bull publisher which will either be true or false so if it's so if it's greater than four and it's less than eight so five six seven should publish as true everything else should publish as false so i'm gonna come down to our section here now i'm just gonna refresh our page just to make sure our standard publisher is still working so we get the one two three four five all the way to ten um but we're subscribing only to the passthrough publisher right now so let's also listen to that bull publisher so to do both of those we call dot combine latest and you can see here we can combine multiple publishers which we'll get to in a second but right now the only a publisher that we want to combine is the data service dot bull publisher so now this publisher pipeline is going to listen for the pass-through as well as the pool publisher and the values that we are then going to get after this it's not just going to be an integer but it's going to it's going to be the integer as well as the bool and that's why we're getting this error down here so let's comment out this map quickly and let's so what we're going to do here is maybe compact map and here we can transform you can see we're getting an integer as well as a boolean and we need to we can convert it transform it into some other type so in here this will be my int i'll do lowercase and then bool and maybe we want some logic in here to determine how we want to handle both of these publishes so we could do some logic to see if maybe for this example maybe we only want to publish the in value if the latest publish from our pool publisher is true maybe we're like manually toggling it or throttling it on or off so we'll say here if bool so if bull is true let's return a string of the integer and then we can say else so down here if we don't return there we can just return nil so now the only values that publish should be the ones where the boolean is actually true which is our six seven eight and here's a perfect example of when we might want to remove duplicates because we can see here that we can see here that the number seven is coming through twice and you might be wondering why is it coming through twice obviously we're only publishing the number seven once if we look back up here we have to remember now that this publisher and this publisher are separate so when we publish values here it's going to emit and go through the whole pipeline when we publish values here it's going to emit and go through the whole pipeline as well so although this is in the same for loop here this publish and this publish are going to be separate they're going to be two separate instances maybe they're back to back and they're maybe less than a second between each other but they are two separate publishes so when we get down to our code here we're listening for the publishers here and we're listening for the publishers here so anytime either of these get published we're going to come into the rest of our flow so on each of those loops we're actually going to publish twice and an idea here might be to call dot remove duplicates like we learned a little while ago so here same kind of concept except we're moving if the back to back publishes are the exact same and our output looks much cleaner and i did just notice that the um the number nine is coming through and i don't want you guys get confused this x is less than eight we're referring to the index not the actual value because we're starting here at number one which has index of zero and that's why we see nine here so don't get confused by that but um coming back down here uh this compact map just to remind you guys there's a quicker way to write that so we could always just compact map open the brackets and so this would be the money sign zero and this would be the money sign ones so here we could say a ternary operator we could do money sign one question mark so if bool then we return string with the money sign zero otherwise nil and i will comment out this compact map here and we'll get the same thing with a little bit shorter code just get you guys more comfortable with these shorthand writing because once you get into these heavy combine once you get into these big combine flows um it's definitely important to keep all this nice neat organized highly readable i also want to point out here before we finish up that when we use combined latest we are not going to enter the rest of this flow until we actually get a value from both of these so if we are only getting these publishes and we're not getting these publishes then we're not going to run the rest of this code yet we need at least a starting publish from both of these and an easy way to manage that if you don't want to deal with it is to start your publishers as current value subjects or as are regular at published because those will have a starting publish and that will emit as soon as you initialize the value but if you want to delay that until you maybe download and get some data and actually send that through we could use a pass through subject like we're doing here and and let's do another example here i'm going to comment out the remove duplicates or let's just delete the remove duplicates for a second uh and the compact map again uh right now we're returning the number right now we're turning the number only if the bull is true and if it's not true we don't return anything but for example sake so we can get a publish every time that this comes through let's actually just do otherwise and return maybe n a just so that we do get some publishes coming through every time something emits and then we can see that some of them are the numbers but most of them are going to be n a right now and let's come back up to our data service here and let's create another uh publisher we'll say let's call this int publisher set it equal to a pass through subject with int error just like our first one except this publisher we're only going to publish when we are between the four and the eight so in here i'm going to say self dot int publisher dot send and let's just send maybe the number 999 doesn't matter but i want to point out here that this in publisher when we are on number zero and number one and number two this in publisher has not yet published anything whereas this has published on every single loop this publishes on every single loop but the int publisher is only getting published on the numbers five on index five six seven so coming down here let's subscribe to the bull publisher and we can also subscribe to the data service dot ins publisher i'm gonna comment out this compact map and we're gonna have to redo write one more compact map here now you can see we're getting three values through here so i'm just gonna call this let's call it int one from our regular publisher let's call this bool and let's call this int two so now we're listening to all three of these so if all three publish separately we're gonna get this is going to publish three separate times so in here let's do basically the same thing that we were just doing here so we could do if bull and if it's a bull let's return a string with into one and then uh else let's just return n a and what i want to show you guys is and what i want to point out here is that when we press play i'm gonna wait one two three four five and then we're gonna get those publishes obviously it's publishing three times now because each of these have separate publishes but the big thing to point out here is that the nas are only coming through at the end of this and that's because this code is not going to execute until we have at least one publish from all three of these publishers so this publisher obviously is publishing from the get go and so is this one on every single loop but the int publisher when we hit it up here the in publisher only publishes on the fifth index so because of that we actually aren't going to run any of this code until we get to that fifth index and we have a actual value for all three of these so it's just something to keep in mind when we're using multiple publishers is that we do need to wait basically for publish from all of these all right so now i'm going to comment this out as well and let's comment out the combined latest as well let's put our map back in and the next thing we're going to do is dot merge so we can merge a publisher with another publisher now the thing here is that both publishers need to emit the same values so our first pass-through publisher we know is passing is publishing integers and so is our new int publisher so here i'm going to merge it with the int publisher and we can then see what we're getting out of here and we are just merging both of those publishers into the same pipeline so this is great if you have maybe different classes that have different the same type of data but maybe it's coming from different sources or something and you just want to merge them all together combine them all together and then just you know put that onto the screen here so we can see the the 999 coming from the int publisher that we set up and the rest of the values coming from our original publisher and we can also let's comment that out we can also dot zip this is a lesser known command here and we can use multiple publishers here but one thing that we need to realize that this combines elements from two publishers and delivers them as tuples and let's do a quick zip here so i'm going to press enter on the publisher here and we're going to add in our bool publisher so if we look back up at the top here we know that on every loop we're going to publish the integer on every loop we're also going to publish either true or false so there should be at the end of this there should be 10 integers publishing here and 10 booleans publishing through the pool publisher so here we want to zip those two publishers together so instead of compact mapping and turning them to another type we're basically just going to zip them together to create one publish one published value that is a tuple so here we could call dot map and let's do the transform and you can see there's an extra parentheses inside inside here that's because this is coming through as a tuple now so we'll call this i'll call this my tuple and here we can do some mapping so i can return and the tuple will then have a value with zero as being the integer that came through and then one being the boolean if you look at self we can see the two values here so this is one publish that has an integer and a boolean so it's like if you were creating a custom data type and that data type might have maybe like a an integer and then a boolean inside of it this is the same thing so we have a tuple that has an integer and a boolean and this is different than the compact map here here this integer this boolean and this integer are separate publishes and then we're going to map them convert them to some type to to output here we are zipping the different publishes together so that only one value gets published and it is a tuple and that one value then contains an integer and then a boolean so here we could do money sign dot uh one is the integer and we can just convert that to a string like like so and we can get our code back on here we can actually do maybe plus the tuple tuple.1 dot descriptions the description of the boolean so we can see here that we are publishing uh one two three four five so now we learn how to zip and let's do one more zip because we could do multiple publishers let's also zip it with our data service dot int publisher and then here after the description i'll add a string for maybe the tuple dot 2 which should be that final integer and i wanted to point out here that when we zip we need a i want to point out here that when we zip we need the same amount of publishes from each of these publishers so we are creating tuples but we can only create a tuple three times in this scenario because the int publisher is only publishing three times so we can't create this tuple unless we have a value from all three publishers and this is really important so it's really important here when you're zipping to make sure that you have basically a one-to-one ratio of each of these publishers so if the first one is publishing 10 times and the second one is publishing 10 times probably want the third one to publish 10 times unless of course you want the outcome to be the minimum published amount of all three of them all right let's put this map back in here and the next one we're going to do is dot catch and we can see here that here we can catch errors and return a publisher so what this is is if we get errors we can actually refer and return values from a different publisher so if one publisher is failing we can have a backup publisher or maybe another publisher somewhere in our code so in here let's press enter and this will be the error now of course let's now for example say let's actually force an error so here i'm going to try map and this will be an integer and we're actually now going to use the int because we're in a try trimap we can throw and let's just throw a url error that bad server response so all 10 publishers right now will just be forced into publishing a url error and then we can catch and here as it said is we can return a publisher so here we can return self dot and i'm going to return the data service dot int publisher so if we get an error from our first publisher we can actually catch and then use this next publisher for the rest of our data flow here so i'm going to press play and on that first publish and on those first publishers we're going to catch and throw errors and then once the in publisher is ready and starts publishing we're going to be using the int publisher instead of our original publisher so we are catching those errors and just to give another example here we'll say maybe if let's add back in our integer if int is equal to five uh we'll throw and then otherwise we'll just uh otherwise it will just return the end so we get our regular publishes and then once it throws an error instead of throwing the error let's switch and pop into our int publisher now again once we throw that error we are completing the flow for that publisher so we're not going to get those final publishes at the end because we've now switched to the int publisher all right guys a lot to cover here but we are almost done uh do not worry i hope you guys are following and understanding what's going on because this is definitely a pretty complicated topic uh let's wrap this up here this section this little bit of code at least i'll use the multi-line comment with the forward slash asterisks and then a asterix forward slash at the bottom and then up here let's option command left arrow to fold up our code um we have a little bit more to cover we are not done here even though we're folding up the code uh for this next one i'm gonna delete this bit of code here um i wanna just let's like clean this up a little bit so i'm gonna leave the so this publisher i'm gonna copy it i'm gonna comment out this first one and let's just move it down here so it's all together in one piece of code we're gonna ignore all the stuff we have in here for now so this is all the only code that we are subscribing to right now uh and this is great when we have just one place where we want to receive this value but what if we wanted to share this value and maybe update different different view models or update different screens or maybe just different arrays and maybe we want the values to come out of this publish maybe it's the same publish but we want to map those arrays differently so that maybe one one subscriber is turning these into strings another one's maybe turning them into booleans who knows so what we're going to do is on the screen let's first create another uh let's call this a data bools and this will be an array of boolean and let's copy this and then add it here so i have two separate subscribers and in this one we are just going to do some mapping to convert it into booleans so in here i'm just going to say if money sign zero is greater than let's do greater than five let's map it to true otherwise false and we are going to then get the returned value which will be a bool so we'll call our databools.append that return value and finally on the screen let's do a different for each loop so we have our v stack let's put the v stack inside maybe an h stack and let's copy this v stack and then do another one so it's on the right of it we don't need the error here and let's do for the data bools and we'll put in the bull dot description so now as we get these publishes we have two separate totally separate subscribers so this subscriber is totally separate from this one so they're listening to the same publisher it's the same passthrough publisher but all of this this subscriber flow is totally separate from this one even though it's dealing with the same original publisher and this is cool that we can do this and subscribe multiple times but this might not be as efficient or even as elegant as really just what we want to do is only listen and subscribe to this publisher once but we want to then share that published value to two different locations so instead of two separate subscribers we want one subscriber that's subscribing to the publisher and then basically we fork from there with the values and we can send those in different directions so what i'm going to do is actually up here let's create let's say let shared publisher and we'll set it equal to the dataservice.passthrough publisher and we could even do some kind of manipulation on this pass-through publisher if we want so for example maybe we want to uh the drop the first three items so one two three don't get published maybe so we can do manipulation on this data before we share it and and then instead of subscribing directly to the publisher itself we can subscribe to this shared publisher on both of these flows here so we dropped the first three and then we share this publisher just two separate subscribers but these subscribers are subscribing to this publisher not to the one that's actually in our data service so this way we can do a little bit of manipulation first and then share the outcome but the one caveat here is obviously this works and we can't really tell here but what we need to actually do is call dot share here and here we can share the output of an upstream publisher to multiple subscribers so so it's pretty much the same thing except now we can confirm that this we are really sharing this publisher so whatever output is coming through here we are going to this and then we're going to this and now we get our our data on the screen and we are sharing a publisher so this is often what you want to do if you have multiple routes where if you're listening to a single publisher and where if you have maybe a view model that's listening to a single publisher and then you want to uh take those publishers and then do different things and not just receive one value but change it up maybe maybe one affects your loading indicator maybe one affects your data source or something like that we can share it all right and a final thing that i want to go over here which i am definitely not an expert on this subject with sharing and multi-casting i want to show you guys what i know about i think there's probably much more to multicast than i've ever dived into but i know multicast is often used along with the share function when we use a multicast we basically can create another publisher another pass through subject publisher that can hold a reference to the publishers coming through this publisher so for example i can do a multicast and i will create a subject here we'll create another pass-through subject with an integer and an error same type that we are publishing through and then when we're running our simulator here we can see that nothing is publishing you're probably wondering why and that's because when we add multicast it makes this publisher a connectable publisher so all the publishers that we've been using that are normal that we normally use are auto connected so an auto connected publisher will automatically connect and automatically start publishing the data so all your subscribers will automatically get all of those publishes but when we do a multicast we can basically i don't know if it's exactly what it's doing but we can basically store the publish in another publisher here and then we can determine programmatically when we want to actually connect to this publisher so coming down here i'm going to add maybe a dispatch queue dot main the async after let's do it after now plus five seconds and after five seconds let's reference the shared publisher let's call dot connect so we're going to connect to the publisher after five seconds and now we need to store this as well in our cancelables i think we have to store it because it might be storing this new publisher here i'm not a hundred percent sure but now if we run our now if we run our uh code here so we're not gonna be connected to the publisher for the first five seconds and after five seconds we'll connect into that publisher and let's remove some of this uh logic here so the uh the drop first we don't really need that anymore i'm just gonna comment that out and let's now press play on the simulator so so we're subscribed but it's not connected yet and then after five seconds it's going to connect and then we can see the publishers start coming through so we can delay connecting to the publisher if we need to and this is pretty helpful when you have maybe multiple subscribers and you don't want some of them to start immediately you can programmatically connect into the publisher at a specific time so here we connected after we connected it published 7 8 9 and 10. another way to do this if we didn't want to add in the password publisher here we could at the top of our file we could set up maybe another let let's call it multicast publisher equals the pass through subject and we can then come down here and do multicast i'll comment that out actually so that we have the reference to it and we can do multicast with a subject and we can pass on our multicast publisher so same thing now and we are connecting after a delay to our publisher flow from what i've read it should be used along with a share because this is obviously when you're using multiple subscribers and maybe you want to delay one of those subscribers or a couple of those subscribers and then connect later but i am definitely not an expert on the share in multicast and i'm not a hundred percent sure on the the full difference between the two i'm sure there's much more that i don't know about but uh this is probably enough for you guys to get going in your apps this is a relatively rare case because you got to be doing some really complex stuff with combine to actually need this um and if you ever get confused with doing a multicast you can always kind of just share and then subscribe multiple times and add delays and there's a lot of other quirky things that you guys can figure out all right guys so that was my little section on advanced combine pipelines uh there is more believe it or not to combine that i have not covered uh i'm probably not going to cover because i think this is more than probably uh enough you could definitely build plenty of apps without having to learn anything more but if you want to dive deeper into combine i do have a reference here that i want to share with you guys um it is heck j dot github dot io backslash swift ui dash notes and uh if you just google like swift ui combine this will probably one of the first this will probably be one of the first results this is a phenomenal resource to learn and dive deeper into combine um a lot most of what i've covered uh is covered here as well and it's very structured i definitely read through all of this before uh creating any of these videos but they have some really nice like images and diagrams to really explain some of these publishers and subscriber flows and there's this awesome table of contents where we can just jump into some different things so there are more such as like futures which i'll cover in the next video we have uh record deferred which i have not really covered but there is more to combine so if you want to dive deeper this is a great resource for you guys to check out so with that said you guys are now combine experts if anyone asks how to use combine i hope that you have an answer because uh we have created some really really cool flows here we've done a sequence mathematic filter timing operations and then we dealt with multiple publishers and subscribers which pretty much should cover the vast majority of situations in your app and if you have a situation that maybe wasn't covered here uh i mean you can leave a comment below but chances are maybe you're thinking about the problem the wrong way maybe not the way that we should do it in combine anyway thank you guys for watching i would love to hear your comments below whether or not maybe this was challenging or too hard maybe this was super helpful i don't really know but i do know that going into some more advanced apps now we can start using some pretty complex publisher flows and when we start coding like this we can get some really nice elegant efficient code which is going to be awesome thank you guys for watching as always i am nick this is swiftful thinking and i will see you in the next video [Music]
Info
Channel: Swiftful Thinking
Views: 1,304
Rating: undefined out of 5
Keywords: swiftui combine, combine swift, swift combine, swift combine publisher, swift combine subscriber, swift publishers, swiftui combine publishers, swiftui combine subscribers, how to use combine swift, how to use combine swiftui, how to use combine framework, what is combine swiftui
Id: RUZcs0SWqnI
Channel Id: undefined
Length: 113min 34sec (6814 seconds)
Published: Tue Nov 09 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.