Okay, we’re live. can everyone hear us?
Hello. Hello.
Audio coming in. Good. Yes. Okay, good. All right. So this is our fourth live stream. And we, the main reason we’re doing this is because, about a month or less than a month ago, we hit, our seventh year anniversary for point free. And so we thought a live stream would be fun.
Kind of celebrating the moment, also celebrating all the open source we’ve been able to work on over the past seven years. we’re even gonna, release a new open source project today, on the live stream. I think probably a lot of people think They know what it is and and maybe they do but but then there’s going to be a sneak peek at some upcoming stuff that I don’t think anybody knows about.
and then also as just a little fun thing we’d love to like give something back to the community. So we’re going to give away seven free yearly subscriptions, to one of our viewers on the live stream today. We Have a link at the top of the live streaming page. If you were to, you may have to refresh the page cause we just had a few minutes ago, but it’s a little Google form filled out with their name and email.
We’ll, we’ll, pick seven people at some point throughout the live stream. And then we’ll, we’ll reach out to those people or have those people email us or something. and. And yeah, so we, we’ve got a lot of fun stuff to cover, before doing any, like, big announcements or anything like that, we wanted to just show off some of the things from our sharing library, because We kind of released it at the end of the last year, and it was a wonderful, like, fun thing to release, but we didn’t really get to show off a lot of the advanced things that had come out of it, since splitting it off from the Composable Architecture.
There’s a lot of fun stuff in there that hasn’t gotten its full treatment, and so we wanted to show those things very concretely in a live stream. It’s, you know, we don’t really have time to cover that in episodes, so we’re going to just start showing that off, and I think I’m going to go first by, let me bring up My Xcode, oh, wait a second.
I got the secret project open right now. Let me get the other project open. Okay.
No spoilers.
Yeah, no spoilers. All right. So I’m gonna, I’m gonna switch over. Is everyone seeing this?
Yep.
Okay. so the thing that I, I’m going to show off, is showing how the shared reader key, this, this, this protocol that allows you to tap into loading from an external data source and subscribing to changes in that external data source, how it can relate to things that We’re, we’re really not on our mind when we built this thing.
When we built this thing, what was on our mind first and foremost was user defaults and a file system. And then later, we found that a lot of other things can be plugged in. And in particular, we’ve got a demo that shows a very basic Firebase, application that uses the shared key. And so let’s look at the call site first, and I’ll get a preview running.
at the call site, what we want to be able to do is just say that we’ve got some remote config out there with this key, and it’s a boolean, all right? And we want it to make a network request to Firebase to get that config and populate the state, but then more importantly, if we go into Firebase and flip that boolean to true, Our state should update.
Our view should update. Everything should update. All right. And so the way this config is being used is we’re going to show a little promo, down here that says, if show promo, we’ll say our promo has started. Nothing too fancy, but I do happen to have a Firebase app open. Let me show my Safari. All right.
So we’re not very familiar with Firebase. This is the only time we’ve ever dipped our toes into it, but there is this thing called remote config and it allows you just to have some simple key value. parameters that you can propagate to your apps. And I’ve got one here called show promo and it’s defaulted to false, but I’m going to open it up.
I’m going to change it to true. I’m going to hit save and we further got to publish and we further got to confirm publishing. And I think if I bring this over and we can look in our preview right here and I publish after a moment. It should show up that our promo has started. So, so the preview, even the code in the preview has subscribed to this external system and it’ll always stay in sync.
And of course I can run the demo too, or run in the simulator. and I’m gonna hide my Safari. Well, no, I’ll leave my Safari. I’ll bring up the simulator. So here we got the simulator. And so over in the remote config, I can now flip it back to false, hit save. Publish, confirm, and a moment later it should go away.
All right. And so that’s really fun. let me hide some of this, some of this stuff, and we can look at some of the code. what we have is this custom shared key, shared reader key down here. And the way it works is. We conform to this protocol in order to perform conform to the protocol, we need to provide a load endpoint and a subscribe endpoint and Firebase provides, APIs for both of these things.
We can call fetch and activate to get us into a state where we have a remote config and then we can ask that remote config. Hey, give me the config value for this key. And decode it, and then in subscribe, there is an add on config update listener, and, that allows us to get live updates, from Firebase.
And so every time this gets called, we will then need to go into the config and activate it so that we can then grab the config value and decode it. And that’s basically all it takes. so. I mean, it’s fun, you know, we, we really didn’t have Firebase in mind when we, when we built this, but we think it’s a really fun application of it.
And then there’s also another one that’s kind of in the same spirit, but, and we even got a Q and a about this, a question in our Q and a of, could you use shared to kind of represent API requests? And we think you absolutely can under certain circumstances. He wouldn’t want to do this for every API request, but we do have a demo for this too.
let me get the preview going. Where we have our, let’s see, I guess, switch over to API demo, where we kind of our, our, our beloved counter demo, where you’re allowed to count up and down and load facts about the number. We love this demo. We’ve built it over and over and over. all right, here it is. And so you can count up, you can get the fact.
A little progress indicator. but do I have internet? I mean, I certainly have internet. is the info P list? Is
there app transport settings? Okay. Hmm. I don’t know. Maybe this demo is actually broken. Let’s run it in the simulator.
Oh, it works here. Oh, you know what? I mean, could this just be that we don’t. Oh, it works here too. Maybe I didn’t give it enough time. so, so yeah, this is our, our, our standard demo that we like to play around with to explore concepts. but what we did differently about this is we didn’t, we don’t have like a, a dependency for facts, to, to load facts like we’ve always done.
What we do, what we have instead is a shared reader with a fact strategy. All right. It starts that you have a fact for nil, which a fact for nil is just, we have no fact, and then what we can do is when the count changes, we can say, all right, I want to, load a fact or rather, when, when we. Oh, oh, alright, so this, this, yeah, so, when the count changes, we want to clear out the, the fact.
So we say, let’s load a fact for nil to reset the state back to nil, but when we pull to refresh, let’s load a new fact for that number, or, when the get fact button is tapped, let’s load a fact for that number. So I can also pull to refresh, all that starts going and I get a new fact. Alright, and so this is showing off a bunch of new features that we’ve never gotten a chance to, to describe.
Which is, there is a load endpoint on the projected value of sharedState, where you get to change the key that’s powering that sharedState. and so here we’re saying, you know, this sharedState, I want to put in a new key, which is a fact for this number. And then also, there’s some properties on the projected value that allow you to check if it’s loading.
That’s what’s powering. This, progress indicator down here, but then also there is a try await interface to this load, which allows us to power the refreshable too, alright? So a lot of fun things here, and then if we go to the, to the actual key, it’s just simply, when you hit the load endpoint, we We’ll, so we handle some cancellation.
You can kind of ignore that, but we do kind of your standard thing of try, wait for the URL session. We throw in a sleep just to make it last a little bit longer. and then we resume if we get, with the continuation or get a value. And we’re also doing this thing where if you try to get a number, a fact for a negative number, we throw an error because we also have some error handling capabilities.
We can check, does the projected value have a load error so that we can show something. And so if I count down and get a fact, we get a failed to load fact. It’s a boring number. so, so this is how you do error handling. This is how you do progress handling. This is how you do, async await with loading.
and then, and then there is no subscribe because we have no concept of subscribing to an external source that updates us from the facts and, and that’s okay, that’s not the point of this. So this shows how you can do API requests within shared state. Anything coming through the chat, Stephen?
I’ve been answering a few things.
one question came in about the, Firebase demo, which is what if you’re in the middle of a flow and you switch an A B test and, I think there was a good reply to it, but basically you, you probably wouldn’t rely on the shared for the immediate state there. You would use it for the seed state for that flow.
Okay.
Mostly just. It was like folks are into it. Okay, cool. Excitement in the chat.
Cool. Well, I mean, yeah, that’s, that’s what I want to show. I think you’ve got some, some, maybe even more cooler things, I think, or my opinion.
Well, yeah, let’s see what everyone else thinks. I should be able to take over now. So, we have a Wasm demo here.
we want Swift Sharing and a lot of our other libraries to kind of be as cross platform as possible. there’s a lot of interest in Android that folks may be seeing right now with Skip. And so we have this demo. We have some, information on how to set it up. And I actually have the
Terminal for wasm already building this application and I can hop over to the app just to give a brief tour. but yeah, we get access to a couple of libraries that aid in building for the browser, which are JavaScript kit and the JavaScript event loop, which allows things to. just spin up in, into the web app and just run over time.
But otherwise we just kind of have a main app entry point. Like you would see in any Swift application, we’re using our Swift navigation library, another very cross platform library, which brings observation and navigation patterns basically on to any platform. you may need to write some of the tools yourself, but here’s an example of how you could do it with Wasm.
And then when the main entry point. Spins up, we Set up the global executor, and then we actually build our application. right now I just have shared stubbed out with an in memory count. but we’ll explore building a custom strategy that will be powered by local storage in the browser. And then, yeah, we just reach out to the global document, we build some, elements, and add them to the body.
And then down here, we set up observation using Swift navigation so that whenever the increment button, increments the count or the decrement button decrements the count, we just update the count label. And so I can open up the browser and there it is. And I can increment, I can decrement. So it’s all being observed by Swift, which I think is pretty amazing in the browser.
but of course, if I refresh, we go back to zero and that’s because persistence isn’t hooked up at all. And so let’s change that
and make sure to hide your safari too. Yeah. Yeah. Okay.
So I have a empty file here, to build this local storage key. I just need to import the sharing library. I’m going to import, I think I’ll need access to JavaScript kit
and let’s see if I need anything else.
Let’s start with that. So the way that you build your own persistence strategies or reader strategies is by, writing a structure class that conforms to the shared key protocol. And in our case, local storage isn’t just a reader. It can also write back into the browser’s local storage, but I’m going to call this local storage key and it will be a shared key.
And Brandon, you went over some of the endpoints before, but basically we have a load, a subscribe, and a save. And so we’ll want to stub those out.
I think there’s also a question from the Q& A that we could even kind of ask at this moment. of, when does a class for a shared key instead of a struct make sense?
Does using a class for a shared key come with drawbacks? Does it change anything?
yeah, there aren’t really any drawbacks, I think we even power some of our shared keys using reference types, file storage is one of them, if, for example, the subscribe needs to hold on to some state that is updated over time, or if you rely on things like de initialization, I think those are all great opportunities to use classes instead, I think just in general, it’s good to start with a struct and then move to a class if you hit a limitation.
But moving on, we can, introduce a value generic. I think we will want to kind of encode and decode to JSON whatever we store in the key to be the most flexible. So I can just introduce the value here. We’ll want to mark it codable.
And then also we, are going to want to be able to assign in the key. I can add, Maybe some of the JavaScript kit code just to show the API that we’re working with.
Basically, if I reach out to the global JS object, then I can chain into the window and I can reach right into local storage. In fact, I could maybe show this in the browser real quick. You
can right click and if you have the developer console, you can go right into JavaScript, where we have the window and we have local storage, and on that, there are a bunch of things you can do. in particular, we want to be able to get the item for, let’s say, count. And we had this demo running earlier, so when we have storage, there it is.
but maybe we can remove it so it gets cleared out later on. And now it’s null. And so we want to start interfacing with this directly in Swift. And so I will hide Safari and dive back in Xcode. And so, yeah, we have local storage and I want to get the item. And here we need a key. And so this local storage key is going to be unique to every shared reference that specifies it.
And so. It should just hold directly onto that key. You can do that as a string.
And then we can invoke this method. With the key is the first argument.
And I think the way that we can Yeah, we have a JS value here, which means we need to convert it to
A string value. Which we can do by just chaining on with string. And so this is going to be basically our JSON.
Chad is wondering if you’re nervous about live coding with 265 people watching you.
Oh, it’s 265 now.
And then, and then on top of that, using completely dynamically typed APIs and JavaScript through Swift.
Now that always scares me, but basically if we’re unable to load it. I think we can just take our context and, or the continuation rather, and we can just resume returning the initial value. And this is the initial value that you assign to the shared. basically just like app storage, you need to provide a default and this will just.
Say, Hey, use the default
and then there’s a quick question. Like, I think we could adjust for a moment. I’m just thinking if, if sharing could abstract data source similar to a repository pattern, so that, for example, if you change databases, no change has to be made to the call site. And I just think there is.
a lot of pros and cons to to that. It can be really nice to put a really abstract interface and just saying I want to get some data or I want to set some data. And, you know, on the inside, you could decide how you’re actually going to do that. And that kind of what that is, what the shared storage key is.
But then, of course, there is a lot of power to to your, your dependency, knowing exactly what the storage system is, because is. SQLite has a lot, or SQL has a lot of powers that a NoSQL database would not have. And so you just gotta decide, do you want those powers or do you want to fully abstract them away?
And it’s going to dovetail with some of the stuff we’re going to talk about in a moment. Yeah,
absolutely. Okay, so once we actually get the JSON string, we can just decode it. So let’s get a JSON decoder where we can decode the value from the JSON string. All right. Now this of course can fail, and while we’re not technically in a failing context, that’s exactly what the continuation is for.
And we have a few other endpoints on it. Including this resume, returning, resume, throwing, and then resume with, that actually takes a result. And I think maybe it would be convenient to do that because we could use that result catching initializer and then just do that work on the inside. Oh, and of course we need foundation.
Okay. So that is loading. technically we could subscribe to updates as well from local storage. I don’t think we’re going to be showing off that functionality. so, or no, I think we are going to show that off. So, we’re going to be doing some more dynamic Javascript, so I hope I get everything right.
but in here, let’s take our JS object global, go through the window and we want to add an event listener. And of course, well, it looks like there are some helpers here and the event listener we want is against storage. And then in here we need a JS closure that it’s like a callback for whenever an event is emitted.
And so I’m just going to create a listener right in here. By creating a JS closure with a Swift closure. I’m going to ignore the arguments that get passed along. It’s like the event, I think, and we don’t really need that. but once we’re inside here, I think we could just take our subscriber and yield a value to it.
And I think basically it’s going to be all the same work that we’re doing down here.
So let’s yield with results.
How about while you, while you get that in, which is similar to all that stuff. There’s a question that came in. so shared is used for representing data where the source of truth is outside the system. I’ve been thinking about using it for Bluetooth devices. Would it make sense to extend shared of a Bluetooth key with methods like BluetoothDeviceDoThing?
and I think well, I, I, I think it could potentially make sense. I think there’s a lot to, to think about and I’m not familiar. And I, I’m not, it’s been a long time since I’ve done a Bluetooth thing. So I, I, it’s hard for me to really concretely say, but I think it’s definitely worth exploring and maybe it could, make a fun case study for us someday, but yeah, it’s just really hard to say there’s a lot to think about there.
Yeah. For all these ideas, it’s always fun to jump on the GitHub discussions and chat about it. And if you end up writing anything interesting, definitely share. All right. But now we have our listener. I basically just copied and pasted the work from the load because whenever we get an update, we’ll want to load it.
And then as far as adding the event listener goes, basically every single thing. Returns a JS value and we just got to go out of our way to ignore it. And then one other thing is that subscribing kind of forces you to return a shared subscription. And this is just a long living thing that will get executed whenever, the key goes away, is canceled, deallocated.
And so this is probably a good time to tear down this listener. And so it’s basically the same amount of work. We just call, I think, remove event listener.
I can return that, and then I think we’re just going to have some sendability issues here. And sendability isn’t really a thing in the browser. JavaScript is single threaded, so we can just do non isolated unsafe. Hopefully silence that. All right. Okay. We have loading. We have subscribing. Last thing to do is saving.
I think we can just grab that JS object doc level one more time, go through the window and the corresponding thing to get item is set item. And then we just need to do the work of passing along the key and the string key. I think we’ll bridge to JavaScript convertible. And then we can take our value.
Which gets passed along and encode it. So let’s just use our JSON encoder, code the value, and then we want to stringify it
and this whole thing can throw. Which means we can wrap the whole thing using the continuation resume with the results of whatever happens in here.
So a quick comment, we could talk about that like, how often do you feel like you have to mark things as unsafe just to keep moving the ergonomics of all that feel really awkward.
And there are a lot of Q and A’s about this that we could dive into a bit later too. And I’ll just say that we, we highly prefer to never use unsafe. in this case, this is a legitimate use of it because, JavaScript is actually single threaded. And the funny thing is, is that there’s a lot of proposals on the Swift forums to essentially get Swift kind of into a state where it’s by default, mostly thinking of itself as main thread isolated, and then you get to opt in to actual concurrency.
so it’s kind of flipping things a little bit, but, but yeah, in such a world, you actually maybe not even, you wouldn’t even need this non isolated unsafe, but yeah, in the JavaScript world, this is the actual correct thing to do.
And also it’s, Swift Wasm is just such a new tech still, and I think as it gets more refined and kind of integrated in, into Swift, some of these things may be accounted for, especially with some of the proposals about kind of main actor by default.
I think those will kind of bleed into Wasm in a, in a good way. but yeah, if you, if you see the console in the terminal at the bottom, this has been refreshing and rebuilding application and it’s caught one thing that we still need to do. And it’s up here, which is, we also need to provide an identity.
That’s kind of one of the last parts of the conformance. And it’s just some hashable thing because sharing at the end of the day, the storage kind of is a global lookup. And so the best way to look something up here would probably be by the key.
Quick reminder, Stephen, to maybe speak a little bit louder and lean in a little bit more.
Okay. You can
get an ASMR stream. and the last thing is we need this to be sendable
and value actually needs to be sendable.
Okay. The question is, are we in building order? It looks like the wasm demo is building. Okay. And so let’s do the final step, which is to swap this in memory key. Out for a local storage key, but of course, I think we should get a nicer interface for it. And so we can extend sharedKey where ourselves are equal to, or rather, we need to introduce a generic, so we’ll call this localStorage.
It’ll take a string key. It’ll return self, and then we just need to constrain against localStorageKey.
And then it’s here that we can just construct the localStorageKey, pass it along, and now I believe we’ll be able to swap this out for localStorage.
And what is the confusion here? Oh, oh, static.
Okay, I think we can ignore that failure and take a look at the console, which seems happy.
And let me bring up Safari real quick.
And once you get this demo going, I got a question for you.
Oh, yeah. Can I wait till after the live stream? Yeah. Oh, yeah,
yeah. Yeah. Oh, wait. No, no, after. No, no, no. A question from a viewer. Yeah.
All right. We have fun here. So let’s open up Safari. And I’m not entirely sure it took.
Because, let’s see. We are incrementing up. And it is still zero. So. Could very well be an issue with the code I wrote, but if not, let’s just reboot Wasm and try this. It does look like maybe I have a typo. Oof.
Pilot crash. Yeah, let’s see if maybe in the console we have a better error.
Looks like unexpectedly found nil, unwrapping an optional value. And do we have anything in here that we can go to in our app?
If you expand one of those, I think we should at least see mangled names. Here’s some fun, yeah, those names there.
Okay.
Yeah, I’m not sure I’m seeing it.
so it’s a dynamic member.
Local storage key. Let’s see if we expand this. The save context seems to be having a problem. Did load have a problem? Load seems okay. So, let’s just hide Safari briefly and see if we can debug it. Worst case, I’ll just paste the working demo at the very end.
But yeah, alright, save, end point.
Oh, wait, it looks like local storage. setitem instead of window. setitem.
Ah!
Very good catch. Thank you, Pavel. Thank you, indeed. All right. There is a way to get, WASM, or to get actual demangled, stack traces. We, we just haven’t spent much time on that, but I mean, that, that is, actually possible to do.
Okay.
Cross your fingers, everyone. I’m counting up. No errors, so that sounds good. How to refresh. We have persistence and probably the coolest thing. I’m going to bring up a second browser window here, point it to the same one.
Okay. And it also loaded, but because we are able to kind of synchronize, using the listener, if I increment one, we will instantly see it in the other browser. And so, I don’t know, I think it’s pretty impressive. This is all being powered by Swift, by our sharing library. And, if you’re willing to deal with dynamic languages and kind of dynamic member lookup, it can be pretty powerful.
We had a funny comment that says that Pavel should win one of our, subscriptions. Yeah,
we can talk about that.
but I guess speaking of art, well, so first a couple of questions, that someone saying that they played around with a key using the key chain and shared, I know Stephen, you played around with that a bit.
And, the problem this person ran into was, the whole notification updates, no way to really do that. do you want to share some of your experience? We, we wanted to even ship like a case study for it, but we abandoned it cause it’s so messy and we just. We’d rather other people deal with it, but you want to share any thoughts about it?
Yeah.
I mean, the key chain is just complicated and it’s a pretty old API that hasn’t been updated in a while and you can see a bunch of the wrappers out there and they make it a lot easier and it might be best to bring one of those in. But I think, I think that is correct. You can’t always subscribe to updates.
And so instead you would need to use the load endpoint on the, on the shared key in order to explicitly load it when you need it.
but speaking of that giveaway thing, I completely forgot about it. And I think we, should maybe announce one or a couple, Stephen, pick a random number between one and 175.
let’s do 64.
Okay. 64 is Ibrahim Kotesh. we will either email us or we’ll email you. And, and I’m gonna make a note of this, but that will be our first one. And let’s go do another one. Pick another random number.
let’s do
Okay. Sean Rich. Okay, so there’s two. We got five more and we’ll come back to it. We’ve got 107, well now 178 responses. So yeah, anyone joining now? there’s a little link in the live stream right above this video. Add your name, email, we’ll, we’ll get, we’ll get back to you.
It looks like Ryan in chat is asking a question about shared.
Basically, is there a suggested maximum number of concurrent locations accessing shared state? there’s no suggested maximum. there is a lock whenever you access or write to shared. So you have to keep that in mind. But in general, it’s a pretty lightweight. Wrapper and you should feel free to throw it at whatever you want until you hit a limitation and and then go from there
cool Well, I think that means we can get on to Maybe an announcement.
Okay
Let’s I’m going to take back over. let’s see, is there another question here? Is there a way to build some tool upon existing sharing strategies, like when file systems subscribe, invoke, or saved? I, I’m not entirely sure.
I think I would need more, more context, but I shared, we we’ve opened just only a little bit of the API. I think, that would allow you to like really layer on things and customize, but we, we did open up more with the whole is loading state and the load error. And the load endpoint, which we’re going to explore a little bit more soon.
So, so there are things there, but yeah, it’s, it’s tough to know without more context and we’re, we’re open to opening it up more. okay, so here we go. all right, well, the library that we are announcing is probably not too surprising to people who’ve been following, but we have, well, here, I mean, might as well just bring up on my screen.
I don’t know if there’s any guesses. oh, and I see, where’s link to the raffle, Adam? It’s, at the top of the, this page on the live stream, there should be a link. and here we go. If I go to my screen share and I bring up Safari.
Oh, that’s still Firebase. where’s that Firebase window? I don’t even have it on my computer anymore.
One second.
I’d have to cancel the announcement.
Yeah, sorry, couldn’t figure it out. here we go. Safari. Ah! Oh, okay, there it is. There it is. so Here is our SharingGRDB repo. We’ve been, I mean, we’ve been doing episodes on it. We kind of completed our episodes. I guess it was last week and, and we’ve finally got it all packaged up and ready to go.
It’s currently private, but I think we can change that right now. And Stephen, you could even send out a blog post email. We got a blog post about it too. We’ll do so I’m gonna change the visibility. Let’s go public. There’s only one star. Oh, you know what? Wait, I need to start and watch it before I do this.
I can’t let, I can’t let someone else come in. So star and all activity.
Okay. Now it can go public. All right. Change to public. I want to make this repo public. I understand. Oh boy. Okay. My free code SharingGRDB oh oh Lord. I don’t need a code.
Alright. I don’t think I can make this public y’all. I’m sorry, . all right, lemme get my password manager. Oh boy. Did not anticipate this.
Okay, here we go.
Okay, it’s public. All right, there it is. And I, there’s a lot of fun documentation. although, Swift Package Index is going to take some time to actually process it. But, there’s a lot of fun examples, example repo or, applications. I’m going to open up the project so that we can poke around a little bit.
So I’ve got it up right here. Let’s, let’s show my Xcode.
Alright, so here it is. It’s got, what it, I mean, what it’s, of course, heavily inspired by all the episodes we’ve been doing, but it’s got a few other bells and whistles, to it, and there’s gonna be more improvements coming soon. But, of course, the main thing is that it is our for a lightweight replacement for SwiftData and the @Query
macro.
we don’t think it replaces SwiftData in all situations, but we think it can replace a lot of, a lot of applications, a lot of usage of it. and you know, let’s, let’s go back to, the, the repo. For a moment, because this renders a little bit better. so, so yeah, in the SwiftData world, where you would use the @Query
macro to fetch all the items, in the shared GRDB world, you can actually execute a, a SQL query.
You get access to everything that SQL has to offer. Whereas SwiftData likes to abstract that away and hide you from, SQLite. And of course, you don’t need to write all your queries as raw SQL strings. There’s a lot. of the tools you can use. There’s a query builder and things like that. but also, most importantly, these tools work from basically anywhere.
They work in UIKit. They work in observable models. They work directly in SwiftUI views. whereas the @Query
macro, of course, its whole power comes from the SwiftUI view. and we’ve got more information on all the different ways you can fetch data. with these tools, then also, you know, the way you start up your app in SwiftData is you got to create a model container, you got to configure it, do all your things with it, and then pass it along to the view.
And in our world, it looks pretty similar in the entry point of the app, you just prepare dependencies and you plug in your database. Queue or database pool and you create and migrate. and then once you do that, the database is accessible to the shared reader, fetch tools all over the place. And then also almost identical to SwiftData is that in the SwiftData world, you, you use environment to get access to the model context, you insert items into the context, and then you save the context.
in this world, you get access to the database from the dependency, you create. The dependencies start a database transaction and then an insert. So here it doesn’t look so different. And we’ve got a whole bunch of articles that explore this. And, you know, our, our main point with this library is to not shield you from SQLite because there’s just so many amazing things you can do.
And we’ve got a whole bunch of case studies. And, sample apps. And that’s what I really want to show right now. So I’m going to go back to Xcode and I want to show off some of these example apps. and in particular, so we rebuilt SyncUps using this library. So currently the, the, the version of the demo we have, uses the file system and JSON serialization, but that really prevents you from doing a lot of fun things.
Whereas SQLite gives you a lot of other abilities, but the app I want to show off right now. I’ll bring up the simulator is we we rebuilt the reminders app Like what’s the app that’s on the phone not not all the features But a lot of the features just to show how you can do very complex querying and as far as I can tell I’m not sure It’s even possible to do the queries that you need To, to build an app like this in SwiftData, the, the queries are just far, there’s too many joins and aggregates and complex things.
I’m not sure how you would do it in Swift. I think you would have no choice but to load everything in a memory and then process it. Whereas here we get to let SQLite do all the work. And so these stats here are live updating. so if I go into one of my lists here and, well, let’s see back here, we’ve got completed three and all.
And, and, well, yeah, I completed three, so if I go back and I check off a couple more, then back here, we’ve got completed five. and of course, all of this is, is live updating. We can show completed. We can sort. All this is done with, with, and so, with SQLite. It’s all done in SQLite. and then there’s a lot of fun stuff where you can actually search, for things.
So we can start searching. let’s see. So here, You know, we’ve got two. I can also complete one. It goes away, but then you get to see how many in the background have been completed, and you can decide, do I want to clear them or not? You could also show them or hide them. and all of this is, of course, just kind of live updated with SQLite.
It’s really powerful. there is a lot of complex associations. You get the ability to add a bunch of tags. You add the ability so these tags are through a join table between a tags table and a reminders table. I could just put this here and save it. So I got this. And then these tags are loaded for each row.
So, you know, that requires a pretty complex join to get all that data. so, I kind of wanted to exercise some of this by maybe live writing a very complex query and see how it goes. I told Stephen I wanted to do this query. just a few moments ago and we thought out loud how it might go, but I didn’t actually write it down.
So this will be live and so it may end horribly, but let’s give it a shot. So over in the reminders list, I’m going to bring up the preview or I guess actually even before I do that, I haven’t even shown any of the code that powers all this. So let’s, let’s show some of the code. you got the simulator still, still hiding stuff.
Yeah. Let me get that out of the way. Okay. So, so a lot, none of this should be too surprising if you followed along the series, we’ve got this idea of doing a fetch of some fetch key request and in here we get to bundle up all the information we need, to, to make our queries and you can even execute multiple queries in one, transaction and that’s what stats does.
So some of these stats. are, like we’re selecting the count for reminders where the date is today. So that’s the to date count. And I, I decided to just write this as a raw query because it’s, it’s easy. I know how to do it. same with this. I can count all the ones that are scheduled. That means ones that are greater than today.
But I can also use the Query Builder if I want. I can say, let’s just get all the flagged ones and count them, or all the completed ones and count them. And then I can just bundle all that data up into one big value. and, and so we are able to execute these four queries. These are the, all the stats data in just one database transaction.
But, then also kind of complicated is the reminders list. or no, this is not complicated. so this does a fetch of reminder, all the reminders lists, which was the, the, those lists I had, the personal and work and stuff like that. but then I need to join it to the reminder so I can count how many reminders are in there.
Alright, so I’ve got that. and that allows me to get the reminder count for each row along with the actual reminder. So, so that’s fun. but of course you can get some really complex queries. I think one of the really complex ones. Is maybe in the detail. Yeah, because you’ll notice, so we got this, this bad boy of a query, cause you’ll notice we now need to load up all the, all of the, reminders, but we also show all the tags.
All right. And. We can actually form a list of tags directly in SQLite without ever loading all those tags into memory and then processing them by joining the names separated by hashes and spaces and stuff like that. We have the ability, somewhere in here, this font is gigantic, so it’s kind of hard to see everything, but we have the…
Group concat?
Yeah. I can’t even see it. Where is it? Oh, here, yeah, here it is. So I get to, Select from the reminders, join in through our join table of reminders tags, and then join into tags. So I’ve got every single tag associated with the reminder, and I choose to aggregate all those tags to be comma separated.
Comma separated tags out of that. So along with every reminder, I get a comma separated list of tags without ever loading those tags in the memory. All right. And so this is the kind of stuff that you just can’t do in SwiftData because you don’t have access to the underlying, SQL light. And so we can do, we can really make use of everything that SQL light has to offer.
And this is maybe another reason why. You don’t want to go with the repository, repository pattern where you just have the abstract idea of getting and setting, because then that also prevents you from getting access to SQLite. sometimes that is appropriate, but in this application, we’ve decided it’s not appropriate.
So yeah, getting all the, this really nuanced complex logic and baking it into SQLite and it’s all testable, is really powerful. So.
Ax has a question. If one value changes, does it fetch all the other queries as well? And because we’re using GRDB’s ValueObservation
, it, it’ll only update queries for each observation.
So I think at the top of the file, Brandon, you have two different shared readers. And so each of them will only independently update if the associated queries change.
Yeah, yeah.
So
yeah, yeah. So yeah, SQLite has this, this idea of ValueObservation
and you can further, yeah, set up regions of observation.
And so if you, if you had two queries act on two different tables that only selected within those tables, a change in one table would not necessarily cause the other table to update unless that query joined to that table. And things like that. and, and you can even get more precise. Like if you do a primary key lookup, SQLite will even just observe just that one single row.
okay.
Yeah. One, one more good question that came up. Chris had some good eagle eyes and was wondering what the difference between @State.SharedReader
is versus @SharedReader
.
Yeah. Yeah. and it looks like, did we not update? That was a late update that we made her say, do we not update this demo?
It was added to the dynamic queries only.
Okay. Okay. So, so that is something we did and, and we’ve got, some docs about it now. And in a last moment, we decided to add the concept of a version of @State
that essentially works with SharedReader
. And so I guess I had that back over in the detail. Is that what it was?
I think so. You could do a quick switch. Yeah.
So. So we find that we needed this tool because what’s happening with dynamic queries, and I guess I should show that off to, let’s, let’s take a look at where we’re using the projected value. So we set that we use the projected value to set up like kind of the initial query that that loads these reminders, and then later we use it to update the query based off of how ordering.
And show completed has changed. Alright, and so the problem with this is that, in the act of a parent view needing to recompute and causing this view to recompute, it will cause this initializer to execute again and will reset this reminder state with a fresh query here. And that fresh query could be some kind of default state for the screen that’s not hooked up to all the dynamic.
Properties that go into it, and which means the state will just be cleared out. And so it’s like if you’re going to use these tools directly in SwiftUI view, you may want these shared readers to be actually connected to the life cycle of the view. And so that is what this is like an amalgamation of SwiftUI state and the @Shared
and @SharedReader
that will allow that to persist.
It’s really only a tool that ever needs to be used in a SwiftUI view and then further only ever used if you’re using dynamic keys. If you change The underlying key being used dynamically in the view. but otherwise you don’t really have to worry about it. you could also just decide to always use it in a view if that’s, if you don’t want to even think about those caveats.
There’s no cost to using it in the view. okay. Okay. And actually that Ryan has, I’m just looking over the chat, saying that the primary key observation must use the row ID. And that’s, that’s a good point. cool. All right. So, let’s see. I just happened to look over. I see something about, do you have an, in the Q and a, do you have an example of a binding to a shared property?
yeah, we’ve got lots. We’ve got a lot of examples of that in the case studies of the sharing repo. So that could always be looked at. Alright, so, so I’m gonna try to write a new query and see how it goes. so what if, what if, we had, so we got these top level stats over here that say today and scheduled and stuff like that.
What if we had a new grid row, and I think I can even use grid cell columns to make it take up the full row. Alright, so there it is. And this is going to be the really important stuff. and what I’m going to do is I’m going to get everything that is marked as high priority. Priority right now is just an integer 1, 2, or 3.
So I’m going to take all the priority 3s. I’m going to take all the flags. And I’m further going to look into all the tags and see which ones are tagged with, Something in particular. Let’s go even look at what are all the tags that are available to us. we’ve got car kids optional someday. So I’ll do kits.
So if we got something that’s up to do, that’s with the tag kids, that’s going to be a really important thing. So I need a query that can gather up all the reminders with all of those conditions with any of those conditions satisfied. All right. And so I’m going to call this, I’m going to have a @SharedReader
for the
really important stuff count. All right. And I’m just going to do it all in line right here. I’m not saying that this is the best way to do it, but I’m just saying that this is a very powerful thing we can do. and I can do a fetch one with some SQL and here we go. Oh, also there’s. Animation is supported and all this stuff.
I don’t know if people have noticed, but things do animate and you can supply an animation here, but I’ll just leave that off right now. All right. So how do I write this query? So I’m gonna do a select. We’re gonna do a count. I think SQLite likes to lowercase that. And we’ll do from, definitely from reminders.
All right. So that will at least just select all of the number of reminders we have. So let’s even go down and plug this, plug this in now. So this count here will be the really important stuff count, and that should already show up here and it’s 10. That, so it just matches that number. No big deal. But we want to say if it had, where, it’s priority, is three.
All right. So, all right. It looks like we only get three that are, priority three or is flagged. See if that number goes up a little bit.
All right. Went up to four. So apparently we’ve got one item in there that’s flagged and, high, high priority. All right. And then further, or if somehow we get a check, does all the tags of this particular reminder that we’re counting. Does it contain, the word kids? And so what we can do is say, I believe we can do is say, is kids.
Do I do this single quote to say that the string kids in
the string? Yeah.
Yeah. So, and then we do a sub query and this is where like SQLite is just so amazing for this stuff. We’ll select, all the tags. Or the tag name
from tags. Now I further got to get in, wait, maybe actually a subquery isn’t the way to do this. Should I just, I should just join through over to tags, but then how do you do an or? so let’s,
let’s get the subquery a try.
Okay. So now, but I mean, I now do get a inner join. Into reminders tags on tags to ID equal to reminders tags dot, tag ID.
And then I further, and actually if I have my console, I’ll even see if I’m getting any query. So I don’t have any purple warnings in here. So my queries are correct so far. It’s just not, it’s valid syntax, but it’s not actually computing the thing. So I now need to say that reminders tags dot reminder ID is equal to reminders.
ID this, this reminders. Does that seem right?
I hope so.
Went down to zero, so that’s not right. and yeah, so, oh, oh, do I have a double quote somewhere? Near, oh wait, near reminders tags.
So, you forgot where, I did forget where, thank you. Not giving a free subscription for that. No, I’m joking, maybe it will. All right, five. So, wait, wait, it went up. So, wait, if, prior to doing this, let me cut that out. It was at four, right?
Alright, so that means we must have a reminder somewhere that is not high priority, is not flagged, but is tagged with kids. And we could really check that, like, I’m gonna go over to family. And I’m gonna add a new one. I’m gonna say soccer game and we’ll add kids, but it will not be high priority or flagged.
In fact, I put a low priority. I don’t care about the soccer game, but this is six. It was automatically updated. All right, so I I don’t know. I think that’s cool I don’t know. I don’t know What chat thinks about it, but I mean I and of course this should be extracted out You know, I I’m not advocating that in a view you literally have 15 lines of code here, but the whole idea is that you just, you no longer think about observation and you no longer think about data staying in sync because now SQLite is the source of truth data source and you’re just projecting views into it to see, you know, what kind of data you want to extract.
And I just really don’t know how to build something like this in Swift, data. I mean, maybe there is a way, but I, I would wonder if. If it really is executing a query like this, or if it’s just going to load everything in a memory and then compute it, because we, we, of course, could have loaded all of our reminders in a memory and then check to the tags, but you kind of don’t want to do that.
so
that seems pretty happy.
Okay. That’s good. That’s good. I see a Q and a something about house performance with many items, a hundred K plus. I mean, SQLite is famously very performant. it can deal with millions, tens of millions. you, of
course, you probably don’t want to throw those into the SwiftUI list.
Don’t
want to throw all one million into the SwiftUI list. Also, you don’t, you don’t, I mean, if I go back to Safari, we had a very clear section there saying, SQLite knowledge required. We highly encourage everyone to learn about SQLite. So that would mean figuring out how to, normalize. Or de normalize.
Is that the, or normalize your, your table. So our tables look like this. We’ve got a table for reminders lists. It’s a little bit awkward to say, but it’s list of reminders. You’ve got a table for the reminders. You have a table for tags. And then you have a table for reminder tags, so we didn’t bake tags directly into the reminders table, we have a separate table for it, so all reminders can share those tags, but you need a way to join those tables together, so this is called a join table.
And then, so you want to denormalize your tables, but then also you want to add indices. I actually don’t think we have any indices, but, if you had a particular column that you needed to be able to search for quickly, like maybe, maybe we should have an index on priority. Although I think indices on columns, which don’t have lots of variants in the data, aren’t.
aren’t particularly useful, but, but still you, you may find that you need to add indices to certain columns. for example, one thing I’m doing in the search, or we’re doing in the search is just doing a, a straight up like, like, well, I can just search like, over in search reminders. and this. Is, you know, one way of doing search, but there’s a far more efficient way in SQLite, which is using full text search.
So you may want to look into using full text search when you’re actually searching for things. and I just want to show a few more things cause we also have these case studies. And they’re, they’re a lot of fun and they, they try to boil down the problem, or try to specify, like find one concrete problem and attack it.
Whereas this is just a gigantic app. Got to enable. I got a
good question in the chat about testing from Mads. How would you go about testing SQL queries are correct? And I think you can go into it more, but one of the cool things that you just demonstrated was. Those previews are all actually interacting with SQLite, and so you can be pretty confident that the, you know, query is correct because it’s actually exercising logic to the database.
Yeah, yeah, and so, we, I guess this is also a good time to To talk about that soon, although it’s not our next series, but soon we’ll be going into a series of episodes that we’re kind of calling modern persistence, where we will be showing how to use these tools at a very advanced high level, and a part of that series, of course, is going to be testing.
And so it is absolutely possible to write a test that is using shared throughout your features logic, and in the background queries are actually being made to SQLite, but an in memory version. That’s quarantine to your exact test case. And if that, if that SQL query through an error, you would get an error in your test, just like that purple warning I saw a moment ago when I didn’t have the where clause.
So you would get errors if you had actual syntax error. And then further, you’ll be able to snapshot and assert on the state that’s actually in your database. And so you’ll be able to make assertions on what is the data loaded. From that query. So you’re just getting beginning to end testing on the whole thing.
and then of course also these tools work wonderfully with the composable architecture. so, all right, I got the case study building. I’m a hop over. so we don’t, you don’t run it, these, you don’t run in preview or in the same way. You only run these as previews because it was a pain to have a whole bunch of different databases flying around.
so I’m going to open up some previews,
any, any questions that we could knock out.
So I see something about any plans to add API similar to SwiftData’s fetch results collection to sharing JDB for efficient fetching. so I, I think we, we would want to explore something like. offsets and pagination and stuff like that. But, it’s so plans too, but nothing concrete to share.
I think one of the problems there is shared is kind of, aimed at like those live updates and observation.
And as soon as you throw pagination in, kind of throws that out the window. And so kind of what our thoughts are is that when you need pagination, that kind of thing, you probably wouldn’t use shared directly. But it’s not something that we’ve explored in depth. So maybe we’ll actually crack it.
Yeah. Yeah. We have to, yeah, have some kind of tool, but, but then I would also want to maybe push back on a little bit. And because I think we’re getting another question is so loading all that data into memory at once. Well, I mean, let’s think about for a moment if you have 100, 000 rows, and, but they’re just, I mean, here we’re showing these little strings, these facts, it may not be the worst thing to load only the data you need for each row.
And it actually is completely possible with the tools we’ve built, which are DB and with some tools, we’re going to announce a little bit to just load just the little bits of data that you actually need. So even if your model has. 30 columns with a bunch of blobs of text or data or anything, you don’t load all that if you just need to show a single string and a number in every single row.
And so then I would say, is there actually a problem to loading, you know, thousands of strings into memory? I don’t know, probably not, you know, so. Alright, here we go. I’ve got, I’ve got this demo. It’s been going for a while now, and it’s on a timer where it, fetches a fact for each number in the timer and we see animation working, and that’s kind of the only point of this, is to show that it is possible to have this stuff working with animation.
but then there’s a version of that for dynamic queries where we can say So here we, we see the facts coming in and we got a little animation, but then I can start searching in here. So I see something in here about, dimensions. And so if I type DIM, we can now live, see that there’s a total of 13, 14 facts, but well, all right.
A live, a live fact came in, or actually multiple are coming in that match this query. So while I’ve only typed di. New facts are being inserted the database that then satisfy the query, and they automatically come in. And not only do they appear here, but they even update the count. All right, and then if I do dimension, then we get that one that I saw at the beginning.
All right, and so that’s, that’s kind of incredible. I, this, this demo is incredibly simple. I’m, I’m not doing anything magical here. I, I just have a single fetch facts here. And that fetch facts will get the, The, all of the, we order it by most recently inserted, we filter it for the body being like this query, and then we can fetch them all.
We can fetch their count matching that query, and we can just fetch all of them and everything just stays in sync. All right. you know, we got stuff showing transaction. We got UI kit demo because of course all this stuff works in UI kit. so I’ve got a collection view. So here, this is UIKit and it works the exact same.
I would have been able to put a search field here too. It’s all, it all works. I’m just using a collection view diffable data source and all that. alright.
Yeah, folks should check out the repo and, you know, poke around.
Yeah, yeah, so let’s, let’s get back to the talking heads. And Let’s see, I, we got, got a little bit more, but let’s see if there’s some Q and A, we could kind of chat about, I see, you know.
Do another giveaway or two?
Oh yeah, let’s do that. Let’s do that. All right. We’re up to 230, 250 now. Okay. I wonder if people are entering multiple times. I don’t know.
Let’s try.
So.
Anything between 250 and
Anything between, actually, actually the first row is taken. So anything between 2 and 251, go.
Alright, let’s do 250.
Oh boy. Okay. Leonetto. Okay. Marking your name down. Let’s do one more. Let’s do
Okay. Ch ch ch ch ch ch. Peter Bohack. I wonder if any of these people are in the live stream right now. So we’ve got four of the seven, so maybe we’ll leave the last three in a little bit. there was, oh, oh wow, alright, there’s a really great question that came in QA, but we’re gonna take it in one moment.
I’m gonna, I’m gonna throw up a question real quick. We, I don’t know, maybe, maybe it’s not even worth Doing all of this just to get that appearing. Is there a way to sync the data with iCloud? and this is something that we, we’ll probably look into at some point. I think probably better would be for people who really need access to this, to investigate it, maybe ask questions in discussions and give us their thoughts.
But there is the CKSyncEngine
that should. allow some of, you know, this stuff. And I think there’s an open source project out there, called Harmony that aims to get some GRDB stuff working with CKSyncEngine
. So, you know, there, there’s a possibility. There’s a possibility for sure. All right.
And then maybe I’ll put this question in, in order to dovetail with what Stephen is about to talk about.
All right. What do you think about this, Stephen? What’s your view on Swift compiler not holding our hands with raw SQL query written in the string open to accidental typos and errors? Oh boy.
Oh boy. that’s a good question. Let me take over. All right. so I have your reminders demo open right now. And, Something that we wanted to explore was the, the fact that, well, it’s very powerful that we can kind of extract these things away in order to get access to a slightly, safer interface.
Like you have this annotated method from GRDB that has many, the count, all these will guide you to, at least syntactically correct SQL. But unfortunately, we had to define this whole other type and kind of scroll it away down here when it might have been nicer to be able to use that query language directly in line here.
Unfortunately, this type kind of requires hashability, for a shared reader to know where to look up the information about that key. And unfortunately, the built in query builder that GRDB has is not hashable, and so this little bit of indirection is, is necessary. that said, I think we would like for the ability to write kind of more type safe queries directly in line here, especially when they’re very simple.
Brandon and I started to explore just that. And so this is the, the one more thing of the, the live stream, which is we are working on a very experimental project and it’s called Swift structured queries.
And this is just a completely kind of. Agnostic library. It doesn’t care about SQLite or GRDB, Postgres or MySQL, and it’s not a very complex library. I mean, we still have weeks before we would release anything. But what it allows you to do is you can define a struct for a table. We can use the player example.
And then you just describe the data. You might have an ID that’s an integer, your player might have a name, they may be injured or not. And then the main tool that comes with the library is a macro called table.
And I’m in a test target, so I’m just going to import testing to explore what this macro has done. And I’ll also import inline snapshot testing. So if we had a test for some basics. what this table macro has done is it has given us the ability to start building a query using this player and generates a lot of things.
But the way you get into that builder syntax is you can invoke all. And this itself is already a query and it has used the information that the table macro sees in order to generate it. So I could even write a quick inline snapshot of player. all.
And we have a little helper strategy here called SQL. If I run this.
While it’s building, we could, there’s, questions of, what happens when there’s multiple values in. A struct to query for and we want to dynamically enable disable actually, I guess I don’t fully I thought maybe as I read out loud, I’m not actually sure what that’s saying. if. There could be some clarification, I’d be happy to answer that though.
Alright, so it generated this query. we get very granular information about every single field that it’s selecting. And in fact, we could chain on to this. And maybe I will set the, I was going to set it to maybe have record mode on, but I’ll just do a second snapshot instead. Instead of just grabbing everything, we have the ability to filter, select certain fields.
So I could even go in and say, I just care about the player’s name. And all these builders take a closure where we can actually take a look. Where it takes the input, which is just all of the columns on the table. And so we can open up the closure and we get, maybe I won’t get it in this test target.
Usually the autocomplete works, but if I were to just say, I want to grab the ID from the player, or maybe just the name,
we’ll see that it’ll filter just the name we could.
And.
Yeah.
And yeah, oh yeah, and worth mentioning that this is, these properties are, only actual stored properties of the player. You, if you had a computed property, you’re not allowed to go in and try, accessing. Yeah. So if you just had like a, like
a slug string that combined the ID with the name and let’s see if we get autocomplete and the where clause.
So if I reach in, no, I don’t know why I’m not getting autocomplete, but. if you try to complete slug, or try to use the slug, that is not going to work.
Alright, so get some good questions, So, will keypaths work with the select? I mean, And, yeah, let’s give it a try. Seems happy with
that.
Yeah, but I think the thing to call out here is that this is not your normal keypath.
That is not a keypath to the string property of the player type. Alright, we’ll leave some of that magic. For another time, but it’s, this is what allows you to only select columns are actually present. You can’t select uncomputed properties. Cause that would be invalid SQL syntax. Yeah. another question about how do you handle pragmas with the setup?
it’s worth mentioning this, this query builder is very tuned specifically for acquiring tables. We’re not building a just fully general purpose query, anything you’re always allowed pragmas, however you want, and GRDB has, of course, execute helper, or you can just. Reach out to the opaque point or SQL light and execute it.
And we even want to support just embedding raw SQL, wherever you want, you should be able to just inject raw SQL and just give it the type that you expect it to be. And so it’s a little less safe, but we want it to be as flexible as possible.
And we also want this to support Postgres and MySQL in the future too.
Yeah,
but yeah, I mean, for where we could say we just want to select the injured players and that’ll work, but also the types that we’re dealing with in here are just kind of SQL expressions. And so, there is, stuff on top of that we could negate that is injured and Let’s see what happens there.
let’s just see. It is building. So we’re just going to have to deal with some diagnostics issues here.
Okay, and so yeah, it generated all of this from kind of this Swift friendly language. And, I’ll take it a little bit further before diving into what the table’s doing for us because we could have a join table for the team. That the players on,
and so it’ll have its own ID as a question. Yep.
Does this work with core data and well, and if this is in reference to the structured queries in particular, at least, sadly, no, because kind of core data’s whole point is to hide SQL light from you. this is very specifically trying to make SQL light.
And the upfront.
Yep. But let’s see what we can do here. So this time, maybe we want to select all the teams and then we want to join
on the players and we can give it an on, and in here we actually have two, sets of columns available to us. So we can say the team ID should be equal to the players team ID. And then from there, oops. We could even select
all the information for the team. But in addition to that, I want to get the player count from the team. So we could go into the player’s ID, get the count of it. And then I know in order to get the proper count, we also need a group clause. And so we can just group by, and yeah, we want to do the team ID
and I think. That’s it. So let’s run the test again and see what that spits out.
And so, yeah, we got all the fields from the team and the count of the players for each team. We got the groupby, the join, and so, yeah, I mean, it’s been a lot of fun to explore this, and I think we’re really excited to be able to apply this to SharingGRDB, but we can give a quick peek behind the curtains, because Oh,
just a quick, before you expand the macro, or, or quickly unexpand it.
I’m joking. but just do, do another where just to, just to have that, or someone asked, where is there a where? And so, yeah, I guess you have it in the second test. so, so a where you do a dot where open up a closure, you got access to all the columns and maybe do some logic in there. Can, can you say.
put it and name equals Blob, you know, let’s just try to, or, or, oh, yeah, we’ll make sure, yeah, go ahead, do it there too, or not equals Blob. So let’s, let’s count, let’s get all the info for every team and the count of all the players on the team that are not Blob, which we also got a good Q and A about who Blob is.
And so I don’t think we’re ready to answer that question though.
Let’s see if we have time.
All right. So here we have the where clause, players name is not equal to Blob. And notably, we were able to do this on this part of the join. So these where clauses are kind of incorporated into the greater team join clause. And yeah, all these things build up. So I could even refactor this a bit and say, I don’t care about the players in the group.
So I could just group directly with the team’s ID.
There’s a question here of why does each field in the SQL query have to be separately quoted?
it doesn’t have to, but. We want to have maximum flexibility. This format could change by the end, but the bottom line is, if we expand the macro, every single one of these columns, you could specify anything in here, and so, it is totally valid to have a column with a space in it, it just would need to be quoted, and you could also have a column with a quote in it, and that would also need to be quoted, and so we just do the extra work of ensuring that these are always going to be balanced equal.
Yeah, it’s just the easiest way to just not have to worry about what’s even in there. Yeah. all right. All right. Sorry. You’re, you’re doing the macro.
but yeah, the, the macros here, these are kind of just. placeholders that this extension uses to understand. So if, if you omit them entirely, it’ll just default to using the actual field names, but you’re free to specify columns very explicitly.
And then this is the gigantic thing that makes everything work. we extend this player table to conform to some protocols. it can work with both tables that have primary keys and Tables that don’t, and then we have this column struct, and it is the thing that defines the kind of schema of the table.
And so these are the properties that you’re getting access to when you’re in these kinds of builders down here. So you’re actually accessing a column type here and not the actual, player type.
And there’s some other fun things that we generate. for primary key tables, we, automatically define this draft type, which can be used for uninserted things. And so we could take that for a spin too. I think that’s pretty fun. I
think, I think so.
And so let’s, I think
if, if just, just to set it up, if anyone has, played with SQLite and try to create struct types representing the, the, tables in your database, you’ll probably immediately come face to face with the fact that you want to have a primary key, like an ID, but you seem to kind of be forced to make it optional because If a player hasn’t been inserted yet, there is no ID to use, and you want SQLite to choose that ID for you.
But then also, it’s a real bummer to have an optional ID just floating around your, data, your code base. You’re always having to worry about that optional, and then also, that makes your type not really, you can’t really make it identifiable, because an optional ID is very dangerous for identifiable, because nil, all values having nil are somehow identified together.
And so we thought we could fix that.
Let’s see if we can get better autocomplete here, but we might have to just wing it. the draft type is going to have everything except for the ID. So it’ll be name is injured and team ID.
And so we’ll have Blob. He’s in good shape. And we’ll just say he’s on the first team. And then in addition to select, yeah, I’m not even getting, Oh, in addition to all, well, you know, this is a preview, but we do have an insert and we can hand it an array of as many things that we want to insert in this case, blob, and we will do that as SQL,
or maybe that was causing some problems.
Okay.
Let’s
try that out or
I feel like you may have to
close and reopen.
Yeah. But I think there was actually something here.
I want to comment that out.
Yeah. There’s a comma after your group. Your group has a comma. Oh, yeah.
Okay. Does that mean everything is autocompleting? Nope.
No, no, it does not.
Oh, and group by.
Okay. Okay. Okay. Now we’re getting some, oh, there it is here.
Okay. And yeah, it will generate inserts and, all these things keep track of all the types and so on. So if I actually show,
yep, throw in a dot returning self. yeah. I was going to show the types.
Oh, sorry. Yeah. Yeah. So if we have an insert kind of statement here, we’ll see that.
Well, it’s, it’s not even letting me do that. Okay. Let’s just show returning. And so basically. Insert here will return kind of a void return statement, because by default, insert doesn’t actually return any data, but there is something called a returning clause, and this gives you the opportunity to jump in and grab the columns, and so we could return just the ID if that’s the one we care about, but we could also return the entire player, and so if I rerun this, We should hopefully have an updated SQL.
And so, yeah, it will return all the information about the player, including the new ID. And so you basically be able to go from a player draft to a player, just right after you run the query.
And this, this is like the perfect tool to use to like hand a draft off to a, like a add player screen, which doesn’t need to be worried about the ID.
It can make all the mutations to that. And then you get to insert it and get the full player after that.
I’m going to just show off one more tool because we got all the different query types working and so there is the opportunity to update. So we’re going to say, we’re going to create a new name, And here you can just actually kind of mutate the record.
So we could say, I’m going to update the name, all the players to have Esquire.
Someone’s asking if there’s parameter packs involved. And indeed there are, there are,
and we have had to move heaven and earth to make them work in some cases. But yeah, this will generate an update statement, set all the players names to their current name plus Esquire.
And for people who don’t know, that or sign in SQL is string concatenation.
okay. And so, yeah, in case people, haven’t, or I think we’ve alluded to it, but this is the sneak peek of our next series of episodes. We are gonna build this from scratch, see what it takes. And our whole motivation for this is to have a query building language that is hashable from the very beginning so that we can stick these kinds of queries into shared rather than using a raw SQL string and rather than having to define this whole separate type.
That defines the query and then plug that in and have things separate. We just want it all in one place. And then once that series is done, we’re going to do our modern persistent series where we build a very complex app and layer it on step by step, have everything hooked up and ready to go. I think, I mean, this went longer than I expect already, but I think we should answer some questions because we have so many unanswered questions.
Yeah, let’s talk about that.
Yeah. Can’t wait to. To show it off in a few weeks or a few months. Who knows? Yeah.
Yeah. Who knows? All right. Let’s, let’s do some, I’m going to start by popular now and, and, and try to get some in, I mean, I, I gotta, I gotta put this one up, even though we’ve kind of already, answered it, but it is the most popular one.
So to make sure that, we see that it has been answered because I also like how it’s phrased. Sharing with SQLite is in my opinion, what SwiftA should be with this implementation allows syncing with CloudKit. I agree with the first part, but, but yeah, the, the second part we had mentioned, it’s going to take some time and so hopefully we can work on, but we’d also just love to see what the community can do, start discussions and play around with it.
That’ll, that’ll certainly help, help us out. and Stephen, let me know if you, if you see any that you want me to throw on, but there’s one that’s kind of fun too. The second most popular. So I’m having to take screenshots of questions and manually put them up because Vimeo removed the ability for us to do this automatically.
so we have this very manual process. So, any plans to introduce some workshops and trainings. I would be first in line. so we, we actually do do workshops. And in fact, we did a two day workshop with a company just this week. So we’ve been live streaming a lot this week. so, so far we’ve really only done.
Kind of company level trainings and workshops, we would love to try to open it up and have it more like people just anybody could sign up and we just host something like this. so it’s something like we would consider for sure. We, we would maybe need to kind of test the waters and see how many people are really interested in it.
but we certainly do for companies, full day and multi day training. so. So there’s that.
There’s a good question about Swift on Android. A few down.
Yep. Yep. I’ll grab that. I
really wish this was easier. Okay. I
can start reading it while you go. Yeah.
Yeah. You can take it.
Yeah. Ratnesh asks, with the recent announcement of Swift on Android working group and efforts by skip. tools community, would it be possible to have composable architecture, dependencies, sharing, etc. packages compatible on Android in the future?
And the good news is other than composable architecture, a lot of these are already building on Android and folks from the skip tools community have been doing a great job of submitting pull requests for the small changes that need to happen. we kind of talked about it earlier, though. We, we do strive to keep things as cross platform as possible and the only reason that the composable architecture isn’t cross platform yet is for some legacy reasons, but even that is something that we, we do want to address.
Yep, there was a quick question that kind of was related to the, the structure query stuff you did in the chat. Just, I, I’m assuming this generates SQL. So can I plug it into things other than GRDB? And absolutely, yes, it is. It is a going to be a completely dependency list, just SQL building library.
And we hope to allow it to build Postgres and MySQL and things like that too. all right. Let me know if you see another one.
Question about TCA 2. 0.
Okay, yeah. If you want to read it, I’m trying to find it. How many likes does it have?
Okay. Where is that?
Anonymous asks, what are the plans for TCA 2.
0? will the minimum iOS deployment version be raised by it? we have a few. Things that we want to do with 2. 0, and we’ll be documenting it, I’m sure, in episodes. one of the plans is that cross platform. That’d be pretty nice. another is to better have the actor model figured out. we know that some folks want to use the composable architecture and spin up stores that work on actors or background threads.
And that’s currently not possible, but hopefully by 2. 0. And then we, we also just have a fundamental change of the internals that we want to make that will unlock a lot of things that aren’t currently possible. And it’s kind of inspired by, how SwiftUI kind of, you know, has this shadow DOM of a view hierarchy that it keeps track of over time.
And right now, the value type of the reducer is kind of like, every single time it just spits it out. It runs it on your state and then it’s torn down and we want to just add a little bit more state in the background and it’ll unlock some really cool things that we’re excited about.
I’ll throw up this one here.
any foundational episodes on barring, consuming, non copyable? I like the Swift concurrency series. There was also another question somewhere I couldn’t find it. Someone saying they also like the back to basics and any more of those come in. And yeah, there definitely are. We, we even considered blocking the structure query episode.
In order to do a back to basics, generics, and existential deep dive, and that would have been fun, but it just, it didn’t feel quite right to be blocking that, that material just yet. So, we do have the plans of a back to basics, generics, existentials, free for all, especially with all the parameter packs and variadics and all the new existential powers that have come over the last couple years.
we of course need to do a lot more concurrency. It is a moving target, unfortunately, so it’d be nice to see. How things are going to shape up with that before really diving in, but we do want there. I mean, as TCA 2. 0 comes, we will be covering a lot of that. And then, of course, the barring and consuming and non copyable stuff.
We have high hopes that that’s going to we’re going to be able to put that in a few key places in our libraries. And so when we get to that point. We’ll have episodes on it where we’ll do the deep dive and maybe we can kind of carve it out as a, as a full dedicated series also. So it’s absolutely, we would love to do that.
and then I’ll just throw up this one because it is now the most popular. So we got to address it. What are thoughts on things like Kotlin multi platform? What do you think happened in the future of native hybrid cross platform stuff? And that kind of is kind of what you were talking about a moment ago, but specifically here, the Kotlin multi platform stuff.
I mean, it seems great and it seems like people get a lot of use out of it, but, you know, of course it’s, it’s you, you know, going over and putting all your eggs in the Kotlin basket. And so, you know, if that’s what you and your team want to do, you know, that, that’s all good. But, you know, you know, when, when we see the.
Stuff like the structured queries, like a lot of that is coming from things that are just in Swift. You know, this is, you know, I think there’s other languages that can also do things like that. You know, Rust has very powerful macros and very powerful type system capabilities that allow that kind of stuff.
But, from my experience with Kotlin, it doesn’t allow stuff like that typically, but I, yeah, I don’t know. It’s just, it’s really just up to your team, but it seems like a great thing to have in general.
Got one come up?
there are a few interesting ones. there’s one that kind of is a callback to, the functional start of PointFree. got a few new upvotes, and that’s from Brian Lane. And yes, in the beginning we showed off custom operators, currying, zurrying, other functional programming techniques, all very cool, but it seems much less frequently used in all the open source frameworks that we write now.
can we explain why? And yeah, I think you’ll probably expand on this, but when we first got started, we had a lot of success applying. Functional programming techniques and things from functional programming languages to the kickstarter code base and unlocked a lot of interesting things. And that kind of was the impetus for point free, sharing that kind of stuff with a broader audience.
However, it does go against the grain of Swift. The ideas work and they compiled, but as, you know, Swift grew, the ability to use those tools became a little, less friendly. And I would say that even today, a lot of the things that we do in our open source frameworks, including the new sharing stuff, the structured query stuff, all these kind of have a shadow of the FP beginnings.
But it’s just, we, we really do want to embrace Swift, the language itself and use it to its best ability.
Yeah, yeah, I agree with all that. I just rapid fire screenshot at a few that we could go through. so here’s one is TCA still your main offering or are you branching off to focus on more libraries?
I, I just want to call this out that we’ve never had a main offering on only TCA. We’ve always, even in episodes, we’ve always intermingled TCA topics with completely vanilla switcheride topics. And we even dedicated multiple series to just UI kit. So we, we actually don’t feel like we focus on TCA. a majority of the time and we’ve got an, and as we like to describe it now, kind of TCA has really created a whole constellation of libraries because at this point we have split off maybe 10 or I don’t know, nine, libraries.
We’ve got the custom dump, we got swift navigation, we got case paths, we got issue reporting, we got dependencies, sharing. Yeah. So, so yeah, to us, it’s TCA has just kind of been the best incubator to split out all these other libraries. and then, and then kind of related to that is just, would you consider making a series about advanced navigation patterns, with or without TCA and SwiftUI?
And we feel like we. We had dedicated series to modern SwiftUI and navigation patterns. So I think we, we think we kind of have covered this stuff. So it really be more of just kind of figuring out what, what are the things left out there to, to explore. And then this is a very funny one. You used to very consistently use explicit self, but that changed mid 2024.
Can you explain the reason you tweaked your programming style? This is very astute. I think if I’m remembering correctly, Stephen, I feel like we just, had lost, we didn’t see that change come through in Swift that allowed rebinding Swift with the whole guard, let, weak ify self dance and where like at that point, well, wow, yeah, that’s actually, it works out really great.
That about right?
Yeah, I think at the very beginning we liked explicit self as just a way to know that, you know, you’re in a specific context also with reviewing code on on GitHub. But, yeah, as soon as we realize that, in fact, omitting the self helps you catch, extra bugs in closures that are escaping, we, we try to omit it wherever possible, because now self is very explicitly just calling out those moments where we are capturing something.
Mm hmm. Oh, man, I, we’re knocking them out, but more coming in, and I remember seeing some really good ones. I, I wanna kinda, alright. Oh, all right. So yeah, this is one, this one, it’s not voted a lot, but I like it. So I’m gonna pull it up and Steve, let me know if one catches your mind. I can, can screenshot it.
but I like this one. someone who’s been digging around our point free code base and let’s see, kind of screenshot this thing. Digging around our point free code base and they noticed something called style guide V2 HTML. And it is true that we kind of rebuilt how we do HTML on our website. And for people who don’t know our entire website’s built in Swift and it’s all open source.
And we did it in a way we had two goals for it. one was to be able to use result builder syntax, cause that’s. It’s far superior to how we used to do things before result builders existed. But the second thing was we wanted to be able to style components in a kind of method view modifier style that we all know from SwiftUI.
And secretly what that does is build up a style sheet in the background for you that you can then just inline directly into the CSS of your page. So our website has all the CSS just embedded right in the head. We do not load. Any external style sheets and that may seem strange and that may scare people.
But, our CSS, there’s not a single page on our entire website that the CSS is more than 8 kilobytes or 9 kilobytes. And so if, if the CSS can be minimized to just that size, there really is no downside to just inline it directly into the webpage. And so, and so we do that. And And we don’t have this yet, but we know it’s possible and we want to do it.
We could even have it so that it streams the, so that you can stream the document and add the styles as you go. And so we had fun with it, but it’s still just all embedded in point free. and we’d love to split it out and maybe even do episodes about it, but that’s just, there’s so much that we’re doing right now.
It’s, we’re not sure when that’s going to happen.
Hopefully eventually. Yeah. I don’t know if it’s necessary to screenshot it, but Amir commented that he’s not a fan of SQL migration, and is there any way to make it easier to manage? And, in our opinion, GRDB does a good job here, provides migration tools that ensure that it only runs migrations when it needs to.
And even though if you use core data or swiftData, it may seem nice that you get some automated migrations. As soon as you add something like a uniqueness constraint, it’s, it’s really a lot more work and a much bigger pain in. Our opinion to, to migrate than using just SQLite.
Yeah. oh, you know, that’s a good point.
can I quickly show something fun in the documentation? I’m gonna, I’m gonna switch over to my screen and bring up Safari. So this is the SharingGRDB docs. although it lost it. So. There it is. and in the documentation for SharingGRDB. We’ve got a dedicated article about the comparison of SwiftData, oops, and this new tool.
And in particular, these migrations, we want to kind of point out to people that lightweight migrations, although it’s not, this is doc C code, but lightweight migrations in SwiftData are really fantastic. They allow you just to define your model. And when your model container kind of resolves everything, it’s like, boom, it migrates everything for you.
And you can even just add additional fields and suddenly all that gets migrated too. Whereas, you know, yeah, with SQL lite direct access to SQL lite and in our library, you do have to actually add a migration and do the work. However, these lightweight migrations that SwiftData can do. They’re wonderful when they work, but when you can’t do a lightweight migration, there is a lot you gotta do, and we actually call out the seven steps.
And we have a side by side, although it’s not, rendering here, but the, the end of it is, this is the, the SwiftData way. You gotta define a version schema, you gotta duplicate your models multiple times, you gotta set up a schema migration plan, you gotta set up this thing for the will migrate and did migrate.
And then you got to use that in your model container. So when I look at this, this migration plane here versus what one would do in a SQLite, it’s a no brainer. And this, and what this was doing is adding a unique index to a column because you got to delete any duplicates before you can add that unique index.
So yeah, so to us, actually, this is even a place where SQLite even continues to win in our minds.
yeah,
a fun little one from, Pietra SM. what is the meaning of numbers often used in point free episodes like 42 and 1729?
Yeah. Oh, you want me to answer that? Well, the 42 is the whole Hitchhiker’s Guide to the Galaxy. You know, you can look up that number. The more fun is the 1729, which is the, There’s this apocryphal story, I don’t know if it’s true, maybe it’s true, but of a very famous Indian mathematician, Ramanujan.
Who was essentially at this time on his deathbed and a fellow mathematician comes in and the math, the mathematician comes in to visit him remarks that his taxi number was unremarkable and the number was 1729 and Ramanujan being this person who could just see numbers in a way that no one else could, he said, Oh, that’s an interesting number because it’s the sum of two cubes in two different ways and it’s the smallest such number, you know, I don’t know if it’s true, but yeah.
It’s a great story.
anything else popping up?
I mean, there’s just so much. Yeah. There’s a question here. Do we work full time on point free content libraries or do we still do some consulting work? as we mentioned a moment ago, we, we did do some workshops and we do consulting work very rarely every once in a while, but, but point free episodes and open source libraries is our full time, full time job.
all right, I’ll address this one too because it is now the most highly, rated one. And it is, what do you think of the current state of Swift concurrency? This is a tough one, but let’s get it up. so what do we think of the current, state? I feel like you have to have a PhD to use it correctly.
and I, I completely sympathize with like how it has seemed a little bit out of hand with how things went, but, but I, I do feel like the tools are actually. Incredibly powerful and that there is a migration path to be able to ignore a lot of the noise of concurrency for a long time and try to dip your toes into it.
and I think, yeah, I think there’s just been kind of a lot of misinformation passed around when When it kind of comes to like getting on social media and like getting just getting angry about things. but the tools are incredibly powerful. We use them to guide how we build our libraries. It gives us a lot of confidence that we can make changes, be caught early on things.
Of course, we’re a little bit fortunate that we’ve got these like small modular libraries all over the place that we’re working on. I think if you just have a gigantic Xcode project that hasn’t been split down into smaller components, that then, that, that is tough. And that probably is how the majority of applications are built out there.
And so that’s, that’s definitely annoying. and so I think some of the new proposals coming soon are going to, going to help with some of that, right? It’s going to be more switching to a single threaded frame of mind and opting in to concurrency a little bit, a little bit more slowly.
Yeah, I agree.
Question from Zev about snapshot testing, we have question directed at my sqlite. swift library.
Oh, yeah.
I can just tackle that quick. the question is why our focus was on GRDB and not sqlite. swift. And I, I haven’t touched sqlite. swift in many years. before, Brandon and I even met and worked together at Kickstarter.
I had kind of. Sunset it and another group of folks started maintaining it. but I don’t think it is very actively maintained at all. And GRDB is quite actively maintained and it’s a great library and we wanted to, you know, give it its due.
There’s a question in the chat that it’s the thing that just keeps on popping up every couple of months.
It’s a real engagement bait of any idea about SwiftUI architecture MV versus MVVM. and this is just a conversation that is really unfortunate because it’s just, it’s so difficult to communicate and, and have everyone on the same page of what are the terms we’re even describing. you know, I think there’s.
people who want to advocate for the idea of, of leveraging the Swiftride tools to the, to the maximum of their abilities, which completely sounds reasonable and we absolutely support, but without considering where are the downsides of those tools and what can we do to fix the downsides of those tools.
And then, And then also to further kind of say that we should just look to the Apple sample code as, you know, this is how Apple says we build things. when in fact, we just recently did a deep dive, archeological exploration into dozens of Apple sample code projects to just really look at what it is that is common across all of them.
And I think we just came back with the impression that. The purpose of all those sample codes, the fruit truck, the backyard birds, fruta, all that stuff is just to, show how to use frameworks, how to use APIs. You got your store kits, you got your SwiftData. They just want to show how to use those things.
They are not giving us an app that we say, Oh, this is how I structure all these screens and navigate around. I’m going to follow this step by step. I just do not think that is the case whatsoever. And so, and so in that void of, we, we have to figure out how do we want to use the tools and how do we fill the gaps that the tools leave behind?
and so, yeah, that’s, that’s where we lie.
I think we should do the last few giveaways.
Oh yeah. I keep forgetting about that. Alright, we are up to 272 now. Alright, that’ll be the end of it. So, random number between 2 and 273.
Okay. Alright, Stephanie Finn. There we go. Alright, we got two more.
Let’s do 152.
Okay. That is Stephen A. All right. Last one.
Hmm. Let’s do. What was the last number? 271?
Let’s do 171. 171.
Okay, Josh Kinney. Alright, we got our seven. So we will reach out. And, I mean, Yeah, we’re at two and a half hours. That’s a lot. But we got 181 people hanging around. I don’t know if we want to answer some more questions.
And, It looks like, Yeah,
there’s a question about swift embedded. Kind of being a new thing and do we have plans to embrace this platform and it’s definitely on our radar. We want to explore it, especially with the kind of wasm experimentation that we’ve done so far. yeah, definitely something that we would like to cover
a question about the Swift syntax recently emerging a commit, for precompiled binary.
So if people aren’t aware of this, this should be the first step into. Reducing the pain of Swift syntax, there’s been mention of it could be a Swift 6. 1 thing, but I don’t know, I’m not going to get my hopes up, but at least there has been a little bit of movement in this area.
yeah, we got Zevs as now one of the highest rated ones of where do we see the future of our snapshot testing library? In fact, I think I have it locked and loaded here. do you have plan to update, overhaul it? Any indication that Apple supports snapshot testing, we got zero indication of that.
I mean, currently even Swift testing doesn’t support attachments, though I think maybe it’s been implemented and coming soon. But, yeah, we don’t, we don’t have a lot of contact with Apple, so we don’t, we don’t know what Apple’s doing. but, the future of snapshot testing, we would love to Spend some time with it.
And we do have plans on doing that. It’s just, it’s really tough to prioritize because the thing, the thing works for the most part, we know that there’s annoyance with deficiencies and certain snapshot strategies. and there’s a lot of PRs and issues open, but we’ve said this a few times in various issues in PRs, but our view on the library is that we would much rather people.
Maintain their own snapshot strategies rather than fully depending on our strategies. We, we provided some baseline strategies, but there, there are, have been multiple PRs that fix something in one strategy and then it just breaks for someone else because it’s a very tricky topic to try to snapshot these simulators and, and things like that with the pixel accuracy and all that.
So our ultimate goal would be to get it to a place. We have some additions we want to make too, cause we want to make it async await friendly and stuff like that. And in the process of doing that. Hopefully remove a lot of the really controversial strategies and have other people maintain them. that way we don’t have to field so many issues cause they’re just, it’s almost daily and definitely weekly that we get issues about.
It’s just very difficult to maintain this and really unfortunate that we’ll fix one thing and just break a bunch of other stuff. So that’s our, our plans for that.
[02:00:13] Yep.
there’s a little, comment, not a question, but we, we also address this in our TCA FAQ. So I like to just bring it up as, the issue with TCA is that it becomes so much of the core of your app that many people are reluctant to depend on it as a central library. And our, our response to that is like completely understandable.
And you know, do what’s right for your team, but is it really that much different to kind of reading some person’s blog post, hearing their thoughts on something and then kind of cramming that into your code base? Also, you you lose all of the context of what did that person go through to build this? Are they still happy with it a year later?
Did they make any changes to it a year later, or are they truly following that exact pattern one year later? You know, it’s just, there’s a lot of nuance there. And so it’s just, there are no easy answers to any of this. You just really have to decide, you know, what, what is good for you and your team.
We got a lot of the, the bigger questions. I’m not sure if anything else is kind of standing out to you.
let’s see. Well, the top two voted ones are basically the same question of how do we go about learning things, and someone making that comment that I feel pretty dumb and have to rewind when I watch the videos, but, I mean, we’re lucky that this is our full time job.
We are just doing this all the time. We pair, we use Tuple for pair programming, and I, we’re probably their most Most used account. I feel like we’re pairing eight hours a day. We just do lots of explorations. We like to have goals in mind of some problem we want to solve. And then just use the tools to keep chipping away at it.
Iterate over, over and over. The thing that comes in the video is many, many, many hours of writing a full transcript to dive into it. Us going over it on the video. It’s just so chiseled by that point. It’s not us just kind of You know, off the cuff doing things. I mean, you saw earlier, Stephen, messed up his local storage.
I messed up my SQL query, you know,
that’s, yeah, there, there’s plenty of editing in the episodes and also it’s not like we’re just diving into the problem. We we’ve spent weeks on every episode kind of figuring out every little thing.
And there’s one person who asked about a community chat and yeah, we do have a Slack and someone posted the link, into it. So we got us. oh yeah, I think, I mean, this, I think is the longest we’ve ever gone in, oh, any chance Oregon has a point free conference for the future? This is just kind of similar to, we would love to do like a workshop, you know, kind of thing.
We just got to really gauge, you know, what is the interest in it? we do do workshops for companies, but doing something a little bit more public. yeah, I think, I think we’re getting to the point now. I mean, we got through 35 Q& A questions, but, Yeah
We’ll pull them down and you know If there’s anything that we feel like we could put into a blog post or you know, bring up the next one.
Mm
hmm All right Well, all right. I think we’re gonna end it.
Yeah. Thanks everyone for joining. Yeah, and Till next time.
Okay