Leo Dion (host): Hey folks.
As you can here I am slowly setting this studio up here in my new home.
Before I go off to Jolly England to talk about Vapor, I want to
release the first part of these episodes I did with Ben Scheirman.
of NSScreencast.
I hope you enjoy this one, and the
second part will be coming out next week.
If you're on Patreon, you can get both right now as well as a bonus chat I
did about setting this whole studio up.
So I hope you enjoy and have a good rest of your week.
Welcome to another episode of Empower Apps.
I'm your host, Leo Dion.
Today I'm joined with Ben Scheirman.
Ben, thank you so much for coming
Ben Scheirman (guest): Yeah.
Thanks for having me.
It's, it's really good to be here.
Leo Dion (host): Obviously a lot of people know you from
NSScreencast, but I'll let you go
ahead and introduce yourself.
Ben Scheirman (guest): Yeah.
It started NA Screencast in 2012.
Yeah.
So it's been.
You know, 12 years of, of doing the screen casting
thing.
And,
I've also done a course on.
Combined Swift.
We can have links to all this stuff in the, in the show notes later.
Yeah.
And that's, you know, I, I have a
couple of
side apps that I've work
on independently.
One of them is side Mirror, which is a Mac app.
I have another one called Tonal Therapy, which is for tinnitus.
And that's on the iPhone.
And I am currently working on another independent app because I'm
a glutton for punishment, and I just
can't, I can't get these ideas outta my head unless I try
to build them.
But then on top of all that, I, I'm also a lead software engineer at Nike.
So that's my full-time gig.
And so I'm just constantly, you know changing
hats into, into different modes.
Leo Dion (host): Yeah.
Awesome.
What do you do at Nike?
If you mind, what apps do you
work on?
Ben Scheirman (guest): I've worked on the sneakers app, so
it's, it's for all of the sort of street wear and sneaker community,
all of the
The sort of hot drops.
Hot, hot drops.
Leo Dion (host): like everything has a hot drop right now
because we all gotta be
Ben Scheirman (guest): Yeah.
Leo Dion (host): temu for
Ben Scheirman (guest): They literally measure the releases
as in terms of heat level.
So like, there's like an atomic and a high
heat.
And because, because they have to scale traffic to accommodate
the demand, it's, it's,
You know, not unlike, you know, iPhone, pre-orders or Taylor
Swift tickets or whatever.
I mean,
you
know, in a different scale, but same ideas where you have a lot,
you have a lot of people who want the thing at that exact
moment.
And so that actually makes some fun design choices that you
get to make on the backend and on the client.
Leo Dion (host): That's awesome.
So we're chatting in Chicago.
I wanted, I've been wanting to have you on for ages and one of the things
you mentioned is like trying, like slowly migrating your apps to swift ey.
And that migration is one
of my favorite topics.
It's a little bit boring, but at the end of the day,
there's good majority of what people do in the real world
is
Ben Scheirman (guest): Yeah.
How, how often do you do file new projects?
Leo Dion (host): right, right.
I did a whole video about migrating from a Objective-C because there's still
apps with Objective-C and good luck trying to hire an Objective-C developer.
Let's just migrate it to Swift and people will know what to do with it.
Or there's
just stuff you can't do in Objective-C like Swift UI
for instance, which we'll talk about today.
But
yeah, I think it's like a real world
topic that a lot of
people have to deal with is migrating
from UIKit, in this case, the Swift UI or something like that.
When do you think, when is a good point at which you're
like, yeah, this is not worth me
doing another storyboard or another zip or doing
another programmatic layout.
Let's
just migrate this to Swift y.
At what point do you think that decision needs to be made?
Ben Scheirman (guest): Certainly is a nuanced question.
We have, I mean.
I'm gonna say we a lot because I have, you know, in my, alongside my
independent apps, I also work on a team.
And so,
you know, I
have to
consider that for, for
my own apps, I'm usually willing to
adopt the newer thing sooner.
I'm willing to
sort of bite the bullet and you know, procrastinate by exploring some new
technology or some new technique.
And it, it, to me, it, it, like,
it, it becomes a way for me to explore
and learn and
flex the muscles and all that stuff, you know about learning
new technologies and those, those I may cover on a, in a screen
caster, of course.
But when you're in a company and you're on a team
and you're shipping a product some of the things are sort of low risk
changes and some of the things are
high risk changes.
So like a setting
screen is pretty low risk.
There, it's something that's not core to the app flow.
A user may only go to
it once ever.
And it's also pretty easy to build.
So it's like that's an easy
win.
You could just rewrite it in the time it
takes probably to update a storyboard or update a view controller
and add auto layout constraints and, and things like that.
Where things become a little bit more challenging or when they are in the
core flow of your app, or if certain
experiences are a must have and those may be challenging
or missing in swift ey.
And so for those I think you need to, you know, take
a more cautioned approach.
But I think that, you know, nobody's going to, going to just
let you stand still for six months while you rewrite the whole app.
And so the approach is generally gonna be sort of rewrite
little pieces as needed.
If something doesn't need to be changed you know, don't change it.
But in our case, in a lot of cases I've been on, the cost of new
features just sort of generally goes up over time because the
code base is harder to work in.
It was never designed to do the thing you're trying to do.
And so getting that flexibility means you have to be able
to refactor the code.
And refactoring the code that was written a decade ago sometimes
is a little challenging.
You know, this code is probably not written by you, or it's
written by people that are no longer at the company.
And, and so I do mention this with a, with a slight warning that it is sort
of a natural programmer tendency to just be like, oh, I didn't write this.
I wanna rewrite it.
Leo Dion (host): Yeah.
Ben Scheirman (guest): know, like as your way of
understanding, you rewrite it.
And that's a, you know, that's not a great trait, but there are cases
where I know that if we have written this in the, in, in a way that is more
flexible, that it will adapt to the, the needs we have now and then some.
Leo Dion (host): Yeah.
Ben Scheirman (guest): And so that's kind of the approach we're, you know,
we've taken and slowly but surely, more and more important and riskier,
You know, I say riskier, but like more important
screens are being
migrated.
And so we do that so that we can tackle these new
features and not have to deal with some legacy code that's
been there for 10 plus years.
And and then you risk, you know, breaking something that used to
work and nobody ever understood why, you know, things like that.
And so that's kind of our, our approach has been,
To do that just one piece at a time.
And slowly but surely, like the lar larger and larger parts
of the app become Swift UI.
Leo Dion (host): One of the things you mentioned in your
notes is the fact that you still support iOS 16 is how does that
affect the migration?
Are you
like, eh, we gotta do this really delicately because we still have people
on older phones, or like, how does that
work?
Ben Scheirman (guest): so if we rewind a year,
we were supporting iOS 15 and at that time we were like, oh, Swift UIs, rough
edges are mostly
sanded over, right?
There was some, there was some kind of really
rough things in I 13 and 14, but by 15,
I'm trying to think of the
the flag
pole features that they had or,
Was it navigation view?
No.
Navigation view.
Was
there navigation stack or No, a navigation stack is
16.
Leo Dion (host): Okay.
Okay.
Ben Scheirman (guest): I'm forgetting Anyway,
So there.
Yeah,
so we were like, okay, at 15, let's
start looking at Swift UI.
And this is, so one year ago, so we're, we're building
for Iowa 17, but supporting 15.
And and so then, you know, we implement a screen and we ended
up running into some problems.
And then Iowa
16 comes out, and then that's different behavior again,
which is like, okay, this is,
You know, it's not, it's not nice when you have like a workaround that
you did for iOS 15, and then Iowa 16 comes out and changes
that behavior and that workaround either no longer works or behaves
differently, and so you end up with
sort of this conditional code.
So it is really challenging actually, if you're not going to,
Target the latest version or even like n minus one.
It, it definitely is challenging.
Like Apple released so many nice things in iOS 18, and I'm
like, I can't use any of them.
So an example of that.
Is pull to refresh
behavior.
So we have a custom pull to refresh in our app.
And this is implemented as a sort of taps into the scroll
position of a scroll view.
Any, any scroll view throughout the app.
It does not use the UI refresh control like API because that I think is too
limited to do what we're trying to do.
Like one of the behaviors of the system, one is that
if you
keep pulling, eventually it'll just start refreshing.
Leo Dion (host): Right,
Ben Scheirman (guest): But, But, we wanted a like sort of a mid state of
like release to refresh, so that way you
can sort of see what the threshold is.
And if you, if you let go after the
threshold, it refreshes if you move your thumb back up, it
doesn't.
And then on top of that, you've got like the progress of that drag so that
you could do all kinds of interesting things like animate shapes or whatever,
you know, you could get creative with it, whereas apples is kind of
boring.
It's a, you know, it's just pulled
Leo Dion (host): And you did this all in UIKit, right?
Ben Scheirman (guest): originally, yeah.
And, and you say you, but I
didn't do it.
It was done long, long ago.
Right.
So, like,
you know, again,
nobody on the team now were there when it was written.
And so, so we're like, okay first understand how it
works, can we customize it?
Yada, yada yada.
Then we go to start building something in Swift UI and we're
like, oh, this screen is really easy.
It's just, it's literally a list of things with image And, text, but we
needed that pull to refresh view on it.
And
so trying to get that to work was really tricky because we needed access
to the collection view underneath,
or the table view.
And so.
We, you know, looked around a lot.
Intro Spec was the option that we went with.
And at the time had a, the newer API is definitely better, but the original
API for Intro Spec, and for those who don't know, intro Spec is a library that
sort of lets you peek under the covers at what Swift UI is doing and access
some of the hidden UIKit hierarchy.
Underneath it is,
it's a glorious hack, and it's like kind of a last, last,
It's like your last resort,
And it, I call it the Swift UI Escape Hatch.
And I actually published a video on this,
Which is, which is a free, it's a free video on, in a Screencast and
to show how that technique works.
And so what, what happens is you create a UIViewRepresentable,
Which is a way of
wrapping UIKit views and treat them like Swift UI.
So and there's also a view UIViewControllerRepresentable as well.
So if you add a UI, view representable as like a background
or an overlay, that's just like
an invisible view.
It gets installed in a view hierarchy that has access to
super view and parent controller and stuff in the case of a
controller.
So at some, at that point you've like created this
like this little thing that it gets hooked into this
environment that now you
traverse.
And so what it would do is it would walk up
the, walk up the hierarchy until it finds a known parent and then walk
back down and look at siblings to see like, oh, is this a scroll view?
Is this a collection view?
Is this a table view?
So
Leo Dion (host): Basically the kind of
stuff that you get if you were to like, look at the UI hierarchy in Xcode,
if you
Ben Scheirman (guest): Yeah, so like, if Apple said, if Apple said hey, we have a
new photo
picker control and controller,
and that thing is a UIKit view controller.
You could look at his view controllers, you could look at his sub views.
Even though those are con considered like private API,
you could technically go in there and fiddle with things.
So the
the thing about Swift UI is it hides all that stuff from you.
So if you need access to the underlying the underlying scroll view or,
or table view or collection view.
I say those three separately because it's, that
distinction becomes important in a minute.
The, the, at the Swift UI call site, you would just say
Intro spec on a Swift UI list.
You'd say do introspect, and then it, and then the closure that you
provide is passed in the scroll view.
And so then you can do what you want.
You can add a delegate or you can add an observer for the content offset.
And so, so it's like, it, it enables you to sort of stitch
these two things together.
Well, so when I was 16 came out, that broke because lists became.
Stopped being implemented by a table view and instead is now
implemented with collection
view.
So then we had to add a, add a separate one for that.
And so this is kind of like the nature of it.
You're, you're you know, programming against an API that is not public,
that the, it's an implementation detail that Apple decides.
And one day it may never be based on a UI ki view controller ever.
You know, they can do whatever they want under the hood and it's an opaque
API to us.
And so introspective is certainly not a long-term solution.
I do like the newer API that it has though, where it codifies the
things that are known based on a OS
version.
So you could say, I want to introspect this list on iOS
15, and the value you get
is a squirrel view.
But then you can also say on, I do the same thing, I
wanna introspect it on iOS 16,
the value you get as a collection view.
So it sort of codifies what is known at
that OS version.
And so then when iOS 18 comes along, you now have a build error and you gotta,
you, you have to like figure out what is the current way of doing that thing.
So it, you, sort of catch it right away as soon as you
start building with the new
Leo Dion (host): That's so interesting because like with Bushel, my VM
app for the Mac I used, I ended up creating like what do you call it?
A MO view modifier so I can then grab the NS window and add a delegate to
it
And it's the same
idea.
I would assume they're never going to go away from NSWindow, I would assume,
you know, anytime soon.
Ben Scheirman (guest): That's a pretty, that's a much safer assumption than
some component that happens to be implemented with collection view.
Right, But, but at some point that may break,
Leo Dion (host): right, right.
And I know this year they added a
bunch of window stuff to Swift UI,
so maybe a lot of that can be deprecated and things like that.
So yeah, that makes total
sense
Ben Scheirman (guest): and it's important to know that, like as you find
these workarounds or these things I did a series on building a Mastodon app for
for the Mac with Gui
Rambo.
And I talked to him about this idea of
like, is it a failing of swift ey if you have to drop
down to NSViewRepresentable or UIViewRepresentable.
And, and his, his take on that was,
it's necessary.
It's like you're always
gonna have to do
it because Swift UI is still a young framework.
They're, they haven't
implemented every last thing.
And if you don't want your apps to look and behave exactly like
the mail app on, on iOS, right?
If you want anything custom, any kind of custom non-standard behavior, which
I think the, the industry is moving back towards that things that look nice and
feel nice, you know, a little bit of skew amorphism, things like that I think
should be celebrated.
A little bit of surprise and delight.
You know, like I mentioned with the pull to refresh
indicator, the, the stock one is
super boring.
And I think that you can get creative with stuff like that and, and,
if, if Apple doesn't provide it, right?
The UI refresh control, sorry, the refresh control modifier in Swift UI
only gives you the default behavior and you can't customize it at all.
So sort, sort of going back to that, that example
I.
Of needing to customize that.
We ended up using intro spec as, yeah, this kind of sucks, but
we don't have a better solution.
So we, we went with it and I was 15, you know, targeting, I was
15, I was 16, ended up having different behaviors, so we had to fix that.
And then now we actually have a real solution that doesn't require such hacks
but is sort of a new beast on its own.
So I think that I, I mean, I can talk about this, this idea, it's, it's sort
of specific to what we are trying to do, but but I think that this is kind of
like the, the strategies that you have, if you start using Swift UI, everything
is nice and clean and neat and you know, you can iterate fast and then all of a
sudden it doesn't do the thing you want and you have no view modifier that can.
Make it do the thing you want.
The, the tool at your disposal is UIViewRepresentable or
UIViewControllerRepresentable to like, okay, I know how to do this in UIKit,
how can I translate that to Swift UI?
So we came up with something, I, I have
a, it's a terrible name, but it's basically a collection view Swift
UI view that wraps a UI collection view that uses compositional layout
under the
hood.
And we do that for two reasons.
One is performance.
If you have an infinite list of content, so imagine you're building
a Mastodon client and you're just scrolling through stuff that has
images and links and text and stuff.
The
lazy
VST stack
is just not gonna work and list.
Leo Dion (host): Do you know what it does underneath That makes it
so, so much slower.
Ben Scheirman (guest): so if you can kind of squint and look at the.
What Apple was doing
with collection
views and table views to make things fast.
And, and especially with
around layout, like knowing how tall a cell is is really important
for knowing like how far you are scrolled through the content.
You know, what does the scroll bar look like?
How big should the scroll bar view be?
Scroll bar B,
because if that stuff changes dynamically, then you gotta
stop and compute, compute
it.
La So we run into problems
with lazy vs stack because it needs to know sort of the layout of all the
different items so it knows like, how many am I showing on this first page?
So if you, if you look at LazyVStack and you, and you try to chain like a
task modifier or a, an appear modifier onto an element, you may be surprised
that it gets called right away, even though it's the last item in the
Leo Dion (host): Okay.
Ben Scheirman (guest): And so like something like an auto paging to the
next page in Pure Swift UI, one way to implement this would be to have
your, for each, with all the items in it, and at the very end add like a
loading row that, that has a spinner, and then the, on a peer modifier for
the loading row kicks off the, the fetch for the next page of content.
And so the idea is you get to the bottom, it loads, you get
to the bottom, it loads again.
So with LazyVStack, because it was trying to like probe to see how many
items it could fit on the page, I got my appear modifier right away and I
loaded the page number two Right, away.
And
Leo Dion (host): Yeah.
Ben Scheirman (guest): so it was, it was like just kind of
frustrating 'cause there, there wasn't a lot that I could do
to I, I'm sure there's maybe a different approach I could
take, but so that was one issue.
Performance on these lists was not, I.
Fantastic when we did just some initial tests of like
infinitely scrolling lists.
But I still wanted to use Swift UI for the content of the cells.
So I was thinking like, let's make a collection view
driven skeleton, where all, all the, all the meat is Swift UI and and
Apple provided UIHostingconfiguration.
It, and I feel like a lot of people, myself included, just kind of
avoided all those modern collection view APIs that were like, here's
a sales content configuration.
And instead of, instead of like unsetting things in the wheel
display cell and then there, there was like prepare for reuse that you
could override on a cell subclass.
You would have to like unset everything that you set before.
You know, like if, if, if there was a label, like say a subtitle label.
And one of the elements didn't have a subtitle and you just
skipped over setting it.
Well, then if that gets reused, it would just inherit the
value from the previous one.
So, you know, like there was stuff like that that, that apple was trying to
make better and they, they implemented that cell content configuration
and background configuration
and there's stuff about
updating the
state when
somebody hovers or, or presses and holds or whatever.
Leo Dion (host): wanna say like of all the things that like Apple has updated
on UIKit, it's been collect collection view, you could just see that's still a
keynote, or not keynote, but maybe like
primary piece of UIKit that's constantly being improved.
So yeah, I'm not surprised by that.
Ben Scheirman (guest): yeah.
And it's it's complex and
I,
I don't love the
APIs that.
Because they just, they don't just
like come naturally.
It's, this is
a, a component that has been iterated on a bunch, but they
also have to like have backward compatibility.
But so I mentioned the content configuration stuff.
They added a UIHostingconfiguration where if your entire
content comes from a
single cell,
sorry, single Swift UI view,
then you can set the cells content configuration to a
UIHostingconfiguration and give it a Swift UI view.
And now you've got Swift UI cells inside of a collection view.
So we implemented that, I, I did it in a test with an infinite list of
like just random emoji basically, and gradients and images and stuff.
So I could scroll through and see like how it behaves.
Yeah.
Then now that I have a collection view, I have the ability to say and this
is the kind of the behavior you want.
I never want them to see a loading row at the end.
Leo Dion (host): Right.
Ben Scheirman (guest): would like it to be like, if you're scrolling
down and you're like certain number of screen lengths from the bottom,
like the fetch more content.
And I know collection view has a way to like prefetch Prefetch
rows or something like that.
I'm trying to remember the API for that.
But anyway, so this lets me do it because now I have access to
the collection view because I have access to the collection
view.
I also have access to the raw content, offset content inset.
And there's another one called
it's not, is Active.
It's on scroll view.
I can look it up in a minute, but,
So what it is, is, is so like how do you know if you, let's say you're Swift UI
and you have a binding to a content and set property, right, which is a float.
And you, and you might think that that's all you need to
do, pull to refresh, right?
Like, if I pull down that content, inset becomes positive,
right?
Because as you scroll through the normal content, the content
inset becomes negative, right?
There's our content content offset.
So
or maybe I have that reverse.
I, I don't remember.
So you pull down
and the,
The pulter refresh thing is going to activate as soon as
you've passed that threshold.
Right, but what happens if I'm like, okay, I scroll up a little bit and
then I scroll down really fast.
You'll get that bouncing rubber banding behavior when you hit the top, right?
I don't want that behavior to show the partial pull to refresh
logic,
Right, I only want it to, when your fingers down is tracking,
sorry, is tracking is the name of the property on scroll view.
So scroll views is tracking, tells you if the user's finger is on the scroll
view.
And so this is how you can implement that preventing rubber banding from
also showing your pull to refresh logic.
But also like if you want a release to refresh.
Part of the pulter refresh cycle, that's also useful.
So you
can, you can do all that.
And so I take all those values, I bundle 'em up into a struct and I
send them to Swift U iland, and then all of a sudden my pulter refresh
is like mega easy to implement.
I can, I can customize it however I want.
I've got a progress for how far you have to drag, which I use to animate like a
little line that spreads across the, the
header.
And when, when that line touches the edges, that's when you're reloading.
Leo Dion (host): Got it.
Okay.
Ben Scheirman (guest): And so, so anyway, so I had to do this
by implementing a mega UI view, representable component that
is backed by a collection view.
And I had to do my own like compositional layout, which, you
know, I thought maybe I would never have to do that stuff again.
But turns out it was really important.
Now, the benefit of this approach, despite it being a lot more code.
Is we, this will never change, right?
Like Apple can change their mind on how lists work or whatever,
but this is our component.
We can decide to, you know, ditch it in the future if the apple
comes up with something better.
But the, like, for instance, the scroll view stuff in iOS 18, it's gonna be
two more years before we can use it.
So in that time, I need to be able to make some progress on it.
Also, I don't think that
the is tracking touchdown, you know, logic is part of that
new iOS 18 scroll view, API.
And so you would probably still need to have some sort of UI view
representable, which was, which was tracking touches
down, touches began, or whatever.
Just to know.
So I think it would be possible and probably be cleaner, and
I always say teen, but still I don't think goes all the
way that we that to meet our needs.
Leo Dion (host): Do you like, how do,
you look at a piece of code and figure out so I guess if you have
a new view, If it does what you
want it to do, you would go with swift y pretty much, or try to go
to Swift UI as much as possible.
Ben Scheirman (guest): If I find it to be far more productive
than, UIKit and especially like, I haven't used storyboards in a long time.
I sort of
saw that, that like w wasn't what I wanted.
And, and so I was like, got used to writing constraints by hand and
I, I sort of, I can kind of get a sense for what's going on when
I see
constraints.
But nowadays when I look at 'em, I'm like, this is a lot of
code to express something pretty
simple.
And,
And, there's,
I won't say there's no
scenario that Swift UI just can't
handle that constraints can, but there's
very few,
Leo Dion (host): right?
Ben Scheirman (guest): I, I, remember seeing a challenge.
Somebody's like, oh, somebody
implement this in UIKit, and I watched Chris eof do it
and he couldn't.
And so if if Chris eof
can't do it, then I certainly can't it probably can't be done.
Leo Dion (host): Maybe he could do it in type script, but not, yeah.
Do I what?
Oh gosh.
Now I forgot what I was gonna say.
Oh yeah.
If your design team comes up with something that's oh my
gosh, this is do you have any wiggle room to be like, you know what?
We could easily do this in Swift UI if we
don't do this, and this.
Do you have
any
Ben Scheirman (guest): Implementation costs and difficulty and
complexity and stuff like that is definitely, it's a definitely
conversation and we just, sometimes designers aren't aware
Leo Dion (host): great.
Ben Scheirman (guest): of, of, like, what are the
standards on iOS?
What, what is
free and what costs 10 grand?
You
know what I mean?
And so like
if, if that's not important to them or they're like, oh,
I, you know, I just put that
in there because it looked good, Or whatever.
But there are gonna be
Leo Dion (host): it's just an afterthought that's oh, I
assumed that, that's what it ha.
Yeah.
I,
Ben Scheirman (guest): And so, but there are gonna be
things that, that designers will be like,
no, this is a primary interaction or animation that, that is gonna like, make
our app feel more fluid or whatever.
And it's
worth that time to invest it.
I feel like the design and engineering
should always be two way conversation and it
shouldn't be a
handoff,
right?
Because there are things that come up that you're like,
Hey, actually now that I'm in
it, I realize that this is gonna be really tricky because
when you scroll, this other
thing comes on top.
And
So so you have to
kind of come up with those solutions and workarounds and trade-offs.
And
I think
that implementation
cost is a hundred percent part of
that conversation.
Leo Dion (host): One thing I was also gonna mention was the idea
of having to step back to UIKit for doing more complex things.
It's not, I don't think it's cheating because in some sense there was always
the ability in UIKit to step down if you need to customize thing, God forbid,
like you would always have core graphics or core animation or something else.
So there's always gonna be another layer removed from that abstraction
to always customize things anyways.
Right.
So it's it's just a be like you said,
is it worth the $10,000 to do it like it might be, I don't know.
Yeah.
Ben Scheirman (guest): Yeah.
And I think, you know, I'm, I'm just becoming more comfortable with that
decision of like, it's I used to get frustrated, like there should be a way
to do this in pure swift ey.
And sometimes those
involve really complicated
solutions that involve preference keys and geometry readers
and like.
You know, to do something simple like I need to know how big
this frame is so that I can replicate that size in a different place, in
a different view hierarchy, right?
So like doing preference keys for that.
I, I still feel like, the preference key system, it's like
for people who don't watch all the videos and like read all the articles
and stuff like that, people who are just sort of doing their job, I don't think
people really understand how this works.
And so
as soon as you start seeing preference keys,
like this preference construct and extending environment
values or preference
you know, all those different like Swift UI
things that are like, what are you trying to do?
Oh, I've gotta create these three types I need to extend to this
thing.
I think that kind of gets lost 'cause it is kind of
complex.
And so like some of
the pure Swift UI solutions aren't quite as clean as you
would hope they would be anyway.
Leo Dion (host): I also wanted to chat a bit about stages.
Do you, so we talked about if you have a new thing, you'd try to go
Swift two eyes as much as possible.
Do you have stages from going from a UI view controller to a Swift UI
view, or is it just kinda like we're just gonna rewrite this as a Swift UI
view?
I'll give you an example.
For instance,
like.
When it comes to like Objective-C to
Swift, like the first step is to you know, maybe have ui te U unit tests
perhaps.
It might be adding syntactic sugar to the Objective-C so that you
can specify whether something is nil or what generic type it is.
And then maybe I might create like an NS object class NS object.
Even though you'd wanna do a sw, like a struct, at some point you might
do like a class that derives from NS object, so that way it can be
taken still ingested from Objective-C while still
being written in a swift, you know what I mean?
Are there stages you can split up a
migration?
Ben Scheirman (guest): So I think that some of the things I've been talking
about are both changing the architecture
and the UI
paradigm, right?
Because you also have to consider like, how does the data flow into this system?
And
if it's like interactable, how does that data come back out and mutate your
model?
And in UIKit, that's usually very
different than a Swift
ui.
Bindings can help a lot there
You know, because you can, you know, pass a mutable reference to
a thing, into a binding, and then
the edit screen, for
instance, can mutate it.
And then when you hit back, when you go back, it's already done.
That sort of thing.
But I think, you know, if you're just sort of trying to
start small and work from from the inside out is probably the best
approach if you're not trying to
also improve the architecture as you go.
So so if you imagine like I keep going back to the Mastodon example because I
think most people are familiar with like social apps favoring an item, right?
So like,
say you tap on the star and it does a little dance,
I don't know, do that in swifty y and put that in your UI table view cell
and that's, you know, one step closer.
You know, over time you'll get more confident to take larger leaps.
Like maybe the whole cell becomes Swift UI before the,
the surrounding screen becomes.
Leo Dion (host): Yeah, it, that reminds me of the whole thing with like
navigation too, where it was like yeah, I mean, you want at
the outside, you want that app
delegate
still, and then you have a hosting controller, and then you could
deal with navigation that way as opposed to the other way where it's
yeah, you're just gonna have an
application and then work like, yeah, I think that makes total
sense.
You start with the
smallest piece on the inside and then
working
Ben Scheirman (guest): really tricky.
Leo Dion (host): it is really
tricky.
Ben Scheirman (guest): when you're mixing.
I, I think it's tricky
in
general.
I am not a fan
of the,
the approach
that
view controllers know that they need
to push the next view controller in the chain.
Like in a navigation controller,
Scenario, it means that your view controller knows
it's embedded
in a navigation controller,
which
it shouldn't shouldn't know.
Rou, Klu has this
amazing
quote,
I think it was at NS Spain in like,
I dunno, a long time ago.
He said
children
shouldn't tell their parents what to do.
Leo Dion (host): Yeah.
That is
Ben Scheirman (guest): software and in software,
he would
argue That children know shouldn't know
who their parents are.
Leo Dion (host): Yeah,
that is
Ben Scheirman (guest): And it's like, it's a good
sort of, I think about this a lot because this
is kind of where the
coordinator pattern came out, where you have these view controllers that can be
embedded
in different contexts if you needed.
But the point is, is they don't know how the navigation works, they just
delegate it up to a coordinator.
And the coordinator is like, oh, I'm in a navigation stack.
I need to push this other thing.
And what, what That allows you to do is avoid the
situation where, say you have A, B and C view controllers, and the C
view controller has a couple
dependencies.
So now A needs to create and push B.
B needs to create and push C, but B doesn't have C'S
dependencies to pass along.
And so you either you.
Sort of what I would
consider anti-patterns, which is like static service
lookup and where C says, Hey, give me my global dependency for this.
I don't know terms of service fetcher or whatever.
I, I much prefer the dependency injection approach, but B
doesn't need that and shouldn't even know it exists, right?
So it's a dependency problem where, where I think that these
controllers create in creating themselves or creating their
siblings is kind of a pattern that I don't like in, in UI kids.
So this is why, I mean, that navigation is hard and navigation in Swift
UI is, has similar problems, but I think that that can be smoothed over
by the fact that you can put things in the environment and then you can
go three levels farther and then pull them outta the environment.
So you don't necessarily have to carry them along each step of the way.
But the problem that, that I see is that
like the, the model for Swift UI navigation is.
Either a binding to a Boolean of whether or not a thing is presented
or a binding to a, an array of path elements which identify
the items being presented.
So like when this path element has a value of the a user, I'm
gonna present the user edit
screen.
I like that sort of decorative model based approach because then
when the user dismisses the screen, that binding gets set back to
nil, meaning nothing is presented.
But in UIKit, you don't have that.
You just have push and
pop and there's no way for you to tell, for instance, if you, if you
want model this since if Youi, but then part of this is a UIKit view hierarchy,
Leo Dion (host): Yeah.
Ben Scheirman (guest): it becomes difficult.
Like how do you, how do you present something in UIKit and then bubble
up the fact that they dismissed to a Swift UI binding so that it can
be modeled appropriately in Swift UI.
And I found, or I, I was introduced to this idea from
the point free video series.
They have a recent series on UIKit, sort of building observable models and
sort of adapting Swift UI tools to UIKit and it's really, really great series.
And they implemented a on dnet class that holds onto a closure.
And then you assign that on dnet handler as an associated
object.
So you're going way back to Objective-C runtime, trickery
set that as an associated object on the view controller.
And that way you don't have to touch the view controller
that you're presenting at all.
It doesn't need any new
API, it just has this this extra bit of state that is attached to it
by the Objective-C runtime system.
And so for folks that aren't aware there is a Objective-C like
BJC associated value and get associated value,
Leo Dion (host): you
can deep dive into the object and do whatever the heck you want with it.
Yeah,
Ben Scheirman (guest): yeah.
And so like this is a way to like sort of piggyback some storage onto the,
onto the lifecycle of another object.
And
so what, what you would do there is you just assign this on DNA
helper as an associated object.
And then when that view controller goes away, it clears out that on
dnet object that you just set.
And so the idea is that in the, in the Dnet
of the on Dnet helper, you execute the closure.
So that gives you
a hook that you can
apply to
any UI
view controller.
Leo Dion (host): When you're talking about
Ben Scheirman (guest): idea is that.
Leo Dion (host): ahead.
Ben Scheirman (guest): So the idea
there
is
that you that's where you
set the binding back
to, to is presented false or nailing out the, the presented
model whatever your binding
is.
There's, there's those two different flavors of one that is just a true
false and one that takes like an item.
Leo Dion (host): I was just thinking back to any work I've done with web
development, I've, I still do some like VJS stuff and like some type script and
I'm like, how did they do navigation?
And then I was like, well, either the
parent is responsible for it or uses the URL and I'm like, that
sounds a lot more like what you were saying about using the path thing,
right?
Where you have an enum of the path and you have an environment variable.
It's like that's basically what that
Is
Ben Scheirman (guest): The url, the ur l can be constructed
to say, Hey, I handle all
slash admin.
Leo Dion (host): Right?
Right.
Ben Scheirman (guest): And from that point onward.
And then from inside of admin, you have admin slash users
or whatever, and that
can, and then
that one has a sub route of user slash
you know,
25.
Leo Dion (host): And you almost.
You could kind of do that,
right?
You
sort of could do that with
Ben Scheirman (guest): can do
that in Swift UI.
Leo Dion (host): with an associated URL too, right?
So yeah.
Ben Scheirman (guest): you can do it with deep links.
Leo Dion (host): Or deep links.
Sorry.
That's what I meant.
yeah.
Ben Scheirman (guest): I, that that system is kind of confusing,
Leo Dion (host): Yes it
is.
Ben Scheirman (guest): but it does work.
The,
But I, I like the
path element approach that
navigation stack uses in iOS
16.
So, so the Swift UI binding to that item
that you can, you can mutate because some other UIKit thing did a D
in it is really, really helpful And allows us to do things
like, we have a,
Flow that you have to go through as like, sort of like
onboarding.
And this has to be checked every time you log in.
And we need to make sure that your
locale is in a supported locale,
For.
For like the markets that we're, that we operate in and that you've agreed to,
to, or I've, you've confirmed on like a mobile number.
So there's a bunch of sort of checks and it goes
through this state machine,
Leo Dion (host): Yeah.
Ben Scheirman (guest): to say like, okay, I'm gonna go from here to
here, I'm gonna check that and if it's not there, I need to pause
here, present some ui, let the user go through that flow and then,
and then come back to that spot.
And that is a pure swift just model
of model of the, of the walkthrough the user has to go through.
So knowing when they're done with a particular flow that is in UIKit and
those components are built by another team that we don't influence, that
is really handy to be able to, to do
that sort of thing without having
to
wrap in a parent view controller,
you know, without having to do a PR on that particular component.
So,
The other thing though, that's really, really important, I
think it's really easy to, I.
For or not realize that you have retained cycles in a view
controller,
which would lead to that view controller never being deallocated.
And then, then your
on DN net
would never get called.
Yeah.
If you're using like,
Combined subscriptions and you do like dot assigned to inside of a
view did load which it's just so easy to do, it's so tempting to do that.
But
that if, if you if you assign to anything,
it's basically calling self
value equals new value and capturing that self.
And so if the self is holding onto the subscription and the
subscription's holding onto
self, then
that view controller just never goes away.
Right.
This is a
common, common
Leo Dion (host): Could you do a disappear and cancel
on disappear or something?
Or
What can you
Ben Scheirman (guest): Yeah, you could,
Leo Dion (host): okay.
I'm just trying to think
Ben Scheirman (guest): some way of breaking that.
Yeah.
And then the, the real answer is usually just
do a
sync
and in the sink closure do weak self, so that way you're
never capturing
self.
But yeah, I think just knowing what, what, a retain cycle looks
like, you should be able to spot it pretty quick with closures, right?
Like it
I think with async
streams, this is also a big problem, that it's not always
obvious that you're retaining self,
Leo Dion (host): Let's,
Ben Scheirman (guest): Especially because tasks have an implicit self.
So it's like you can just call methods directly.
And so I think it can hide,
Retained cycles.
So I would definitely say.
Use the you know, use a memory graph, debugger.
There are some other tools you could use, like
Christophe Slosky has a tool called Lifetime
Tracker, which you can say like, Hey, I only ever want one of these screens
to be created, ever one of these few
And so basically it asserts, it creates like a
global counter and it asserts if you ever get more than your,
your, so for instance, like user profile
controller, there should literally only ever be
one ever.
And if there's ever two crash
Leo Dion (host): Yeah.
Ben Scheirman (guest): crash via like debug assertion, which is really nice
'cause you want, you wanna catch these
things early.
Leo Dion (host): Yeah, yeah, totally.
Yeah, that's Awesome.
We'll put links to all that stuff too in the show notes
so People can look that up.
Well Ben, thank you so much for joining me for today's episodes.
We'll be talking more in the second part about Swift UI and
UIKit and migration, as well as some stuff about Swift packages.
So be sure to be on the lookout for that well.
Ben, where can people find you online?
Ben Scheirman (guest): You can find me.
My
website is ben Scheirman.com and,
I'm on Mastodon at ben
And yeah, my website will have links to NSScreencast and Combined Swift
and some of the other apps I work on.
I.
Leo Dion (host): Awesome.
People can find me on Twitter company is Bright Digit.
If you're looking for a developer Apple space, please reach out to me.
You can find me on Macon at Leo Dion CI am.
If you enjoy this episode, watching this on video it's please, and subscribe.
I greatly appreciate it.
And if you're listening to this on a podcast player, gimme a review.
Thank you so much.
I look forward
to talking to you in the next half of this episode.
Bye everybody.