Video #267: Point-Free Live: Observation in Practice
Episode: Video #267 Date: Feb 19, 2024 Access: Members Only 🔒 URL: https://www.pointfree.co/episodes/ep267-point-free-live-observation-in-practice

Description
To celebrate our 6th anniversary we had another livestream! We updated an app from the most recent Composable Architecture tour to the latest observation tools, showed how these tools can improve UIKit-based apps, showed off some recent enhancements to the @Reducer macro, gave a sneak peek of a highly anticipated topic, and answered your questions along the way.
Video
Cloudflare Stream video ID: e11e1a6100fa1548706d3d1b3dd896c8 Local file: video_267_point-free-live-observation-in-practice.mp4 *(download with --video 267)*
Transcript
— 0:13
All right. We are live and I hope everyone can hear us. Thanks everyone for being patient with our delays. We’re supposed to do this last Monday, but Stephen, you want to update us on what went down? Stephen
— 0:31
Yeah, I, I lost power for quite a few days, but I have power now, thankfully. And it’s been now, I think a whole year since our last stream and we’re pretty excited about it and future streams.
— 0:43
And just so all of our viewers know, we have a Q and A that we’re going to be answering questions from throughout the stream. And so you can just kind of tab over to the ask button in chat. there’s already a few in there and you can vote on existing questions that you want answers to. Yep. And we’ll just be answering them throughout the stream.
— 1:03
we also have a few topics that we’re excited to talk about that don’t really fit in episodes. we just have a bunch of topics that we want to make episodes. It’s on and sometimes it’s difficult to fit it all into that weekly schedule that we have. so we would need to start releasing multiple episodes a week to cover everything and we just don’t have the bandwidth for that yet. Brandon
— 1:22
No, no, we don’t. And so we’re going to start by discussing the latest release of TCA that the one that brought all those observation tools to the library and we’re going to show off those tools by refactoring One of those complex demos that comes with the library, that, that SyncUps app that we had ported from Apple’s ScrumDinger, and we’re going to convert it from the old style to the new style. Stephen
— 1:44
And that’ll be fun, but then we’re going to demo a tool that came out with observation that we didn’t really have time to cover in episodes, and it makes it possible to use Swift’s new observation tools in UIKit, which is pretty amazing. Brandon
— 1:56
Yep. And, then we’re going to live release. It’s a brand new version of the library that brings all new superpowers to the Reducer macro.
— 2:05
And this is something brand new we haven’t shown anyone yet. Stephen
— 2:09
Nope. And if you like to model your navigation as enums, you’re definitely going to want to see this. Brandon
— 2:14
Yep. And then finally, we’re going to just give a very quick demo of the next big feature coming to the Composable Architecture. And it’s going to be the topic of the next series of episodes.
— 2:22
And it is going to address probably the number one question people ask when they’re writing features in the library. And in fact, Just in the past week, we’ve probably seen this question pop up maybe five or six times, and we’re going to attack it, and it is going to be, the solution was far better than we even thought was possible, like we kind of sat down to solve it, and then things just kind of kept on coming out of it, and it was very exciting. Stephen
— 2:45
Yep, absolutely. And, of course, all of our viewers, feel free to ask questions at any time, and we’ll try to answer as many as possible. I think last time we answered 18 out of over 120 questions, and so we’ll see what we get to this time. Brandon
— 2:58
Yeah, hopefully we do more. Alright, so I’m gonna kick things off with, the observation tools.
— 3:04
hopefully our Video is still going through. Well, it looks like I can see from the chat. Observable SyncUps
— 3:11
I think it is going through so I am going to Start sharing my screen. All right, so if we go over here, hopefully people can see this Let’s yep close in on this. All right. So what I’ve got here is the The SyncUps app, that is, we are pointed at the newest version of the Composable Architecture, version
— 3:41
1.7.3, that has all the observation tools. But I just copied and pasted all of the code that was from 1. 6. So this code is still using things like, let’s see, what do we got in here? We got withViewStore and stuff like that, and all that is supposed to be the stuff that just, you can delete. And so we’re just going to go through.
— 4:03
And start updating this and see just how simple things things get. so what? Let’s start at the our migration guide recommends that we start at a root feature and kind of work our way to the leaf. So that’s what I’m going to do. the first thing we do is just mark the state struct as observable state.
— 4:26
and let’s see, observe. All right. So one question we’ve gotten a bunch is why do you have to do this when the reducer macro could apply it? And the reason is that it would actually be a severely breaking change for legacy apps. If we just started applying this macro directly, it would cause features to massively, massively over observe if they hadn’t yet embraced all the observation tools.
— 4:49
So that is why we’re forcing you to do it manually right now, but in the future and like a 2. 0, the reducer macro will apply. this macro automatically. And then as far as the reducer goes, I think that’s actually all you have to do because the observation tools are more of a view concern they’re not really a reducer concern.
— 5:07
And so we get to skip all of this and we go down to the view. And ideally, what we’d like to do is be able to clean up a lot of this noise here with all new observation tools. So, for example, there is no more navigation stack store. You can use playing navigation stack and provide it a path. And you can actually derive a binding to the path from the store.
— 5:31
And you do that by doing at bindable on your store. And if you were supporting iOS 16 and earlier, you’d have to use perception. bindable. But we’re not going to mess with that now. We are on iOS 17 or actually, no, no, actually I, Oh, no, no. I made this project also to be iOS 16. So we are going to show off the fact that all these tools work in iOS 16, 15 and 14.
— 5:54
even though the vanilla SwiftUI tools only work in iOS 17, so we are going to do perception bindable. And once you do that, you get to derive. bindings from stores by just doing dollar sign store scope and then providing the domain you want to scope down to, and so, oh, yep. And so that was a very helpful message.
— 6:15
You also have to make your path reducer observable state. So we’ll be nice once the reducer macro can do its thing. So then with that, what we get to do next. Is also kind of nice is this destination closures handed a store and we can now just switch on the store state and instead of doing this case let stuff we can just do an if let to unwrap a store by further scoping.
— 6:41
So this store is scoped down to the path domain, which is an enum and we want to further scope to a particular case like we want to scope to the case of. The detail and the detail actions and once we get it down to that we can if let unwrap that and then pass that store along to the sync up detail view.
— 7:09
the meeting view isn’t even a TCA feature it’s just a plain vanilla SwiftUI view and we just hand data along nothing to do there and then I’ll just copy and paste this because we do the same for record. Where we have record, record and record meeting. All right. And with that, I’m almost certain that this compiles Stephen
— 7:35
and the simulator is hiding some codes.
— 7:38
So, Brandon
— 7:38
Oh yeah, let’s
— 7:45
get that simulator out there. Don’t need that. Okay. There’s all the code and it did compile. And we just converted this over to the new observation tools. We get to use just simpler if let syntax and we get to use navigation stacks. Instead of navigation stack stores, we get to derive bindings directly from the store.
— 8:05
it’s, it’s pretty great, but this feature doesn’t even really show like just how nice it gets. So then we can move on to the next root kind of feature, which would be the list feature. And so from here. You get to navigate to a couple of places and let’s just start doing it so we would make this observable state.
— 8:25
We would also make the destination. So these are all the different places we can navigate to. It also has to be observable state and then. I think that’s it for the reducer. We go down to the view and we can just start deleting stuff. So here we’re doing a with view store focused in on sync ups. Cause we, that’s all we, the only data we needed it in here.
— 8:46
So we wanted to minimize view recomputations. We don’t need to do that anymore, but we do have to do with perception tracking because we’re targeting iOS 16 and then we get to just change. All the view stores to store. So this would be store dot syncups. We can just reach right in and grab any state we want, and the minimal changes will be observed.
— 9:10
We can send action straight to the view store. Alright, so here we want to show an alert from a store, and this is now an old soft deprecated API. If we make this bindable perception bindable, we can now derive a binding straight from the store and provide it. Stephen
— 9:31
And there’s actually a good relevant question.
— 9:33
Yeah. That was just asked. retinas asks when to use at state versus at bindable when kind of declaring that var store. Brandon
— 9:43
Yeah, that’s a great, that’s a great question. it all boils down to if the parent domain has constructed the store for you and passed it to you, use bindable. If this view wants to create a store from scratch and hold on to it, use at state.
— 10:00
And so this style would be more if you want to kind of split off a little isolated store that wasn’t composed from a parent store, you would want to hold it in at state because you’d want the view to own it and hold on to it. we have seen, however, there are a decent number of quirks with Holding on to, stores and state and it’s not a store thing.
— 10:20
It’s, it’s a, it’s a just observable object thing in general. so it can get a little bit tricky, but for the most part it does work. Anything you want to Stephen? Stephen
— 10:31
No, no. I mean, that sounds great. I think. You and I have encountered bugs, between at state versus at observed object. And it seems like at bindable behaves more like at observed object than at state.
— 10:44
So just keep in mind that the tools are different. You might see a little bit of weirdness. Brandon
— 10:48
Yeah. Yeah, at stake can be really bizarre for whatever reason. all right. Stephen
— 10:53
So then we, we have one other chat message. I was looking at chat and may have not noticed, but John wonders if you missed the width perception tracking view in the previous view. Brandon
— 11:04
So in the previous view, I don’t think so because I don’t think we were actually accessing store directly. Or, well, so. Guess in the scope. In this, oh yeah. No, you’re right. And so yeah, the navigation stack store. Well, so I mean, I could also, you know, get us into a compiling state, run it and I’ll show off the runtime warning.
— 11:28
But I think john is correct. And we’ll we’ll show the warning and then we’ll fix it. Alright, so we can now derive a binding. to the destination, but we don’t have to worry about the projected value. We can optionally chain into the destination alert case, and then also chain it to the destination alert actions.
— 11:49
And this is now a binding to an alert domain, hand that off to the alert view modifier, it takes care of the rest. And, now also sheets can be used. We can use the vanilla switch UI version, just sheet item, but we derive a binding, drop the projected, optionally chain. And then that should be good, and then we need to start sending actions directly to the store, but here, this store, we’ve kind of shadowed it, so I’m going to rename this to, what, like a form store, and then I get to just send actions directly.
— 12:21
And let’s see if that compiles. It does not. Something up at the top. And, ah, yes, okay, so property wrappers don’t play nicely with The kinds of macros that like to swap out stored properties for like a computer property store property combo. The observable macro does this and our observable state macro does this.
— 12:43
And so we have a macro version of presentation state, you just say presents. And in a future TCA, we may be able to get rid of this entirely. Is that what you’re about to say? Stephen
— 12:55
No, I was going to say that I think we even provide a helper helper that does a fix it if you forget. Oh, yeah. Brandon
— 13:00
Yeah. Oops. Let’s go back.
— 13:05
Yeah, I should have looked at the Yeah, so here we go. We can fix it. All right. Now we should be compiling and let’s run it and see if we get that runtime warning. Hope we do.
— 13:24
Alright, so now I got to turn on simulator so people can see it’s running. And as soon as some UI shows up, yeah, we got a purple warning, so I’m gonna turn off the simulator, go back here, and this is when we detect that you’re accessing state directly in a store without, being wrapped in a with, perception tracking, we admit this, and if you Go through this stack trace, you should hopefully find somewhere that’s in your view, right here, and it, it shows you, yeah, we are, a scope is a state access, and so we are accessing the state without being wrapped in with perception tracking.
— 14:03
So, let’s do that. Alright, now if I rerun it, hopefully, there’ll be no warning. It’s the simulator, Alright, yep, the simulator’s up and there are no, purple warnings, in Xcode. Alright, so we have now converted two features and I’m probably just going to do, I think, one or two more of these because I think everyone kind of gets how it goes.
— 14:33
We just get to delete code and use Vanilla Swift UI and Vanilla Swift concepts rather than having a TCA version of it to even know about. So, I think One that kind of pays some dividends is maybe the sign up form. Yeah, because here we need bindings to various fields and we’ve got a binding state property wrapper.
— 14:57
We’ve got, And then we’ve got, oh yeah, we’ve got the, the view store. It doesn’t look like we’re using view specific state because it’s a leak feature. That’s not a big deal. Maybe, maybe in the record meeting, there was something interesting too. let’s go down. Yeah. Okay. So in the record meeting, we wanted to focus in on just the bare minimum because this feature holds a lot of state that we don’t care about and that updates often, but the only thing we really care about.
— 15:27
Is when seconds elapsed changes when the speaker index changes and stuff like that. And so let’s just update this one real quick, because what it means is we can do observable state. We can drop this to presents. And then I don’t even there’s not even a destination enum or, reducer in here. So there’s nothing to do with that.
— 15:50
And so ideally, we would just be able to get rid of this entire thing. We shouldn’t have to worry about what. State we’re touching. It should just be if you touch that state, you are tracking it. And so we wrap this in with perception tracking and then anywhere. What’s that trick? Is it what command option E?
— 16:09
I think so. Yeah. All right. So yeah, if I go through all these view stores Through all yeah, there they are and I just change them all to store that should get us pretty far But I think the alert, we’re doing the old style, so we want to derive a binding to this. And in order to do that, we got to do perception bindable. Stephen
— 16:36
We’ve had a few chats and questions asking about sugaring up the destination closure at the root in the app view. Brandon
— 16:43
Oh yeah. Yeah, we’ll stay tuned. Stay tuned. Do not go anywhere. all right. So that now builds, and I’ll just do a really quick one, the form, because what’s also nice about this is that we no longer have to think about binding state.
— 16:58
In fact, binding state is now soft deprecated. We’ll be removed in 2. 0. And so it is just observable state. My
— 17:11
keyboard is acting up and it is, we get rid of the width view store. None of that.
— 17:28
All right,
— 17:30
so, oh wait, but we do. We do need with perception tracking, for now, and then take a look at all these view stores. They can now all become just stores and we want to derive bindings from the store. So we’re going to make that a perception bindable. All right, so here’s where things kind of flip a little bit.
— 17:54
Rather than taking the store and deriving a binding to the sync up. You actually take a. Binding of the store and derive a sync of binding from it. So you just got to do just a little flip on these, just move the dollar sign over. And that should get us compiling again.
— 18:15
Let’s see what else we got. Did I miss one? And then we’re down here where we have to synchronize focus between the view and the store. And hopefully that compiles, it does OK. I’m not going to do the rest. I think I’ve only got one more feature left, the sync up detail, but I mean, that’s basically it. And we’ve got a full migration guide out there that hopefully shows step by step how this process goes.
— 18:44
you know, I’m going to go back to, to our talking heads here. and that, that is the observation tools. they’re Are any questions, but yeah, the whole point of these observation tools is to allow us to get rid of the concept of view store, even from the very for early days of TCA, we never wanted the concept of a view store.
— 19:08
It wasn’t until like almost probably maybe a month or two before releasing the first version of the library. That we’re like, all right, this over observation thing is a problem. SwiftUI kind of promises this idea that you don’t have to worry about it, but I mean, at the end of the day, if you’re hammering the view body, it becomes problematic.
— 19:25
and so we just hoped we wouldn’t need it, but we did need it. And, and, and luckily now with Swift’s new observation tools, we do not need it. And luckily we’re able to backport it so people can just start using it immediately. And also it was a minor version of the library. So people can start incrementally using it immediately.
— 19:42
We, it’s not a breaking change whatsoever. Observable UIKit Stephen
— 19:47
Oh, I think we could move on to some new things with observation, which is UIKit. Let me. Try sharing my screen here,
— 20:00
see if it comes up, okay, Brandon
— 20:04
there’s a saw something in the chat. How much smaller is the library? Well, the library only got bigger with 1. 7 because it was a non breaking change. So we did not delete anything in 2. 0. We will be able to delete thousands of lines of code and documentation. Stephen
— 20:19
Can’t wait. Brandon
— 20:21
And just to check, are you recording Stephen?
— 20:23
I am. Okay. And your safaris or your notions on screen right now? Oh, or, Oh, no, no, sorry. No, it’s not. That’s not. Stephen
— 20:33
All right. All right. So, right here I have, one of our demo apps open. because now that you’ve given a recap of observation, and updated probably the most popular demo that we feature a lot in the series.
— 20:49
I wanted to show how apps and features that are written outside of SwiftUI can leverage, these new observation tools. And so, this demo is another one that lives in the Composable Architecture repo. We haven’t really featured it that much in past episodes. And it’s a tic tac toe demo. it’s actually a demo that other popular library based architectures have implemented, and that includes Uber’s Ribs and Square’s Workflow.
— 21:14
And we thought it would be a nice way for folks to kind of compare how the composable architecture approaches problem solving versus other architectures out there. And like SyncUps, it is a moderately complex app. Around a tic tac toe game and it features dependency management, navigation, and a whole lot more.
— 21:36
But unlike SyncUps, it has been built in both SwiftUI and UIKit to show that a composable architecture producer can be reused across many different paradigms. And it is also unlike SyncUps, just. Heavily modularized. we have a bunch of modules here in the package and they are all kind of depending on each other in the most minimal way possible to isolate themselves from one another.
— 22:06
And that includes, Basically, a module for every single feature. So we have the root app feature. We have a game feature, a login feature, a new game, even a two factor auth feature. And then we have several modules for each feature because we have the core logic. As well as the SwiftUI version, as well as a UIKit version.
— 22:32
And then, finally, everything comes together in a project file, just in the most minimal way. We have an app entry point that just renders this root view, and that root view just kind of manages the SwiftUI and UIKit versions, so you can play with each one. And so, let’s run it in the simulator. I’m going to build the app.
— 22:59
Going to bring the simulator on the screen.
— 23:20
All right, and here we are at the root of that application where we can drill into the SwiftUI version of the app and the UIKit version. So I’m just going to drill right into the UIKit version. She pops up, and it has this kind of login flow. So We can basically type in an email. There’s some notes up here where if we have a two factor off in the email, it will go into that flow. Brandon
— 23:48
Oops. Oh yeah. Sorry. Okay. I did it. Sorry. Sorry. Stephen
— 23:53
And then the password is password. If I tap login. After a second,
— 24:02
we should drill into the two factor auth screen where we can type in our code, log in. All right, now we’re in the new game flow. So, we could play a game together, Brandon? Let’s play, and I will let you win this round. So, it’s a pretty fun app, but what’s great about it is we are even driving this UIKit version and SwiftUI version with the same store.
— 24:34
So At any time, I can drag this away, drill into the SwiftUI version. And we deep link to the exact same state. So this shows another kind of superpower of the composable architecture, where when you have this kind of single source of truth in the store, you just get to unlock a lot of magical things. but yeah, we can quit out log out and yeah, that’s kind of the demo.
— 25:02
and it’s in the repo. So if anyone wants to play around with it, they can feel free to clone the repo and play with it. So let’s actually dive into the actual code and see how UIKit apps can take advantage of the new observation features of the, this latest release. And I am going to start by backgrounding the simulator, and I am going to go into kind of the point that we see when we first present, which is the login.
— 25:37
And so we have three modules here, one for UI kit, one for SwiftUI, one for the core feature. And I’m not going to spend much time in this file. I just wanted to note that we have already updated this application to be observable as far as the state is concerned. And all the SwiftUI features have been updated, but the UI kit features are still kind of being created in the old style.
— 26:02
And that is. The tools provided by TCA, which we’re just using combine. So combine is how we traditionally have UI kit features subscribe to store state over time so they can always have the latest and greatest. And so here we have a pretty simple view controller, but it’s how we like to structure things.
— 26:20
we hold onto a store just like a Swift UI view would hold onto a store, but we also hold onto some combined cancellables to manage the subscriptions to various store state. And then we. Go to the view, did load. We do a bunch of view configuration, kind of the old school imperative style. And we activate our layout constraints.
— 26:45
And then finally we have a wall of text where we kind of bind the store state to various UI components. And the way we do that is just using a publisher of store state. It’s a special property on the store and allows us to chain into various store state, and then we have a publisher to that store state.
— 27:06
And then we can just use vanilla combine operations to kind of bind them to the view. So assign is kind of the typical way you can do that in the most direct way possible. You just tell it, I want to assign it on the login button to the is enabled property on that login button. And finally, we got to store that cancelable and that’s.
— 27:28
Quite a bit of code, but it’s also pretty simple. It’s just a little noisy and I think we can do better. but just to kind of go through the rest, we do have, you know, a few screens worth of these store publisher calls and some of them can get Pretty complicated. So if we were to just, rethink this from scratch and just use vanilla Swift, I think it’d be pretty nice if we could just say I got this login button and whether or not it is enabled should depend on whether or not the store says it’s enabled.
— 28:06
And now basically all the noise of combine goes away. We don’t have to think about cancelables. We don’t have to think about assign operations and it’s just a simple one liner. And so that would be pretty nice. but things get a lot more complex and combined when you need to transform data in some way, or you need to kind of inspect the state in some way.
— 28:29
And we’re seeing this even with a simple email fields, because as far as the store is concerned, email is just a. Simple string, but as far as you, I is concerned, text fields have optional strings as their text. And so we got to kind of contort ourselves a bit with a combined mapping to just wrap it in an optional.
— 28:51
In fact, if I were to comment this line out, it’s not even going to build because the types don’t line up. And so it’d be nice if instead. You just say, I have this email text field and I want to update the text to be the store’s email using simple vanilla Swift. We get to get optional promotion for free.
— 29:18
And now we have just two lines of code kind of doing what we had seven lines of code doing before. so let’s continue to do this. It’s pretty straightforward. So maybe. I don’t know, Brandon, if you want to take a look at the chat or Q& A and see if there’s anything interesting to answer. Brandon
— 29:40
yeah, there’s a couple, nothing about UIKit.
— 29:43
I feel like people, yeah, there’s no one doing UIKit. Yeah, there’s no one using UIKit these days. I feel like even, no matter how much you want to do 100 percent SwiftUI, you got to do You gotta drop into UIKit at some point. all right, here’s one. Will this be relevant to AppKit? Yes. Yes, if you have AppKit or UIKit, anything that’s not SwiftUI, then yeah, this will be handy.
— 30:06
And then there’s, would love some tips on UIKit plus navigation. We’ll also be seeing that in just a moment. Yeah, Cosmic Flamingo thinks they’re the only one using UIKit. They may be right. So, Yeah, it’s Yeah, I think all this is, is just really interesting to see what, what you’re, what you’re showing here is how it would.
— 30:33
This is how we would do things in Swift UI, you would, you would want to just be able to just assign things or like, you know, construct these things and, and so let’s see what happens. Stephen
— 30:42
All right, so I get to get rid of all the publisher noise. We now have basically six lines of code for what we used to have to do over the course of each like 25 lines, a little less than that.
— 30:55
Ignore the white space, but of course, we have never been able to actually write things like this historically, and that’s because we’re in the view to load. It’s going to execute once. We’re not going to get the latest state over time as a store updates. And that’s exactly what these combined publishers are doing.
— 31:12
We are saying, I always want to know the latest is login button enabled and bind that to the login button. And these subscriptions are maintained for the lifetime of the view controller. And It just works. It’s just a little noisy. well, with the observation tools, we get to kind of simplify all this with a new tool, which is this observe method that is available for all kind of NS objects.
— 31:41
And so we can just wrap all of this with observe, and it just takes a void to void closure, and then in here, every single bit of observable state that is mutated, as long as it gets accessed in this observe, we will rerun this closure, and so this maintains the subscription over time. Now it is managed over time.
— 32:03
So we’re getting these errors that the store needs to capture self and we need to be explicit about it. Well, luckily we can just weakify self to avoid any kind of retained cycle. We can guard, let self to unwrap it.
— 32:23
And then assuming that we have ourselves, everything else just compiles thanks to, I forget the proposal number, but there’s one where you can be implicit about self after unwrapping it in one of these week closures. Brandon
— 32:37
Yeah, here we got a question that you and I grappled with as was whether this tool would work for the perception library to and right now we did stick it in TCA because it kind of came in the 11th hour of the observation tools, but I mean, we already got two questions about this.
— 32:54
So it kind of seems like we probably should extract it out into the perception library because the implementation has nothing to do with TCA. It’s only about. Perception tracking or observation tracking. Stephen
— 33:05
Yeah. I think at the launch we wanted perception to be basically just a backport of observation and nothing else.
— 33:11
I guess it does have bells and whistles as far as the perception checking goes, but it does seem like we should enhance it. Brandon
— 33:18
Yeah. And then also a question about whether publishers will be, deprecated. Like a view store or store that publisher and probably not, but we will definitely, as we move forward, hopefully detangling TCA from combine, it will be guarded in like an if can import or, you know, something like that.
— 33:39
Or maybe we’ll even have an async sequence version of it someday. but yeah, that that’s not something that really needs to be deprecated. Unlike some of the other things like I showed earlier, we do want to deprecate those things because those tools should not be used anymore. Like navigation stack store and stuff like that.
— 33:54
Yeah. and then also one more or not so much a question, but maybe it could be interpreted as a question is they, this person uses stored up publisher and a coordinator class instead of the view controller, I’m guessing the question is like, could they still use this? Well, if that coordinated class can, inherit from NSObject, then yeah, you’re good to go.
— 34:14
If it can’t, then I don’t know. Maybe we got to think about having a non NSObject version of this, but then you would be responsible for tracking a cancellation token. We can’t just squirrel it away inside an NSObject magically. so, so we could, we’ll, we’ll think about that, but it seems like, yeah, if you have a, like a pure Swift class, you may need.
— 34:35
Access to this tool. Stephen
— 34:36
Yeah. And we’re always looking for feedback too, from people that are using these tools day to day. I think you and I, Brandon are working in SwiftUI a lot of the time. So we spent a little less time finessing these tools just because we’re using them less often. So any feedback that we can get, situations.
— 34:55
Just file a GitHub discussion and we can chat about it. Brandon
— 35:00
And there’s another question, will this observe closure be triggered when value inside state, which is not used for the UI changes? Stephen
— 35:08
And this will only re execute if you access state and it gets mutated. So the state we’re accessing here are just these fields.
— 35:19
And then if any of those fields are mutated, it will update, but any field that is not accessed in this observed closure will not cause this to update, if it gets mutated. So the same benefits we get in Swift UI we get for this. Yeah. Okay. Okay. But yeah, I don’t know about everyone, but I think this reads a whole lot nicer than all of this and should even be able to comment it out and believe everything is still building.
— 35:51
It is. So let’s open the simulator again and just make sure it all works.
— 36:00
Drill into UI kit. And if I type in blob and type in a password, well, I don’t know if you noticed, but as soon as I enter the password, the login. button became enabled, so should be working and I believe I could even set a breakpoint in the observe closure type of character and yep, sure enough, every single time observe state is mutated, it executes this closure and it’s just doing its job. Brandon
— 36:33
All right, we’ll get more questions. Okay. So all right. So a couple of these are are closely related. So just I’ll just summarize what. So there’s concern of, say, the is log in button enabled changes, even though email and text field enabled and password. None of those change. Of course, this closure is going to be called again.
— 36:58
You want to say something about that? Stephen
— 36:59
Yeah, so In general, we think you don’t need to worry about getting too granular about this. we think that UIKit is doing a lot of work under the hood, in KVO to prevent this actually doing any work if is enabled, this just being set to the same values before.
— 37:17
that should be pretty lightweight to do. but the nice thing about these observe closures is technically you can get as granular as possible. I could break this up over multiple steps and it becomes a little more verbose. And I don’t think you and I would suggest doing this, but if people do hit a performance problem, then they are free to like break things up into as many observes as they want.
— 37:43
we wouldn’t recommend it until you hit a performance issue or a problem. And so we think it’s good to default to this style. And this is really kind of similar to Swift UI. Anyway, if any state accessed in the Swift UI view, changes, you’re rendering that whole view. And we just need to expect Swift UI or UI kit to kind of do their job and.
— 38:06
And know what to do in the minimal way possible. Brandon
— 38:10
Yeah, yeah, this is just really no different than how it works in SwiftUI. And yeah, we just think that setting isEnabled to true when it’s already true is not a big cost. And in fact, it may be more of a cost to break up multiple observes, because then you’re setting up observation tracking machinery multiple times.
— 38:28
and then also you don’t have to worry about removed duplicates kind of stuff either because it’s also a very good chance that the cost of checking equality on complex data structures is more than just executing this closure again. But we do recommend breaking apart observe when you do have something legitimately heavy, like if you did a reload data or if you did UI collection view dipping and stuff like that, you know, that stuff should maybe be in its own observed so that only when that data changes does.
— 38:57
does this get recomputed so that’s and then the one really quick one taking a lot of questions or a really quick one was the benefit of using observe versus just a big old store that publisher sink taking in the whole state and the yeah, you go for it. Stephen
— 39:11
I mean, that goes right to what you just mentioned where, store publishers are going to do equality checks and.
— 39:18
That can become pretty heavy over time. yeah, plus, I mean, you’re not having to worry about cancelables. You’re not having to import combine necessarily. So this is just a simpler tool to reach for. All right. Brandon
— 39:30
So another one, is there a way to cancel observation with this? And yes, it cancellation is automatically done tied to the lifetime of the NS object, but also it returns a canceled token.
— 39:43
That’s a discard, a discardable result. Yeah. And so you could capture that and then clear it out whenever you want. So that’d be good if you wanted to be observing only when the view is actually live on the screen. And so you could observe and view will appear and unobserve and view did disappear.
— 40:01
Yep. Stephen
— 40:03
Usually you don’t even have to worry about it and I’m not gonna worry about it here. But yeah, I’m sure we’ll get more questions soon. I am just going to take this code and move it below all these comments here so that we can focus on the last two publisher fields, which are a little more complex.
— 40:22
We’re not just assigning values into fields of various controls. Brandon
— 40:27
All right. Sorry. I’m going to one more question because I just, I just want to make sure because it’s very subtle. So, so can you scroll up to the observe? all right. This. That it’s the mere act of touching a field in the store is what subscribes this observe, trailing closure to updates.
— 40:51
So if, if you were to comment out, you know, every single line in there from login button down to activity indicator and no, nothing in the store is touched. This closure will never be called. So, so the, the process of like scoping down to observing the minimal amount of state is just happens for free.
— 41:09
Because if you don’t touch the state, then it won’t be, this closure will not be called. and, and of course, if you touch multiple pieces, pieces of state, then when any of them change, it’s kind of like a combined latest, I guess. If any of them change, the closure is called. And if you have heavy work, break it down into a small observe.
— 41:28
If you’ve got simple work like this. Cram it all into one observe, or, you know, or do whatever, you know, feels good. Yep. Stephen
— 41:35
And that goes back to the question about just using a combined sync on the entire thing. If you add fields there, you’re going to observe it, whether you actually use it in the view or not.
— 41:46
This really does kind of give you the minimal number of updates. Which is great. Okay. All right. Brandon
— 41:53
So I’ll give you a good. Yeah. Yeah. A good. Yeah. I’ll stop asking questions. Stephen
— 41:58
Okay. keep asking questions, but we’ll, we’ll get to them a little bit. so yeah, we have two more fields here, that manage an alert and a presentation.
— 42:07
Cause we have that two factors screen down here. Let’s start with the alert. There’s a lot more work being done here. We actually have to do a sync in order to get access to an alert value. And it is a optional, alert state. And the way that it works is whenever we get an alert, we check if we can unwrap it.
— 42:28
We check to see if we have an alert controller presented and we actually have a. An alert controller and a two factor controller, just kind of living out in the view, did load to manage over time. If we haven’t presented it, we just assign it, find the alert data to it.
— 42:46
We have to add this okay action, and then we can finally present it.
— 42:49
And then on the flip side, if we get a nil alert and we are presenting the alert controller, we, we can dismiss it. So it’s a lot of presentation kind of being managed here. let’s move it over to the observe. So it should be pretty simple. I should be able to copy and paste most of this, but now instead of getting an alert value, we can just reach right into the store, reach right into the store.
— 43:24
And we need to bind the alert.
— 43:31
And now for an alert action, we can re weakify self and then self is not weak because we’ve unwrapped it. So we can just present directly. And so it looks basically the same as this, but there are things to like about it. we no longer have to worry about cancelables or syncing. we’re already in a context where self has been unwrapped.
— 43:57
basically any work that you used to do in a complex sync, you can just do inline in the observe. And it should behave exactly as before, but we did sneak in another tool in this release that makes alert presentation even easier. And that is we added a new alert controller initializer that takes a store of alert state and presentation action.
— 44:21
And so instead of doing this, if let alert unwrapping, what we could do instead is if we get an alert store by scoping to the alert state. And action,
— 44:42
then we can just pass that alert store along.
— 44:49
And because this is focused in on presentation state, it will automatically add an action for dismissing. So we can get rid of that. And now this is looking really nice and succinct. All right. So that, that should all work just as before we have another big combined publisher, we can come in out. Let’s move this again to be a little bit closer to everything.
— 45:19
Okay. So you have one. Presentation. Brandon
— 45:22
Yep. And what you’re about to do is going to, and so what you just did and what you’re about to do is going to answer a question. So do it. And then we can talk about how that answered a question. Okay. Stephen
— 45:33
All right. So for this final one, we have some new operations to talk about, but it’s still very combined driven store comes with, a normal scope helper.
— 45:45
And then we have this, if let helper, which is a combined operation that takes a one or two closures. That when it can unwrap state actually unwraps a non optional store on that state. And so this closure will emit whenever the two factors store becomes non nil. And then we can manage the presentation and then the dismissal.
— 46:08
Now that we have observation, we can actually use the same tools that we introduced for SwiftUI, which is. In this closure, we can do an if let on a store that is scoped to the two factor auth state. So if we can get a two factor store out of that, and we should handle this conditional as well, because we only want to worry about this if we haven’t already presented the two factor controller.
— 46:42
Then we can just do this logic right here, simplify this. And then instead of using this kind of pseudo else closure, we can just use a regular old else and we can do this work here. And we probably should also have that check where, you know, if the stores two factor is now, and the two factor controller is already presented.
— 47:20
And that should allow us to get rid of all of this still a lot going on here, but a little cleaner and still a little shorter. And then I can finally take all of this code and move it above the subserve.
— 47:45
All right. And so I think we’ve done it all. I’m going to get the simulator up in front and build the final time just to make sure nothing went wrong and drill into UI kit. Type in blog, type in the wrong password. We get the alert can dismiss it, but go to the two factor off screen
— 48:17
it presents. So I think we’ve kind of fully hooked this screen up and we could even clean it up a little bit more because we have a lot of code up here. Let’s, let’s compare. So the after is just 29 lines of code and the before and formatted nicely was 60. So basically half the code. And it’s probably performing a little bit better as a result of all the observation tools.
— 48:48
I’m just going to delete all of that and then finally can even delete the cancelables and we no longer even need to depend on combine. So I think that’s a pretty great refactor. Brandon
— 49:02
All right. And this kind of dovetails with some of the questions and concerns around. Doing too much work in this observe because we have decided to not only put in updating UI components, but we’re doing navigation inside there, but you’re getting like kind of a removed duplicates kind of thing for free because we’re only showing an alert if the alert of the store scope succeeded and the alert controller was already nil.
— 49:26
And so it doesn’t matter. I mean, that that check is extremely easy, you know, scoping stores are cash, so that’s a dictionary look up plus a nil check, so it doesn’t matter if you. Call that, you know, maybe a couple of times then, you know, necessary, and then the same thing with navigation too. So, so I kind of like remove duplicates or like a process for not pushing multiple times.
— 49:48
It’s just baked into this, you know, automatically. and then, you, you kind of answer. There was a question about, Yeah. will this work for pushing views and how will it be called? How will it work if it’s called multiple times? So, I think this shows how that works. And then also, any plans to add helpers to UI Navigation Controller to better support enum based navigation?
— 50:13
I mean, this just already, the tools are already there. You can do a store. scope to enum, destinations and it just works. Like, there actually are no, I mean, maybe we could have some syntactic sugar to make it like a look a little nicer or something, but this code works perfectly fine and it wouldn’t be shortened that much if we created like a whole new tool for it.
— 50:34
So the tools are actually just there already. Stephen
— 50:36
Yeah. I mean, let’s even kind of show that off a bit because. At the very root in the app feature, we actually have an example of kind of an enum version of state to show whether or not we’re in the logged in or logged out views. And sure enough in SwiftUI, we’ve simplified things where we can just switch over the store state and handle each case.
— 51:01
And then embed it, but unfortunately in the app view controller, it’s not too bad. And it’s using tools that we just kind of showed where we are scoping to a particular feature and then if letting on it. we kind of lose a few things here in the SwiftUI version. We can exhaustively handle everything.
— 51:22
And here we aren’t really checked by the compiler. we’re just kind of having to scope to everything individually and just hope for the best. But I think now what we can actually do is I can just copy this entire switch over to the app view controller, paste it in. And instead of rendering a SwiftUI view, we can just set the view controllers.
— 51:49
So we have the login store. Let’s do that work here. We have the new game store. Let’s do that here, comment all this out. And then of course, for it to work in Observation, we just need to wrap the whole thing in Observe. Brandon
— 52:11
There’s a question of, do you have to hold on to the view controller, I think in reference to the previous screen and, and yeah, we do because we need the ability to dismiss it later, but we, you were holding it inside view did load. We didn’t even need to have it as an instance variable, which was kind of nice. Stephen
— 52:29
And yeah, I think that’s all there is to it. We are now also exhaustively handling each case, and let’s make sure it still works. So I’m going to. Get that simulator going, rebuild.
— 52:50
Okay. Starting the login screen. Let’s just log in real quick.
— 53:00
Okay. So basically works exactly as before, but it is much simpler using the same kind of tools that we get to use in SwiftUI we can now use in UIKit. Brandon
— 53:12
Okay, cool. Stephen
— 53:14
So I think that’s about it for that section. I don’t know if there are other questions or a bunch during the sections. Brandon
— 53:22
Yeah, yeah, there were, I’m gonna, I’m starting to, I’m starting streaming now.
— 53:26
So we’re back to the talking heads. Yeah, well, I mean, yeah, there were a lot of questions. So, and I don’t see any new ones right now. but I think we should move on to the next session because we’re, we got, we still got a lot to cover actually. And we want to still be able to show that preview of that new thing that no one has seen yet. Live release of @Reducer macro superpowers
— 53:46
But before doing that, we’re going to do a live release. We’re going to release a brand new version of the composable architecture right now. We’re going to give a quick little demo of what is in that release. And so I will go into screen sharing. It’s a little funny. all right. So I’ve got Safari up and let’s see, I got to bring this over.
— 54:19
No one look at that. okay. So we have got a draft of a new release that adds all new reducer macro features. And I think I just go into edit and just hit publish. Don’t delete it. Don’t delete. Hit publish. It is now released and now I’m gonna go to the, let’s see, I guess, I guess I need a clone. I’ll just clone a new, a new TCA live.
— 55:05
I didn’t think this through, but let’s do a git clone. let me, let me back or yeah, it doesn’t matter. Git clone. Stephen
— 55:16
I
— 55:20
think you could open the next demo as well and just update the version of. TCA that it’s pointed to. Brandon
— 55:26
Right. Oh yeah, I could. That would be fun. Yeah. Let’s do that. Okay. Yeah.
— 55:36
Okay. So let me go in. All right. So get rid of Safari. All right. So here we are and I’m going to update this and hopefully I’ll find 1. 8.
— 55:54
Let’s see. Okay. There it is. 1. 8. And what I. So I’m going to show off a quick little feature of the new reducer macro and then you’ll show off the next one. And the one I’m going to show off is pretty simple. all it is, is that now when you apply the reducer macro, let’s see. Okay. Yeah. So now when you, apply the reducer macro to like, say you’ve got some feature, this right now will compile.
— 56:25
Well, as if I enable all this stuff, All right. And then I guess if I also compile Swift Syntax. let’s see. Let’s see if I can expand that. I can’t expand it yet. All right. Bear with me while I compile Swift Syntax. I’m sure everyone Stephen
— 56:45
Maybe we should talk a bit about overhead of Swift Syntax. Yeah. Yeah.
— 56:49
Because we get to cut a lot of that out of the episodes. Brandon
— 56:52
Yeah. Yeah. So I’m sure people have been feeling the pain that Swift Syntax takes about 15 to 20 seconds. to compile and we’re living that reality right now, and it’s very unfortunate, but it, it is necessary because the main reason we need macros and Swift syntax needs to be compiled for macros is because of case paths.
— 57:16
we, we have depended on a runtime reflection style of case paths for a very long time. They’re They’re problematic, and we found a way of generating all that code for free with the case pathable macro, and so it is just a necessary thing, and hopefully Apple is hearing all of the pain that developers are going through, and will fix the situation soon.
— 57:42
but, so now this compiles, alright? And that is with no requirements filled in whatsoever, and if we expand this, we’ll see that it added A little empty struck for us. It even, conforms it to all the things it can conform to, puts in an empty enum, an empty body, conforms us to reducer. And so the question is, why would you ever wanna do this?
— 58:03
Well, the reason is because it is very, a very friendly, kind of progressive disclosure way of building features. You now get to just put in the most basic scaffolding of a feature and then can start integrating it into the rest of your feature. Like already, I could go in. Or actually, let’s, let’s move this over to the app feature and I’ll paste it up at the top and without even thinking about domain modeling and logic and behavior of the features already, I could go down to the path reducer and say that I’ve got a new feature that I want to be able to throw on to the navigation stack.
— 58:40
So I fill in these little bits right here
— 58:47
and then already this too will be compiling. And I’ve integrated a completely empty feature into the navigation stack. Let’s see. Oh, well, no, it doesn’t come out because I also have exhaustive checking here. And so, and so then, you know, I need a view here. Let me put an empty view. And so then what’s cool also is that I could have a little struct
— 59:16
feature. My keyboard’s messed up again. View. And we could have it hold on to a store, a feature. And have a body
— 59:31
and then down at the bottom where that compile error was, I can now even construct a feature view and pass it, one of these stores. Stephen
— 59:45
Funny you had to do more boilerplate to get the empty view into place than the empty producer. Brandon
— 59:49
Yeah, yeah, yeah. And I think you’ll have something to say about that very soon. so let’s see. Is something not compiling down here? So I think feature view
— 59:58
might’ve had a typo. Yeah. Okay. All right. And so this is a really cool that now I’ve already fully integrated.
— 1:00:05
A, a new feature into the navigation stack, logic and behavior and into the view. And I can even add a navigation link that, drills down to this feature view all without even thinking about the domain modeling or anything like that. And it’s just a really nice way to build a kind of very slowly layer on all of the complexities of a feature.
— 1:00:25
And then once I get to the point of actually needing to build this thing, I can even just start with just the state if I want, so I could start with this. Make it observable. And then I can go down to the view and I can start filling in things and I could be like, Oh, well, I, I know that I need a count here in order to show, you know, my user their, their favorite number.
— 1:00:48
And so I just add my count and now this compiles and then later on, or let’s see. Oh, all right. So now I added, some state to it and I dropped the, equatable. So now I got to make this equatable. All right. So now this should compile. And then later as I’m working on the, the app more, I’ll find that, oh, there’s a button too that sends some user action.
— 1:01:17
So now I want to send that action of increment tapped. And then I would go up and now I can fill in my actionium for the first time and throw this in, and now I still don’t even have a body of the, of the reducer and I can leave it blank and I can continue filling in, oops, I can fill in this, this view, more and more, add more state, add more actions without even thinking about the logic or behavior, alright?
— 1:01:42
So, That’s the basics of that. Basically, the reducer macro will now fill in all the requirements for you, so you don’t have to think about it at the beginning. Stephen
— 1:01:55
But we did the extra mile, and we enhanced the reducer macro even further. Enum reducers
— 1:02:05
So, if you’d like, I can take Yep. And here we go. All right. So I am in the SyncUps app that you just migrated.
— 1:02:14
And, we have a bunch of different kinds of navigation in the app that use enums to drive them, and we can even search. Or struct destination to find a few and so, for example, in the sync up detail, we have this destination reducer that lives in the sync up detail reducer and it just manages, an alert presentation as well as an edit screen.
— 1:02:45
And currently, whenever you need to define one of these destination reducers, you define your state with a case for each destination, same thing for the action, and then in the body, you would typically scoped every feature as well, and we don’t need to scope to the alert because it dismisses itself automatically.
— 1:03:07
All right, so. We have now enhanced this reducer macro to work with this style of presentation reducer, and basically let, let all this boilerplate go away. And the way that you do that is you update your reducer to be an enum. And rather than define state, action, and body. You just define a case for each feature that you want to use, and it actually holds on to the whole reducer or in the case of alert state, we just hold on to the alert state.
— 1:03:45
And that means this action can mostly go away, but we do want to keep these alert actions around. And this now lives directly in the destination. And then also this body can go away. And when you’re not dealing with alerts and alert actions, it’s even simpler. It’s just a line for every single feature that you need to be able to kind of navigate to.
— 1:04:14
And this is now so short that I think I can just move it to the top of sync up detail, because it’d be nice to just see where, where are all the destinations that we can navigate to when we open up this feature. Okay, now we’re not building quite yet. And that is because we made a substantial change here.
— 1:04:34
And. The message we’re getting is that state is no longer equatable, and that’s because the reducer macro here is actually generating the state and action and all that for us. And we don’t want to automatically bring in things like equatable. Brandon
— 1:04:49
you want to expand the macro? People are asking for it. Stephen
— 1:04:52
Okay. Some spoilers in here, but basically expanding this. we see that we get an enum state generated for us automatically. It’s case pathable, has dynamic member lookup, has some, something else, and then we have actions. And it even generated this body and some of this stuff looks a little more verbose.
— 1:05:17
And we just need to do that because the way that macros are run, It doesn’t always have things like the, builder context in order, but it all works, and we have a few other things that we’ll demonstrate in a little bit. But notably, this state conforms to something called case reducer state. We’ll get to that later.
— 1:05:40
But it doesn’t conform to Equatable. Now, ideally, what we would be able to do is just kind of extend the Syncup, Detail, Destination, State to be Equatable.
— 1:06:03
And hopefully that would just work and we would be able to build. Unfortunately, there are some bugs in macros, another pain point of macros right now, that prevent this from actually working. We would need to move this into its own file, which means we would need to define the conformance from scratch, which isn’t great.
— 1:06:24
So for now, what we’ve done as a compromise is when working with enum reducers. You can just specify the conformance is of state and action. And so we can say we want this reducers state to be equatable and ideally in the future, once all the macro bugs are fixed, we can just delete that line of code and.
— 1:06:47
Confirm the old old fashioned way Brandon
— 1:06:50
and I think that that bug may have actually been fixed recently or at least a One of the circular reference bugs and so it’s probably more of like a sweat 511 kind of thing Stephen
— 1:07:00
But yeah, we can hope yeah
— 1:07:03
But notably this this is compiling now, so that’s nice And yeah, is there a question? Brandon
— 1:07:09
There’s a pretty, yeah, there’s a really good question from Chris. Do you feel like modifications like this break the mental model for those reading a reducer? Yeah. What are your thoughts on that? Stephen
— 1:07:20
I think there, there is a danger for macros to do maybe. Too much magic. if you are able to refer to things that are kind of defined behind the scenes, and if you’re expected to do that, you apply this reducer macro and all of a sudden, you know, that it’s done bindable stated and it’s added a binding case and all that stuff.
— 1:07:45
as cool as that is, it does kind of break the mental model, I think. and I think maybe you agree. I think that is not the case here because we have state in action, which are kind of associate types that you just expect to exist. And then everything else is just kind of contained. You are fully describing the alert and edit cases.
— 1:08:08
It’s really just deleting boilerplate. It’s not adding new interfaces that. Normal protocol couldn’t also just add for you. Brandon
— 1:08:19
Yeah, I think, I think this one, we, we really try to be extremely careful to tow the line of like, yeah, where, where is it just too much magic. And in this case, the mere act of adding app reducer to desk to enum destination means this thing is.
— 1:08:35
Reducer like, and so you expect to have a destination dot state and dot action in there. And then the body was just all that scope kind of, you know, garbage that you had to do all this case key path and key path stuff and everything. And we get to now just remove all that noise and just generate it automatically.
— 1:08:53
And so we feel like this, this example. Has towed that line correctly. And there are ways that we, we could have even pushed it harder and we decided to pull back a little bit. so yeah, we think, we think this is, is okay. Stephen
— 1:09:09
Always open to feedback and discussion though, because we, we don’t know everything for sure. Brandon
— 1:09:14
Yeah, and then another question is what if you had moved that equatable extension to the bottom of the file? Unfortunately, it’s it’s not a matter of it being at the top or bottom. It’s just a simple reference to a nested type in a macro Kills the compiler, but I think it has been fixed. Yeah. Stephen
— 1:09:35
So hopefully we, we get a fix in this beta series, but if not, it should be in the next Xcode after that.
— 1:09:42
All right. But we do have a few, build errors now from that migration. first of all, the, if let style needed to hold onto like the producer or the feature that it’s embedding. And that was just creating that destination struck before. Now we’re in enum that describes all the cases, so we can’t do that anymore.
— 1:10:02
technically, the way that these reducers work under the hood is that they are statically known, and so they actually get a static body property instead. but because this is statically known, and this is compiling now, we have an overload of if let, where we can just omit this entirely. cause there’s really nothing else that we would want to do in there.
— 1:10:25
And so that is now compiling. And if I scroll down, I think the last few issues is just, I moved that alert state action out of the action. Enum. And so, okay. I think that might be good. We are building successfully. And so let’s just power through the second one, because I think we can do it pretty quickly.
— 1:10:52
We know that we are going to want to make this destination equatable because state is equatable.
— 1:11:00
Can upgrade the struct to be an enum. We can refer to these cases directly and we can refer just to the feature. Brandon
— 1:11:10
How about while you go through this kind of mechanically, I’m going to answer a question. there’s a question about wondering if calling this at reducer for destinations would be ambiguous somehow.
— 1:11:22
Is the type being an enum versus a struct determine how the macro expands? and it is true that yeah, enum versus struct expands differently, although Is it the case that if we find a date struct inside an enum, do we abandon out of that path and expand it the old, and just do the old work? Stephen
— 1:11:46
every time we encounter something that the macro would add for you, we, we do our best, there may be bugs, and if you encounter anyone Any of them let us know, but we, we just skipped generating that.
— 1:11:56
So we’re not going to add a state, structure enum. If you’ve already added it yourself, we’re not even going to add the static body to this new enum style of reducer. If you’ve already added it yourself. So you can, Brandon
— 1:12:07
or what about, what if you had an enum? Well, all right. So, so also with this question, let me back up a moment.
— 1:12:13
The idea of enum reducers is probably not a particularly like where, where literally that reducer type is an enum is not particularly. A useful concept. We have never once come across a situation where that that was useful. You want state to be enums very often, but not like the reducer itself. So so in that case, we don’t think it’s ever really.
— 1:12:34
It’s going to be an ambiguity problem by allowing at reducer to change its behavior when applied to an enum versus a struct. However, if it turns out that someone has this really great use case for enum reducers that we just couldn’t have possibly imagined, what we would probably do is update the macro so that if you all right, well, a new more comments came in.
— 1:12:56
But if you if you ever did your full implementation of the reducer inside your enum reducer, we would bail out of this like customized logic. and then there’s like a So it looks like clarifying the point that maybe it’s more about reducer destination macro versus reducer macro. We don’t personally think that really buys much, especially because as I don’t know if we’ll have time to show, but this also applies to path reducers.
— 1:13:24
So do we need a reducer path and reducer destination and reducer macro? We, they’re all reducers. We think it should just be applied as at reducer. Stephen
— 1:13:34
Yeah, yeah. And that’s. Take a look at the path because I did already migrate this over. It was a simple matter of just shortening this destination to be smaller and an enum.
— 1:13:44
But yeah, we do have a navigation stack in the app too. So I’ll search for struct path. And then at the app feature level, here we go. We have another one of those kind of verbose struct based reducers that has to just iterate over all the cases. And we even have that one meeting case that you looked at earlier, which doesn’t actually hold onto a feature.
— 1:14:06
But. We should even be able to support that. so yeah, let’s migrate it. So I’m going to update the reducer and let it know that the state we’re generating is going to be equatable. I’m going to collapse all these down to reference the feature and yeah, we get to just delete all of this and I’m going to move this up the top because it all fits now.
— 1:14:41
Okay. And yeah, of course this needs to be an enum.
— 1:14:47
Okay. And it’s like we have one failure and that is, yeah, we no longer need to construct the path at all. We also have an overload of for each. But I think other than that, this migration is even simpler and it’s, it’s already building. And so, yeah, back to that question that they had where, you know, would you want to add destination reducer?
— 1:15:14
Would you want to add path reducer? We think that this pattern is just a little more general and I think it. Hopefully applies to all the different kinds of enum reducers you can think of. but, but with this feature with paths, we actually have a kind of a special superpower, which is down in the view,
— 1:15:36
in this destination closure, and people have been commenting about this in the chat,
— 1:15:41
It’s a little verbose to switch on the store state and then match on the detail and then scope down to the detail all over again. So the nice thing that I kind of glossed over when expanding the macro is it automatically adds a conformance.
— 1:15:57
That allows you to switch over the store’s current case instead. And what that allows you to do is it allows you to pattern match on each feature and extract a scoped down store. So this is kind of like a new scoping operation. And that means you no longer have to scope on the detail. You can just immediately render that sync up detail view.
— 1:16:20
and then case will also just unpack normal data if you are not scoped down to a feature. But in the case of the record screen, we are, so we can let bind to the recording store and we don’t have to do that verbose if let in there. And so this is just a, another really nice feature that we’re able to unlock by just enhancing that reducer macro with a bit more kind of, bells and whistles. Brandon
— 1:16:47
And I don’t think we have time to show it. We should probably move on, but, it would also work in, and a UI kit situation too, right? Yep. Absolutely. okay. So let’s, let’s, I’ll, I’ll take over the stream. Alright, we’re getting towards the end, but we got one more thing to share. One more thing. One more thing, and let’s see.
— 1:17:21
I’m gonna, I guess I do have to actually clone. Stephen
— 1:17:27
Evan’s asking, is this reducer macro stuff available today in 1. 8? And yep, that is what we just open sourced, and we’re even using the new version in Xcode. Brandon
— 1:17:38
And, there was another, oh, the, the docs probably are going to take a little time to generate. So you’ll, you’ll want to check out the migration guide directly in the repo, but the docs will hopefully be generated.
— 1:17:50
So since we’d like just merged it live 30 minutes ago, it’s going to take a little bit of time to generate. okay.
— 1:17:58
So let me get this open because now we’re on to, Sneak peek: Shared state
— 1:18:20
okay, so now we’re onto something brand new that. No one is seen, and it’s the topic of next week’s series, if we, if we get all of our stuff together. Maybe the week after that if we don’t get everything together, but, let me share my screen in a moment. where do I want to do this? Where should I play around with this?
— 1:18:48
I’ll just be in case studies. All right. Yeah, I’ll be in case studies. so yeah. Lots of predictions in chat. Stephen
— 1:18:59
Oh yeah. Unveil server side TCA. Oh. Or point GPT. Brandon
— 1:19:04
Oh boy. Well, might disappoint some people and make some people happy. All right, let’s get to it. So I am on a, and let’s, let’s make sure, looks like SPM’s still resolving.
— 1:19:21
yep, Swift syntax always. All right, gotta enable some macros. What’s going on here? Doesn’t seem very happy with this. Stephen
— 1:19:35
You might need to reset the SPM cache. Brandon
— 1:19:39
Yeah, then it’s gonna All right. Bear with us. So data Stephen
— 1:19:47
integration is a question, you know, Brandon
— 1:19:50
I mean it’s This, what we’re about to announce, makes that far more of a possibility than ever before.
— 1:19:57
While this is resolving, what, I’ll just go ahead and just sketch some stuff out. so say we had a little feature, now I don’t think we actually have merged all that new reducer macro stuff into, into this experimental branch we had, so I’m going to just quickly fill in this kind of empty stuff.
— 1:20:26
Okay. And let’s see, can I just enable, I, it’s like, let me try re adding this. And it seems a little strange. Stephen
— 1:20:36
I’ve had to reboot Xcode when I got into this state. Brandon
— 1:20:39
Oh, it doesn’t even see TCA. All right. Avatar is all messed up. All right. I’m rebooting Xcode. No, no, Xcode doesn’t want people to see this, I guess.
— 1:20:52
Maybe we got to keep it secret a little bit longer. Stephen
— 1:20:55
Yeah. Well, I guess we can wrap up the Brandon
— 1:20:58
stream. Yeah. Stephen
— 1:21:01
Okay.
— 1:21:08
There it is. Brandon
— 1:21:25
Now let this be. Alright, there we go. Enable macros. Okay. All right. So while Swift syntax is building, what we have been working on is finally addressing the shared state problem. and so this is just something that comes up over and over in the Slack and discussions and we get emails about it. It is the concept of I’ve got multiple features, sometimes parent and child, sometimes sibling, and they’ve got this little piece of state that just needs to be instantly kind of known to both features and always be in sync.
— 1:22:06
And we’ve got even a case study on it. And the case study is not the best case study to really look at, you know, it gets the job done, but we don’t really recommend it. And there’s been, we’ve given just. You know, in discussions, never actually written it down in any official place, but we kind of told people loosely that dependencies are a good way of doing this, and now we’re finally ready to kind of talk about some of the real tools to do this.
— 1:22:30
And so if we were to create a little counter feature and we were to, you know, have some actions and we were to handle those actions.
— 1:22:48
And this, this part of the live stream is going to be very loosey goosey where we don’t want to show too many details. And so I didn’t prepare a ton on this, but we’ll, we’ll just give you the basics. So if you do this silly thing, this silly feature,
— 1:23:13
and then I guess I got to create a view.
— 1:23:23
So be a feature state with feature body,
— 1:23:33
and then we’ll show off the count. And we’ll have a button.
— 1:23:45
Okay. So it’s compiling. Make this observable state. Alright, so I’m gonna, I’m gonna put this in the entry point of case studies.
— 1:24:02
And, let’s see. What’s going on? I feel like I’m in bad state.
— 1:24:20
Nope, that’s a, that’s a correct, that’s on me. Stephen
— 1:24:28
Okay. Brandon
— 1:24:38
Alright. Stephen
— 1:24:38
Bear with us. I got a lot of helpers in chat. Yeah. Just reduce that reducer. Brandon
— 1:24:48
Oh, okay. They saw it before me
— 1:24:54
looking at chat while simulators booting.
— 1:25:07
Okay. So, all right. So yeah, it works of course, but now for some of the nice things. So in order to take advantage of shared state, there’s a new A property wrapper called shared and if you use it in an unadorned way like this, it forces you to pass it in from the outside and so then that would mean that we’d have to pass in some shared state here which we can just construct like this and the feature would work exactly the same.
— 1:25:35
But the key is that because you’re not allowed to provide a default here. In fact, this is a compile error. It forces the parent to provide a little bit of shared state, and the moment it does that, the shared state is synchronized between parent and child immediately. No additional work has to be done, no dependencies have to be involved, no synchronization of state where you do on changes in the parent to play the changes to the child and things like that.
— 1:26:01
It will just work. And then further. There are options you can provide to the shared property wrapper where you can say in what way is it persisted. And so we will have an app storage persistence where, that now any changes to this value will be automatically persisted to user defaults. But when you use a persistent strategy, you do have to provide a default because then, you know, what if there’s nothing in user defaults, you would want to be able to.
— 1:26:30
You know, seed it with some initial value, right? And then that means we no longer have to provide this here. So let me run this.
— 1:26:42
Getting an emoji wall in chat. Yeah. Pretty excited. Yeah. And this is just not even scratching the surface. But so, so yeah, so the app still works the same. But behind the scenes, it is saving data to user defaults. And they give the simulator enough time to actually persist it to user defaults because it doesn’t always happen immediately.
— 1:27:02
Hopefully it has actually persisted. And if I relaunch the count should be back at five. You, you want to show your, Oh no. Can’t even see what I’m seeing. Okay. So let me, let me do that again. Cause now that’s not very, it’s not very, all right. So, so it’s a counter app that works the exact same, but secretly with each one of these changes, data is being saved, to user defaults.
— 1:27:30
And if I give the simulator enough time for user defaults to actually be saved, I’m going to background it. Sometimes it takes a little bit of time for iOS to do its thing, but hopefully it did its thing, and now it should, it relaunched with 12, which is what the last value was. and then further, you, we have a file storage strategy, where you can provide a URL.
— 1:27:51
So we could do like, the documents directory, and we could append, append, Is it a pending, a pending path? And we could, you know, call it count dot Jason, any codable data will do. And now if I run this, well, count that John’s, it’s all the same. let’s run this in the simulator. All right. So it went back to zero because there was no data at.
— 1:28:22
Count. johns in the file system. But now when I increment, it is secretly saving this data to the file system. All right. And so again, if I quit the app and relaunch it, it should come back with the value seven because that data was saved, to the disk and, and also if I had multiple features here, side by side, and in fact, let’s just, let’s just do it.
— 1:28:46
Let’s do a. A pair of features
— 1:28:57
where we will have the state hold on to a feature one and a feature two, and we’ll have the action hold on to a feature one,
— 1:29:16
any questions while I, I could just kind of get this in place, you could answer a question if there’s anything. Stephen
— 1:29:22
Yeah, there’s some very specific questions. A lot about John’s new format. Okay, just going to compute with the new pickle. Oh, yeah, the format. Yeah. Yeah. Brzz asked if custom types need to be codable or hashable or something.
— 1:29:41
We’ll get to that. There aren’t any codability requirements. we want this to be kind of configurable and customizable. So, well, Brandon showed off app storage and file storage. we want it to be kind of fully extensible. So you could even bring your own GRDB or realm strategy, that kind of thing.
— 1:30:02
Mm hmm. Brandon
— 1:30:03
All right. I’m almost there. You could probably take another one. Stephen
— 1:30:08
It’s another question about key chain support. That should also be feasible for sure. Brandon
— 1:30:15
All right, here we go. So now we’ve got a parent feature and I can show the feature view with now we’re going to pass in the store from the parent.
— 1:30:29
So now this will take a store will scope it down to feature one
— 1:30:37
feature one and then right next to it. We will show feature two. All right, and so What we should see if I now throw in the pair of features view into the entry point
— 1:30:57
is two counters that are immediately in sync. Stephen
— 1:31:10
A few more questions coming in. one question is would this be a good tool to use for like a global user data object? And I think absolutely a great use case for like Having settings or a current user something that you need access to from a lot of different places. Brandon
— 1:31:29
All right, I got the simulator up.
— 1:31:30
And so now when I increment the top one, the bottom one immediately changes bottom one, the top one immediately changes. And let’s remind ourselves why that would be impressive. It would be impressive because this feature can be completely isolated and say it just has some shared state powered by file storage.
— 1:31:50
And we are showing two versions of it, two completely separate versions of it side by side. If we had the time, we would actually create a whole separate feature that also uses a shared state to show that, you know, it doesn’t have to be the same feature used repeatedly, but you can have a feature A, feature B, separate modules, no dependency between them whatsoever, yet their state is just immediately.
— 1:32:13
In sync with each other and it’s being persisted to disk and then I can also show that like a little in memory. If I if I go back to just being at shared and no persistence whatsoever, then that just puts the onus on me to provide a shared state and then you can’t pass it in. Stephen
— 1:32:34
Then this is a question that was just asked to basically if you don’t give it a persistent strategy.
— 1:32:41
the question is, will the data be erased on the next in it? Brandon
— 1:32:43
Yeah. Yeah. Or exactly. Yeah. Yeah. So so now it is if we told the parent feature now needs to provide a shared state to these two features. And just for a moment, what I’ll do, the easiest thing I could do is just initialize it here. But there’s other ways of doing this.
— 1:33:01
This isn’t necessarily the most foolproof way, but we could do feature one is this and we’ll pass it. some shared state. Let’s get rid of this. Oops. And what we’ll create some shared count. It’ll be a shared zero. And that is what you provide there. Is this right? Am I doing this right?
— 1:33:27
Yeah, okay. So, so now it’s it’s basically the responsibility of the parent and in a real application. Most likely the parent would also hold on to like some shared state and it would just pass it down to those features. But now when we run the count should go back to zero. Because data is not persisted and and there’s there are times when you want a shared state that’s not persisted And so that’s why we’re supporting all these things.
— 1:33:54
So we are back to zero, but we are instantly in sync between these two features And that is the very very very basic Idea of shared state. All right, going back to the talking heads and and that’s it. So we We’ve gotten I think longer than we did last time But I mean if people are interested we can just do a few more Q& A. Q&A Stephen
— 1:34:20
I think so there are more questions about shared. I think it’s still a work in progress. It’s going to be the next topic So we’ll have plenty of time to dive into the details. Yeah And yeah, we’ll Save the chat and make sure that we answer a lot of them in some fashion. Brandon
— 1:34:37
Yeah. There’s one, one good thing, which I didn’t meant to mention a moment ago is how is this persistent strategy going to behave in tests?
— 1:34:45
It is 100 percent testable. and it’s even exhaustively testable, which is the big. The actual big accomplishment to this thing is because we could have always just thrown a reference type in state and pass that thing around and boom, you’ve got shared state, but it takes a little bit more work to actually make this testable and that’s one of the big things we had to work on.
— 1:35:10
I’m just going to go through and answer if you want to answer. Let’s just answer a few more. Yeah. Yeah, yeah. Okay. If you want to, there’s one here. Does it save on every change or debounce? So the app storage saves with every change because I mean, I think even Apple just says, just hit user defaults, you know, do whatever you want with it.
— 1:35:30
file, storage though, it does debounce. And so it will save after a couple of seconds have passed since the data was changed. Stephen
— 1:35:39
Yeah, and all of that has to do with, like, the persistent strategy. So there’s nothing about the shared type itself that cares about debouncing. Okay, I had a bunch of questions before this. Brandon
— 1:35:53
Yeah, I’m gonna I’m gonna sort by most popular so we can talk about this stuff. Okay. And there’s Yeah, the very the top one is one actually, I think, came up again. later in the Q& A too, is, is right now when you scope, you do a scope with a state argument and an action argument, would it be possible to somehow collapse that into one argument?
— 1:36:15
you wanna, you wanna take that one? Stephen
— 1:36:16
It’s, it’s a tough problem and we haven’t found a solution that we’re totally happy with yet. it also requires leveraging even more macro things that can kind of break the compile time expectations. So while it is something that we would like to fix and we think the new enum reducer is kind of a preview of what’s possible, we, we don’t have a solution there quite yet. Brandon
— 1:36:42
So it’s, it would be nice and, yeah, but hopefully like, yeah, some of the changes we demoed today is clearing up so much code that the, the, the pain of two scope arguments will be less, all right. Stephen
— 1:36:56
We do have, Vlad is asking Chad if we could pull out the questions that are being answered and That was a feature that we used last year, but unfortunately, our Vimeo provider no longer has that feature.
— 1:37:09
And so, we’re kind of having to just read him this time around. Brandon
— 1:37:12
Yeah, and we found that out in the 11th hour. So we, maybe next time we can come up with a whole situation to, to show these better. All right, so then there’s, a question about Swift UI navigation being buggy, and so many people need to use UIKit for navigation, and then SwiftUI for the individual scenes.
— 1:37:36
Is TCA suitable for that approach? it is suitable for that approach, and I think there’s even some navigation libraries out there, that Allow you to use basically UI navigation controller under the hood, but then with like a Swift UI, nice city on top. And, and I think really ideally you would not, there’s also a second question of should you structure your reducers?
— 1:37:59
How should you structure your reducers? Really? You should structure reducers based off like the domain modeling problem, which has no. Concept or no care in the world for things like UI navigation controllers or navigation stacks Swift UI and all that stuff you you model that as pristinely as possible and then you try to bend the view to the will of the domain and so We just think yeah, it is completely possible.
— 1:38:24
Maybe you should check out some of the The libraries out there that can kind of help with that. Stephen
— 1:38:30
Yeah. For folks doing their own UI kit, we, we kind of showed a very basic version of that in tic tac toe, but yeah, it’s totally possible to use. just a TCA store to drive navigation and UI kit. Mm hmm. Brandon
— 1:38:44
let’s see.
— 1:38:46
Trying to look around for anything kind of new Stephen
— 1:38:52
question about examples of how to use swift data with TCA There’s a lot of interest in that and we do hope that the shared stuff that we’re exploring now will kind of Pave the way right there. Brandon
— 1:39:03
Yep. There’s a question about That the nature of TCA encourages tightly coupled features and reducer trees I’m not sure I agree with that.
— 1:39:11
But what are some best practices to decouple features to put them in separately compiled modules? I, the, I mean, the idea is we’ve got a whole bunch of articles and episodes about what we like to call tree based versus stack based navigation and tree based navigate. And there’s a huge pros and cons.
— 1:39:28
And in fact, our last live stream, we talked about this. there’s a lot of pros and cons to the two styles. It’s it’s just the the reality of it. and navigation stack is the most powerful tool for really decoupling something because you just get this flat layer of features with no dependencies between them.
— 1:39:45
But there’s a lot of cons to that style of domain modeling. And so sometimes you do want that actual coupled of saying, you know, feature a can navigate to feature B, therefore. I depend on Feature B’s code in Feature A, like sometimes that is so powerful that it’s worth the little bit of coupling. and so yeah, I just recommend watching the videos, the old livestream, and we have a whole bunch of articles in the TCA docs about this concept. Stephen
— 1:40:14
just going to chat, Flo asks if it’s a good idea to migrate an existing large e commerce app to TCA.
— 1:40:23
I would say do your research and see if it’s right for you. But, it is possible to use TCA in a migration based approach. You could take an existing app and kind of piecemeal add TCA to a new feature and test out the waters. Brandon
— 1:40:38
Yeah, we’re never going to tell anyone that they absolutely must do this thing or it’s the best way. There’s just trade offs all over the place. That’s why we’ve also spent a ton of time on like modern SwiftUI techniques where we showed how to do navigation and modern SwiftUI apps with no TCA whatsoever.
— 1:40:54
so yeah, we get no interest in trying to like bully people into using anything. you really just gotta, gotta like take it all in and see where your priorities lie and see what, what feels good to you. So, okay, so here’s also another thing about shared state. Any hints or clues how changes from user defaults file manager being published to the reducer?
— 1:41:22
well, I mean, y’all can go look at the code when Oh, so next week we start the series or maybe the week after, I don’t want to commit to next week. But, and when we start that series, we’ll be releasing the beta, period for the shared state, just like we did for observation. And reducer protocol and all that stuff.
— 1:41:37
And so you’ll be able to look into it, but yeah, this is something that we didn’t mention, but if someone makes changes to the external system, to the file system or the user defaults directly without going through the shared property wrapper, those changes will still feed in to your state in your features.
— 1:41:54
So everything is fully kept in the sink. Not only the features amongst themselves, but even the external system. Stephen
— 1:42:02
Yeah. And I think there’s a lot more that we’re going to be exploring over time as we. Like a lot of the stuff that we’ve discovered, we’re kind of in the discovery phase of, of all the shared stuff right now.
— 1:42:13
it’s just kind of spiraled out of control with all the possibilities. Brandon
— 1:42:17
It’s, it’s really great. Oh, here’s a good one in chat. any plan to look at app kit, Mac apps, multiple windows, stuff like that. We’ve also found that shared state fully unlocked, how. Like, Windows can be approached in Mac apps and iPad apps.
— 1:42:34
So we were experimenting with that quite a bit, and it just, it opens up all new possibilities. And so, yeah, so looking at multi windowed iPad apps and Mac apps would be a fun thing for us to do soon. And maybe we’ll at least get some case studies in place for it. Stephen
— 1:42:52
There were some questions around server side Swift, and I, I think that is something we would like to return to eventually.
— 1:43:00
Not on the immediate roadmap, but there are exciting things, in, in server side Swift in general, since we first created the, this website. And I think we’d also like to see. You know, TCA running in the browser, and that’d be pretty cool. Brandon
— 1:43:18
question about a roadmap. just that we for, for the shared state in particular, just that we’ll have episodes soon.
— 1:43:27
Beta period, we’ll start with the episodes. And then when the series is done, and we’ve collected, I mean, the beta periods aren’t there. For just some kind of vanity thing or something that has served a real actual purpose where we get the basic version of the tools in place that we are comfortable with, and then let lots of people use it.
— 1:43:44
And it, honestly, the observation beta probably changed the most from when the beta started to when we actually merged it. there was a ton of feedback we got from people and a lot of stuff changed. So, the beta period is a really important. Part of the process. It’ll be some number of weeks and then it’ll be merged.
— 1:44:04
let’s see. Stephen
— 1:44:05
We hope to get a lot of feedback and make things even better. Brandon
— 1:44:08
Yep. All right. Here’s a question about, why couldn’t Apple backport observation? and at the end of the day, Apple has got far bigger problems than we do. Like they are maintaining. A level of backwards compatibility in the way that they have to develop software is far different from how we have to develop software.
— 1:44:27
so the main thing that the reason that they could not backport observation is because SwiftUI would not be able to take advantage of that observation, of those observation tools in iOS 16 and earlier. The observation framework itself could have easily been available in iOS 16 and earlier. But it wouldn’t have worked with SwiftUI.
— 1:44:48
And, and technically, ours doesn’t work with SwiftUI either. You have to wrap everything in with perception tracking. And that just probably isn’t the type of thing that Apple is going to want people to do. So, so that is, yeah. So we, we haven’t really accomplished anything too magical. I mean, it, it feels great.
— 1:45:02
We’re glad we did it. But it’s, you know, at the end of the day, we didn’t really talk about this in episode or anything, but all we did was take Apple’s observation code base, copied and pasted into a new project, had to do some fixes. Then we added some niceties like the with perception tracking in the runtime warnings and stuff like that.
— 1:45:20
but yeah, there, there wasn’t, I wouldn’t say like, this is like, you know, a great feat of engineering that we got this work. It just, it just took some hard work and we got it done. Yeah. Stephen
— 1:45:28
We’re, we’re standing on the shoulders of giants already. So, yeah. There’s another good question about observation from Kyle, which is if Swift observation expanded to include value types, would we move back to, to use it, or do we do enough more than vanilla than it’s worth keeping?
— 1:45:46
That kind of goes back to, there’s a post by John McCall, one of the compiler engineers on the forums about what it would even mean to make These value types observable and the language just doesn’t have the features yet to support such a thing. it’s something that maybe they would eventually want to explore with ownership and they have the idea of like, what a location of a value type would be.
— 1:46:11
these are all questions that aren’t going to be answered anytime soon. And so we don’t think there’s going to be a need for us to move to it. but as long as it has that kind of model in the future, it’s something that we would for sure entertain. Brandon
— 1:46:25
Okay, so then there’s this question about any other things we’re thinking about for the 2.
— 1:46:31
0 release and and yeah, there there are some things we can’t give any concrete details but we can say that there is a kind of a new way of looking at some of the The ways that reduce the relationship between reducers and stores. if right now they kind of live in two separate worlds, but there’s a way to marry them a little bit closer, get them a little bit closer.
— 1:47:02
That, is going to unlock a bunch of really amazing things. And we, and we actually think that, that change. While significant and impact is, is not going to be a super breaking change or difficult thing to migrate. Maybe we can even make it a hundred percent backwards compatible. We’re not sure, but there is going to be something very interesting around that. Stephen
— 1:47:24
I think the most exciting things about 2. 0 will be all the code we get to delete. Brandon
— 1:47:29
Yeah. Yeah, right. Cause all that code that I mentioned, that was like soft. deprecated when I was showing converting syncups observation, like we are maintaining all that code still and that’s hurting compiled times that’s hurting type checking times and all these things, all that stuff goes away.
— 1:47:48
so then, all right, so some, I think some easy ones to answer, does TCA have something similar to vanilla environment object, that would be our dependency management library or now the upcoming shared. tools could kind of have some of that if you need, but for the most part, it’s probably the dependency management system.
— 1:48:13
and then a question here, isn’t sharing breaking isolation as the child has to for declare that some piece might be shared? I’m not sure I agree with that because, I, the alternative is everything is shared. If, if a child doesn’t have to say, I want a little piece of shared state, I want something to be in sync with other parts of the application, then the alternative is just everything is always shared.
— 1:48:38
So we think just having this tool to precisely apply is far better than the alternative. Stephen
— 1:48:44
Yeah, that even is similar to kind of some of the dependency and environment stuff in Swift UI where there’s just a single object that is attached to a type, when you do like dot environment and hand it a model and that just doesn’t seem appropriate for the default.
— 1:49:03
it seems like you do want to provide a key to some app storage if you really want that functionality. Mm hmm. Brandon
— 1:49:10
all right. So I, you know, I mean, We’re losing viewers very slowly. People can, you know, feel free to pop up. I’m having fun answering these questions. So let’s, let’s just do a few more. I, I see this one about possible to share a struct like, like a product, a struct product.
— 1:49:28
And yeah, the, the shared property wrapper has no qualms about what You put in it. If you’re not using a persistent strategy, you can share whatever you want in there. struct enum. so yeah, you absolutely can share any day. And then once you use a persistent strategy, then you may have some restrictions because, you know, if you need a persistent disk, Codable is a good way to do that.
— 1:49:49
Things like that. yeah, so. all right, so yeah, so, all right, there’s, it looks like two people are, are wanting to boost this question of, will, reducers be able to react to shared state changes? And yes, we will have a way for, in an effect, you could subscribe to changes of some shared state so that you can then send an action and react to it.
— 1:50:23
let’s see, will shared replace dependency? No. No, shared state is all about just state, it’ll share, it’ll replace the dependency style of doing shared state. that is something that we’ve described in various discussions of something you may want to do to represent shared state, but dependencies, you know, an API client, a database client or location manager, all those are dependencies.
— 1:50:49
And those, those are sticking around.
— 1:50:57
Here’s a, a fun question of just how do you balance your responsibilities as open source maintainers and as content creators? and I feel like we’re very lucky that they’re actually just one in the same, right? Like, yeah. Oh, spilled my drink. Careful. so yeah, we, we, the, the process of creating our episodes is what creates our open source libraries.
— 1:51:22
And it’s what I think allows our libraries. To, to kind of be as concise and just kind of buttoned down as they are. It’s, it’s the fact that we spend almost eight hours a day, like writing episodes and trying to think through every little edge case and trying to describe to someone from scratch why we should be doing the things we’re doing, like that is kind of the best way to motivate, you know, making a library.
— 1:51:51
And so, yeah, they luckily just kind of go hand in hand. Stephen
— 1:51:54
Yeah, I think time and again, we find that our libraries are just in a much better place because of the episodes. Brandon
— 1:52:02
Yeah, exactly. It’s and and then you know, of course our subscribers make it all possible. It’s like, you know, it’s really, you know, we couldn’t You know, this is our, yeah, this is our full time thing and so that’s, that’s how we get it done.
— 1:52:19
let’s see, let me, I’m gonna sort by popular one more time just to, let’s see, Stephen
— 1:52:30
there were a couple of questions about going back to some of the older functional topics that we’ve had in the past. we were way more explicit about it. We weren’t even that explicit. There were a lot of like more functional concepts that we, we, we dressed up to become a bit more digestible. I’d say we still kind of tackle these in every episode.
— 1:52:52
We, we just don’t go too explicit on it, but it would be fun to get back to some of the fundamentals. And, I think it just depends on the topic that we’re exploring and whether or not it fits in. Brandon
— 1:53:04
Yeah. Yeah. We’ve kind of gotten more into the mode of, When there’s like some little side excursion deep dive, we can do on like, for example, when we introduced the task result, which is now deprecated, but when we introduced that in the async series of episodes, we use that as an opportunity to really explore existential types.
— 1:53:25
And so we’ve just found that doing doing the deep dive of something abstract paired with something concrete worked really well. And so that’s how we’ve kind of been. So, so if you watch enough of our episodes, you’ll see. The fun little deep dives into something more abstract as we’re doing the more concrete things. Stephen
— 1:53:49
k. There are some good questions, I think it would just take too much time to really dive into an answer, and so we’ll just have to note them for either next time, or for a blog post, or a topic in another video. Brandon
— 1:54:06
Yeah, well, I don’t know, we could, maybe we just end it here. We just crossed two hours, so that’s already 30 minutes longer than last time. Stephen
— 1:54:15
So, I think it was another great success, I really hope we can do this more often, if folks are interested, and yeah. Brandon
— 1:54:24
Okay. Stephen
— 1:54:26
Till next time. Brandon
— 1:54:27
Till next time. Downloads Sample code 0267-pflive-observation-in-practice Point-Free A hub for advanced Swift programming. Brought to you by Brandon Williams and Stephen Celis . Content Become a member The Point-Free Way Beta previews Gifts Videos Collections Free clips Blog More About Us Community Slack Mastodon Twitter BlueSky GitHub Contact Us Privacy Policy © 2026 Point-Free, Inc. All rights are reserved for the videos and transcripts on this site. All other content is licensed under CC BY-NC-SA 4.0 , and the underlying source code to run this site is licensed under the MIT License .