What’s new in Foundation Notes
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 = .primarythanks.mergeAttributes(container)
website.mergeAttributes(container)
AttributedString
views: give us insight of the attribute string, they're Swift collections
characters
- which provides access to theString
instanceruns
- 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
andNSAttributedString
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";