Matthew Massicotte - The Bleeding Edge of Swift Concurrency
Summary
TLDRThe speaker shares their experiences with Swift concurrency, highlighting the complexities and challenges they faced when using it incorrectly. They emphasize the importance of understanding concurrency as a 'deeply hard' problem and discuss various issues such as task retention cycles, actor reentrancy, and the difficulties of maintaining order in asynchronous code. The presentation aims to raise awareness about potential pitfalls, promote best practices, and encourage developers to use the latest APIs and tools to avoid common concurrency-related mistakes.
Takeaways
- π§ The presentation is about the complexities and challenges of using Swift concurrency, focusing on the speaker's personal experiences with its misuse.
- π The speaker emphasizes the importance of understanding Swift concurrency as it is neither simple nor easy, despite its potential to simplify certain tasks.
- π The concept of 'simple versus easy' is introduced to illustrate that some things may be easy to use but have deep underlying complexities.
- π The talk discusses issues with Swift's actor model, such as difficulties with capturing 'self' weakly and the challenges of asynchronous sequences.
- π‘ It highlights problems with main actor isolation in Swift UI and how property wrappers can implicitly change the global actor.
- π§ The speaker shares their experiences with concurrency issues, such as the actor re-entrancy problem when caching values in an actor.
- π οΈ Solutions to concurrency problems are explored, like using 'async semaphore' for managing critical sections in a thread-safe manner.
- β οΈ The script points out the importance of making dependencies explicit in asynchronous code to avoid race conditions.
- π§ The use of 'async unsafe' APIs is introduced to bypass the 'sendable' requirement for closures, which helps with updating UI and protecting mutable state.
- π The speaker discusses the implications of a strict Swift concurrency build setting that led to numerous warnings and the need to address these issues proactively.
- π The presentation concludes with a cautionary tale about the dangers of building APIs incompatible with Swift concurrency and the need for developers to be aware and plan for upcoming changes in Swift 6.
Q & A
What is the main topic of the presentation?
-The main topic of the presentation is Swift concurrency and the experiences of using it, particularly focusing on the challenges and issues encountered when using it incorrectly.
Why does the presenter want to leave the audience with a bit of fear?
-The presenter wants to leave the audience with a bit of fear, or rather a healthy respect, for Swift concurrency to highlight the potential issues and complexities that can arise from its misuse.
What is the difference between 'simple' and 'easy' as discussed in the presentation?
-In the presentation, 'simple' refers to things that are easy to understand once the concepts behind them are grasped, but may require significant effort to initially understand. 'Easy' refers to things that can be used with little effort to learn but may be fundamentally complicated underneath.
What is the presenter's view on Swift concurrency in terms of simplicity and ease of use?
-The presenter believes that Swift concurrency is neither simple nor easy. It is a complex system that, despite its potential to make things easier, can be challenging to use correctly and can lead to many problems if not handled properly.
What is the 'international unit of fear' mentioned by the presenter?
-The 'international unit of fear' is a humorous term used by the presenter to quantify the level of concern or caution one should have when dealing with Swift concurrency issues, with 'scared beakers' being the unit of measurement.
Can you explain the issue with capturing 'self' in asynchronous tasks in Swift?
-The issue with capturing 'self' in asynchronous tasks in Swift is related to the potential for creating retained cycles that can lead to memory leaks. The Swift team decided not to enforce capturing 'self' weakly by default to avoid breaking existing code and to allow for flexibility.
What is the problem with using async sequences and how can it lead to memory leaks?
-Async sequences can lead to memory leaks because if they are effectively infinitely long, they may never finish executing, leading to the retention of 'self' indefinitely. This can happen if the async sequence is started and then suspended without properly completing.
Why does the presenter mention 'main actor' functions and their relation to tasks?
-The presenter mentions 'main actor' functions because they have a specific context that tasks inherit when started. This can lead to unexpected behavior if a task is intended to run on a different actor but instead runs on the main actor due to inheritance.
What is the issue with property wrappers and how do they relate to 'main actor' isolation?
-Property wrappers can implicitly change the global actor isolation of their enclosing type, which can lead to confusion and errors. This behavior has been identified as problematic, and there are proposals to change this behavior in Swift Evolution.
What is the 'actor re-entrancy problem' and how does it relate to caching in Swift concurrency?
-The 'actor re-entrancy problem' occurs when an actor starts an asynchronous task that suspends, allowing other threads to enter the actor and perform the same operation, potentially leading to duplicated work or other issues. This can happen when caching values in an actor if not handled correctly.
How does the presenter suggest handling the ordering issues in asynchronous code?
-The presenter suggests making dependencies explicit to handle ordering issues in asynchronous code. This can involve capturing a reference in one task and awaiting it in another to ensure that the operations occur in the correct order.
What is the significance of the 'async semaphore' in the context of Swift concurrency?
-The 'async semaphore' is a package that provides a semaphore that is safe to use with Swift's concurrency model. It allows for synchronization similar to traditional locks but in a way that is compatible with Swift's async/await syntax and concurrency features.
What are the implications of the 'sendable' requirement in Swift's asynchronous functions?
-The 'sendable' requirement implies that any value passed to an asynchronous function must be safely sendable across different threads or execution contexts. This can be problematic for types that are not sendable, such as UI-related objects, and may require workarounds or changes to the API design.
What is the presenter's stance on the Swift concurrency build setting?
-The presenter advises turning up the Swift concurrency build setting to the maximum to get a full picture of potential issues in the code. They argue that it is better to be aware of these issues early on and plan for how to address them rather than building APIs that are fundamentally incompatible with Swift concurrency.
What does the presenter suggest for dealing with Swift 6's stricter concurrency requirements?
-The presenter suggests that developers should start turning on the concurrency warnings in their builds to find and address problems in their code before Swift 6, where these warnings will become errors. This will help avoid building APIs that won't work with the new concurrency model.
Outlines
π Introduction to Swift Concurrency Challenges
The speaker begins by inviting the audience to recall impactful presentations that evoked strong emotions, setting the stage for a discussion on Swift concurrency. They candidly admit to using Swift concurrency incorrectly, emphasizing the complexity of the topic. The presentation aims to share experiences and lessons learned from misusing concurrency, with the goal of instilling a 'healthy respect' or a bit of fear in the audience. The talk will cover a range of issues from least to most concerning, using 'scared beakers' as a unit of fear, starting with a simple class worker example and moving towards more complex topics.
π The Perils of Actor Re-entrancy and Caching
This paragraph delves into the intricacies of working with actors in Swift concurrency, highlighting the pitfalls of re-entrancy. The speaker uses a counter example to illustrate the problem of caching within an actor, where multiple threads can lead to duplicate work or wasted efforts. They propose a solution involving the caching of tasks instead of values, which prevents the re-entrancy issue by ensuring that the initial value computation is only started once. However, this approach comes with trade-offs, such as not benefiting from automatic cancellation or priority propagation that structured concurrency provides.
π Managing Concurrency with Async Semaphores
The speaker introduces 'async semaphore' as a solution to manage concurrency safely, particularly when dealing with traditional locks that are incompatible with Swift's concurrency model. They discuss the challenges of updating UI elements or mutable states that are not 'sendable' and how Apple's new APIs, 'async(unsafe)' and 'mainActor', address these issues. However, these solutions are only available in the latest OS versions, prompting the speaker to create a package for backward compatibility. The paragraph also touches on the complexities of ensuring the correct order of execution in asynchronous code.
π Navigating Swift Concurrency's Evolving Landscape
This section discusses the transition from synchronous to asynchronous functions in Swift and the implications this has on code ordering and execution. The speaker explains the need for explicit dependencies when managing tasks and states, as implicit ordering can lead to race conditions. They introduce their own solution, 'asynchronous queues', which allow for controlled ordering of tasks, and emphasize the importance of being aware of and planning for changes in Swift's concurrency model, especially with the upcoming Swift 6 release.
β οΈ The Concurrency Compiler Setting and Its Impact
The speaker shares their experience with Xcode's concurrency compiler setting, which controls the strictness of checking for proper concurrency usage. Initially set to the lowest level, an Xcode bug prompted an increase in the setting, resulting in a significant number of warnings. While some were easy to fix, others revealed deeper incompatibilities with existing APIs and Swift's concurrency model. The paragraph serves as a cautionary tale about the importance of addressing these issues proactively to avoid fundamental API incompatibilities in the future.
π§ Embracing Change and Planning for Swift Concurrency
In the concluding paragraph, the speaker underscores the importance of embracing the changes brought by Swift concurrency and planning for the transition. They encourage developers to start addressing the compiler warnings and to be aware of the evolving nature of the language and its frameworks. The speaker also stresses the need for explicitness in managing dependencies and the potential for early warnings to help developers avoid building fundamentally flawed systems in the context of Swift 6's stricter concurrency requirements.
Mindmap
Keywords
π‘Swift Concurrency
π‘Actor Model
π‘Sendable
π‘Main Actor
π‘Async/Await
π‘Global Actor Isolation
π‘Task Cancellation
π‘Continuations
π‘Unstructured Concurrency
π‘Async Semaphore
π‘Concurrency Build Setting
Highlights
The presenter emphasizes the emotional impact of a presentation about Swift concurrency, drawing attention to the feelings stirred up by their experiences.
The misuse of Swift concurrency is highlighted, with the presenter admitting to using it incorrectly and wanting to share these lessons learned.
The concept of 'simple versus easy' is introduced, differentiating between things that are easy to use and those that are conceptually simple after understanding.
An analogy of a 'for loop' is used to illustrate the complexity of seemingly simple programming concepts to beginners.
Swift concurrency is described as neither simple nor easy, emphasizing the inherent difficulty of concurrency systems.
The presenter's experiences with Swift concurrency's sharp edges and problems are shared to prevent the audience from repeating the same mistakes.
The introduction of 'scared beakers' as a unit of fear to measure the severity of problems encountered with Swift concurrency.
A detailed explanation of the complexities and potential pitfalls of capturing 'self' in asynchronous tasks in Swift.
The issue of retained cycles and their impact on memory management when using Swift concurrency is discussed.
The presenter explains the unexpected behavior of tasks inheriting the actor context they are started in, leading to potential UI refresh issues.
A strategy to avoid inheriting actor context is presented, using 'detached' tasks to control execution context.
The limitations of synchronous access to immutable properties across modules in Swift are highlighted.
An exploration of the complexities added by property wrappers in Swift UI, which can implicitly change the global actor isolation.
The presenter introduces the concept of caching in concurrency, discussing the challenges of computing initial values asynchronously.
The 'actor re-entrancy problem' is explained with an example of a counter, demonstrating the issues with caching in concurrent environments.
A solution involving the use of 'async semaphore' is proposed to handle the complexities of asynchronous caching and concurrency.
The importance of understanding the implications of turning functions into asynchronous ones, especially regarding their synchronous call sites.
The challenges of maintaining order in programming with concurrency are discussed, emphasizing the need for explicit dependencies.
The presenter introduces a custom solution, 'asynchronous queues', to control task ordering in Swift concurrency.
The impact of the Swift compiler's strictness on checking concurrency usage through an Xcode build setting is examined.
The presenter shares their experience with a bug in Xcode 14.3 beta 1 that unexpectedly increased the compiler's strictness on concurrency.
A call to action for developers to enable compiler warnings for concurrency to find and fix issues in their code before they become errors in Swift 6.
Transcripts
I'd like you all to think about some
I'd like you all to think about some presentation you saw that left you with
presentation you saw that left you with
presentation you saw that left you with an impact
an impact
an impact stirred up some strong emotions
stirred up some strong emotions
stirred up some strong emotions and this is a presentation about Swift
and this is a presentation about Swift
and this is a presentation about Swift concurrency but I'm bringing up feelings
concurrency but I'm bringing up feelings
concurrency but I'm bringing up feelings because really it's my experiences using
because really it's my experiences using
because really it's my experiences using Swift concurrency
Swift concurrency
Swift concurrency and more specifically my experiences
and more specifically my experiences
and more specifically my experiences using Swift concurrency incorrectly and
using Swift concurrency incorrectly and
using Swift concurrency incorrectly and I just I cannot stress enough I have
I just I cannot stress enough I have
I just I cannot stress enough I have used this thing just horribly wrong
used this thing just horribly wrong
used this thing just horribly wrong now we're going to look at code we're
now we're going to look at code we're
now we're going to look at code we're going to look at a whole bunch of code
going to look at a whole bunch of code
going to look at a whole bunch of code actually
actually
actually but when I was getting started I wanted
but when I was getting started I wanted
but when I was getting started I wanted to think about what feeling I wanted you
to think about what feeling I wanted you
to think about what feeling I wanted you to leave with and I think I want to
to leave with and I think I want to
to leave with and I think I want to leave you with just a little bit of fear
leave you with just a little bit of fear
leave you with just a little bit of fear maybe we'll call it a healthy respect
maybe we'll call it a healthy respect
maybe we'll call it a healthy respect have you uh have you seen this before
have you uh have you seen this before
have you uh have you seen this before simple versus easy
simple versus easy
simple versus easy comes up in programming I think it's
comes up in programming I think it's
comes up in programming I think it's fascinating
fascinating
fascinating the idea is there are these things that
the idea is there are these things that
the idea is there are these things that are amazingly simple once you understand
are amazingly simple once you understand
are amazingly simple once you understand the concepts behind them but getting
the concepts behind them but getting
the concepts behind them but getting that understanding can require a lot of
that understanding can require a lot of
that understanding can require a lot of work
work
work and this compares to something that
and this compares to something that
and this compares to something that maybe it's a little bit easy to use easy
maybe it's a little bit easy to use easy
maybe it's a little bit easy to use easy to learn but actually comes down to
to learn but actually comes down to
to learn but actually comes down to being very deeply complicated
being very deeply complicated
being very deeply complicated uh take a look at this for Loop here
uh take a look at this for Loop here
uh take a look at this for Loop here what is going on like we're going over
what is going on like we're going over
what is going on like we're going over some array and then we're checking the
some array and then we're checking the
some array and then we're checking the condition function on items in there and
condition function on items in there and
condition function on items in there and if they pass we're putting into another
if they pass we're putting into another
if they pass we're putting into another array this is just a filter
array this is just a filter
array this is just a filter but if you think this is easy you try
but if you think this is easy you try
but if you think this is easy you try explaining this to a beginner
explaining this to a beginner
explaining this to a beginner now I bring this up because I believe
now I bring this up because I believe
now I bring this up because I believe that Swift concurrency is neither simple
that Swift concurrency is neither simple
that Swift concurrency is neither simple nor easy and that is not a criticism
nor easy and that is not a criticism
nor easy and that is not a criticism the problem is that fundamentally Swift
the problem is that fundamentally Swift
the problem is that fundamentally Swift concurrency is a concurrency system and
concurrency is a concurrency system and
concurrency is a concurrency system and this is just a deeply hard thing to
this is just a deeply hard thing to
this is just a deeply hard thing to solve
solve
solve um I think it's amazing what Swift
um I think it's amazing what Swift
um I think it's amazing what Swift concurrency can do especially
concurrency can do especially
concurrency can do especially considering it's really not all the way
considering it's really not all the way
considering it's really not all the way done and is still under active
done and is still under active
done and is still under active development
development
development it really can make things easier
it really can make things easier
it really can make things easier but while I was learning about it and
but while I was learning about it and
but while I was learning about it and introducing it into my own code I ran
introducing it into my own code I ran
introducing it into my own code I ran into a lot of problems I mean I cut
into a lot of problems I mean I cut
into a lot of problems I mean I cut myself on so many sharp edges and so
myself on so many sharp edges and so
myself on so many sharp edges and so what I want to do with this presentation
what I want to do with this presentation
what I want to do with this presentation is introduce them to you explain them
is introduce them to you explain them
is introduce them to you explain them and hopefully prevent you from having
and hopefully prevent you from having
and hopefully prevent you from having the same problem
the same problem
the same problem so what I uh what I did was I wrote down
so what I uh what I did was I wrote down
so what I uh what I did was I wrote down all the problems that I had and I kind
all the problems that I had and I kind
all the problems that I had and I kind of sorted them from the least scary to
of sorted them from the least scary to
of sorted them from the least scary to the most scary I needed some kind of a
the most scary I needed some kind of a
the most scary I needed some kind of a unit to describe those things so we're
unit to describe those things so we're
unit to describe those things so we're going with scared beakers here this is
going with scared beakers here this is
going with scared beakers here this is the international unit of fear
the international unit of fear
the international unit of fear uh we're starting with one
uh we're starting with one
uh we're starting with one uh so take a look at this this is a
uh so take a look at this this is a
uh so take a look at this this is a class worker and it's going to run some
class worker and it's going to run some
class worker and it's going to run some sort of uh some sort of asynchronous
sort of uh some sort of asynchronous
sort of uh some sort of asynchronous long-running operation
long-running operation
long-running operation and then we need to kick that off
and then we need to kick that off
and then we need to kick that off synchronously so in the start function
synchronously so in the start function
synchronously so in the start function we just like start a task
we just like start a task
we just like start a task and this is one of the first kinds of
and this is one of the first kinds of
and this is one of the first kinds of things I did I was immediately confused
things I did I was immediately confused
things I did I was immediately confused why isn't the compiler telling me that I
why isn't the compiler telling me that I
why isn't the compiler telling me that I have to catch yourself
have to catch yourself
have to catch yourself this is intentional
this is intentional
this is intentional uh what's going on is the Swift team was
uh what's going on is the Swift team was
uh what's going on is the Swift team was thinking well the only times we really
thinking well the only times we really
thinking well the only times we really want to ensure that you capture self is
want to ensure that you capture self is
want to ensure that you capture self is when it's likely that you're going to
when it's likely that you're going to
when it's likely that you're going to introduce a retained cycle and it was
introduce a retained cycle and it was
introduce a retained cycle and it was decided that that is not likely in this
decided that that is not likely in this
decided that that is not likely in this case
um that has sort of had I think mixed
um that has sort of had I think mixed results in my experience it's true that
results in my experience it's true that
results in my experience it's true that usually tasks start and they run all the
usually tasks start and they run all the
usually tasks start and they run all the way to the beginning and then they
way to the beginning and then they
way to the beginning and then they finish even if maybe they take a little
finish even if maybe they take a little
finish even if maybe they take a little while along the way
while along the way
while along the way but if you do run into a problem you're
but if you do run into a problem you're
but if you do run into a problem you're thinking well no big deal I'll just do
thinking well no big deal I'll just do
thinking well no big deal I'll just do what I do for all closures and I'll add
what I do for all closures and I'll add
what I do for all closures and I'll add a weak self in there and this does work
a weak self in there and this does work
a weak self in there and this does work with the slight Quirk that everything on
with the slight Quirk that everything on
with the slight Quirk that everything on the right side of an await is strongly
the right side of an await is strongly
the right side of an await is strongly retained while it's running including
retained while it's running including
retained while it's running including while it's just suspended
while it's just suspended
while it's just suspended um so let's might not introduce a leak
um so let's might not introduce a leak
um so let's might not introduce a leak it certainly could extend life cycles
it certainly could extend life cycles
it certainly could extend life cycles Beyond where you might expect them to be
Beyond where you might expect them to be
Beyond where you might expect them to be there's one other case where I think
there's one other case where I think
there's one other case where I think leaks can be real and happen is with
leaks can be real and happen is with
leaks can be real and happen is with async sequence and in that case what
async sequence and in that case what
async sequence and in that case what you'd have is a task it's maybe starting
you'd have is a task it's maybe starting
you'd have is a task it's maybe starting up some four or a weight
up some four or a weight
up some four or a weight the problem is that many async sequences
the problem is that many async sequences
the problem is that many async sequences are effectively infinitely long and so
are effectively infinitely long and so
are effectively infinitely long and so while it's true in principle this thing
while it's true in principle this thing
while it's true in principle this thing will finish in practice it never
will finish in practice it never
will finish in practice it never actually happens and you end up holding
actually happens and you end up holding
actually happens and you end up holding on to self forever
here's another example we've got some
here's another example we've got some main actor function that's refreshing
main actor function that's refreshing
main actor function that's refreshing our UI in some way and it's got to do
our UI in some way and it's got to do
our UI in some way and it's got to do that it has to run some expensive and
that it has to run some expensive and
that it has to run some expensive and synchronous function and so you think
synchronous function and so you think
synchronous function and so you think well before I used to like do some async
well before I used to like do some async
well before I used to like do some async on a global queue I'll just throw a task
on a global queue I'll just throw a task
on a global queue I'll just throw a task in there but this does not work this
in there but this does not work this
in there but this does not work this actually still runs on the main actor I
actually still runs on the main actor I
actually still runs on the main actor I think this surprises a lot of people and
think this surprises a lot of people and
think this surprises a lot of people and the reason why is because that task
the reason why is because that task
the reason why is because that task unstructured tasks inherit the actor
unstructured tasks inherit the actor
unstructured tasks inherit the actor context that they're starting in so
context that they're starting in so
context that they're starting in so because we started on a main actor this
because we started on a main actor this
because we started on a main actor this task will run on a main actor as well
task will run on a main actor as well
task will run on a main actor as well and this can be convenient in a number
and this can be convenient in a number
and this can be convenient in a number of cases but in this case it's the exact
of cases but in this case it's the exact
of cases but in this case it's the exact opposite of what we want
opposite of what we want
opposite of what we want the solution is just to throw a detached
the solution is just to throw a detached
the solution is just to throw a detached in here and this is another API that
in here and this is another API that
in here and this is another API that works exactly the same except it does
works exactly the same except it does
works exactly the same except it does not do this inheritance thing
all right two scared beakers now uh
all right two scared beakers now uh let's take a look at an actor this is a
let's take a look at an actor this is a
let's take a look at an actor this is a really simple actor in fact it does
really simple actor in fact it does
really simple actor in fact it does nothing at all it just has one property
nothing at all it just has one property
nothing at all it just has one property and that property is immutable
and that property is immutable
and that property is immutable and in some other actor over here we're
and in some other actor over here we're
and in some other actor over here we're referencing that value and we're doing
referencing that value and we're doing
referencing that value and we're doing it synchronously typically you would
it synchronously typically you would
it synchronously typically you would have to do this always
have to do this always
have to do this always asynchronously when you're Crossing
asynchronously when you're Crossing
asynchronously when you're Crossing actors like this but in this case it's a
actors like this but in this case it's a
actors like this but in this case it's a it's synchronous and the compiler's
it's synchronous and the compiler's
it's synchronous and the compiler's happy
happy
happy as long as you do it within the same
as long as you do it within the same
as long as you do it within the same module but this is not allowed across
module but this is not allowed across
module but this is not allowed across modules
modules
modules what's going on here
what's going on here
what's going on here well it's interesting so Swift is
well it's interesting so Swift is
well it's interesting so Swift is preserving your ability to change that
preserving your ability to change that
preserving your ability to change that let in the future into a VAR
let in the future into a VAR
let in the future into a VAR and I think that's really cool because
and I think that's really cool because
and I think that's really cool because you can do it without making any Source
you can do it without making any Source
you can do it without making any Source uh it doesn't introduce source
uh it doesn't introduce source
uh it doesn't introduce source incompatibility
incompatibility
incompatibility however maybe you spent a lot of time
however maybe you spent a lot of time
however maybe you spent a lot of time working on module a and you even realize
working on module a and you even realize
working on module a and you even realize this until you got into working on
this until you got into working on
this until you got into working on module B finally so this is definitely
module B finally so this is definitely
module B finally so this is definitely something worth keeping keeping in mind
how about Swift UI just talking about
how about Swift UI just talking about Swift UI uh here we got some view it's
Swift UI uh here we got some view it's
Swift UI uh here we got some view it's got a state object and in the body it is
got a state object and in the body it is
got a state object and in the body it is running some function on a peer that's
running some function on a peer that's
running some function on a peer that's calling some other function main actor
calling some other function main actor
calling some other function main actor isolated no problems until for some
isolated no problems until for some
isolated no problems until for some reason you decide to change that state
reason you decide to change that state
reason you decide to change that state object into a state and now the compiler
object into a state and now the compiler
object into a state and now the compiler is complaining about main actor
is complaining about main actor
is complaining about main actor isolation
isolation
isolation what could possibly be going on here
what could possibly be going on here
what could possibly be going on here well it turns out that property wrappers
well it turns out that property wrappers
well it turns out that property wrappers can implicitly change the global actor
can implicitly change the global actor
can implicitly change the global actor isolation of their enclosing type this
isolation of their enclosing type this
isolation of their enclosing type this is super weird and has surprised so many
is super weird and has surprised so many
is super weird and has surprised so many people in fact it has been such a
people in fact it has been such a
people in fact it has been such a problem that I lifted this code from the
problem that I lifted this code from the
problem that I lifted this code from the Swift Evolution proposal to change this
Swift Evolution proposal to change this
Swift Evolution proposal to change this and that's coming although right now it
and that's coming although right now it
and that's coming although right now it still works this way
still works this way
still works this way the solution of course is to get rid of
the solution of course is to get rid of
the solution of course is to get rid of this implicit main actor isolation and
this implicit main actor isolation and
this implicit main actor isolation and instead apply an annotation either to
instead apply an annotation either to
instead apply an annotation either to the view appeared method or to the whole
the view appeared method or to the whole
the view appeared method or to the whole type
okay
okay three scared beakers now we're getting
three scared beakers now we're getting
three scared beakers now we're getting into some more interesting stuff
into some more interesting stuff
into some more interesting stuff um
um
um caching I mean like how hard could this
caching I mean like how hard could this
caching I mean like how hard could this be right
be right
be right well let's look at a counter and it's a
well let's look at a counter and it's a
well let's look at a counter and it's a little bit of a strange counter in that
little bit of a strange counter in that
little bit of a strange counter in that for some reason this counter's initial
for some reason this counter's initial
for some reason this counter's initial starting value is expensive to compute
starting value is expensive to compute
starting value is expensive to compute for some reason
for some reason
for some reason so what we decide to do is when we
so what we decide to do is when we
so what we decide to do is when we increment we're going to increment a
increment we're going to increment a
increment we're going to increment a Delta and then when we need that
Delta and then when we need that
Delta and then when we need that absolute count we will just go and
absolute count we will just go and
absolute count we will just go and compute it asynchronously and then we're
compute it asynchronously and then we're
compute it asynchronously and then we're going to add these values together
going to add these values together
going to add these values together and we think well why should we do this
and we think well why should we do this
and we think well why should we do this every time why don't we cache that
every time why don't we cache that
every time why don't we cache that initial value in some private variable
initial value in some private variable
initial value in some private variable so that we can only have to do it once
so that we can only have to do it once
so that we can only have to do it once and you know we know we're going to use
and you know we know we're going to use
and you know we know we're going to use this thing concurrently and this is an
this thing concurrently and this is an
this thing concurrently and this is an actor and actors are magic solutions to
actor and actors are magic solutions to
actor and actors are magic solutions to all concurrency problems we have nothing
all concurrency problems we have nothing
all concurrency problems we have nothing to worry about
to worry about
to worry about uh but this actually a classic example
uh but this actually a classic example
uh but this actually a classic example of the actor re-entrancy problem
of the actor re-entrancy problem
of the actor re-entrancy problem and what's going on here is get into get
and what's going on here is get into get
and what's going on here is get into get count some thread goes into get count
count some thread goes into get count
count some thread goes into get count initial is nil so it goes and kicks off
initial is nil so it goes and kicks off
initial is nil so it goes and kicks off this await suspends and that makes it
this await suspends and that makes it
this await suspends and that makes it free for other threads to come into this
free for other threads to come into this
free for other threads to come into this actor and do the exact same thing
actor and do the exact same thing
actor and do the exact same thing uh now in this case it's not really a
uh now in this case it's not really a
uh now in this case it's not really a huge deal I guess you're just kind of
huge deal I guess you're just kind of
huge deal I guess you're just kind of like doing some wasted work but you can
like doing some wasted work but you can
like doing some wasted work but you can imagine situations where this would be a
imagine situations where this would be a
imagine situations where this would be a big problem
big problem
big problem if you search online you will find
if you search online you will find
if you search online you will find solutions to this and here's kind of
solutions to this and here's kind of
solutions to this and here's kind of what they look like it's a change where
what they look like it's a change where
what they look like it's a change where instead of caching caching the initial
instead of caching caching the initial
instead of caching caching the initial value you cache a task that returns that
value you cache a task that returns that
value you cache a task that returns that initial value
initial value
initial value and if you look at get count what's
and if you look at get count what's
and if you look at get count what's happening now is that we are reading
happening now is that we are reading
happening now is that we are reading initial tasks or creating one and
initial tasks or creating one and
initial tasks or creating one and writing to it with no await in between
writing to it with no await in between
writing to it with no await in between there's no opportunity for suspension
there's no opportunity for suspension
there's no opportunity for suspension and so we are able to do this safely
and so we are able to do this safely
and so we are able to do this safely within an actor
within an actor
within an actor um this works this is correct but it
um this works this is correct but it
um this works this is correct but it does come with some disadvantages you
does come with some disadvantages you
does come with some disadvantages you see we're using tasks unstructured
see we're using tasks unstructured
see we're using tasks unstructured concurrency and this means that we are
concurrency and this means that we are
concurrency and this means that we are not going to participate in automatic
not going to participate in automatic
not going to participate in automatic cancellation or priority propagation
cancellation or priority propagation
cancellation or priority propagation that might not be a huge deal but if you
that might not be a huge deal but if you
that might not be a huge deal but if you wanted to do this we can we have to take
wanted to do this we can we have to take
wanted to do this we can we have to take a look though at what it even means to
a look though at what it even means to
a look though at what it even means to be an asynchronous cache so let's model
be an asynchronous cache so let's model
be an asynchronous cache so let's model that
let's model that with a um with some
let's model that with a um with some State and it can be in three possible
State and it can be in three possible
State and it can be in three possible three possible States so we have our
three possible States so we have our
three possible States so we have our caches ready that means it has a value
caches ready that means it has a value
caches ready that means it has a value and it can just return it
and it can just return it
and it can just return it the other possibility is we're in
the other possibility is we're in
the other possibility is we're in progress and that means some other
progress and that means some other
progress and that means some other thread has started filling the cache but
thread has started filling the cache but
thread has started filling the cache but hasn't finished yet and we now have to
hasn't finished yet and we now have to
hasn't finished yet and we now have to wait for that process to finish this is
wait for that process to finish this is
wait for that process to finish this is in progress
in progress
in progress and then the other state is nobody's
and then the other state is nobody's
and then the other state is nobody's filled the cash and there's nobody
filled the cash and there's nobody
filled the cash and there's nobody waiting so we're the first and we're
waiting so we're the first and we're
waiting so we're the first and we're going to talk about that one right now
going to talk about that one right now
going to talk about that one right now so in this case the first thing we got
so in this case the first thing we got
so in this case the first thing we got to do is transition this state so that
to do is transition this state so that
to do is transition this state so that all future requesters will get funneled
all future requesters will get funneled
all future requesters will get funneled to this in progress condition
to this in progress condition
to this in progress condition then we're safe now to do this await we
then we're safe now to do this await we
then we're safe now to do this await we don't have to worry about being
don't have to worry about being
don't have to worry about being interrupted and we can return that value
interrupted and we can return that value
interrupted and we can return that value but we still need to take care of these
but we still need to take care of these
but we still need to take care of these other potential tasks that are waiting
other potential tasks that are waiting
other potential tasks that are waiting and we're going to do that using
and we're going to do that using
and we're going to do that using continuations somehow
continuations somehow
continuations somehow let's look at what we do in progress
let's look at what we do in progress
let's look at what we do in progress so the first thing we need is an ID that
so the first thing we need is an ID that
so the first thing we need is an ID that uniquely represents this particular
uniquely represents this particular
uniquely represents this particular request
request
request um this is kind of contextual but it can
um this is kind of contextual but it can
um this is kind of contextual but it can be easier complicated depending
be easier complicated depending
be easier complicated depending we now need a continuation and we're
we now need a continuation and we're
we now need a continuation and we're going to get this from the built-in with
going to get this from the built-in with
going to get this from the built-in with checked continuation function that gives
checked continuation function that gives
checked continuation function that gives us a continuation which is safe to
us a continuation which is safe to
us a continuation which is safe to escape from this closure and we're going
escape from this closure and we're going
escape from this closure and we're going to associate it with this ID and you're
to associate it with this ID and you're
to associate it with this ID and you're thinking but wait a minute I see this
thinking but wait a minute I see this
thinking but wait a minute I see this await keyword so doesn't that mean we
await keyword so doesn't that mean we
await keyword so doesn't that mean we have to worry about actor reentrancy and
have to worry about actor reentrancy and
have to worry about actor reentrancy and uh this function is weird because it
uh this function is weird because it
uh this function is weird because it guarantees that its body will be
guarantees that its body will be
guarantees that its body will be executed synchronously before it ever
executed synchronously before it ever
executed synchronously before it ever gets suspended and that is very strange
gets suspended and that is very strange
gets suspended and that is very strange that is not the way of the await keyword
that is not the way of the await keyword
that is not the way of the await keyword is supposed to work and yet this is how
is supposed to work and yet this is how
is supposed to work and yet this is how this function works
this function works
this function works um but with this we're able to take care
um but with this we're able to take care
um but with this we're able to take care of resuming waiting continuations but we
of resuming waiting continuations but we
of resuming waiting continuations but we still have to deal with cancellation now
still have to deal with cancellation now
still have to deal with cancellation now we need to rely on yet another built-in
we need to rely on yet another built-in
we need to rely on yet another built-in function called with task cancellation
function called with task cancellation
function called with task cancellation Handler also weird also runs its body
Handler also weird also runs its body
Handler also weird also runs its body synchronously
synchronously
synchronously but that allows us to associate the
but that allows us to associate the
but that allows us to associate the continuation with that particular ID and
continuation with that particular ID and
continuation with that particular ID and execute that continuation
execute that continuation
execute that continuation and I have omitted all kinds of code
and I have omitted all kinds of code
and I have omitted all kinds of code here but I think we can agree this is
here but I think we can agree this is
here but I think we can agree this is very complicated
very complicated
very complicated and when I realized this I first started
and when I realized this I first started
and when I realized this I first started thinking I mean there's got to be some
thinking I mean there's got to be some
thinking I mean there's got to be some kind of alternative
kind of alternative
kind of alternative to understand that let's look at our
to understand that let's look at our
to understand that let's look at our original incorrect implementation again
original incorrect implementation again
original incorrect implementation again remember the problem is in get count
remember the problem is in get count
remember the problem is in get count we're reading initial we're then waiting
we're reading initial we're then waiting
we're reading initial we're then waiting and then writing to initial so we have
and then writing to initial so we have
and then writing to initial so we have this suspension point in between a
this suspension point in between a
this suspension point in between a critical section
critical section
critical section and if we're not using Swift concurrency
and if we're not using Swift concurrency
and if we're not using Swift concurrency critical section we're going to start
critical section we're going to start
critical section we're going to start thinking lock of some kind but
thinking lock of some kind but
thinking lock of some kind but traditional locks are not safe to use
traditional locks are not safe to use
traditional locks are not safe to use with swift concurrency
with swift concurrency
with swift concurrency mercifully somebody made one that is
mercifully somebody made one that is
mercifully somebody made one that is it's called async semaphore and you can
it's called async semaphore and you can
it's called async semaphore and you can throw it right in there keep code that
throw it right in there keep code that
throw it right in there keep code that looks much more familiar to what you'd
looks much more familiar to what you'd
looks much more familiar to what you'd used to but it even supports uh
used to but it even supports uh
used to but it even supports uh cancellation
cancellation
cancellation this is a wonderful package and I think
this is a wonderful package and I think
this is a wonderful package and I think it's something you should at least be
it's something you should at least be
it's something you should at least be aware of while you're working with swift
aware of while you're working with swift
aware of while you're working with swift concurrency
okay we're getting to the real stuff now
okay we're getting to the real stuff now uh
here's some code that probably can be
here's some code that probably can be found in every application that has ever
found in every application that has ever
found in every application that has ever been written
been written
been written um unfortunately with concurrency
um unfortunately with concurrency
um unfortunately with concurrency checking on this produces warnings and
checking on this produces warnings and
checking on this produces warnings and remember that in Swift 6 These Warnings
remember that in Swift 6 These Warnings
remember that in Swift 6 These Warnings are going to become errors
are going to become errors
are going to become errors and I'm pretty sure this is an example
and I'm pretty sure this is an example
and I'm pretty sure this is an example where those errors are rapidly going to
where those errors are rapidly going to
where those errors are rapidly going to become tiers now let me explain why
become tiers now let me explain why
become tiers now let me explain why there's two warnings actually here so
there's two warnings actually here so
there's two warnings actually here so the first one is talking about capturing
the first one is talking about capturing
the first one is talking about capturing self which is unscendable and self is
self which is unscendable and self is
self which is unscendable and self is just it doesn't matter some sort of UI
just it doesn't matter some sort of UI
just it doesn't matter some sort of UI related object typically not a sendable
related object typically not a sendable
related object typically not a sendable thing and definitely not sendable here
thing and definitely not sendable here
thing and definitely not sendable here but it's necessary because Apple has
but it's necessary because Apple has
but it's necessary because Apple has updated dispatch cues async method to
updated dispatch cues async method to
updated dispatch cues async method to only accept sendable ascendable closure
only accept sendable ascendable closure
only accept sendable ascendable closure um this is a real problem for us and
um this is a real problem for us and
um this is a real problem for us and it's actually a problem in many other
it's actually a problem in many other
it's actually a problem in many other cases besides just updating the UI
cases besides just updating the UI
cases besides just updating the UI because dispatch queues can often be
because dispatch queues can often be
because dispatch queues can often be used to protect mutable state that is
used to protect mutable state that is
used to protect mutable state that is inherently unscendable
inherently unscendable
inherently unscendable Apple has recognized this as a problem
Apple has recognized this as a problem
Apple has recognized this as a problem and so they've introduced new API called
and so they've introduced new API called
and so they've introduced new API called async unsafe which is exactly the same
async unsafe which is exactly the same
async unsafe which is exactly the same but removes the sendable requirement
but removes the sendable requirement
but removes the sendable requirement that takes care of one warning but
that takes care of one warning but
that takes care of one warning but there's another
there's another
there's another uh
uh
uh it's telling us that up this update UI
it's telling us that up this update UI
it's telling us that up this update UI main actor function is not being run on
main actor function is not being run on
main actor function is not being run on the main actor but how can that be main
the main actor but how can that be main
the main actor but how can that be main is like right there
is like right there
is like right there the thing to understand is that async
the thing to understand is that async
the thing to understand is that async unsafe and async these are defined on
unsafe and async these are defined on
unsafe and async these are defined on all operation queues not just the main
all operation queues not just the main
all operation queues not just the main instance and apple was not able to apply
instance and apple was not able to apply
instance and apple was not able to apply main actor isolation because of this
main actor isolation because of this
main actor isolation because of this fortunately they've introduced yet more
fortunately they've introduced yet more
fortunately they've introduced yet more new API that we can use
and what this will do is say okay the
and what this will do is say okay the compiler doesn't see This Global actor
compiler doesn't see This Global actor
compiler doesn't see This Global actor isolation but we promise that it is
isolation but we promise that it is
isolation but we promise that it is there and should that not happen at
there and should that not happen at
there and should that not happen at runtime this actually will crash
runtime this actually will crash
runtime this actually will crash so with these two changes we've now
so with these two changes we've now
so with these two changes we've now eliminated all the warnings but we've
eliminated all the warnings but we've
eliminated all the warnings but we've got another problem here both of these
got another problem here both of these
got another problem here both of these apis are brand new only available in iOS
apis are brand new only available in iOS
apis are brand new only available in iOS 17 and Mac OS 14.
17 and Mac OS 14.
17 and Mac OS 14. uh I just wasn't able to deal with that
uh I just wasn't able to deal with that
uh I just wasn't able to deal with that so I made the package that includes
so I made the package that includes
so I made the package that includes backwards compatible versions of these
backwards compatible versions of these
backwards compatible versions of these two apis you can check out here on
two apis you can check out here on
two apis you can check out here on GitHub and I also added a whole bunch of
GitHub and I also added a whole bunch of
GitHub and I also added a whole bunch of other a uh unsafe versions in places
other a uh unsafe versions in places
other a uh unsafe versions in places where I needed them but Apple has not at
where I needed them but Apple has not at
where I needed them but Apple has not at least yet added those apis
okay there's one more level four problem
okay there's one more level four problem that I've run into and it's ordering uh
that I've run into and it's ordering uh
that I've run into and it's ordering uh and this is pretty fundamental to
and this is pretty fundamental to
and this is pretty fundamental to programming so how hard could this one
programming so how hard could this one
programming so how hard could this one be
be
be well to understand the ordering issues
well to understand the ordering issues
well to understand the ordering issues you really have to look at what it means
you really have to look at what it means
you really have to look at what it means to be an async function and so here's
to be an async function and so here's
to be an async function and so here's one that is not some work function that
one that is not some work function that
one that is not some work function that has a completion handle of some kind
has a completion handle of some kind
has a completion handle of some kind and when you make it async you get rid
and when you make it async you get rid
and when you make it async you get rid of all that and you have just this nice
of all that and you have just this nice
of all that and you have just this nice simple looking thing but you've really
simple looking thing but you've really
simple looking thing but you've really you fundamentally changed how this
you fundamentally changed how this
you fundamentally changed how this function works
function works
function works and to understand that let's look at the
and to understand that let's look at the
and to understand that let's look at the call sites so previously you'd call work
call sites so previously you'd call work
call sites so previously you'd call work and it would take that completion
and it would take that completion
and it would take that completion Handler but you could call it
Handler but you could call it
Handler but you could call it synchronously it gave work an
synchronously it gave work an
synchronously it gave work an opportunity to run code
opportunity to run code
opportunity to run code now and then also run code when it's
now and then also run code when it's
now and then also run code when it's finished but once it's async that await
finished but once it's async that await
finished but once it's async that await keyword means you must have an async
keyword means you must have an async
keyword means you must have an async context and that removes your ability to
context and that removes your ability to
context and that removes your ability to run code synchronously right now
run code synchronously right now
run code synchronously right now this transition from
this transition from
this transition from sync to async is always necessary and
sync to async is always necessary and
sync to async is always necessary and depending on what your functions are
depending on what your functions are
depending on what your functions are doing that can end up being very
doing that can end up being very
doing that can end up being very perilous
perilous
perilous so let's look at why some sort of
so let's look at why some sort of
so let's look at why some sort of timeline here and it's a synchronous
timeline here and it's a synchronous
timeline here and it's a synchronous timeline of events so before we recall
timeline of events so before we recall
timeline of events so before we recall in work synchronously and then doing
in work synchronously and then doing
in work synchronously and then doing some other stuff in the middle here and
some other stuff in the middle here and
some other stuff in the middle here and then we were going to call work again
then we were going to call work again
then we were going to call work again now we've made it async which means we
now we've made it async which means we
now we've made it async which means we can no longer call it this but we have
can no longer call it this but we have
can no longer call it this but we have to use a weight and to preserve the same
to use a weight and to preserve the same
to use a weight and to preserve the same ordering in exactly the same way we
ordering in exactly the same way we
ordering in exactly the same way we really have to do this inside of one
really have to do this inside of one
really have to do this inside of one single asynchronous context
single asynchronous context
single asynchronous context but there's all kinds of stuff in the
but there's all kinds of stuff in the
but there's all kinds of stuff in the middle I mean we could be calling all
middle I mean we could be calling all
middle I mean we could be calling all kinds of nested functions in different
kinds of nested functions in different
kinds of nested functions in different subsystems across modules we could even
subsystems across modules we could even
subsystems across modules we could even be bouncing back and forth between apple
be bouncing back and forth between apple
be bouncing back and forth between apple Frameworks and our own this can all
Frameworks and our own this can all
Frameworks and our own this can all happen synchronously but it's just
happen synchronously but it's just
happen synchronously but it's just totally impractical to have a single
totally impractical to have a single
totally impractical to have a single context so what do you do you make two
context so what do you do you make two
context so what do you do you make two and now you have a race because the
and now you have a race because the
and now you have a race because the system is completely free to start and
system is completely free to start and
system is completely free to start and even finish task number two before task
even finish task number two before task
even finish task number two before task one has run
one has run
one has run the uh the solution here is to make this
the uh the solution here is to make this
the uh the solution here is to make this dependency explicit so you capture a
dependency explicit so you capture a
dependency explicit so you capture a reference in the first task and then you
reference in the first task and then you
reference in the first task and then you await it in the second and while this
await it in the second and while this
await it in the second and while this does solve the problem it kind of
does solve the problem it kind of
does solve the problem it kind of suffers from the same drawbacks where
suffers from the same drawbacks where
suffers from the same drawbacks where passing this task reference around can
passing this task reference around can
passing this task reference around can be just as impractical and it might be
be just as impractical and it might be
be just as impractical and it might be possible but it might be at best kind of
possible but it might be at best kind of
possible but it might be at best kind of inconvenient and at worst just not
inconvenient and at worst just not
inconvenient and at worst just not something that you can really do
now this is the thing you really have to
now this is the thing you really have to keep in mind is that when you're
keep in mind is that when you're
keep in mind is that when you're starting tasks and working with any kind
starting tasks and working with any kind
starting tasks and working with any kind of state or stateful system you always
of state or stateful system you always
of state or stateful system you always have to be thinking about races and when
have to be thinking about races and when
have to be thinking about races and when you're changing things are you changing
you're changing things are you changing
you're changing things are you changing it from a situation where it needed to
it from a situation where it needed to
it from a situation where it needed to be run synchronously and that was
be run synchronously and that was
be run synchronously and that was dependent and now no longer necessary
dependent and now no longer necessary
dependent and now no longer necessary to fix this your dependencies always
to fix this your dependencies always
to fix this your dependencies always have to be explicit it is always up to
have to be explicit it is always up to
have to be explicit it is always up to you and it's much harder in this async
you and it's much harder in this async
you and it's much harder in this async world to hide that kind of dependency
world to hide that kind of dependency
world to hide that kind of dependency from callers
from callers
from callers I have found for myself
I have found for myself
I have found for myself what I really wanted was like this this
what I really wanted was like this this
what I really wanted was like this this await keyword I really wanted to just
await keyword I really wanted to just
await keyword I really wanted to just shove this thing into a queue so that I
shove this thing into a queue so that I
shove this thing into a queue so that I could control the ordering
could control the ordering
could control the ordering unfortunately neither dispatch queue nor
unfortunately neither dispatch queue nor
unfortunately neither dispatch queue nor operation queue support this
operation queue support this
operation queue support this and so I I made one that does it's on
and so I I made one that does it's on
and so I I made one that does it's on GitHub
GitHub
GitHub and I have found these asynchronous cues
and I have found these asynchronous cues
and I have found these asynchronous cues to be at
to be at
to be at we're kind of ranging from incredibly
we're kind of ranging from incredibly
we're kind of ranging from incredibly convenience to absolutely essential and
convenience to absolutely essential and
convenience to absolutely essential and I think this is another construct that
I think this is another construct that
I think this is another construct that you should at least be aware of
all right here we are maximum fear level
all right here we are maximum fear level five scared beakers
five scared beakers
five scared beakers this is an xcode build setting
this is an xcode build setting
this is an xcode build setting and what it does is control how strict
and what it does is control how strict
and what it does is control how strict the Swift compiler is about checking
the Swift compiler is about checking
the Swift compiler is about checking your use of concurrency
your use of concurrency
your use of concurrency it has three levels and the default is
it has three levels and the default is
it has three levels and the default is the lowest and I kind of just left it
the lowest and I kind of just left it
the lowest and I kind of just left it that way because I figured somebody at
that way because I figured somebody at
that way because I figured somebody at Apple must have thought this through
Apple must have thought this through
Apple must have thought this through um
and then there was xcode 14.3 beta 1 and
and then there was xcode 14.3 beta 1 and that introduced this weird bug that
that introduced this weird bug that
that introduced this weird bug that bumped this thing up from the default
bumped this thing up from the default
bumped this thing up from the default minimum to just one level up and that
minimum to just one level up and that
minimum to just one level up and that introduced warnings in a whole bunch of
introduced warnings in a whole bunch of
introduced warnings in a whole bunch of people's projects including my own
people's projects including my own
people's projects including my own and that got me interested in what was
and that got me interested in what was
and that got me interested in what was going on here so then I kind of I bumped
going on here so then I kind of I bumped
going on here so then I kind of I bumped it up to the maximum I want to get the
it up to the maximum I want to get the
it up to the maximum I want to get the full picture and I ended up with 1600
full picture and I ended up with 1600
full picture and I ended up with 1600 warnings
warnings
warnings now I have to be fair some of these
now I have to be fair some of these
now I have to be fair some of these things were really easy to fix now you
things were really easy to fix now you
things were really easy to fix now you put a main actor over here or you just
put a main actor over here or you just
put a main actor over here or you just throw like ascendable over there these
throw like ascendable over there these
throw like ascendable over there these are no big deal and there were some
are no big deal and there were some
are no big deal and there were some others that I resolved using some of the
others that I resolved using some of the
others that I resolved using some of the things we just spoke about
things we just spoke about
things we just spoke about but there was this class of problem that
but there was this class of problem that
but there was this class of problem that was much more difficult to solve and I
was much more difficult to solve and I
was much more difficult to solve and I want to talk about that one and I'm
want to talk about that one and I'm
want to talk about that one and I'm going to use a protocol as an example so
going to use a protocol as an example so
going to use a protocol as an example so here it is very simple it's got one
here it is very simple it's got one
here it is very simple it's got one value get her and a Setter and
value get her and a Setter and
value get her and a Setter and critically there's no Global actor
critically there's no Global actor
critically there's no Global actor isolation here
isolation here
isolation here now you're using this protocol on some
now you're using this protocol on some
now you're using this protocol on some class and you discover that in order to
class and you discover that in order to
class and you discover that in order to fix other warnings you're going to have
fix other warnings you're going to have
fix other warnings you're going to have to mark this thing main actor but when
to mark this thing main actor but when
to mark this thing main actor but when you do that now the compiler is
you do that now the compiler is
you do that now the compiler is complaining about your conformance and
complaining about your conformance and
complaining about your conformance and what's happening is you've applied
what's happening is you've applied
what's happening is you've applied Global actor isolation to the type but
Global actor isolation to the type but
Global actor isolation to the type but that protocol did not specify actor
that protocol did not specify actor
that protocol did not specify actor isolation and this is a conflict that is
isolation and this is a conflict that is
isolation and this is a conflict that is not allowed
not allowed
not allowed what are your options here well you
what are your options here well you
what are your options here well you could potentially add main actor to that
could potentially add main actor to that
could potentially add main actor to that protocol I mean if you look at if you
protocol I mean if you look at if you
protocol I mean if you look at if you look at UI kit and app kit apple did
look at UI kit and app kit apple did
look at UI kit and app kit apple did this all over the place that might be
this all over the place that might be
this all over the place that might be appropriate
appropriate
appropriate um but you know maybe you use this
um but you know maybe you use this
um but you know maybe you use this protocol in other places that are not
protocol in other places that are not
protocol in other places that are not main actor isolated so maybe that's not
main actor isolated so maybe that's not
main actor isolated so maybe that's not going to work out it can also be a very
going to work out it can also be a very
going to work out it can also be a very viral change change One's little thing
viral change change One's little thing
viral change change One's little thing and all of a sudden you have to change
and all of a sudden you have to change
and all of a sudden you have to change everything so you think okay
everything so you think okay
everything so you think okay um the other option is uh apply the
um the other option is uh apply the
um the other option is uh apply the non-isolated keyword this removes actor
non-isolated keyword this removes actor
non-isolated keyword this removes actor isolation and this also could work but
isolation and this also could work but
isolation and this also could work but you may need that it could be that it's
you may need that it could be that it's
you may need that it could be that it's not safe to do this
not safe to do this
not safe to do this and what's really happening here is
and what's really happening here is
and what's really happening here is we're discovering that we have this API
we're discovering that we have this API
we're discovering that we have this API that just doesn't really fit nicely with
that just doesn't really fit nicely with
that just doesn't really fit nicely with how swift concurrency works and that is
how swift concurrency works and that is
how swift concurrency works and that is the five beakers of fear issue
the five beakers of fear issue
the five beakers of fear issue right I have found it easy to make API
right I have found it easy to make API
right I have found it easy to make API that is just fundamentally incompatible
that is just fundamentally incompatible
that is just fundamentally incompatible with concurrency
with concurrency
with concurrency and how how did this happen well the
and how how did this happen well the
and how how did this happen well the reason it happened is I had this build
reason it happened is I had this build
reason it happened is I had this build setting
setting
setting turn to the minimum so I was building up
turn to the minimum so I was building up
turn to the minimum so I was building up this mental model of how swift
this mental model of how swift
this mental model of how swift concurrency Works where nobody was
concurrency Works where nobody was
concurrency Works where nobody was checking that I was doing it correctly
checking that I was doing it correctly
checking that I was doing it correctly uh and that's this is dangerous because
uh and that's this is dangerous because
uh and that's this is dangerous because I really need you to start turning these
I really need you to start turning these
I really need you to start turning these things on so you can find the problems
things on so you can find the problems
things on so you can find the problems in your code now
in your code now
in your code now and it is true that this is a little bit
and it is true that this is a little bit
and it is true that this is a little bit in flux there's a lot of things changing
in flux there's a lot of things changing
in flux there's a lot of things changing including Apple's own API so you may
including Apple's own API so you may
including Apple's own API so you may uncover warnings that are either going
uncover warnings that are either going
uncover warnings that are either going to be fixed or maybe you're going to uh
to be fixed or maybe you're going to uh
to be fixed or maybe you're going to uh by the um by the Frameworks or maybe by
by the um by the Frameworks or maybe by
by the um by the Frameworks or maybe by the language itself but I would rather
the language itself but I would rather
the language itself but I would rather you know about those things now so that
you know about those things now so that
you know about those things now so that you can come up with a plan for how
you can come up with a plan for how
you can come up with a plan for how you're going to address them and avoid
you're going to address them and avoid
you're going to address them and avoid building things that are just
building things that are just
building things that are just fundamentally not going to work
fundamentally not going to work
fundamentally not going to work uh and this is going to be necessary
uh and this is going to be necessary
uh and this is going to be necessary because remember that in Swift 6 we're
because remember that in Swift 6 we're
because remember that in Swift 6 we're going from a state of no warnings at all
going from a state of no warnings at all
going from a state of no warnings at all to errors so I think you want a little
to errors so I think you want a little
to errors so I think you want a little bit of early warning here
and that's my that's it thank you very
and that's my that's it thank you very much
much
much thank you
Browse More Related Video
5.0 / 5 (0 votes)