Issue #73
Hi there, welcome to the 73rd issue of iOS Code Review! I'm waving to you from my vacation in Japan. Today's issue is a collaboration with J’aime Ohm and Kate Roberts. Welcome back, J'aime in San Francisco. Hello, Kate in London. Enjoy! 👇
Sign up today to secure the current license price before the 9.0 increase. You can save an additional 50% on your first payment with the discount code
KK-BF24-LXX8YV8
: https://keyboardkit.com/proYour sponsorship makes this newsletter possible! As 2024 wraps up, please consider us in your budget for 2025. More info here or simply request a slot by sending an email to ioscodereview@hybridcattt.com.
Xcode 16: Try Swift Testing
We encourage you to write your next new test using Swift Testing instead of XCTest. Swift Testing improves your test suite's readability and makes it easier to run certain kinds of tests.
Let's swap out XCTest for Swift Testing:
// import XCTest becomes
import Testing
// class ExampleTests: XCTestCase becomes
struct ExampleTests
class ExampleTests // if you want deinit
// override func setup() becomes
init()
// override func teardown() becomes
deinit()
// XCTAssert calls become one of these
#expect(...)
#expect(customVariable > 10)
#expect(throws: ) { ... }
#expect(throws: CustomError.self) { ... }
#expect { ... } throws { ... }
// XCTFail becomes
Issue.record("Custom failure message")
// Prefix your test functions with the Test macro
@Test func ...
@Test("Custom display name") func ...
We like that multiple XCTAssert
formats are collapsed into the new expect
macro, due to some cleverness under the hood. For more examples, check out Majid's article on the basics of Swift Testing.
Secrets to Success With @MainActor
Swift 6's (included in Xcode 16) stricter concurrency checks inspire us to discuss our do's and don'ts. The MainActor attribute is a hot topic because it has consistently caused confusion since Swift 5.5; here are some secrets to success with this attribute.
DO: add the MainActor attribute to all your observable classes
@MainActor @Observable
class ...
DO NOT: use actors for your observable objects
// DONT DO THIS
@Observable
actor ...
// NOT THIS EITHER
@CustomGlobalActor @Observable
class ...
If you kickoff a background task, make sure you return to the MainActor if it needs to be there for completion.
Task { @MainActor in
// Do stuff
}
For an example using ObservableObject (we use the newer Observation framework's @Observable in the above code snippets) or for more ways to return from a background task, check out the following article by twostraws.
More Secrets to Success With @MainActor
We will all run into this. One day, Swift 6 will give you a confusing error that you have a "call to... main actor... in a synchronous context". Prove us wrong and avoid this error entirely with the following DONT.
DO NOT: call your main actor method synchronously from code that could execute on a thread that is not the main thread. In Swift 5.x, this will cause the @MainActor
annotation to be silently ignored and the method will not be called on the main actor:
DispatchQueue.global().async {
exampleMethod()
}
...
@MainActor
func exampleMethod() {...}
To reflect on MainActor as a global actor, learn to create your own global actors, and review more ways to use the MainActor attribute, you can go deeper with Antoine van der Lee here:
Customising Writing With Intelligence
When it comes to Apple Intelligence - available in iOS 18 - the most currently visible feature is Writing Tools. Writing Tools uses AI to offer to rewrite your text.
We use the new writingToolsBehavior
modifier to customize Writing Tools on any Text, TextEditor, or TextField.
.writingToolsBehavior(.autocomplete) // default
.writingToolsBehavior(.complete) // turn it on
.writingToolsBehavior(.disabled) // turn it off
.writingToolsBehavior(.limited) // simplified
This article provides further customization of WritingTools through UIKit and discusses the features, animations, and ubiquity of Writing Tools.
Swift Evolution Proposals
[Accepted] We are delighted to see that trailing comma in comma-separated lists has been accepted. This will make for smaller diffs and make it easier to append, remove, and re-order comma-separated lists.
[In Review] Does the lack of a native Vector type concern you? Or do Arrays adequately serve your needs?
Upcoming Events
- Black Friday Deals: Like last year, Marius Landwehr's Github offers a master list of Black Friday deals. We recommend bookmarking this (it will grow) for returning to on Black Friday.
- Advent of Code returns with free daily Swift challenges starting December 1
- Mikaela Caron and Paul Hudson are back with Swift Over Coffee, discussing everything from Apple Intelligence to Swift 6. Next episode’s poll: what are your Swift papercuts?
✌️
Alright, that's it for today! Let's spread the good code vibes ✨🧘🌈☀️
You can now use the comment section on the website to share your thoughts. Thank you to KeyboardKit Pro for sponsoring this issue and thank you to J'aime and Kate for joining us today ❤️
Member discussion