What’s new in Foundation Notes

Andrey Zonov
3 min readJun 23, 2021

AttributedString

New AttributedString type

  • Swift Value type
  • same character-counting behavior as Swift String
  • Localizable
  • Safety and security in mind

Example:

var thanks = AttributedString("Thank you!")
thanks.font = .body.bold()
var website = AttributedString("Please visit our website.")
website.font = .body.italic()
website.link = URL(string: "http://www.example.com")
// AttributeContainer - a place you can hold attributes and values on their own without the string
var container = AttributeContainer()
container.foregroundColor = .red
container.underlineColor = .primary
thanks.mergeAttributes(container)
website.mergeAttributes(container)

AttributedString views: give us insight of the attribute string, they're Swift collections

  • characters - which provides access to the String instance
  • runs - which provides access to the attributes
var message = AttributedString(localized: "...") 
let characterView = message.characters
for i in characterView.indices where characterView[i].isPunctuation {
message[i..<characterView.index(after: i)].foregroundColor = .orange
}
let runCount= message.runcs.count
var insecureLinks: [URL] = []
for (value, range) in message.runs[\.link] {
if let v = value, v.scheme != "https" {
insecureLinks.append(v)
}
}

Localization

  • both AttributedString and NSAttributedString are fully localizable
  • in Swift, we can now use string interpolation, like in SwiftUI
String(localized: "Would you like to save the document “\(document)”?")
AttributedString(localized: "Would you like to save the document “\(document)”?")
  • Xcode can generate your strings files from these new initializers using the compiler: Turn this on by going to your build settings, look for the localization settings, and turn on Use Compiler to Extract Swift Strings

Markdown

AttributedString comes with markdown support, including links and custom attributes:

This text contains [a link](http://www.example.com).This text contains ![an image](http://www.example.com/my_image.gif).This text contains ^[a custom attribute](rainbow: 'extreme').

Here’s an example of custom attribute and how to parse it:

// Attribute scopes 
extension AttributeScopes
struct CaffeAppAttributes: AttributeScope {
let rainbow: RainbowAttribute
let swiftUI: SwiftUIAttributes
}
var caffeApp: CaffeAppAttributes.Type { CaffeAppAttributes.self }
}
let header = AttributedString(
localized: "^[Fast & Delicious](rainbow: 'extreme') Food",
including: \.caffeApp
)

Formatters

  • bran new API
  • no need to cache formatter anymore

Date formatter example:

func formattingDates() {
// Note: This will use your current date & time plus current locale.
// Example output is for en_US locale.
let date = Date.now
let formatted = date.formatted() // equivalent to date.formatted(.dateTime)
// example: "6/7/2021, 9:42 AM"
print(formatted)
let onlyYearDayMonth = date.formatted(.dateTime.year().day().month())
// example: "Jun 7, 2021"
print(onlyYearDayMonth)
let onlyYearDayMonthWide = date.formatted(.dateTime.year().day().month(.wide))
// example: "June 7, 2021"
print(onlyYearDayMonthWide)
let onlyDate = date.formatted(date: .numeric, time: .omitted)
// example: "6/7/2021"
print(onlyDate)
let onlyTime = date.formatted(date: .omitted, time: .shortened)
// example: "9:42 AM"
print(onlyTime)
}
  • all these formatted(date:) parameters are called fields
  • fields order do not matter
  • all fields have a default for the shortest versions of the API

Automatic Grammar Agreement

Makes localization easy when a certain sentence need to change based on the quantity and gender of the objects, we only need to provide the singular word, Foundation will take care of plurals and more.

Example:

func addToOrderEnglish() {
// Note: This will use your current locale. Example output is for en_US locale.
let quantity = 2
let size = "large"
let food = "salad"
let message = AttributedString(localized: "Add ^[\(quantity) \(size) \(food)](inflect: true) to your order")
print(message)
}

These are all the localization strings needed:

// MARK: en.strings"Add ^[%lld %@ %@](inflect: true) to your order" = "Add ^[%lld %@ %@](inflect: true) to your order"; "Pizza" = "Pizza";
"Juice" = "Juice";
"Salad" = "Salad";
"Small" = "Small";
"Large" = "Large";
// MARK: es.strings"Add ^[%lld %@ %@](inflect: true) to your order" = "Añadir [%1lld %3$@ %2$@](inflect: true) a tu pedido"; "Pizza" = "Pizza";
"Juice" = "Jugo";
"Salad" = "Ensalada";
"Small" = "Pequeño";
"Large" = "Grande";

--

--