View profile

🎯 Handling Focus in SwiftUI List Views - Not Only Swift Weekly - Issue #10

Peter Friese
Peter Friese
Hello everyone 👋🏻
This issue is slightly different from previous ones: it is the first in a series of issues in which I will write about an app that I am working on in more detail. If you follow me on Twitter (or have been a subscriber at least since issue #9), you will know that I started re-implementing Make It So. Make It So is a task list application, written in Swift. The idea behind this app is to see if it is possible to replicate Apple’s Reminders app using SwiftUI and Firebase. 
And of course, the name is a nod to one of my favourite sci-fi series - Star Trek Next Generation.
When I first created MakeItSo in early 2020, SwiftUI was still really new, and some things just weren’t possible in SwiftUI. For example, SwiftUI didn’t have swipe actions, and it was impossible to manage input focus. SwiftUI has come a long way since then, and I felt it was time to pick the project back up again and see how close we can get this time.
Re-implementing this app will take some time (I’ve got some other obligations as well), and you can follow along as I implement the app and share my progress with you on Twitter, GitHub, and eventually on the Firebase YouTube channel.
I will also share some insights in this newsletter and on my blog - so make sure to subscribe if you haven’t done so already.
As always, I am keen to hear your feedback, so if you’ve got anything you’d like to share with me, just hit that reply button and let me know! Seriously - I do love hearing from you, don’t be shy!
Thanks for reading,
Peter
P.S. if you’d like to say thanks beyond subscribing or sharing this newsletter with a friend, you can now send me a coffee through the internet!

Handling focus in SwiftUI Lists
Focus management is a usability feature that is especially important for productivity apps. Let’s face it, users don’t use a to-do list app because they enjoy planning their day so much - they’d probably much rather read a book, watch a movie, or go for a walk. The ideal user experience in a productivity app lets the user get their job done as quickly as possible. And this is why focus management is so essential.
In Apple’s Reminders app, the user can create a new reminder by tapping on a button. This will add a new, empty reminder, and place the cursor into the new reminder, allowing the users to start entering the title for the new item straight away. Once the user has finished entering the reminder, they can just tap the [Enter] key to advance. This will create a new reminder in the next line:
Demo: Apple's Reminders App
Demo: Apple's Reminders App
If you watch closely, you will also notice that, once they’ve entered the text for a to-do item, the user can tap Enter again to create a new to-do item below. This makes entering items very efficient: the user doesn’t have to tap on any buttons to create new items - everything can be done by just using the keyboard.
Apple introduced APIs for managing focus in iOS 15, and there are a bunch of great articles out there that cover how to use this feature in input forms. Unfortunately, there is little information about how to use this feature in List views, and my own experimentation showed that focus management doesn’t work in List views. Or so I thought - until I brought this up in this Twitter thread that I use as a sort of development journal. @erithacus_ and @thecraftybrit mentioned they had been bothered by this as well, and shortly after @thecraftybrit mentioned he had solved this for Xcode 13.1 beta and iOS 15.2 beta, which allowed me to implement this in MakeItSo.
So even though this doesn’t work on physical devices just yet, let me walk you through the individual pieces of the solution.
1. Indicating which UI element should be focused is easy for UIs that have a predefined number of elements - we can define an enum with a value for each of the UI elements. This doesn’t work for lists, as the number of rows is dynamic. To solve this, we will use an enum with an associated value. This associated value will contain the id of the selected element, which will allows us to easily focus an element or track which element the user focused.
2. As we want to keep as much of the applications logic outside of the actual views, we use view models to hold the code that is closely related to each individual view. This means that the code for adding new elements also lives in a view model. So, when we add a new to-do element (e.g. when the user tapped the New Reminder button, or when they placed the cursor in the list and hit the Enter key), we need to set the focus from inside a method on the view model. Unfortunately, @FocusState can only be used on View s, so we cannot use it on our view model (which is a class conforming to ObservableObject). We can solve this by implementing a mechanism that syncs between a property on the view model and the view. I know… this sounds a lot like an @Published property, but since property wrappers cannot be composed (yet?), we need to use a different approach, and sync using the .onChangeOf view modifier. Trust me, this works brilliantly.
3. When the user taps the Enter key while one of the to-do items is focused, we want to create a new item below and place the focus inside this new element. Detecting the Enter key is actually one of the things that is pretty straight forward - we can use the .onSubmit view modifier.
4. And finally, we want to remove empty elements from the list once they lose focus. To achieve this, we will use a Combine pipeline (on the view model) that tracks the previously focused element and removes it from the list of reminders if it is empty. As you will see, building this pipeline has its own challenges to make sure the animations on the list look smooth.
To learn more about how all of this works in detail and see all the code snippets, head over to my blog to read my article Managing Focus in SwiftUI List Views. You can also check out this commit on the repository - it contains all the relevant bits and pieces.
Thanks to @thecraftybrit and @erithacus_ for helping me implement this feature and convincing me to not give up when I thought this wasn’t possible in pure SwiftUI. Check out our conversation on Twitter if you’re interested - it’s great to be part of a community that supports each other.
Swift
Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns. The goal of the Swift project is to create the best available language for uses ranging from systems programming, to mobile and desktop apps, scaling up to cloud services. Most importantly, Swift is designed to make writing and maintaining correct programs easier for the developer.
For the past couple of years, most of use have probably used Swift for developing on the frontend (mostly iOS and watchOS, and - to a lesser extent macOS and tvOS), and maybe wrote the odd shell script.
Using Swift on the server still seems to be rather niche (and if I understood Tim Condon’s talk at SwiftLeeds correctly, the server-side Swift community is still relatively small). But Apple seems to be pretty serious about Swift on the server. There might not be an official framework from Apple, but Vapor seems to have become the de-facto standard, and it now supports async/await!
If you’re interested in learning more about Swift on the server, watch this recording of Tim’s talk from iOS Conf SG 2020:
Full stack development with Swift and Vapor - iOS Conf SG 2020
Full stack development with Swift and Vapor - iOS Conf SG 2020
Tim knows a thing or two about server-side Swift, and he seems to be pretty stoked about the recent announcement of Distributed Actors:
Tim Condon
It's hard to explain and get across how powerful this is 🤯 It's also difficult to comprehend how this, structured concurrency, task locals etc practically make Swift leap frog every other server side language out there. It's seriously incredible 🚀 https://t.co/AA3fNxObps
If you’re eager to learn more about Swift’s new concurrency model in general or actors in particular, check out the following resources:
Donny Wals 👾
Is async/await a Combine killer? Or more specifically, is Swift Concurrency's AsyncSequence a Combine killer? Find out how you can use AsyncSequence in your code and why I don't think it's a Combine killer in this new post I just published 😌

https://t.co/v86HKImfOI
Antoine v.d. SwiftLee 
Adopting async-await in Swift

https://t.co/fdDzXPdBoW

🚀 Async await explained
🏭 Refactoring tips and tricks
✨ Different ways of adopting

#swiftlang #iosdev
Marin Todorov
🎉 Incredibly excited to announce that “Modern Concurrency in Swift” is out now! It's a practical guide to Swift 5.5 's concurrency model, covering async/await, tasks, task groups and actors:
https://t.co/v9n5MKUVgT
Firebase 🔥
I’ve mentioned Firebase Summit 2021 a couple of times before in this newsletter (it’s taking place next week - have you registered yet?), and this week we’re celebrating Firebase Summit Community Week! There are no less than 9 videos created by members of the community. Topics range from how to land your dream job, using Firebase Test Lab to test your Flutter app, fighting inequality using PWAs running on Firebase Hosting, to security and scaling your apps.
Check out my handy viewing guide:
Peter Friese 🥑
It's #FirebaseSummit Community Week!

We've asked some members from the global @Firebase community to share some of their experiences and insights with us - and all of you!
Computer History
I promised you some computer history when I started this newsletter, so here we go - the history of spreadsheets. Did you know Steve Jobs was involved in creating spreadsheet software?
Chris Hladczuk
3.2 billion people use a spreadsheet every month.

But what you don't know is that the spreadsheet led to Apple's early popularity and changed computers forever.

Here's the story of VisiCalc👇 https://t.co/CiaLJXuK3k
Thanks to the Internet, we no longer have to install software on our computers - software like Google Sheets just runs in the cloud (or rather, we download it from the cloud and run it in the JavaScript sandbox of our browser). Here are some pretty cool tips and tricks that will bring your Google Sheets skills to the next level:
Blake Burge 💡
Google Sheets isn't Excel...

But it's more powerful than you think.

8 things Google Sheets can do, you'll wish you knew yesterday. 📊
Jobs
I’ve seen a couple of interesting job postings in the past few weeks, and thought it’d be good to surface them here as well. Instead of linking to the job postings, I will link to the people who mentioned them on Twitter, so you can reach out to them for more details (or to get a referral).
Ellen Shapiro
Go work with @twannl, he’s a great guy in addition to being an excellent developer and writer.
https://t.co/QWOoutsTGL
Douglas Hill
We’re looking for an experienced iOS developer to join my team at @PSPDFKit. I’ve been here over 6 years so clearly I recommend it! Details at https://t.co/4Pvi5RFqUm
DM me with questions!
Swifty McSwiftface
Really enjoying my new job at The Guardian, if you can commute to London there are a couple more iOS Development roles still available on my team!

https://t.co/46ywcdm5OX

https://t.co/WeMOkos9i4 https://t.co/40gSqm912e
And finally
You might have seen this Playing With Time video on your Twitter timeline this week - it is just brilliant:
Playing With Time
Playing With Time
Now, if you just want to enjoy the video, and remain curious how it was made, I suggest you stop reading now.
However, if you’re curious how it was made, check out the following video by Captain Disillusion. Actually, I am not sure which video is more amazing - the original, or the one that destroys the illusion and disseminates the process of how the original most likely was created. Brilliant!
Playing With 'Playing With Time' | Quick D
Playing With 'Playing With Time' | Quick D
Did you enjoy this issue? Yes No
Peter Friese
Peter Friese @peterfriese

Xcode tips & tricks, Swift, SwiftUI, Combine, Firebase, computing and internet history, and - of course - some fun stuff.

In order to unsubscribe, click here.
If you were forwarded this newsletter and you like it, you can subscribe here.
Created with Revue by Twitter.