Coding is a simple word that means “all other words from now on will be complicated, designed to make you believe that you’re entering a world of the arcane”. And that pretty much sums up “Concurrency”. Why they couldn’t have come up with better and more self-explanatory words to hang the concepts on, I don’t know.
Anyway, “Concurrency” is a way of explaining how a system that can only really do one thing at a time handles doing many things at the same time.
Usually, this is not an issue – a system that works fast enough that you don’t realise you’re waiting on a task to finish before the next one is handled isn’t going to present you with a problem. However, if the system is one in which we expect immediate response, such as a mobile phone, then it’s just not good enough to “wait on a task to finish” before carrying on. It’s one thing to wait a second for a web-page to load, but it’s quite another when your phone becomes unresponsive for a second. A web-page being unresponsive for a moment doesn’t mean the system is unresponsive, only that the page is slow, but if your phone is unresponsive you immediately believe it’s crashed. That’s not a good user experience.
So, how do you keep the system responsive when it’s handling a task that’s resource-intensive and time-consuming? That’s where “Concurrency” comes into it.
“Concurrency” effectively allows the system to work on multiple tasks in small bite-sized chunks so that no task is waiting on another.
The system has multiple “threads” on which tasks can run but, as mentioned above, each thread can only do one thing at a time.
The “main thread” is the one which deals with the display and user interaction, and no task can begin until the previous one has finished. If it’s used for other things then the system can appear to freeze because the next task is not allowed to start until the previous one has finished, and that’s just not good practice.
Then there are “background threads” of lesser importance. The background threads can still only do one thing at a time but they split multiple tasks up into manageable “chunks” so that no task is waiting on other tasks to finish before it can start. That’s concurrency. Concurrency gives the appearance of doing multiple tasks at the same time on a system that can only do one thing at a time.
This is logical and sensible, but the application of concurrency is anything but. I’ve been using my own app to try and understand how concurrency works in practical use.
An example I have is that I want to pre-fill my app with default data that I’m currently supplying from a JSON file that’s created on a remote website (on a server that’s not particularly snappy!). I want to check a control JSON file for a date code, compare that to the date code stored in Core Data (if there is one) and then, if they don’t match, update Core Data from a larger JSON file (a file that could get pretty big by the time I’m done). I don’t want to be fetching that larger file if it’s not needed, but I do need the app to know if that file has been updated to know if it needs to fetch it.
Sounds simple, right? But if multiple tasks are handled in bite-sized chunks, the one that’s checking the date on the first JSON file may not finish downloading & decoding the JSON before the task to compare the dates is running. Therefore the “date check” might believe Core Data holds the latest data when, in reality, it doesn’t.
It took me quite a time to figure out how to handle that scenario, using both the Core Data Mastery in Swift UI book from Big Mountain Studio and past lessons from CodeWithChris. I eventually ended up with a system that works, and works pretty well, but I’m also very aware that I’ve barely scratched the surface of Concurrency. There’s much more to learn with .perform
, .await
, background queues, etc. There are probably much better ways of achieving that which I’ve achieved, but experience comes with practice, and practice takes time.
I’m discovering that the best move I made was to work on my own app in order to incorporate techniques such as Concurrency. Reading about concepts as complicated as this is one thing, but to even begin to understand the practicalities of it, you’ve got to be working on something of your own. You need to see the bugs, the errors, and experience the frustrations and the hair-pulling. For only by making mistakes and resolving the errors do we really understand that which is happening and that which the system requires of us.
My app may not cover all scenarios, but that’s a good thing at this time. Taking things in small steps, focussing on getting “something working”, and understanding how it works is a rewarding step on the road towards understanding the full process. I’ve had to learn (and understand) a lot about Core Data as well as Concurrency, figuring out how and where to use FetchRequests in a declarative language like SwiftUI, and ensuring that tasks are carried out in the right order. I feel that I’ve learned a lot, understood quite a chunk, but I’m also wary of just how vast this ocean is when I see many apparently simplistic words that may as well be magical incantations.
One tip I can offer that’s helped me enormously is that I’ve littered my code with copious amounts of print()
messages. I have a Constants
file such as this:
struct Constants {
static var showDebug: Bool = true
}
And throughout my code, I have sections such as this:
if Constants.showDebug {
print("[DM/lR] Trying moc.save() ...")
}
The code in the square brackets tells me exactly which class/method/function the message comes from so I can find the exact location in the code by looking at Xcode‘s Debug area. Flipping the constant to false
turns off the messages, so I can have different report levels – messages, errors, debug, etc.
When trying to understand the flow of code, when tasks begin, when they end, when others started, it’s so much easier when you can see placemarkers such as this scrolling up in the console.
Seriously, fellow coders, if you want to do more than just scratch the surface of Core Data and complicated techniques such as Concurrency, you’ll do yourself a big favour in checking out Core Data Mastery in SwiftUI from Big Mountain Studio.
The free “Quick Start” guide is available now and, although you’ll need to wait for the full book for advanced techniques such as Concurrency, this is my “go to” guide for wading through these concepts.
Grab it now. Mark Moeykens of Big Mountain Studio has put an awful lot of work, effort, and time into this to help make things easier to understand.