Leo Dion (host): Welcome to
another episode of empower apps.

I'm your host Leo Dan today.

I'm joined by Brandon Williams.

Brandon, thank you so
much for coming on.

Brandon Williams (guest): Yeah.

Thanks for having me.

Leo Dion (host): I know a lot
of people are fan, fans of your

work point free with Steven.

But I'll let you go ahead and introduce
yourself before we get started.

So

Brandon Williams (guest): Yeah, sure.

Yeah, I'm Brandon.

I do run a site called Point
Free with Stephen Sellis.

It's a video series, educational
video series, kind of concentrating

some of the more complex and advanced
aspects of the Swift language and we

do a lot of open source work we'll
probably be talking about at least

one of the libraries today, but
but yeah that's the general thing.

Leo Dion (host): you've got a big
talk coming up at Swift Toronto

which is going to be awesome, it is
about controlling your dependencies,

not letting them control you.

And now initially when I saw that
I was like, Oh, it's going to be

like about swift package manager,
Cocoa pods or something like that.

But in fact you have a broader
and probably more correct

definition of what a dependency is.

Do you want to explain
exactly what that means?

Brandon Williams (guest): Sure.

Yeah, I think anything you probably
add into your SPM package of course,

you would call that dependency because
you're bringing in someone else's code.

But I think the yeah, the definition
could be a little bit broader.

And it's really just when you have
to touch API's and other people's

code that you can't control.

And so that would even
include Apple's code, right?

You don't ever add a dependency
in SPM to get access to core

location or anything like that.

That's, it's just kind of ambiently
there ready for you, but that still

is a dependency because whenever
you make a direct call to some core

location API, you're touching code
that you can't possibly control.

And that puts you in kind of a
compromised position when it comes to

running your code and certain situations
that you would want to, but it turns

out you can't because you just, you
don't have control over that code.

Leo Dion (host): And I think that's
the key point, is that this is pieces

of your code you have no control over.

So like you, you talk about core
location and how that works.

Anything networking,
obviously, anything database.

Brandon Williams (guest): Yeah

Leo Dion (host): even sleep
timers and that kind of

Brandon Williams (guest): timers,
user defaults it could be it's, and

I'll say even date generators and
UUID generators, all those things

that just can extract out like
new information out of the void.

Like when you ask for a new
date, you get the current

time, all that kind of stuff.

Yeah.

Needs, it can wreak
havoc in a code base.

Leo Dion (host): Yeah, I like, that was
the part that I found interesting is

when you talked about UUIDs and dates.

Because I don't think of those
as you can't control them.

But in a sense, you do get a wild
value every time you call them.

Brandon Williams (guest): Right.

Right.

Yeah.

Every time you do like capital UUID,
open, close, print, that initializer is

you just get a whole new value of it.

And you may not think it's.

that big of a deal to get
a new value out of it.

That is the point of it, but it can
cause trouble for that one in particular

can cause trouble with testing
because you may want to assert that

some model equals some other model.

But if you can't predict the ID that
was generated in the first model,

then you've got No way of comparing
those two as two like models together.

You can't have to compare them
field by field rather, and then

it's on you to remember when you
add a new field, you got to go add

a new assertion for that new field.

Whereas if you just asserted that
this model equals this model, it would

take all of care of all of it for you.

Leo Dion (host): Yeah, so let's
jump into testing, because I

think that's the big thing.

There's two things you mentioned
Xcode previews and testing is

where you run into these issues.

You want to go over some let's start
with the testing part since you

just mentioned that, what are the
particular issues that people will run

into when it comes to testing these
kind of uncontrolled dependencies?

Brandon Williams (guest): Yeah it
just so if you start sprinkling in

code like access to location managers,
network request into your code, you

it becomes very difficult, if not
impossible to unit test and unit

testing is a very particular way
of running your code in which you.

Kind of put it in a little sandboxed
isolated execution environment

where you need to be able to run
it without having spun up a view

controller or spun up a SwiftUI view.

It needs to be just able
to run all on its own.

And if you reach out to a
network request you have no

way of predicting what is the
data that's going to come back.

You know, if you reach out
to some API that loads like

Mastodon posts, you don't know.

what's going to come back.

And so you have no way of then
testing how that data flows

through your application.

And the point of controlling
the dependency so that you can

predict what is coming back.

It's not so that you can exercise what
the Mastodon API is doing where we just

have no choice but really to assume that
they're going to do the right thing.

And they'll send us back some posts.

What we want to test is.

When the post come back, how
does that flow through our logic?

Where does that data get
filtered and transformed and

put into everything else?

That's what we want to test.

And you can't do that unless you in
some way control that dependency.

Leo Dion (host): Yeah, I think
that's one of the biggest challenges

I feel like people get when they
start unit testing is they're like

how do I do this with the database?

How do I do this with the network?

It's no, like you're not testing
that the database works or

the networking call works.

You're testing your behavior
based on what you get back.

Brandon Williams (guest):
Yeah, it's tricky.

I think, and a lot of people
stumble on that when they're first

learning about testing and it
can, I think it can set you back.

If you look at it, you're like, wait.

I don't want to test,
you know, Apple's APIs.

I don't want to test core location.

So why would I do this?

And that's kind of not the, that's
the point is that to not test those

things and to get just to pretend that
their API worked the way you expect

and it fed data into your thing.

But it's very tricky.

Leo Dion (host): So before we
get into solutions what let's

talk about Xcode previews.

What are some things that Xcode previews
like, what are some challenges that

Xcode previews gives you different
from just what unit tests do.

Brandon Williams (guest): Yeah
there are so well, there's a couple

of aspects to this for the first
part, it's the more you compile in a

particular project, the bigger chance
you have of just breaking previews.

And so your first step to Improving
the stability previews is kind of

break up your project into lots
of little modules and that can

greatly improve the stability.

So once you've done that, you've got
a far more stable preview, but then

there are a lot of APIs out there that
will just completely break the preview.

And in a couple of ways first of all,
like things like a core location,

if you just indiscriminately.

Request permissions for location and
you use that check to kind of block

other things happening in your UI, what
will happen is you'll load the preview.

You'll never see that little iOS
alert saying, you know, do you

want to get permission because
previews do not support that.

And I don't know if that's.

considered a bug in previews, or if it's
just how previews are expected to work.

But you'll never get that little
permission box, and therefore you'll

just never be able to see in your
preview what happens afterwards.

And so that just completely ruins that
kind of iterative cycle on previews.

You're just not allowed to use it.

And then there's other kinds of things
that just will completely crash the

preview, because some APIs require
That you have a little info P list in

your application that describes why
it is you want to access that thing.

And that's like anytime you
access the microphone, the

contacts, even the core location.

But for some reason, core location
doesn't do this, but other ones do.

They will just simply crash your
app if you try to access the API

without that info P list being there.

And so then what happens is you've
got your preview and it just crashes.

You just, you literally can't run it.

And that's honestly just the beginning.

Like these dependencies a lot of them
will just make your previews very

flaky, not stable, make them inert and
functionless or just completely crash.

Leo Dion (host): So now we're going to
add it into some like terminology fun

here, but the way I've done it, and
it's kind of what you kind of get at.

And your talk is using mocks, but I
might be using the wrong term because

there's 50 different definitions
of mock stubs and blah, blah, blah.

But that's essentially what I
ended up doing is creating mocks.

And in some cases I'll create a
special mock for previews or a

special mock for unit tests, but
it does whatever you tell it to do

essentially where it's like, Oh here,
in this case, give me this lat long.

Just hard code it essentially, or,
you know, with networking, like

here, give, throw this error in it.

And that seems like basically
what it comes down to as a

solution for this kind of thing.

Is that correct?

Brandon Williams (guest): Yeah.

That's the entire solution is rather
than reaching out directly to like

core location or URL session to
load data, you put a little, a

lightweight interface in front of it.

And then in previews and
tasks, you can swap out.

A implementation to put into your
feature so that it just immediately

returns you some data and I agree.

I don't know whether that's a mock
or a stub or a fake or a spy or

whatever I like that zoo of terms.

It doesn't really matter, because all
that really matters that we have an

interface and at runtime we want to
swap it in with something that does

something a little bit different.

Like when you run in the
simulator on your device, you

want to use the real thing.

You want to use core location.

You want to access those APIs,
but in almost all other cases

you want to put in something.

Just simpler, something just, you say,
give me the current location, and it

just hands you back immediately some
lat long, and then your preview will

hum right along, and it'll be good.

Leo Dion (host): So in your talk,
you talk about protocols is typically

the way I've done it, but you
had some reservations about that.

You want to explain what those are?

Brandon Williams (guest): Yeah alright,
this is like a long, I mean, Steve and

I have been talking about this thing
for many years, and protocols are, yeah,

certainly the most popular, the de facto
way of putting interface in front of

something, but it's not the only way.

There is this slightly different
way in which you can use a struct

that has closure properties.

And I can substitute in for
a protocol for the most part.

And in particular, when it comes to
controlling dependencies protocols, you

typically just got two implementations.

You've got the live one, and then
you got kind of like a mock one.

And in such situations, the protocols
may be a little too high powered,

although Protocols have gotten a lot of
features recently that make this a lot

nicer you know, Rewind Early Swift the
Struct version was a lot more enticing.

Now protocols do satisfy a lot of
things that allow this to be easy.

But anyway, so if you use the Struct
interface, it allows you to do some fun

things, like you get the ability to kind
of hot swap out one single endpoint in

the dependency for new functionality.

So you could.

You could have a mostly live
dependency that accesses the true API

endpoints but you just swap one of
the endpoints to do something with

a mock or something, you know, more
custom and that can be really powerful

for really sculpting very specific
situations that you want to test.

Leo Dion (host): okay.

Interesting.

What were the things that you were
going to mention with protocols that

are new, that make it more enticing?

Brandon Williams (guest): Oh,
just the fact that we have more of

the existential machinery and the
protocol with associated types.

That stuff,

Leo Dion (host): primary
associated types, yada.

Okay.

Brandon Williams (guest): All that
stuff makes it so that it is far

less painful to deal with protocols.

Like prior to swift five, I don't
know if that was six or seven.

It was to me, it was an absolute
no brainer that you would use a

strike for these things afterwards.

I'm just like, whatever suits your
fancy, just, you know, go for it.

But I do think there are pros
to the stroke, the struck style.

But yeah, it's if you're used to the
protocols and go for the protocols.

Leo Dion (host): Yeah, I think I do
protocols probably like 80% of the time

and then the other 20% if I can throw
in a closure and just do it that way.

I'll do that as well.

If it's simple enough, it's just when
I end up having other dependencies

or when I have the protocol needs
to do a little bit more complicated

stuff, it's yeah, I'll probably end
up Do you need to do a protocol?

Brandon Williams (guest): the
more complicated the protocol,

the more it pushes towards like
protocol being the way to go.

But I also say that Apple's, some of
their recent APIs have even skewed

protocols in favor of just simple,
like basically bags of closures.

Like some of their collection view,
like data source stuff, historically

that would have all been protocols.

You would have made your object or

Leo Dion (host): Delegates,
probably delegates,

Brandon Williams (guest): yeah, Delica.

So all that stuff has like slowly
been going more towards Closure Base.

So I mean, it's, yeah there's
nothing all that strange about it,

but they're, yeah, you just got to
think about it a little bit more.

Leo Dion (host): right?

Right.

So one of the other things you
ran into with testing was the

problem with persistence, right?

Especially UI testing where you're
like running, like A series of

tests and something does set
some value, then you run the test

again, but that value had been set.

So now you messed stuff up.

Do you in those cases, I mean, for
me, what I typically do is my UI test

just ends up resetting it every time
it runs, maybe as part of setup or

maybe as part of the test itself.

What do you see as a solution
in that, in those cases?

Brandon Williams (guest):
Yeah, that's, the UI test is

particularly tricky because it
runs in a fully separate process.

So even from the XE test file,
you get no way to do stuff.

You can't clear out.

It's user defaults or
anything like that.

Whatever it does with it, you
know, it happens in a process.

That means you get to add the cleanup
code to your actual application project.

And the way you communicate
between the two is to set like a,

I think an environment variable
or something like that to kind of

communicate to your process that,
all right, I'm running in a UI test.

And then you would set up your
dependencies to use like a fake

file system, something that doesn't
write to the desk, but instead

has a little dictionaries mapping.

URLs to data blobs and then and
the application was like, Hey,

load me some data from this URL.

It'll just give you this data blob.

It won't even touch the
file system or anything.

And so that's the way
you typically do it.

Leo Dion (host): I was going to ask,
going back to the talk about protocols

and you're mentioning file system.

Do you end up having a protocol for
pretty much everything that needs to be?

Like mocked essentially.

So Oh, you are, you never use
URL session, use the protocol

and the protocols implementation
might have URL session.

Is that how you do it?

Brandon Williams (guest): Yeah,
essentially you need an interface in

front of each of your dependencies.

So one, whether it's struct or
protocol, but yeah, interface in

front of your API client, interface in
front of your file system, interface

in front of user defaults database,
core location, all those things.

Leo Dion (host): Do you were you
ever an Objective-C developer?

Brandon Williams (guest): Oh yeah.

Leo Dion (host): Do you miss
swizzling and just being

able to change the classes?

I don't, I'm not that kind of developer
but I can, I see a lot of older, like

Objective-C folks who are just like,
man, I hate how like everything is.

Strong typed and so and that's
like I okay now I see a little bit

of the benefit with Objective-C,

Brandon Williams (guest): Yeah, I
don't really miss it just because

I also remember the terrible
things that when it works great.

But like it injects so much uncertainty
into the rest of your application.

That's really hard to feel comfortable.

And I think other people.

Maybe people are just better
at Objective-C than me.

They just had a higher threshold
for understanding that complex

system all in their head at once,
and I just, I struggled with it.

Leo Dion (host): Yeah.

Yeah.

Yeah.

Brandon Williams (guest): but I think
a lot of this pain around writing these

dependencies, these little interfaces,
a lot of that pain could probably

be mitigated with macros and stuff

Leo Dion (host): Oh Yeah, right.

I figured you're gonna say that.

Brandon Williams (guest): yeah,
so I think we're at least swift as

getting to the point where you will
be able to keep the strong types,

but you'll also have the tools
necessary to put the interfaces in

front of them in a lightweight way.

Leo Dion (host): Yeah.

Yeah Mac I was gonna say macros
is pretty much where that head

that tells you where it's going
was there anything specific you

wanted to mention on testing or
protocols or mocking before I keep?

Keep going further.

Brandon Williams (guest):
yeah all right.

I think one thing I'd like to mention
and is that testing, I just don't think

it's super popular in the iOS community.

I think, if you, I also did a lot
of Ruby back in the day and that

testing and that community is
gigantic and JavaScript's got it.

So and I just I think it's
a little bit unfortunate.

I would kind of encourage people
to just dig in a little bit deep

to see what testing really means.

And this idea of being able to run
your feature in this altered like

execution context is extremely powerful.

All the more you're able to do that.

All it means is your feature is Super
isolatable, super decoupled and can

just be understood all on its own.

And that is incredibly powerful.

And you can only write unit tests
if your feature satisfies that.

And so regardless, I know it's a
lot of people have got like really

hot takes and unpopular opinions on
all these things about testing, I

just, I feel like hopefully just look
through all that fog and kind of.

See what testing can do, because
I think it's incredibly important.

And once you're convinced of that,
I think, yeah, the dependency

stuff is falls out naturally.

You got to do the
dependency controlling.

Leo Dion (host): Yeah.

I agree a hundred percent.

I feel like once you start doing
testing, it's then it's Oh, okay.

Now modularization
makes a lot more sense.

Why do you think, like, why do you think
the community is so testing averse?

Do you think that's a Apple's
fault or tooling fault?

Or is it like, I don't know what
Android is like, but I can imagine it

might be similar with Android as well.

Like just.

Brandon Williams (guest): Java's got
a big testing ethos and history too.

So they do quite a bit of testing also.

I really can't give like a universal
answer why I think it is by some

things that just come to mind are like.

Basically, none of Apple's
sample code has tests.

It's, none of it.

It doesn't matter if they're Fruta, Food
Truck, Scrumding, or all those things.

None of them have tests.

Now, I know Apple does do
testing internally, and I

know it varies team by team.

I know some teams take
it extremely seriously.

But the sample code, I think Apple's
approach with sample code is they

like to build Fun applications in
the absolute simplest way possible so

that anyone can kind of just roll in
and start messing with things that I

think that's their goal with it because

Leo Dion (host): Yeah.

Yeah.

Yeah.

Brandon Williams (guest): They're
making sample code for millions of

people, but that doesn't necessarily
mean you would go and build your

next company, the infrastructure,
you'd build it in exactly the same

way that Apple does their code.

You know, it's a learning tool.

Leo Dion (host): Yeah.

It's a learning tool and it's also
showing off of this particular API

and to, to them, I'm not agreeing
with them, but to them it's oh unit

tests would just distract away from
the fact that we want to show off.

Brandon Williams (guest): Yeah.

And I think it's, I think it's
under, I think it's reasonable.

I, you know, I certainly wouldn't
agree that, you know, people should be

building their applications in the exact
way that the Fruita truck was built,

but I absolutely understand why they
wouldn't add tests and stuff like that.

They really just want to show here are
all of our fancy new APIs this year.

Here's how to use them.

But yeah, I just don't
think we can look at.

Apple is being like kind of the
pinnacle, their sample goes the pinnacle

of how you should build these things.

Leo Dion (host): So you mentioned
I wouldn't want to build a

whole company app around Fruita.

And that kind of brings us to like
scaling and speed because in the

real world we all work in teams
and have to deal with other people.

And we have to deal with speed issues
with, as far as like you said, if you

don't do this, you have to run this
crap in the simulator every single

time, or you can't even run it in the
simulator, you have to run it on a

device, and that just slows things down.

Yeah, you want to kind of expand
on that, I guess, and why those

are big issues with scaling.

Brandon Williams (guest): Yeah, I think
what, just like with modularization can

help kind of paralyze build times and
speed up compiled times, dependencies

can improve developer experience and
iterative cycle on things because

there are a lot of APIs out there
that we already talked about, yeah,

that don't work in previews, but then
there's a lot of APIs out there that

don't even work in the simulator.

If you're making something that

Leo Dion (host): Core Motion.

Brandon Williams (guest): yeah,
core motion, if you're using

something that uses the gyroscope.

That doesn't work in
the simulator at all.

And so you actually have
to run on the device.

Now you may be thinking of
course I have to run the device.

It's a gyroscope.

I want to be able to move my phone
around but that's not all testing you do

sometimes what you want to do is just.

A simulate that the device is being
thrown around all over the place, and

maybe you've got a little graph that's
showing something, like you don't have

to take your device and do this, instead
you could provide a dependency where

Core Motion is pretending like it's
doing that, you can have the little

gyroscope meters going crazy all over
the place, and you know, that would

massively speed up your developer,
the development cycle, you could even

run that in a preview, you wouldn't
even have to run it in a simulator.

And these kinds of benefits just
really start to compound and explode.

There, there is so much that we
do as developers that is just kind

of working around the quirks of
our code base that we've learned.

We've just learned to do it.

It's just, we've been in the mud for
so long that we know that we don't

want to ever clean this project
because it takes so long to rebuild.

We know that we can't ever run this
project, this feature in isolation.

So I have my little flow of.

Open up the simulator and I tap
tap and I get to the feature

and I can finally test it.

All that stuff starts to kind of peel
away when you can just control your

dependencies and allow yourself to start
up your app in the exact state you want.

You don't have to be susceptible
to all of that dependency

corrupt and everything.

You should control all that.

Leo Dion (host): Yeah.

Yeah, I'm sorry.

I was like thinking about Apple
trying to unit test their like crash

detection and how they possibly do that.

Do they like throw the phone
against the wall or what do they do?

Because, yeah, I would hope
that they have some mocking

system for something like that.

Because, yeah, like you said, there's
all these little quirks that you run

into where it's you need the flexibility
of putting something in to replace it.

Brandon Williams (guest): That brings
up, you bring up an interesting point

also is that there's also a bit of
a spectrum with this testing, too,

because you've got the unit tests
at one end where you mock things

because you just want to say, I
assume the outside world is doing

what I expect, but I want, I just want
to test my little nugget of logic.

And then at the other end, you've got
integration testing where you start

to pull in more of the outside world.

And that's extremely important.

It's, I'm not saying that one side
is better than the other there.

You absolutely should be testing along
multiple points of this spectrum.

And like this crash testing they, I
assume they probably do actually have

some little laboratory setup, where
they literally throw phones around.

And that's great, because, you
know, they wanna know, like,

when, at the end of the day,
this thing is working that way.

But then that other thing they
want to test is just that an

alert pops up with this messaging.

I don't think you need to
throw the phone across the

room just to test an alert.

Leo Dion (host): right.

Brandon Williams (guest): would
instead, you would just, you

know, make it fake like it did a
crash and then you see the alert.

So there's like a spectrum
there and you got to test at

every level of the spectrum.

Leo Dion (host): Do you think
one side of the spectrum is

more important than the other?

Brandon Williams (guest): I do not.

Leo Dion (host): Okay.

Brandon Williams (guest): I think
you really need a full around overall

you know, testing on all sides of it.

You really do.

Leo Dion (host): What was I gonna ask?

Brandon Williams (guest): I will
say that certainly the integration

side is the hardest side to test.

And so typically you've got fewer
tests over there because then you

can't really make absolute assertions.

You can't say, oh, I made this network
request and I got this exact thing.

Instead, you can just say, oh, I made
this network request, it fed into my

system and, you know, I can assert
that at least something happened,

but I can't assert that exactly what
happened because you can't predict it.

But.

Yeah, integration testing is
far harder than unit testing.

Leo Dion (host): I think now we can
jump into the muck that is how do

we deal with all these dependencies?

So the way I watch your try swift talk
or new york swift talk, excuse me and

Brandon Williams (guest): right?

Leo Dion (host): No,
you're It was your talk.

Yeah.

In New York.

Brandon Williams (guest):
the one from in April

Leo Dion (host): Yeah.

Sorry.

So I watched that and I was like
my solution is supply default

implementations that are the real thing.

And then in your previews and in your
tests, you're just customize them.

Right.

That's the way I do it.

But you don't like that.

And I liked it.

You had a really good quote that
I say defaults are ergonomic,

but not safe and requirements
are safe, but not ergonomic.

Do you want to explain that?

I guess.

Brandon Williams (guest): Yeah.

So they so yeah, the easiest way to
provide dependencies to your various

features is you have an initial you
hold the dependency as a property and

you know, whatever your thing is a view
controller or observable object and

you provide an initializer that has
a default, which is the live thing.

So the thing that.

makes network request or the thing
that actually accesses core location.

And that way, if you construct
that feature and don't provide

anything, it gets the live dependency
and it all seems really good.

And then over in previews, you
get the opportunity to then.

provide one of those mock things.

All right.

So that can be a really good
way to get your feet wet and

start getting things going.

And it's very ergonomic because it means
you could have some deep leaf feature.

You could be drilled down
multiple levels into your

application and you could be
like, Oh wait, this feature now.

A network client.

So I'm just going to provide
it, provide that default.

Everything compiles.

It's all good.

So that's super ergonomic.

You get to add dependencies
with no trouble whatsoever.

However I call that not being safe
because what it means is say some

other feature up the hierarchy
also wanted a network client.

All right.

If it doesn't pass its network
client down to every feature

along the way so that leaf.

And then you run into the situation
where you could be running a

preview of that parent feature.

The parent feature is using the
mock network client, but that leaf

feature is using the live one.

Because no one passed it all the
way down to the leaf feature.

And so that's why it's unsafe.

That you will accidentally use live
dependencies when you don't expect it.

Unless you're strict with yourself
to pass them explicitly along.

Leo Dion (host): That's what I
mean, that's kind of what I do.

And I would just pass them along.

I would still supply the default, but
I would pass it along, but I understand

where you're coming from completely.

Brandon Williams (guest): Gives
the ergonomics, but it takes away

the safety because then it's on
you to remember to pass along

because the compiler can't help you.

If you don't pass it, it's you
got a default, no big deal.

So that's where the safety bar comes in.

Leo Dion (host): Macro save us, please.

Yeah.

I mean, essentially what you
get I like that solution.

And part of that.

I don't want to have to add another
dependency, which is kind of what

you get at is having to add some
sort of dependency injection library.

And I'm actually in my previous
life we used when I was a.

net developer C sharp, we did
we used what was it was Castle

Windsor, I believe was the
dependency injection tool.

And I thought it was awesome.

Like it was amazing how it worked.

And I wish.

And I think maybe macros actually
might help with this as far as

dependency injection is concerned,
but basically there's a plethora of

library and of course you at point
three, you have one as well that

kind of try to solve this problem.

I don't, I know what dependency
injection is, but I'll let you go ahead

and explain that and then what these
tools actually do and how they're.

that kind of fit the role that
you think is better than the the

way we suggested with defaults.

Brandon Williams (guest): Sure.

Yeah.

All right.

So if you go down the route of finally
controlling your dependencies, and you

provide initializers to pass them in
explicitly, and you find that to be non

ergonomic, so then you throw in some
defaults, and then you find that to

be not safe, The only point of adding
a library to this entire system is to

find a balance between something that is
pretty ergonomic and also pretty safe.

You'll never have the fullness of
ergonomics and safety at the same time.

There

Leo Dion (host): It's a compromise.

Brandon Williams (guest): Yeah
but initializers that don't

have defaults are extremely
safe, not ergonomic at all.

And then initializers with defaults are
extremely ergonomic, not safe at all.

So you're trying to find
somewhere in between those two.

And that's the only purpose of a
dependency injection framework is

allow you to provide The network client
and the core location client to the

feature that wants it in a way that's
slightly ergonomic and slightly safe.

And where you draw the line
there is like up to the library.

And so we've got our own library.

There's probably a dozen or more
other libraries in the Iowa community.

And we, yeah, we try to strike our
balance between those two things.

Leo Dion (host): The idea being is
you can say you know, use this type

and then you tell the dependency
injection, use this type essentially.

And then basically a singleton, you
kind of resolve it and then it gives

you the type that you had already set.

Is that right?

Brandon Williams (guest): Yeah,
essentially you'll have some kind

of way of annotating in your feature
like a view controller observable

object saying this object wants
a dependency of this type and it

will be the job of the dependency
injection library to figure out where

does it get that thing concurrency.

Pretty much the only way to do this
would have been you do have this

global Singleton of dependencies and
you get to pick from it and provide

it to to features, but then there
would be additional Functionality.

Maybe you can scope that global
singleton to something smaller so that

this set of features gets a little
bit slightly different dependencies.

There's there's other things you
can do and there's something called

containers and stuff like that you
can really dive into if you want.

But post Swift Concurrency, there's
now something called Task Locals.

And Task Locals allow you to
have something that looks...

global, but it's actually quite safe.

It can be thread safe or it is
thread safe and it plays nicely

with structured concurrency.

And so that's the tool we use
to build our dependency library.

All of dependencies are
basically held in a task local.

Leo Dion (host): I mean to me like the
drawback with that is, For one thing,

do you ever see Apple coming in or?

I'm just saying here's a
dependency injection way to do

things using macros or whatever.

Do you think that could ever happen?

Or do you think that we're pretty
much going to have to bring in

third party libraries to do this?

Brandon Williams (guest): I think
it could theoretically happen.

It would be surprising only for
the fact that Apple has never once

shown any interest whatsoever in The
idea of controlling dependencies.

They do not even build APIs that are.

control friendly.

Like, all right, for example, the
standard library has some, like,

all right so when Swift concurrency
first came out, we had task.

sleep, and that was a way to
sleep an async context for some

amount of time, but that is a
completely uncontrolled dependency.

If you sprinkle that into your
code and then try to test that

code, Your test will just literally
have to wait for time to pass.

So if you needed to wait 10 seconds
before something happened in your

feature, like confetti or something,
you would just, your test would

have to just wait 10 seconds.

So then in the next, like in a update
to Swift concurrency, they released the

clock protocol, which is an abstraction
and interface in front of sleeping.

And then that allows you
to substitute in clocks.

You get to use a continuous
clock when run on a device,

but you could use an immediate
clock or a test clock and test.

So that is one of the only examples
I can think of Apple providing

interfaces that actually make testing
easier, like all their other APIs

core location, core, locate, motion,
context, framework, all of those,

you just get types, you don't get
interfaces in front of those types.

Leo Dion (host): Right.

Right.

Brandon Williams (guest):
it would be surprising to me

if Apple came out with some.

You know, wonderful way of
doing dependency management.

Now, however, I'll say SwiftUI is.

From its very foundation,
a gigantic dependency

management system, essentially.

Like environment variables
and environment objects.

That is all about how do you
take something and push it

deep into a view hierarchy.

So we've got the tool at the view
layer but I think that was probably

out of just pure necessity of wanting
the idea of if you set a foreground

color on this view, the fact that
it needs to be able to trickle deep

into the view hierarchy, I think
That, that drove that nec necessity.

It wasn't that they were like,
oh, we've got dependency clients.

We need to control, therefore
we need the environment value.

I don't think that was their line of

Leo Dion (host): Right.

Right.

Brandon Williams (guest): and so
what our dependency library, what we

take inspiration from the environment
values and we try to give a tool

that looks like environment values,
but you can use it in observable

objects and stuff like that.

Leo Dion (host): I was wondering if that
was like a limitation of Objective-C and

they're like, yeah, we're not going to
do because we can do a bunch of other

stuff we had talked about previously.

But then I'm like, yeah, all
these new Swift APIs are all.

said not protocols or interfaces.

They're just types like Swift data.

It's all strongly typed So yeah, okay.

Yeah.

Yeah

Brandon Williams (guest): Yeah I
can definitely buy the argument

that Apple didn't really ever see
a need for dependency management

control and stuff like that.

And Objective-C day, because
everything was so loosey goosey with

message dispatch and stuff like that.

I just, I don't know what
their plan is for swift.

I don't know how people
within, you know, app like the

people building the weather

Leo Dion (host): Have you ever
looked at any of the open source

stuff and seen how they do it?

Like

Brandon Williams (guest):
what open stuff do they

Leo Dion (host): I don't know like
foundation or collections or algorithms

Brandon Williams (guest): those are
like foundational libraries that

don't benefit from this kind of thing.

It

Leo Dion (host): Yeah, right,

Brandon Williams (guest): If they
ever open source the weather app,

I'd be very curious how they deal
with things in the weather app.

You know, something like that.

Leo Dion (host): right, right.

Yeah, I think we
covered most everything.

I do, was there anything
else you want to talk about

before we jump into WW at all?

Brandon Williams (guest): No.

I mean, maybe something
will come up, but

Leo Dion (host): Yeah.

Brandon Williams (guest):
to mind right now.

Leo Dion (host): So I was going
to ask more focused on WWDC

has any of let's start with the
Swift UI and observation stuff.

Has that changed any of your
views as far as how dependencies

should be managed or is it pretty
much, Oh yeah, it's all the same.

Cause there was a lot of stuff, you
know, with like property wrappers

that essentially are kind of like.

Not needed anymore
because of observation.

Has that changed any of your views
on dependency management when

it comes to especially Swift UI?

Brandon Williams (guest): It
has not changed any of the core

views of dependencies, but the
observation tracking stuff does

open up a new possibility, new
powers we could possibly give a

dependency library like our library
or anyone can add to the library.

So one of the things that we do
with our dependency library is that.

If you start using it in your feature
and then you write a unit test for

that feature and you don't override
the dependency in the test, you

get a test failure the moment your
feature even accesses the dependency.

So if you got like a
method that says the.

You know, load data button
tapped and inside there, you

go into your network client.

You're like, oh, load data that
will trigger a test failure because

the opinion of our library is that
if you accidentally use a live

dependency in a test, that is most
likely not the thing you want to do.

And so we complain really loudly
saying you're using a live dependency.

You probably don't mean this.

If you do mean this, you can be
explicitly say, I do mean this, but

Leo Dion (host): Right.

Right.

Brandon Williams (guest): right?

And so then there's so we force you
to override that dependency and we

have a tool that allows you to write
your test where you get to override

the dependency, but there's a flip
side to that where if you then you

construct your model in the test and
you override all its dependencies,

but say you override too many
dependencies, there is the idea

that maybe that should be a failure.

If you override dependency that
wasn't actually used, yeah.

Maybe that should be a failure
because then you get to trim

down the dependencies you
override to the bare minimum.

And that really allows you to prove that
you know how this feature is working.

And that kind of, being able to have
that insight of what features are being,

what dependency endpoints are being
used and which ones are not being used.

I think all the observation
tracking will help with that.

And that's a completely non
SwiftUI application of that tool.

Just the idea of the observation
in general I think will be.

Really, and it's just going to allow us
to build some very interesting tools.

But the idea of controlling
dependencies, observation alone,

doesn't really change that story.

Leo Dion (host): Right, right.

How about as far as like using macros?

Brandon Williams (guest): Macros
just will make, finally, controlling

your dependencies more ergonomic.

It may be using our library more
ergonomic, but, yeah, it doesn't

change the reason to do it or

Leo Dion (host): Right, right.

But now you have syntactic
sugar to spread around to

make it a lot easier to do.

Yeah.

Brandon Williams (guest): The, for
example, like registering a dependency

within the library so that you
can start using in your features.

It is a multi step process, just like
with environment values, you have to

create this like little environment.

For SwiftUI environment values, you
create an environment key, you conform

it to a protocol, you extend environment
values, you add a computed property.

Leo Dion (host): Right, right.

Brandon Williams (guest): all of
that could be macrofied and, you

know, a one stop kind of thing,

Leo Dion (host): Have you jumped
into creating your own macros yet?

Brandon Williams (guest): Yeah, we've
experimented with it quite a bit.

Mostly for our KSPAS library
and composable architecture

and stuff like that.

Yeah.

Leo Dion (host): Pretty easy,
comfortable getting into swim

syntax and stuff, or what?

Brandon Williams (guest):
Swift Syntax is a beast.

It's it's

Leo Dion (host): know it is.

Brandon Williams (guest): yeah.

And you really need autocomplete to
help you out every step of the way.

Cause that is, yeah,
there's so much there.

There's like this AST, Swift Syntax
Explorer website that allows you just to

paste in some Swift code and it prints
out the Swift syntax code alongside it.

So you can kind of just see what every
little thing corresponds to and that

Leo Dion (host): Yeah.

Yeah.

Brandon Williams (guest):
what the heck is going on.

But yeah, it's Swift Syntax is a beast.

Leo Dion (host): I'm glad you said that.

Not chat GPT.

That's good.

Brandon Williams (guest): Yeah.

Leo Dion (host): And then last but not
least with data, where do you see that

fits in as far as dependency management
is concerned and mocking that?

I mean, I guess you've already
had stuff with core data, which

of course is a dependency as well.

That's certainly a wild card.

Is it kind of the same
way with Swift data?

Brandon Williams (guest): So what?

Yeah, it's basically the same story.

I think so.

So it's really just to enter like
a nicer API on top of core data

Leo Dion (host): Oh yeah, exactly.

Yeah.

Yeah.

Brandon Williams (guest): I
think that story is the same.

But what?

Sorry.

So one thing that's interesting
about core data, and I think I

misspoke a little earlier when
I was like, I'm trying to think

of examples where Apple is made.

APIs that are control friendly
or testable friendly core data

actually does have a little
bit of that because it has the

concept of the in memory store.

You know, usually when you're running
in the simulator on device, you're

Leo Dion (host): it writes
it to a SQLite file.

Yeah.

Brandon Williams (guest): and
there is the option where you get

to say, all right I want to load
up my application and I don't want

to use that sequel light file.

I just want to say I want
it all to be in memory.

That way, when the application is
killed, no data was persisted and that

can be incredibly helpful for you.

I test and unit tests
and stuff like that.

So that's a really good tool, but
That's the same story, regardless

of Swift data or core data that's
just kind of been carried over.

So Swift data, I, to me, I don't
know of anything special about

it that changes dependencies.

I mean, the most special
thing about it is just.

of how it plays nicely with SwiftUI,
like when, because you know,

historically, if you threw a reference
type into a SwiftUI view just in the

most naive way, none of its changes
would cause updates to the view.

And now we've got the machinery that
allows you to throw in a reference type,

and you make a mutation to something,
and it does update the SwiftUI view.

So that is big, and I think
that will be highly applicable.

Outside of Swift data
and observable objects.

I think you will be able to you could
have the idea of a, at one of our at

dependency property wrappers could
potentially hook into that so that

you could actually use it in the view.

Whereas typically right now you use it
in the observable object or the view

controller, but there are other places
I think you could start using it.

Yeah.

Leo Dion (host): Anything else you
want to mention before we close out?

Any sneak previews of
new point free stuff?

Brandon Williams (guest):
Yeah, I mean, let's see today.

So next week we're releasing 1.

0 of the composable architecture.

It's been in development for four years.

And so that's a big release.

And yeah, all the observation
and macro stuff, it really

fundamentally changes so much of what.

We've always wanted to do with
the composable architecture and

what wasn't really possible.

And so there's a lot of big stuff there.

And yeah, we've got this Swift
dependencies library just github.

com slash pointfreeco
slash swift dependencies.

And it's, and there, and we've got
links in the readme to all the other

dependency libraries out there.

Cause I am in no way saying
people should go and use ours.

Like we, we have made some very
particular decisions in the

design of our library that.

Makes it so that we can't even
do some of the things that

the other libraries can do.

But then, on the flip side, is that we
can do some things that they can't do.

It's just, there's tradeoffs
all over the place.

And I highly recommend people
go look at those libraries.

But yeah, we've got our
own version of the library.

And yeah.

I encourage people to check it out.

Leo Dion (host): Brandon, thank
you so much for coming on.

It was great to have you.

Where can people find you online?

Brandon Williams (guest): Yeah it's
MbrandenW on Twitter and then also on

Mastodon on the Hackaderm instance.

Leo Dion (host): Yeah.

Yeah.

Awesome.

And then of course, point free
we'll have links to that as well.

People can find me on
Twitter at Leo G Dion.

My company is bright digit.

If you're watching this on
YouTube, please and subscribe.

If you're listening to
this, please post a review.

Thank you so much for joining us.

And I look forward to
talking to everyone again.

Bye everyone.

Brandon Williams (guest): Thanks.

Bye.

Creators and Guests

Leo Dion
Host
Leo Dion
Swift developer for Apple devices and more; Founder of BrightDigit; husband and father of 6 adorable kids
Brandon Williams
Guest
Brandon Williams
Subterranean homesick mathematician. Working on @pointfreeco and @isowords.Mastodon: https://t.co/CAzMilB4pD

Join our newsletter

checkmark Got it. You're on the list!
image of podcast supporter
Join 1 supporters
Mastodon © Bright Digit, LLC 2018