Empower Apps

Tim Condon stops by to chat about Vapor 5, the biggest Vapor release in six and a half years. We get into dropping futures and promises for Swift concurrency all the way down the stack, Span and strict memory safety, and the new shared Swift HTTP server.

Guest
Related Links
Related Episodes
Chapters
  • (00:00) - Server-Side Swift Today
  • (02:29) - Vapor 5, Span & Memory Safety
  • (13:38) - Package Flags & Dropping EventLoopFutures
  • (18:36) - Streaming & the Swift HTTP Server
  • (24:21) - VaporTesting & Macros
  • (33:18) - OpenAPI & the Future of Fluent
  • (38:48) - AI, Open Source & the New Networking APIs

Watch
Click here to watch a video of this episode.

Transcript

Support the Show
★ Support this podcast on Patreon ★  

Thanks to our supporters:  
Welcome new supporters:

Social Media
Credits
Music from https://filmmusic.io "Blippy Trance" by Kevin MacLeod (https://incompetech.com) License: CC BY (http://creativecommons.org/licenses/by/4.0/)

Creators and Guests

Host
Leo Dion
Swift developer for Apple devices and more; Founder of BrightDigit; husband and father of 6 adorable kids
Guest
Tim Condon
Engineer, geek, triathlete. @codevapor core team. Founder @brokenhandsio. Server-side Swift @kodecodev. Organiser @SwiftServerConf @NSManchester He/him

What is Empower Apps?

An exploration of Apple business news and technology. We talk about how businesses can use new technology to empower their business and employees, from Leo Dion, founder of BrightDigit.

Leo Dion (00:00)
Welcome to another episode of Empower Apps. I'm Leo Dion. Today we're once again joined by Tim Condon. Tim, thank you so much for joining us.

Tim Condon (00:09)
Thanks very much for having me back. great to be back.

Leo Dion (00:12)
I'm excited for today's episode because we get to hear all the big updates in Vapor 5. I'll let you

Tim Condon (00:18)
of you.

Leo Dion (00:19)
go ahead and introduce yourself for those who don't know.

Tim Condon (00:23)
Yes, I'm Tim. I'm one half of the Vapor core team. I'm responsible for building the main Vapor framework. So the kind of core HTTP server that everyone interacts with or web framework that everyone interacts with. And yeah, it's been a busy few months.

Leo Dion (00:38)
Awesome. What before we get started, what's been some of the big changes since the last time you were on as far as how how the whole environment of server side technologies have gone and where they've where they're headed.

Tim Condon (00:57)
So I think for Swift specifically, there's just a period of maturity, I think, kind of several years ago, especially when Vapor 4 was released, it was still kind of relatively early days. Swift has only been open sourced for three, three and a half years. So it was kind of a different time and a lot of the language features that we now can take advantage of didn't exist.

Tim Condon (01:22)
In the server world, I think there's been a of a greater focus on security, especially with kind of AI making it very easy to run security checks on open source code and servers. There's a big push for post quantum cryptography as quantum computing becomes more more tangible and people get worried about encryption being broken.

Tim Condon (01:45)
And then yeah, a lot of streaming stuff, AI is taking over so people want to be able to stream responses and of reactions from especially large language models as they kind of output their responses.

Leo Dion (01:56)
You mean MCP type stuff?

Tim Condon (01:58)
But both MCP and also if you're talking to say the OpenAI or ChatGPT API, when you send it a prompt, it will stream back a response. So you don't get the whole response at once. You get it in kind of chunks, which is why it kind of reads out bit by bit in the apps. And so

Leo Dion (02:12)
Right, right.

Tim Condon (02:13)
kind of that kind of usage of where things are streaming and we now got HTTP/3 as well, which is kind of another layer of complexity on top HTTP that we need to handle.

Tim Condon (02:24)
So those areas are things that we need to focus on as well.

Leo Dion (02:29)
So Vapor 5, you released

Tim Condon (02:32)
Mm-hmm.

Leo Dion (02:32)
the beta, what was it, a couple of weeks ago?

Tim Condon (02:35)
Yeah, last week I think.

Leo Dion (02:37)
yeah, you wanna kinda go over kind of the big picture as far as what big changes we'll be seeing in Vapor 5?

Tim Condon (02:47)
Sure, yeah. I mean, the kind of the I guess the highlight of Vapor 5 is the fact that it's six and a half years after Vapor 4 was released.

Leo Dion (02:56)
Which i which actually take that as a compliment. that means Vapor 4 has been pretty rock solid in a lot of ways.

Tim Condon (03:03)
Yeah, and we've managed to not break our API, which has been very hard in some cases and a bit frustrating in others. But yeah, we've managed to have a stable API. Obviously we've kind of deprecated a lot stuff and added new APIs that we've kind of tried to move people onto. But the old APIs are still there and still work. So the main highlight for me is kind of removing six, seven years of tech debt, which is very nice to do.

Tim Condon (03:26)
There was a lot deleting code, which was very satisfying.

Leo Dion (03:28)
Yes, it is.

Tim Condon (03:29)
But yeah, so the main kind of highlight features for this are we don't have any concept of futures or promises anymore. That's

Leo Dion (03:36)
Mm-hmm.

Tim Condon (03:36)
completely gone. So there's no more trying to understand what the difference between a map and a flatMap and a tryFlatMap and a flatMapThrowing is, which was a definite confusion point for people until Swift concurrency came along. So we are now fully kind of Swift concurrency the entire stack.

Tim Condon (03:51)
all the way down to the low levels of NIO where we have to jump back into a different world, but that's handled elsewhere now, which is nice. So we kind of support structured concurrency properly, which has always been a little bit of a bugbear. We have a brand new HTTP server. the new Swift Networking Workgroup has been working on a new HTTP server.

Tim Condon (04:11)
and some other folks as well, which is a modern take on what an HTTP server can be. it's very, I'm still pre-release. It requires 6.4, it requires Apple platforms 26, if you're working on platforms, because it does use all of the very latest features, which allow us to do nice things like ensuring that you can't like a connection or anything. It's impossible to do because

Tim Condon (04:34)
the compiler will not let you. So those kind of things of allowing connections to escape are not possible anymore, which should make your code safer and your server safer and stay up longer. We have better integration with the ecosystem. So there's a number of packages that have been released in the last several years that we can now properly adopt, like new Swift HTTP Types library that defines kind of HTTP concurrency types, Swift Service Lifecycle, which we can properly adopt,

Leo Dion (04:58)
Yes.

Tim Condon (04:59)
which...

Tim Condon (05:00)
will make it lot easier to integrate other services that need start and stop capabilities and us being able to manage that or you'll be able to manage us alongside. We use Swift Configuration so that you can choose whichever configuration type you want and it will just pick it up automatically. What else we've done? We've turned on all of the HTTP other Swift flags. So if you look at our package manifest,

Tim Condon (05:26)
We have 10 or 11 flags that we enable for upcoming features and experimental features that should make the code a little bit safer and a little bit cleaner.

Tim Condon (05:36)
And then outside of actual core changes, we have, we're kind of streaming by default now. So anytime you want to deal with a request body or response body, that's streaming as a kind of first class citizen. So, which enables us to do some nice things, but we still have all the syntactic sugar on top. So if you want to just decode some JSON, that's really easy to write. There's not any more.

Tim Condon (06:00)
than ever was.

Leo Dion (06:02)
you said something about only supporting OS 26. Why why is that exactly?

Tim Condon (06:10)
That's primarily because of the spans stuff that we're

Leo Dion (06:14)
Okay.

Tim Condon (06:15)
So we're making use of new Swift features that can't be backported, one of which is span, and there's a couple of others that allow us to, not currently, but when we kind of get closer to release, should allow us to be extremely performant because for span specifically, it means we can kind of

Tim Condon (06:34)
essentially safely pass a pointer through to wherever one needs to use it. So that memory is just borrowed instead of having to be allocated and copied everywhere. And then the compiler gives us guarantees that memory is not going to leak, it can't be escaped. So it's a safe, performant way of writing code.

Leo Dion (06:51)
Okay, I wanna really deep dive into that. like what's there's two things post async/await that I'm not super comfortable with because of my lack of experience and knowledge. But like the whole span memory stuff, pointer the new way to do pointers, and then and I don't know if you touch on this in Vapor 5, but like owning non copyable that stuff.

Leo Dion (07:18)
How do you utilize those and w what's the advantage of them and why for instance why even do a span if you could just have a class instead?

Tim Condon (07:28)
Yeah. So on the kind of point of span, so span is a relatively new type. It's been, some of it's been backported to older OS versions, but for the most part it's pretty new and the story is still ongoing. So some of the APIs we use don't have span support. Some of the foundation types we use don't have span support yet, but they have been pitched and should land in 6.5. But essentially the idea is that span is a,

Tim Condon (07:54)
view into memory that you can borrow and the compiler will put constraints on how you use that memory. So if you're, say, you wanna write some JSON to your response, so you produce some JSON, you wanna write it out, what would normally happen is you'd take your response from JSON encoder, which would be data, you then copy that data into a byte buffer, that byte buffer that would be transferred into NIO, which then be copied again, which then be copied out when it,

Tim Condon (08:20)
written to the network.

Leo Dion (08:22)
Yeah.

Tim Condon (08:23)
In an ideal world, would happen, what will happen is that you will create your JSON, which you will then own that data, because something needs to own the actual underlying data. And then you pass a span down into Vapor, which then passes a span down into NIO, or into the HTTP server, which then passes a span down to NIO,

Leo Dion (08:44)
NIO, yeah.

Tim Condon (08:45)
which means that

Tim Condon (08:46)
instead of copying the data every time, you're actually just kind of passing essentially a pointer, which I think literally is a pointer, down so that it can read the memory straight from where it was originally created and where it's owned, and then write that out straight away without having to worry about making copies and stuff like that. Now, realistically, that's not gonna happen the whole time because of back pressure and things like that, but that's the ideal view.

Tim Condon (09:09)
And this, essentially, there's two big things we care about for performance. The first one is reducing the number of instructions, essentially doing as little work as possible and being as efficient as possible with code. And the second one is reducing allocations. So if we can reduce the number of times that we need to allocate memory or make copies of data or stuff, then that's gonna make us more performant.

Tim Condon (09:33)
And by using span, we don't have to do copies anywhere. And so it's the same as passing a pointer in. If you're used to writing C or C++, you just pass that pointer around that star, char star or pointer or in Swift, it's called an unsafe

Leo Dion (09:48)
Right.

Tim Condon (09:48)
buffer pointer. But with span, the compiler enforces the use of the span itself and the lifetime of the span itself.

Tim Condon (09:55)
So if you try and copy that span outside of the scope of where it's being used, won't let you. The compiler will refuse to compile, which means you not have to worry about leaking memory anywhere. And it also ensures things are closed off properly as well. So that's kind of one the big benefits of using span. And it solves, it also solves a lot of the problems we've been having in the ecosystem of we need kind of some kind of generic bag of bytes type.

Tim Condon (10:20)
because that

Leo Dion (10:20)
It isn't Data?

Tim Condon (10:22)
isn't Data, yeah. Because data is part of foundation and there are use cases where you probably don't wanna use foundation and we wanna try and have the same API everywhere. If the ecosystem can agree on it and we can get the necessary kind of features in Swift, which I think we're kind of almost there, then we can just pass around span. So you can own it, own the data, whatever you want, creates the data, can own it.

Tim Condon (10:43)
And if you don't need to copy it anywhere, you can just pass a span around and it doesn't matter what the type is. It could be data. It could be a array of UInt8s. It could be a byte buffer. really doesn't matter. It's just a span. And if you need to read the bytes to make copies or whatever, then you can do

Leo Dion (10:59)
Okay, that makes a lot of sense. one of the flags I saw on your blog post was about something called strict memory. What does that mean exactly?

Tim Condon (11:10)
So there is a setting in Swift that was introduced in, I think it was 6.3, which is strict memory checking or strict memory safety. Strict memory safety, that's one, yeah. And so this is essentially started out as a feature for interacting with unsafe code, which is C and C++, but has kind of expanded a little bit into Swift world. So the idea is that,

Tim Condon (11:34)
If you are doing any potential unsafe code, such as you're dealing directly with pointers or unsafe mutable pointers or whatever, or you're talking to any kind of C code, you can turn this on and the compiler will force you to write an unsafe call at those call sites. And without that, can either get a warning or you can turn them to errors and you'll get compile errors.

Tim Condon (12:02)
And so this makes it makes you kind of think about where you're going to use these things and if there are better ways. So as span becomes more prevalent in Swift, the cases where you need to use like an unsafe mutable pointer or unsafe mutable buffer will reduce so you won't need these. And also makes you look at your code and how you're using it as well.

Tim Condon (12:24)
so you can really verify because it's very easy to find out. If you're doing unsafe stuff, can just search your codebase for unsafe and then really inspect those lines of code. It has also grown a little bit now as well because if you import a library that was written before sendable and concurrency stuff in and you have to do @preconcurrency, that import also has to be marked as unsafe as well with this setting.

Tim Condon (12:47)
because it's potentially an unsafe thing to do because you're kind of giving up sendable checking.

Leo Dion (12:53)
Does this d would I gain anything with regular i iPhone apps or stuff like that, or is it strictly only if you're bringing in older Swift packages that are pre-concurrency?

Tim Condon (13:05)
I mean, if you're writing iOS apps that use packages that are not annotated for sendable checking, it will force you to take a look at those dependencies and question. can say, is this dependency still something I want to rely on if potentially it is unsafe? Because you can do

Leo Dion (13:18)
Right, right.

Tim Condon (13:19)
@preconcurrency and it silences all the warnings from the compiler, but your code is still unsafe unless you've actually opting into full sendable checking.

Tim Condon (13:28)
then your code is still potentially unsafe. So it's good way of finding those things out.

Leo Dion (13:33)
Yeah, that's a really good idea. okay. We talked about well, we talked about the strict memory flag. Let's talk about some other Swift Package Manager flags you have enabled and explain

Tim Condon (13:43)
Mm-hmm.

Leo Dion (13:44)
like why you're using those.

Tim Condon (13:47)
yeah, so like, one that I'm quite excited by is internal imports by default. so in the Vapor 4 days and before, because all of our APIs returned futures or EventLoopFutures, we had to publicly export all of the NIO types that we were using. so if you want, if we wanted to make it easy for people to write

Tim Condon (14:08)
request handlers. Instead of them also having to import SwiftNIO, we could publicly export them and makes it lot easier to write. Which is great from a usability point of view, but from a library maintenance point of view is terrible because it means that we have now tied our public API to SwiftNIO's public API. And that then follows for everything that we were exporting.

Tim Condon (14:30)
So if we wanted to ever change internally what we were using, we can't, because that would then be a major breaking change. So

Leo Dion (14:37)
Very

Tim Condon (14:37)
internal imports by default is a compiler flag that essentially tells the package manager that unless you mark your imports with the public imports, then these are internal imports and the symbols cannot be exposed unless you opt into it. So there are times where you wanna do a public import such as

Tim Condon (14:54)
In our request, we return types from the Swift HTTP Types library and we wanna make those public rather than wrapping them in our own types. So it's kind of consciously opting into those public inputs, but it does mean that the things that are now internal, we can then adopt and change. So if we wanna get rid of SwiftNIO, because internally we're no longer using it, we can remove that and we don't have to do a major version release.

Tim Condon (15:17)
So that's something I'm quite excited by because the public symbols of Vapor have been quite annoying as we try to evolve things for sure.

Leo Dion (15:24)
Right, right. Yeah, I like internal by internal import by default. that's what I've been using pretty consistently. and it's just a it's a great practice to get into.

Tim Condon (15:36)
It is, yeah.

Leo Dion (15:37)
okay. Well let's g let's get the let's get the elephant in the room out of the way. removing removing all the event loop and future stuff.

Leo Dion (15:52)
How how big of an undertaking was it?

Tim Condon (15:55)
Massive. it was, so in Vapor 4.50, which was, I think it was released in 2022, if I remember right, which is just after Swift concurrency. was like a week after, in fact, maybe even a day after Swift concurrency was enabled on macOS. We offered Swift concurrency APIs alongside our EventLoopFuture APIs. So everything was async/await.

Tim Condon (16:18)
So from a public point of view, you could use async/await, but internally, we were still jumping between futures and concurrency. So internally, most of Vapor was still built off futures. So removing them was essentially, we had to touch every part of the framework. So any kind of part we were doing, any IO operations, whether that's talking to files or talking to the network or other libraries, we were doing that with futures pretty much exclusively.

Tim Condon (16:43)
So yeah, it was a massive undertaking because it was effectively meant we had a full rewrite of Vapor at that point. It was massively satisfying because like our code got so much simpler. Even things like the default middleware or the default responder, like we lost three or four nesting lines of code. And anyone who used to work with futures knows it was a pain anytime you kind of had to deal with like a

Tim Condon (17:08)
if branch or something. yeah, it

Leo Dion (17:09)
Right.

Tim Condon (17:10)
was nice, but it was futures were very, very pervasive through the code base. So it was, it was a big undertaking to

Leo Dion (17:16)
they work great, pre async/await. It w did what it was supposed to do, but yeah.

Tim Condon (17:21)
They

Tim Condon (17:22)
work fantastically, but just not particularly user friendly.

Leo Dion (17:26)
Right, exactly. what do you have any big challenges that you faced with that refactor

Tim Condon (17:31)
.

Leo Dion (17:32)
or migration to async/await?

Tim Condon (17:35)
it took, I think it took me a year and a half to land that PR, which isn't just because it was difficult, but, you know, we're working on both alongside each other. So we work on Vapor 4 and Vapor 5. There was a bug fix or a feature request into Vapor 4 that would then get merged. We then have to merge that into Vapor 5 and trying to run the two side by side for almost a year and a half, I think it was, was quite difficult.

Tim Condon (17:55)
trying to keep them up to date. So that was a big challenge. Yeah, I'm glad that we are now merged and Vapor 4 is now its own separate thing for sure.

Leo Dion (18:03)
are you still gonna be doing anything with Vapor 4 for now?

Tim Condon (18:07)
Yeah,

Tim Condon (18:07)
so Vapor 4 will continue. It's still the released version of Vapor. So we will continue to do bug fixes and security releases. And then at the point when Vapor 5 is released, we will commit to a likely, but not confirmed, six month support timeline where we'll support Vapor 4 for security fixes and any serious bug fixes for six months.

Tim Condon (18:29)
but the majority of the feature workers now in Vapor 5, they've branched off and they will start to diverge.

Leo Dion (18:36)
Okay. I had a question about streaming, but you kinda answered that because you said one of the big things has been AI has been a big push towards streaming. Was there something else I'm supposed to ask about that, you think, or you think we covered it?

Tim Condon (18:51)
I

Tim Condon (18:52)
mean, the main push for going streaming first was it was a mixture between performance in that for like small request bodies, it's fine. People don't really care. You just load the whole thing into memory. But as

Leo Dion (19:04)
Right.

Tim Condon (19:04)
soon as anyone wants to do anything that's even moderately big, you kind of want to be able to stream that.

Leo Dion (19:11)
Define moderately big.

Tim Condon (19:13)
I mean, I'd say anything over like

Tim Condon (19:16)
few hundred kilobytes is moderately big.

Leo Dion (19:18)
wow. Okay. Okay.

Tim Condon (19:20)
Not from like a major issue, but if you have a few hundred kilobytes and scale that up by 10,000 users, that's a significant amount of

Leo Dion (19:28)
Right. Right, right. I gotcha.

Tim Condon (19:32)
So yeah, we want to be able to do streaming by default. And we also wanted to make streaming super simple to use as well, because previously it wasn't. So we could make use of some of the new async stuff.

Tim Condon (19:46)
And yeah, the APIs have turned out really nicely. But the main thing is that it's now the same thing. So your request handlers, don't have to choose between streaming or non-streaming. It's streaming always. And you just have like a lazy collection of the body whenever you need it or whatever needs it first.

Leo Dion (20:02)
is streaming is a pretty much supported by nearly every client.

Tim Condon (20:07)
Yeah, well, so there's difference between streaming at the kind of app level and streaming at the kind of HTTP level. So if you send a cURL request using cURL from the command line with like a massive file, that whole file isn't sent in one packet, it's sent in several packets, which is then...

Tim Condon (20:28)
decoded into several chunks of the kernel level, is then decoded by NIO as several chunks. So we just make it super simple to iterate over that. And if you need to load into memory because it's all JSON, sure. But if you're writing that out to disk, then you can just stream that response straight to disk and not have to worry about memory.

Leo Dion (20:45)
Okay, that makes total sense. do you wanna talk about testing or the Swift HTTP server?

Tim Condon (20:53)
Let's talk about talk about the HTTP server first, then we can move on to the shiny new things in Vapor.

Leo Dion (21:00)
So from what you've told me and from what I read on the blog post, there's basically we have a layer that anybody can pick up and use. that's a layer on top of SwiftNIO that's an HTTP server. And the idea being is that anybody who wants to write a competitor or another HTTP server, they can use that. so like Hummingbird could use it and now you're using that in Vapor 5.

Leo Dion (21:29)
what

Leo Dion (21:31)
What what was the thought process and why this was needed and what what are some of the benefits that you've seen using it?

Tim Condon (21:39)
Yeah, so so the original kind of idea or intention behind this was that everyone in the ecosystem is using NIO because it's a fantastic, well-tested, well-used, well-developed, well-maintained library. But no one's writing, wants to write like channel handler code. Yeah, like

Leo Dion (21:54)
The boilerplate stuff? Okay.

Tim Condon (21:56)
it's complex. And realistically, if you're building a web server doing HTTP,

Tim Condon (22:01)
you've got to build an HTTP server and the difference.

Tim Condon (22:06)
between say the Vapor HTTP server and the Hummingbird HTTP server was pretty much nothing. Like the types are different and the kind of way things were exposed are different, but at the core level you're dealing with NIO and then handling HTTP. So the idea was that, you know, there's no point us both writing an HTTP server where we both need to handle with edge cases.

Tim Condon (22:31)
We both need to handle all these different things that are done in exactly the same way. We might as well have a shared implementation. So Hummingbird started this off in Hummingbird 2 with a kind of new HTTP server that was...

Tim Condon (22:44)
in the very, very early days of Vapor 5 was what we were using and built on top of. And then the Swift HTTP server kind of spun out that idea and some of that original work to create a new HTTP server of, what could we build if we wanted to take advantage of all of the latest for features. So the idea was to build a...

Tim Condon (23:03)
bidirectional streaming server that was as compiler safe as possible. So that all of the errors that you might have, all of the program errors that you might write will become compiler errors rather than runtime errors. So that's where things like the non-escapable and non-copyable request bodies come into play. And...

Tim Condon (23:22)
That then means anyone can write a very simple HTTP server. You can just depend on that and set up your route handlers. And then Hummingbird and Vapor and any other web framework on top can then add on routing and middleware and sessions, cookies and authentication and whatever else like fun syntactic sugar on top they want.

Leo Dion (23:41)
Got it. That makes a lot of sense.

Leo Dion (23:45)
What else am I missing? But that's the stuff you provide on top.

Tim Condon (23:49)
Yeah, so Vapor can provide routing and we can build some really nice routing, syntax sugar and fun functions with macros that we'll probably talk about in a bit. We provide session support and authentication support and our middleware types, although that might be shared as well in the future. And yeah, so it allows us to write

Tim Condon (24:09)
really nice framework on top that all of the kind low level HTTP stuff we don't have to really worry about anymore. We can just concentrate on kind of building a really nice application layer framework.

Leo Dion (24:21)
So I did want to talk about VaporTesting. So what is new? I've never used the testing stuff in Vapor, I'll be honest. What what's new in v in the latest version? does it use Swift Testing now or like what what are you really taking advantage of with a new testing library?

Tim Condon (24:43)
Yeah, so VaporTesting has been around for in various iterations for many, many years. It used to be called XCTVapor. In fact, the original library was XCTVapor. And then when Swift Testing was released, we released a a VaporTesting wrapper that would work with Swift Testing. But because we've gone from futures to sort of concurrency from XCTest to Swift Testing,

Tim Condon (25:08)
we had five or six different ways of you being able to write a test in Vapor or a kind of a test route, all doing different things and all not quite working the same way. So with this major release, we took the opportunity to streamline that. And so there is now one way to write a test for an application. You can create your application and you can just call .testing on it and it will deal with routing it to the right request handler.

Tim Condon (25:32)
You can configure it to be either in memory. So it never actually sends a real network request. That kind of all gets bypassed and it just gets written directly through to the responder chain. Or if you need to test things like back pressure or kind of anything where you need a real network, you can send a real request. So it will spin up the server for you, send an actual HTTP request and then deal with that all for you. And now it is built on Swift Testing explicitly. We had to have some weirds.

Tim Condon (25:58)
patch code in old Vapor where we had to detect if you're running a Swift Testing or XCTest to make sure you're in the right one because they don't play particularly nicely together. So yeah, we've gone all in to a testing one way to do it. It's really nice to streamlines and we've made it really easy to write tests. I think no matter what kind of what you're testing.

Leo Dion (26:25)
so use a Swift Testing and then you can basically choose how w from what it sounds like you can choose either r like, I don't want I don't need a full server, I just need you to test routing or I need you to test some particular feature and then you can mock that pretty easily. That's pretty awesome.

Tim Condon (26:44)
Yeah, so for most people and most use cases, if you wanna test your request handlers where you wanna say, okay, well, I'm sending a a post request with a JSON body to this route and I wanna ensure that it only allows authenticated users and it writes this to the database and then returns this response. You can do all that in memory. It's almost instant, the kind of request times, because there's no network requests.

Tim Condon (27:06)
If you're talking to a database, then it adds a little bit time on. But then you have these really quick performance in-memory tests that are nice and easy to write. If you do need a real server you wanna talk to, because you might wanna test back pressure or you wanna test like TLS termination and whatever, then yeah, you can just specify a single argument and say in-memory. If you wanna specify the port that it runs on, you can as well.

Tim Condon (27:28)
but by default it will just pick a port for you that's free. And then, yeah, you can just send us the same API, you can send a real request.

Leo Dion (27:37)
okay I wanted to ask about macros, but

Tim Condon (27:40)
Mm-hmm.

Leo Dion (27:40)
I also want to ask about Fluent. And maybe because one of the big things about Fluent was or is the use of property wrappers.

Tim Condon (27:51)
Yeah, for sure.

Leo Dion (27:53)
And sounds like you've started introducing macros into Vapor. Kind of what's the tension before we get into the macros and Fluent? What's the tension between property wrappers and

Tim Condon (28:07)
So property wrappers and macros look very similar, but they work in entirely different ways.

Leo Dion (28:13)
Yes.

Tim Condon (28:16)
macros is code generation. So you annotate your code with a macro and then at compile time, it'll expand that into whatever the macro is specified into code that then gets compiled and that compiled code is then what's shipped finally. Property wrappers are more of like a runtime thing. So you annotate your properties with a property wrapper.

Tim Condon (28:35)
And then at runtime, it runs through the property wrapper and does whatever it needs to do. So because of this, are a bit restrictive in property wrappers, sorry, are a bit restrictive in what they

Leo Dion (28:44)
Property wrappers are restrictive. Yep.

Tim Condon (28:47)
can do at runtime. And they're also super slow. So one of the big pain points of Fluent has been that for anything where you're dealing with like a large response sets, it's really, really slow to decode all that into your Swift.

Tim Condon (29:00)
Fluent models.

Leo Dion (29:02)
Okay.

Tim Condon (29:04)
So yeah, so in in Vapor 5 we've started adding some macros they are purely additive for now and they are built around routing so if you want to Define a route you can define a route with your macro instead of having to go through the whole Request handler definition now

Leo Dion (29:24)
Okay.

Tim Condon (29:24)
and the macro will expand that for you

Tim Condon (29:26)
And this allows us to do some really nice things. So number one you can define is a, an @GET macro and then an @POST macro that work really nicely. We have a controller macro and this allows us to do things like type-safe routing. So in Vapor 4, if you wanted to pass say like an integer in the path as a path parameter, so you do like slash users slash 42, you would have to specify that as a string.

Tim Condon (29:53)
you then have to convert that in your request handler and pull that out and then convert it to an int or get the library to convert it to an int for you. And make sure that the types match as well because everything was a string key. So you'd have to make sure that the parameter that you're requesting is the same that you defined in your route handler. And it wasn't very particularly, it wasn't a great user experience.

Leo Dion (30:11)
There's a lot of hand holding

Leo Dion (30:13)
to get through it. Yeah.

Tim Condon (30:13)
There's a lot of handholding. Yeah. And there are ways of

Tim Condon (30:15)
making a little bit better and stuff, but yeah, the definition of all string types. So with macros, what we can do is we can take advantage of the kind of code expansion and do all of that for you in a macro. And you can annotate your codes. So you can say @GET, and then you can say this path, and then you can have like Int.self as one of the path parameters and the macro will expand into

Tim Condon (30:40)
a code that pulls that out for you, it names the path parameters for you, it's all written in code, and your request handler has to have an integer as one of the parameters because you're parsing in, because you've asked it to pass in. And if it doesn't, you'll get a compile error. And vice versa. you try and pass in, specify a request handler with extra property that isn't defined in the part in the route registration, then it will also fail to

Tim Condon (31:04)
So those things are really, nice. And also, hopefully the next beta should have authentication macros as well, where we can do similar things with macros so that we can make sure that if you want a user as an authenticated user, then the macro will basically do all of that for you and give you a guaranteed user without you having to write request.

Tim Condon (31:24)
req.auth.require. So I'm quite looking forward to that. But it's been a fun experience dealing with macros, I'll put it that way.

Leo Dion (31:34)
What's what's your biggest headache?

Tim Condon (31:37)
So I think the biggest headache with macros is from an end user's point of view, they're fantastic. From a developer's point of view, like the people developing the macros, it's painful. It's quite hard to test them. And there are things that you would kind of assume would work that don't work. So one of the big challenges we've had is that with the authentication macros, for instance,

Tim Condon (31:58)
they're written and they work and it's great and you pass in your middleware, your authentication middleware into the macro and that all works and it's great but most people won't be able to pass in like a logger or a kind of some kind of setup or a database connection into their middleware that will be the one that retrieves like the authenticated users from the database.

Leo Dion (32:18)
Right.

Tim Condon (32:19)
That's really difficult with macros because of the

Tim Condon (32:22)
timing of where they're evaluated, you can't say specify a connection pool on a controller and then read that in the macro that's then defined inside that controller because at the point where the macro is expanded, it doesn't have the symbols to be able to read the connection. So you get these weird compiler errors. So finding workarounds for that has been really quite painful and quite complex.

Leo Dion (32:43)
What are some of the workarounds?

Tim Condon (32:45)
So

Tim Condon (32:47)
Depending on you're trying to do, you can use a key path. That works reasonably well, but it's not the nicest to write. You can use a computed property that's evaluated later that that seems to work as well. But the way that I think we're gonna go about it is with task locals and just bypass the problem entirely.

Leo Dion (33:01)
Yeah.

Leo Dion (33:02)
Yep.

Tim Condon (33:04)
So I think that I need to see how we kind of get on with the next beta in the next few kind of iterations, but I think we're gonna start to use task locals a lot more for passing things like database connection pools down to where you need them to be.

Leo Dion (33:17)
That makes total sense. you know, just going off of the routing stuff too, like I've I've kind of switched towards using the OpenAPI stuff that we

Tim Condon (33:27)
Hmm?

Leo Dion (33:28)
have. What what are some benefits with going with routing in say Vapor or Hummingbird over just using OpenAPI?

Tim Condon (33:36)
Well, I mean, the OpenAPI stuff still has to do routing. Because you can define your routes up front, the server itself needs to... Yeah. Yeah. Because...

Leo Dion (33:43)
So it uses the stuff that you've set up in Vapor, is what you're saying. Yeah, yeah, yeah. Okay, that makes sense. But why would I guess

Leo Dion (33:49)
why would a user directly want to do routing?

Tim Condon (33:52)
So if you want to define your own routes, there are a number of reasons. Number one is that you might not want to bother writing an OpenAPI spec. If you're building something simple, you don't need to write an API spec. It's like another layer of complexity. And the other reason that you might want to do it is because some people like the server code to be the source of truth for the routes rather than the spec.

Tim Condon (34:13)
In Swift, it's slightly different because of the way that the OpenAPI generator works. It's really nice because if you change your routes, it will recompute the functions that you need at build time. So it's very hard for those documents to get out of sync, which is a really, really nice feature. Other languages might not have that feature. So you have to kind of manually recompute or like recalculate the routes. And it's very easy for those things to get out of sync.

Tim Condon (34:38)
So yeah, Vapor 5 will support OpenAPI routes from both directions. So we'll have the same OpenAPI generator support. That PR is actually up by someone from the community, which I'll be reviewing

Leo Dion (34:49)
Awesome.

Tim Condon (34:50)
today. So that allows you to go from a spec to your Vapor code and generate the routes that way. But we're also gonna be able to, you're also gonna be able to go from your code, run a command and it will generate a spec for you.

Leo Dion (35:01)
Next.

Tim Condon (35:03)
So if you want to go that way, because some people prefer it, then you can go that way as well.

Leo Dion (35:07)
I was gonna say like the the reason I've found is for things that basically are not supposed to

Leo Dion (35:15)
gRPC is a example of something like that, WebSockets, things like that. That's where I've ended up having to do my own routing. and it's funny you mentioned going from code to OpenAPI, 'cause I did that years ago, and we're now trying to move everything the other way, just because the OpenAPI generator is so nice.

Tim Condon (35:35)
Yeah, and previously the going from code to an OpenAPI spec was really painful. It required an extra package. had to change the way that you defined your routes and yeah, it wasn't very easy to use. So I can

Leo Dion (35:48)
Yeah.

Tim Condon (35:48)
make that kind of a inbuilt by default behind the traits.

Leo Dion (35:52)
So we talked about it, Fluent. What's kind of the plan with Fluent right now? 'Cause that's independent of Vapor 5,

Tim Condon (35:59)
Yeah.

Leo Dion (36:00)
more or less.

Tim Condon (36:01)
So back in the very early days, Vapor 2 days, package, Vapor 1 was one big thing, but everything was in one package, which was great, but it made your app a little heavy. So in the Vapor 2 days, all those packages kind of got split out as a means of saying, well, we can then iterate on these separately and evolve them separately and they can evolve their own paces.

Tim Condon (36:20)
Unfortunately that never really happened and everything evolved alongside each other. So we're starting now to see like JWTKit for instance, it's now its own package and that's been on its own new major version for a couple of years. So it's been really nice to able to evolve that independently. And Fluent will probably be similar. So Fluent 4 will be fully supported in Vapor 5.

Tim Condon (36:41)
it works currently. If you create a new route, I'm sorry, a new project using the Vapor CLI with the Vapor 5 flag, it will create a Fluent based app for you that you can use and it works fine. So Fluent will kind of evolve independently. Gwynne, who maintains most of the Fluent, has been working on a lot of the low level stuff. So Fluent is...

Tim Condon (37:02)
not just Fluent, there's all of the drivers. There's also SQLKit which converts Swift code into SQL. So those things have been worked on. And then when we have time, like every other open source project, we can start working on Fluent 5. We have a good idea of what we wanna achieve and how we want it to work, which will be primarily macros.

Tim Condon (37:24)
because we can use those to generate the code that will give us really fast models and queries, rather than having to rely on reflection of everything. But that will be released independently and separately from Vapor. I wouldn't be too surprised if we did a kind of intermediately release of Fluent, which would be a new major version that throws away all of the future based stuff and allows us to adopt.

Tim Condon (37:46)
structured concurrency and some nice things that we're missing there. But that won't be the new full featured fancy shiny thing. That will be later on down the line.

Leo Dion (37:54)
Right. Yeah, that makes total sense. Is there anything excuse me? Is there anything in the database space that's changed that you'd wanna adopt in Fluent?

Tim Condon (38:07)
Not massively, think. think one thing that we've already kind of started to push towards is that Fluent 4 was written as an ORM that would work with both SQL and NoSQL databases, so it supported MongoDB as well. And I think that was a mistake. I think fundamentally NoSQL databases and SQL databases work very, very differently.

Leo Dion (38:32)
Yeah, I agreed.

Tim Condon (38:32)
and trying to

Tim Condon (38:33)
build an ORM that works across all of them just gives you the worst of both worlds. You basically have the lowest common denominator. So yeah, think that's definitely something that will change and we will change going forward. Yeah, that's something that's kind of been decided on.

Leo Dion (38:48)
all right, so we have to do it. We have to talk about AI.

Tim Condon (38:51)
Mm-hmm.

Leo Dion (38:52)
Okay. how has AI affected server side Swift? I guess we've already talked about like streaming being a first class feature. I'm more I'm more interested in like what how has AI made you rethink server side stuff before we get into how AI has affected the development process?

Tim Condon (39:16)
I think for server-side stuff

Tim Condon (39:19)
I think it makes it a lot easier to try things out. I think it's very coupled to the development question to be honest.

Leo Dion (39:27)
Okay.

Tim Condon (39:28)
I don't think AI itself has changed anything that's specific to the server world outside of the development world, other than maybe security stuff in that we have gone from maybe seeing like a security advisory

Tim Condon (39:47)
or people submitting security issues once every few months. Yeah, people.

Leo Dion (39:50)
People people.

Tim Condon (39:53)
Once every few months to like several a month, like a massive influx, which is a server project is particularly problematic because we handle lots and lots of requests. And if that, if there's a way of either crashing that process, that's a big problem, or there's a way of, you know,

Tim Condon (40:12)
losing data or people breaking into other people's data or that kind of thing, that's another big problem. So that has been a big effect and the times that that has or the time that that takes up is significantly more than it used to be. To be fair, some of the reports are legitimate and we've fixed quite a few things and if GitHub can...

Tim Condon (40:33)
Also catch up, we should have some of security advisors coming out because they're also struggling under the weight of requests.

Leo Dion (40:41)
GitHub is struggling a lot right now, so let's

Tim Condon (40:44)
GitHub's one line of reliability is struggling quite a bit, I think previously, like the main issue we have at the moment is CVE times.

Leo Dion (40:52)
Okay.

Tim Condon (40:53)
It used to take one to two days for a CVE to be issued to us when we requested it, which is like a...

Tim Condon (41:00)
For those people that don't know, a CVE is like a global identifier, a common vulnerability.

Tim Condon (41:07)
I want to say enumeration, but that's probably wrong. It's

Leo Dion (41:10)
Okay.

Tim Condon (41:10)
basically an ID that's given to your vulnerability in a global database that people have. And previously we'd do the fix, we'd get the fork up, we'd fix it. We'd request a CVE that would take one to two days and then we'd publish with the CVE and that gets distributed. It's taking months to get CVEs now, which is problematic. So that's a big problem at the moment that we're facing.

Leo Dion (41:31)
Yeah, what else did you want to talk about?

Tim Condon (41:34)
AI development specifically, yeah.

Leo Dion (41:35)
Yeah, of course. Go ahead.

Tim Condon (41:38)
It's mixed. I think it's, AI makes it really easy to automate the stuff that you kind of, the gnarly repetitive stuff that you don't really want to bother, which is great. It makes it really easy to prototype stuff as well.

Tim Condon (41:57)
So for instance, I was working on server-sent events recently, which is a form of streaming messaging.

Leo Dion (42:04)
Mm-hmm.

Tim Condon (42:05)
So being able to spin up a client that can prototype that really quickly and make it work and write tests for it is really easy. I think from a open source point of view, it's like a double-edged sword. It means that we can work on a lot more things in some aspects, but.

Tim Condon (42:18)
The other side of that is that we have all this code that what we do, do we trust it? we submit it? Especially pull requests are fully AI generated. Some of them are valid, but more often than not, they're kind of overly verbose or kind of not going about it the best way. And that's time sink. So kind of our workload of managing all of open source pull requests is increased significantly as well as working on it yourself.

Tim Condon (42:43)
And then the kind of funding point of view is really interesting to watch open source funding just almost drop off a cliff in the last like six months to a year. And I know lots of other open source projects are having the same thing as well, where people would rather spend the money on tokens and sponsoring open source projects because there's a lot of, I can just build it myself. So I'll build it myself.

Tim Condon (43:04)
So it's a weird time for the industry, I'll say.

Leo Dion (43:08)
what's your outlook as far as like how long this is gonna last?

Tim Condon (43:13)
Depends on when the investors want their money back. I don't know. think, I definitely think it feels like things are starting to turn. People are getting a bit more.

Tim Condon (43:24)
We're hearing lots about companies kind of getting worried about token spends, bringing people back that they've laid off because you still need engineers to review what's going on. It does feel like we're kind of at the peak, the top of the peak. And I think AI is going to be a tool that's always going to exist now. But I don't know what format will take and if they started charging how much it actually costs them. I think a lot of people will stop using it quite quickly.

Leo Dion (43:49)
Right, exactly. what are what are some of the you said verbose pull requests, what are some of the particular issues you've run into with code generated via AI?

Tim Condon (43:59)
Number one is kind of.

Tim Condon (44:04)
especially Claude really likes to comment on everything. So you just have lines and lines of comments that are like, just read the code. This is useless. And as a kind of maintainer, you have to read all the lines of code to make sure that the code that you accept is A, both valid and worthy, fits in with what you wanna do and also is secure because we're a large open source project, people rely on and we're not immune to people trying to slip back

Tim Condon (44:29)
back doors and to the projects. We have to make sure that every human review is legit. So that is a problem. But yeah, and then you get things of AI trying to reinvent the wheel and re-implementing stuff. one line of code would solve it, but here's 250 lines of code because it's come up with a new way or something.

Leo Dion (44:47)
Right. Or it overdoes it and builds I think I mentioned in one of my

Tim Condon (44:50)
Yeah.

Leo Dion (44:52)
talks how it's like trying to add a whole new retry and networking layer and it's like what what are you doing? Stop.

Tim Condon (44:59)
Mm-hmm.

Leo Dion (44:59)
I don't need that. Yeah, totally. before we close, are there any Swift packages you'd recommend folks check out either server side or not, that you think people should be really taking advantage of?

Tim Condon (45:14)
general packages.

Tim Condon (45:16)
spot here. I think the, so from a networking point of view, or a server point of view, I think people should definitely take a look at the HTTP server library. And there's also a new HTTP client library that is out there. It's a lot more early stages than the server library. Most of it's still in the kind of API proposal stage, but there are implementations out there.

Tim Condon (45:37)
But that is something that the Networking Workgroup is working on actively. And that will become the new API that Swift will use for making network requests. So URLSession will become an implementation detail. AsyncHTTPClient will become an implementation detail. On Wasm, it will be fetch, but that will become an implementation detail. So everything should go through these new APIs. So I highly recommend people check those out.

Tim Condon (46:01)
because it's likely what they will be using in a year or so.

Leo Dion (46:05)
Wow. Okay, cool. Very cool. Tim, thank you so much. I look forward to seeing you again in Berlin.

Tim Condon (46:11)
Thanks.

Leo Dion (46:14)
where can people find you online?

Tim Condon (46:16)
Yeah, thanks for having me. So people can find me online as 0xTim on Bluesky, 0xTim on Mastodon under the Hachyderm server, or you can find me at timc.dev with links to everything there.

Leo Dion (46:29)
Awesome. Thank you again. People can find me on Twitter at leogdion. BrightDigit.com is my website. Take a look there. join the Patreon if you want to get early access to episodes like this and be a subscriber to the newsletter as well. thank you everybody and I will talk to you again. Bye.

Tim Condon (46:50)
Thank you very much.