WWDC Quick Look 💓 By SwiftGGTeam
What's new in Foundation

What's new in Foundation

Watch original video

Highlight

Foundation turns Locale from a reference type into a value type, and the #Predicate macro moves property-name and type checks for predicate queries to compile time.


Core content

The main thread of this Foundation update is paying down design debt left over from the Objective-C era. Turning Locale from a reference type into a value type sounds like an internal detail, but it kills off a whole class of multithreading bugs: two threads read different locale values the instant the user switches the system language, and formatting results disagree. These bugs are nearly impossible to reproduce; in the past you could only add locks or pray. Now Locale.current returns a snapshot at the moment of the call, and the value in your hands will not change.

The rewrite under JSONEncoder and JSONDecoder brings a performance boost, a free win for apps that handle a lot of API responses. Another practical change is OutputFormatting.withoutEscapingSlashes, which finally lets you control how / is escaped — some backends have always tripped over \/.

The #Predicate macro is the headline. NSPredicate writes "age >= 18" as a string, and you only learn about a typo at runtime; #Predicate lets the Swift compiler check types and property names at build time. Combined with the chainable SortDescriptor API, Core Data queries can finally be type-safe end to end.


Details

#Predicate turns string queries into Swift closures, with the compiler in charge of types. Here is a typical use:

struct Person {
    var name: String
    var age: Int
}

let adults = #Predicate<Person> { person in
    person.age >= 18
}

let sortByName = SortDescriptor(\Person.name)

Key points:

  • #Predicate<Person> binds the predicate to the Person type, and the compiler checks every field access on the closure parameter person.
  • person.age >= 18 is just a Swift expression; misspell the property name and Xcode flags it at once, with no runtime string parsing to worry about.
  • SortDescriptor(\Person.name) picks the sort field with a KeyPath, sidestepping the old typo-prone string keys.
  • You can pass the predicate to a FetchDescriptor in SwiftData or Core Data for end-to-end type safety.

JSONEncoder output options can be combined with outputFormatting:

let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]

Key points:

  • .prettyPrinted adds indentation to make output easier to debug.
  • .withoutEscapingSlashes turns off escaping of /, so you get http://example.com instead of http:\/\/example.com.
  • The two options stack with OptionSet syntax and do not conflict.

Takeaways

  • What to do: audit every Locale.current call in your project.

    • Why it pays off: locale drift across threads causes formatting glitches that are nearly impossible to reproduce, a classic source of production issues.
    • How to start: use Xcode’s Find Navigator to search for Locale.current across the project, and pay extra attention to uses inside GCD queues and async contexts.
  • What to do: migrate NSPredicate strings in new code to the #Predicate macro.

    • Why it pays off: compile-time checks catch most typos and type mismatches, cutting down on production crashes.
    • How to start: begin with new query code, and migrate the rest opportunistically as you touch related modules.
  • What to do: rerun your JSON parsing performance baseline after bumping the deployment target.

    • Why it pays off: the rewrites under JSONEncoder and JSONDecoder need no code changes, so the gain is free.
    • How to start: use XCTest’s measure block to benchmark large JSON payloads, and compare timings before and after the upgrade.

Comments

GitHub Issues · utterances