 which is, you know, try to get used to use RX Swift, which is like something that was very close to my heart ever since I started doing programming. So just a little bit of background. I did four years at accounting at SMU. And, you know, after that, I just got bored of it, just getting sold mundane. So I switched to programming. So I did Android first and I've been a serious, I was a developer for like six months or so. So I think in terms of, you know, experience, I definitely like in a lot of areas, especially like iOS specific libraries and such. But I always try to be a tech generalist in a sense that what I'm about to present to you will be applicable across like whatever platform you use. So what I'm going to present to you today, so has anyone ever used like call data in your production app before? Yeah, do you think it sucks? Yeah, it sucks, right? So I think the thing about call data is I cannot understand why Apple being the stellar company it is produces such a sucky library. Because, you know, call data has a lot of shared state and in an application, if we want to avoid bugs, then we have to cut down the number of shared state that we, you know, have in our app. So with this library, we can have a totally stateless app. So all the state is centralized in one area. So, and it's very, it's used. So you may think that it's just a new cool thing. I may not, you know, I may not have time to pick it up, but we have used this successfully in our production app at HOMAS because we have a product called Glacolite, which helps you like log glucose and log food in order to help you control diabetes. So when I joined, you know, the app was very buggy, a lot of crashes. And then I spent the next five to six months refactoring using the new framework, reactive functional programming with RX Swift. And then nowadays, I think it's 99% crash-free. And unless you intentionally try to make it buggy, there's not gonna be bugs. So, for that, let me just run a sample project which may interest you. And, yeah, we should have opened this beforehand. So here I have a very simple app that, you know, I have a user object that I just wanna change some properties. So the thing about the Apple's classical MVC model is that you have a lot of cross-view communications. So with this library, so let's say I wanna change my name to this, yeah, this is my actual name, and this will be reflected in elsewhere. And there's no direct communication between these two views because in a reactive functional architecture, you only have one source of truth, which is your database. So every, all the views will listen to changes from the database. And then if you wanna make changes, then they will post, they will send an action that update database. So, and, you know, I have a bunch of goodies here. I mean, you can just change a bunch of a lot of these and just gonna update. So the principle behind this. So what I'm going to do right now is try to go through the building blocks and the patterns that I truly believe in. So the first thing I believe in immutability. So to be immutable, you are not able to change properties after you create an object. And how I achieve that is with a buildable pattern, so which is built on the builder pattern. So how it works is like this. So there's a clone builder method in order for you to clone an object and change the properties. So here I have a very handy playground like this. As you can see, let's say I have a class A. I mean, it can be anything. And then I have the builder, A builder. So I mean, everyone should be familiar with the builder pattern, right? Yeah, so how this works is that every builder, every buildable will have one builder and then every builder will have one buildable. So in the sense that, so I will just implement this method called with buildable and then it will set the properties for the A property in this builder object. So in the sense that, so let's say I create an A object, A dot builder dot with, then I set these properties and then I can expose a method called clone builder. So how clone builder is implemented is this. So when the buildable dot builder equals to the builder dot buildable, you can automatically implement it like self dot builder with buildable self. Then essentially this, off on size. Yeah, so essentially this will help you achieve immutability because like after you have set, after you have done the cloning, these two objects will be completely different. So it doesn't matter whether it's a class or struct, you can use a class, but with this pattern, there will be no reference to an object at all. So this is the pattern that I use the most across all my applications. And yeah, that is one useful thing. Another thing is the parallel object model. So if you use call data before, you would know that we are tied to the NSManage object. So in my personal opinion, using NSManage object creates a lot of problems in the sense that if the context you put that NSManage object in, somehow the reference to that context is lost, then all your NSManage object properties will be new. So what I do with this is that I would have a parallel object model. I would have a call data object which is like something that I communicate with the NSManage object context. And then I would convert that into a pure object representation. So that pure object can be a struct, but it's just that it doesn't have anything to do with call data at all. So I have a nice little chart here to show you the benefits of a pure object approach. And there's no inheritance required. It's immutable, it's stable in the sense that the properties, one set, they're never going to be new. And then it's visible in the sense that, so let's say you don't want to use call data in the future. Let's say you want to use RAM. With the pure object approach, you can actually substitute call data because the upper layers of the application, which is like your views and your view models, only know these pure objects. So that's why it's very easy for you to change sub-out call data. And it's immutable. So you are not worried that the state may change at the moment's notice. And to be honest, I don't expect to go very technical into all this because this is very, very complicated. So let's say I have a user object here, which is the thing that I was showing you that was changing in the app. So what I mean by the parallel object model is that I would have a CD object. This is a NSManageObject subclass. And I would have all these NSManage properties, like ID, name, age, visible, updated. And then I would have the user object, which is like the pure object with the exact same properties, but it's just that everything is a get and there's no set. And then for each user would have a user builder, would have a user builder, and then with all these building methods and then with clone builder as well. Then after that, so the library will know the library request framework will know how to handle these and convert between a core data object and a pure object. So I have a bunch of protocols here for the managed object to implement, but the only thing that you have to implement at a minimum is this one. It's the HMCD object master type. And then you would have to implement a few methods like these mutate with pure object. So this is the one where we actually mutate the core data object internally. Because you know that when we use core data, when you mutate the properties of the core data object, it will actually save to the to the main context. And so that's why I would advise everyone not to use the core data object directly because it's very misleading. It's not that safe, so at all. And yeah, let's just skip this part. So more importantly, yeah, that's just like the basics. So let's go to the main thing that I was talking about, the railway architecture. So this is the part we have to use more. So in a railway architecture would have two parallel structures. So let's say this is the railway, right? So this is one part and this is the other part. So let's say this is the success case and this is the failure case. So in a railway architecture, it surmises that the result of the previous request must become the input for the next request. So in the sense that, so let's say I have one request here and this one can either be a success or a failure, right? So if this is a failure and this will go to this stream, become here. But we must find a way to bridge the failure back to the success case. So this part is called an adapter. So we adapt it back. So if we manage to do this, then we have like one whole long stream of requests that is always successful. But it's just that the failure is captured internally. So the railway architecture is the inspiration of this comes from Fsharp because I was reading an article, they were talking about how to do reactive programming, I mean functional programming, not even reactive. So yeah, and then this architecture strikes me as particularly useful when you want to have a very clear and concise flow of logic. And then just one stream, whenever it errors out, it will go back to success stream and then it will propagate the error downstream until it reaches the subscriber. So with that in mind, let's go to, so as you can see here, I mean if you have used Rx Swift before, you would be familiar with things like observable, you know, observer behavior subject, all that. So in a railway architecture, this would be like let's say we have an API called sync local data, which is what we actually have in the Glacolet app. So we would have the fetch user from database. So this will be performed by the request framework. So the previous here is the result from any previous request. So if this is a failure, then the request will just fail. The failure will be propagated down all the way to the subscriber. So fetch user from DB and then you flat map it into a patch user remotely and then flat map into absurd user, flat map into fetch updated payment plans, flat map into absurd payment plans. So let's say here the fetch user from DB returns you an error. Let's say there's no user in a database. So let's say the error is like, how do you say no user in DB the error message. So in this method, in this method, the generator is supposed to unwrap that error and then re-throw it into the result of this request. So if from this steps to this step, the result of this step is also the same error, which is like user does not exist. And it continues down all the way here. This one also re-throws the same error. This one re-throws the same error. This one re-throws the same error. So that's why you only have one stream. If the upper part fails, then that error will be propagated all the way downstairs. So that's why you don't need any if, if this, if that, any try, catch. Everything is one stream, very clear logic. You can put like logging, you can put analytics, you can put beacons, and it all going to be in the same stream. So that's why it's very clear, very easy to understand and very straightforward. So, so let's go to the main meat of the presentation, which is the CoreData wrapper. As I have mentioned before, CoreData has many quirks and I don't like it, but it's undeniably useful in the sense that it has the fetch result controller so that you can actually stream objects from the database to your UI. So, but the mechanism in which it does it is very coupled. So I am very, I'm a big anti-fan of coupling and CoreData, once you, once you use it in a classical way, there's no way you can escape because everything has to be done the same, the way that if it is intended, like you must always have a singleton NSManagedObjectContext, you must use NSManagedObject, you know, you must do this, you must do that. But if you use this request framework, you will have to do none of that. And everything will be abstracted into a common interface called observable, which is like RX Swift. So, yeah, this is the request framework is something that I spend a lot of time writing, testing and is used in the production app very successfully. And the main benefit is just immutable, stateless. And, you know, there's try the end as well as the railway architecture model. So, like once, and this is like used, especially useful for MVVM architecture, which is like model, view, model, view. I mean, I don't understand why Apple still pushes for MVC. I mean, nowadays the apps are getting like very complicated and there's a lot of cross view communications. People are still using delegates, they're still using, you know, they're just having like several layers delegates communicating with each other. I mean, I mean, since it's 2018, what we use at Hormuz is we use a combination of Redux. So, we also have a Redux implementation in Swift. And then we use the request framework in order to perform, you know, so that the views will subscribe to the database stream. And then every time the user object changes or anything you wanna stream, it will reflect on the view itself. So, that's why there's absolutely no potential for bugs. Unless, I mean, RX Swift is buggy itself, which I believe is not. So, the request processor, this one, you know, this took me a lot of time to write, but I'm not going into the technical details here. So, it can help you do, you know, save, upset, delete, reset and stream. And then, yeah, so, I also have a wrapper for the fetch result controller and then it will stream pure objects instead of called data objects. So, that's why this feature enables two-way communications between the UI and database and then it also support pagination. So, in a sense, let's say, because in a Glyclyp app, we have a lot of timelines. So, each timeline will, you know, correspond to, let's say, a comment, a foot log, a step log, or a glucose log. So, we, I mean, in a single client, you may have thousands of timelines in the same database. So, what this stream helps you do is that it will help you paginate based on your page count. Let's say you want like five items per page and then you just need to supply a trigger. So, let's say when you scroll up and then you want to see the next page, something like that. So, I think that's enough technical for today's presentation. I mean, if you have questions, you can just approach me and I will explain in more detail because it's not possible to cover everything technical about this library within 30 minutes. I would say even when I was working with my team, it took us a few months to get comfortable. But I mean, after you go that way, you're never gonna go back because you're so used to dealing with no state at all that you don't want to go back because you don't just don't want to handle state anymore. And then, you know, I think it's a good thing to pick up like functional programming. I mean, reactive functional programming is two parts, reactive and functional. And if we combine both of those, we can have a very stable app and no cross view communication immutable or that. So, I think this is the point where I should show more of a sample app because seeing is believing, right? And although the sample app may look like very simple to you, but it actually encompasses a lot of techniques. So let's say here, we have a user profile BC, which is this one. Let's say I want to insert a new user. And then I use, so the other day, I went for an interview and then they were asking me, so can you tell me the names of the life cycle of a view controller? I said, come on, what year is it? You only need one, which is view that load. There's nothing else you need, okay? You don't need view will appear, you don't need view will disappear, none of that. So in the view that load, what I have is I have a set up views here. In the set up views, I use the wonderful RX data sources library and then it will help you actually, just help you abstract away all the other data source delegate all that. So, and secondly, this is the most important part which is like bind view model. So this is where we actually perform the bindings. We perform the bindings to the view model because the view model will expose like public subject, like all behavior subject so that it can help you, it can create hot observables for you. So let's see, I have, this one's not too important. So the VM.username stream, let's go here, which is this one, this one, this name thing, correct. So this name is the VM.username stream. So when I change this, this will actually emits a new item and it's set name label dot rx dot text. Same similar for this and this and this is where the action happens. So insert user button dot rx dot tap. So when I tap it, I want to create a new, like just create a new random user and put it in a database. And I want to see that new user reflected immediately on the UI because I don't want to wait. So you see, so whenever I, so the entire code for that is just this, it's just this, there's nothing else. There's no communication between any of the views at all. You just need to do, just need to bind this here, bind some of this here. So your view controller code is extremely short. So it's like, you know, probably how many lines, 250 lines? And all, so because all the view state or the view logic would go to the view model. So, but this is not the most important part because what the view controller does is just to display the data. But let's say I want to edit this. So this one would go to the user text cell. So each of the cells here would have one view model. So the main thing that you need to get right about MVVM is that only models can create models and only view models can create view models because this way it will help you with dependency injection. Let's say you have a singleton that you create in UI application delegate. And then you create the master view model which is like your app navigation controller. And then it will keep passing along that singleton instance. So in the sense that you will not have ever have to use singleton in the main app again. So in the user text cell, what I have here, so this is the request framework in action. Okay, the text inputs, I mean just to make matters short, the text input stream is something that it will receive the input. So the text input stream, I think if you put a lot here. Okay, let me just give me a minute to build it. Okay, see, do you see this? So this one is simply a behavior subject that can accept strings. So map non-New or empty is just a custom method. So in order to take away the optional sign. So with latest from the DB user stream and I want everyone to focus especially on this DB user stream because this is what this is what is beautiful about this framework. So in terms of the, okay, so after that I do, I do, I update the value. So update user property, this is the method from the model. And then I filter it to see whether whether it's the same value or not. And after that, okay, let's go. This one, update trigger, update user on text triggered. This thing until changed in order to filter out like duplicate values. And then so this is the main thing. So update perform will be update triggered, flat map latest to update user in DB. And then also use redux here to post an error to display like globally. So, I mean this is just NVVM is nothing special. But what is most important is this method. So DB user stream, okay, where the hell is it? Okay, so the provider track object manager, a manager dot DB user stream. And what is this DB user stream is? Here, this is a track object manager and this is the approach that we use in the Glycolib app. So let's say we have a central user stream, which is simply just a behavior subject and wrap in a try user. And then when we set up, we initialize the user stream, right? So the DB request manager, this is the request framework. Stream DB events for user.self perform on user interactive dispatch QoS and transform the request to use descending sort for updated at flat map into just load the section, get the objects. So if the object is not available for an error or this and that. So after that, we bind to the DB user. And as you can see, if I put a log next here, I think log next prefix, I think it would be easier to see. Okay, so I mean I have a custom string convertible so that it just prints out the ID, right? So every time you update the user object, it will appear here. So with the new properties. So this is how we do reactive functional programming because all these views only listen to one stream, which is the DB user stream. So every time the user object changes its property, then it's reflected everywhere else. So it doesn't matter where you go. How many layers of views you have. You only have one stream and that is all. And that is like the only DB user stream and you don't have to do anything else. You don't have to worry about view will appear, view will disappear. I mean that is like so 2015 I guess. And then another thing about that is that if you see this one, this is the example of the railway architecture. So absurd in memory is the function from the request framework. And then I will map because here I'm using a try. A try is a monad that wraps the error for you because if you have used functional language like Haskell, then they will have maybe, they will have either. But try is more of a Scala concept. So with this, we can be absolutely certain that our Rx Swift streams will never throw errors because I think the biggest catch of a Rx Swift architecture is that sometimes when a stream throws an error and it will just die because on error is the terminal event. So with this try, all events are successes. It's just whether it's a try success or try failure. Okay, try success or try failure. So and lastly, so let's say this library will also help you do optimistic locking because we have tried to solve a lot of concurrency issues with immutable data. So with immutable data, you never have to worry about concurrency, but there's always the risk of the call data database having too many writes coming to it. So sometimes you would have the wrong version and then the user object or whatever object you want to update will become outdated. So this library will help you do optimistic locking in a sense that let's say, I think I mentioned before that the HMCD object master type is the minimum that you have to implement. So if you want more functionality, you would have to implement HMCD versionable master type and then let me comment out some of these and these methods will help you get the correct version in a sense. So the versioning in this case I use is updated at. So every time I update the user object in a database, I would create, I would have a new updated at date so that, you know, so when you use an old object to update the database, it will throw an error. So what I'm going to do is I'm going to artificially create a race condition. So in this case, as you can see here, I have the update user and DB method and then I put an observable timer. So let's say I put five seconds. Five seconds, I think there'll be enough, I guess. Five seconds, yeah. And then something very interesting is going to happen. So let's say when I do this, firstly it will wait five seconds before it update the database, right? But because we use with latest from, if we immediately after we update this, we update this and then this one will take an old object because it hasn't had time to update the user object in the database yet. And now we have intentionally created a race condition because this one will have to wait like five seconds. I mean, usually it's very fast, but it's just that it's for the sake of, you know, because we programmer always try to be perfect. So let's say I update this and then I update this. Okay, just wait five seconds. Okay, then the library will help you detect whether there was a version conflict and it will throw this error. So the existing version, which is like the old version, is, you know, at this time, I think around that. But the conflict version, the version that you use to update the database with is the older one. So that, and then you have not just throw error, you have a lot more strategies to perform optimistic locking. Okay, so this is the singleton type. And then let's say I want to version conflict strategy. So override, so it doesn't matter whether there's a version conflict mod, just override. Don't care. Take preferable to, let's say you want to take the latest version or the older version or to merge. So if you want to merge, then you have to provide a merging strategy. So I think one problem we faced with Leica Leap is that, you know, in the old app before I reflected, there was a lot of concurrent modification to the user object. And sometimes the user object will have like the photos array, there's a string, you know, it's not updated correctly. And then when you post it to the server, it actually, so the user selects a photo, but because you don't use like the correct strategy to update the server, then actually when you update, the user object that is updated in the server doesn't have any photo at all. So sometimes the user would complain that, why, I said a photo the last time, why is it not appearing, this and that. So with this strategy, let's say we don't care which version is the correct one. We just, let's say a merge strategy is like, always take the one with the non-nil photo, something like that. Yeah, so I mean, but I'm just gonna stick with error because that is the simplest case. I mean, you can totally just ignore it. And another thing that the request framework will help you do is that it has middle-west and this is a big, very big feature. I mean, I think if you have developed for backend or Node.js before and you use express.js, right, and you will be aware of like middle-west because middle-west will intercept all requests and then it will help you like transform the request into something that you like. So in this case, I have the request middle-ware here and I want to, let's say I want to add the number of retries. So every time it fails, it retries three times. So this is the clone builder in action, which retries this. And then if you want to add the version control strategy, you can also do that or you want to add logging. So you add logging of the request is also supported. So you see something like performing absurd description, absurd user in memory. Is this the effect of this middle-ware? And okay, so it may not be like immediately apparent in terms of the benefits. But let's say in a very typical app, we would have to have the authentication tokens posted to the server and then we have hundreds of requests. So you have like update user, update timeline, update weight, update foot. And then if you use the classical way, you will have to repeat the code to copy the headers like a hundred times. So, but with middle-ware, we just add it once here, just add it once, and then the headers will be automatically applied. So I think this is a very bad example. I don't know whether I should, oh, I should. I think you guys should delete that. Delete that part, yeah. But it's fine, it's fine, so. It's fine, okay, I'm just gonna copy this. Yeah, yeah, yeah. Okay, so this is the last one. So let's say the network request header, so this is the middle-ware, right? So because we have a lot of requirements in terms of localization, in terms of authentication token, the device ID, all that. So what we do is we get authentication token params, and then we just use a lot of RX methods to transform this. And then we add this middle-ware to the middle-ware manager. So on every network request, this header will be automatically applied to a request object, and then it gets posted to the server. So this is useful because all your requests are very bare minimum. So you only need to specify the endpoint. You only need to specify endpoint operation, get, post, all that. But all the base URL, all the headers, and everything will be taken care of for you with the middle-wares. So I guess that's it. This is a huge, huge effort. Spends so much time on this. And if you want to know more about reactive functional programming, you can also look at the reactive Redux implementation we have on our GitHub page. Homemask. Yeah. So sorry, yeah. So you can also look at our Redux implementation. So I think if you recall correctly, just now I displayed an error due to the version conflict on the screen. So everything with that error, there is a global stream just for errors so that you just post an action to the global Redux state, and then it just gets displayed by a centralized view controller. So I guess I think if nothing sticks, maybe just one thing to take away is that in a reactive functional, I think reactive functional programming is the future. And it will help us build scalable apps. It's better get started now because you know, better late than never. And I mean, if you have any question, I kind of forgot to put my contact details here so just gonna do that. I don't use Facebook. So I'm mostly active on Telegram. You can reach me at HiFarm or you can add my number. Yeah. No, I don't use Facebook. Oh shit, yeah. But anyway, yeah, I'll always be available to answer your question or visit my GitHub page, Protoman92 GitHub, yeah, this one. I have a lot of open source libraries for JavaScript, TypeScript, Java, Swift, all that. And this is a project that is very close to my heart because since I don't have a programming background, I wasn't bound by like OOP shackles. So I wasn't bound by convention. So RX Swift was one of the newer technologies that I pick up very early in my career. So that's why I can vouch that it's very good. If maybe it's just, it's not the shiny cool thing that you hear or start talking about anymore because there are actually people implementing it day to day, which is what we do. We rewrite the app completely, 100%. Just reuse the views and everything is MVVM and RX Swift. And the result has been very promising. So far, like close to a month of testing, no crash because I think because the reason why there's no crash because there's just no way for you to crash because there's no shared state. When you don't have shared state, you don't have cost view communications, then how do you crash? I mean, unless you intentionally put a fatal error, which I mean, most of us don't do in production, but sometimes we do in debug. So I guess, yeah, that's it. If you have questions, feel free to ask me. I'll be hanging around eating some pizzas if there's left. Very exciting topic. So the app has been around for like two years. Yeah, it was written in Swift. So I think the old app was written with like normal call data. So you would have context.fetch and sometimes you don't perform it on the correct thread. So it blocks the main thread and when you scroll very laggy or that. And then thread management in call data is a big topic. Yeah, yeah, I wouldn't even say corruption. So let's say you create a disposable context rate and then somehow you don't keep a strong reference to it and then all the objects in that context will be nullified. So all the properties still. So when you access those properties, then you have to scratch because you cannot access a forced unwrap this and that. And then one more thing, I would advise everyone to just ditch forced unwrap, always use optional just our principle. And then so after I joined, I spent two months at a QA engineer. I was writing like UI test for Android and iOS. So I noticed that UI test is just not effective because if you don't have a very well unit tested code base, it doesn't matter how many QA person you hire. The app will just crash after maybe like you use it for a thousand times and then it's just crashed randomly. The locks and crashly takes not even that clear. Then there's no way. And so all those bugs get downgraded because no one can solve because no one knows where the source of the bug is. Sorry. But if you use RX Swift, the source of the bug is very clear because you only have one strip. I mean, not RX Swift, this library. RX Swift, I think the beauty of it, it supports a lot of different styles of programming. So this is the one that I've taken to follow. And another thing, you can do this, whatever framework platform you're going to develop on. I've used this on front-end apps and Android apps. And so this is more of a general software engineering, sort of stack framework. The quality implementation is just accidental because just at that moment, I needed that. So, any more?