When I see it in code, read it as ‘particular’ instead of ‘some’.
‘any’ is an existential. Relies on dynamic dispatch.
‘some’ is an opaque type to the caller, but still has identity that the compiler knows of.
Easiest to think of as ‘reverse generics’:
"An opaque return type can be thought of as putting the generic signature "to the right" of the
function arrow; instead of being a type chosen by the caller that the callee sees as
abstracted, the return type is chosen by the callee, and comes back to the caller as
abstracted"
Soruce: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0244-opaque-result-types.md
protocol X {
var y: String // this is a customization point
}
extension X {
var z: String {
“Not a customization point!”
}
// default implementation
var y: String {
"hello world"
}
}
// X#z can’t be customized, but X#y can
https://forums.swift.org/t/why-asyncstream-breaks-structured-concurrency/71477/4
Swift gotcha: A Task
does not create a child task.
https://forums.swift.org/t/why-asyncstream-breaks-structured-concurrency/71477/3
Read all answers by robert.ryan in that thread
Visual on different ways to create a task:
https://forums.swift.org/t/should-task-groups-inherit-actor/57547/2
Handy when trying to conditionally compile a lib feature that is backdeployed
but not available for customers on older versions of Xcode:
Quinn the Eskimo: https://forums.swift.org/t/how-to-handle-errors-in-swift-when-coming-from-a-java-world/20821/13
struct S {
var x = 1
}
var y: [String: S] = ["a": S()]
y["a"]?.x = 2
y["a"]?.x // Returns 2!
I figured I would have to do this:
var y: [String: S] = ["a": S()]
y["a"] = S(x: 2)
The first snippet rewritten with an assignment does what I expect:
struct S {
var x = 1
}
var y: [String: S] = ["a": S()]
var z = y["a"]
z?.x = 2
y["a"]?.x // Returns 1
Returning to first snippet. Will didSet handlers get called with this setup:
struct S {
var x = 1
}
final class C {
var y: [String: S] = ["a": S()] {
didSet {
print("Am I called?")
}
}
func mutate() {
self.y["a"]?.x = 2
}
}
C().mutate()
// Prints "Am I called?"
“Marking something as nonisolated tells the compiler that you’re not going to touch any of the
isolated state and therefore this function can be called from anywhere”
https://developer.apple.com/videos/play/wwdc2021/10194/?time=2302
Quinn the Eskimo!
In Swift concurrency, tasks are run by threads from the Swift concurrency cooperative thread
pool. When a task suspends at an await, the thread running that task returns to the thread pool
and looks for more work to do.
https://developer.apple.com/forums/thread/708182
Another Quinn the Eskimo! gem: https://developer.apple.com/forums/thread/760725
actor MyActor {
func doStuff() {
Task { @MainActor in
MainActorState.counter += 1
}
}
}
Also notice how he annotates the static var below as @MainActor. You can’t apply that
annotation to the enclosing type:
enum MainActorState {
@MainActor static var counter: Int = 0
}
actor MyActor {
func doStuff() {
MainActorState.counter += 1
// ^ Main actor-isolated static property 'counter' can not
// be mutated on a non-isolated actor instance
}
}
This does not isolate doSomething:
@globalActor public actor RealtimeActor {
public static let shared = RealtimeActor()
}
@RealtimeActor
final class Foo {
static func doSomething() {}
}
Try it in a sample project. You can call Foo.doSomething
outside of a task, and it will
execute on the main thread.
https://x.com/nsobject/status/1892611473144213584
For functions that return void:
let d = Date()
async let parallelDep1: Void = wait1()
async let parallelDep2: Void = wait2()
_ = await (parallelDep1, parallelDep2)
print(String(format: "Deps loaded in %.2f seconds", Date().timeIntervalSince(d)))
private func wait1() async {
try? await Task.sleep(for: .seconds(1))
}
private func wait2() async {
try? await Task.sleep(for: .seconds(1))
}
Quinn the Eskimo! says:
My favourite trick for this problem in general is to add a pause() system call to the front of
my code. That stops the process until it receives a signal, and the act of attaching the
debugger generates that signal and thus you can just continue from there.
https://forums.swift.org/t/editing-and-debugging-a-swiftpm-plugin/71935/6
private static var myString: String {
get async {
return await getTheString()
}
}
try? await Task.sleep(nanoseconds: 1_000_000_000)
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
print("hello")
}
// or
Task {
try? await Task.sleep(nanoseconds: 2_000_000_000)
print("world")
}
AI bad at POP. When to use customization points vs default extensions.
It can tell you what they are, but it won’t design w/ them. Yet.
From root of package:
swift package dump-package
From within the task:
try Task.checkCancellation()
if case let .longHTTP(wait: wait) = strategy {
request.addValue("Prefer", forHTTPHeaderField: "wait=\(wait)")
}
First, use Decodable. But if I’m roughing something in:
import Foundation
let myData: Data = ...
let deserialized = try JSONSerialization.jsonObject(with: myData)
guard let json = deserialized as? [String: Any] else {
throw MyError("Could not deserialize data to [String: Any]")
}
URLRequest comes with a default timeout of 60 seconds. Can catch timeouts with (see line 27):
https://gist.github.com/lzell/42b47aa5e521a3e20840aba093a7c835
It fails for properties that I have setup with acronyms, e.g.
struct Response: Decodable {
let fooURL // I want this to map to "foo_url"
}
The error:
"Unescaped control character '0xa' around line 3, column 0."
is due to an unescaped newline in a JSON field. Repro:
let response = """
{
"message": "will\nfail"
}
"""
struct Response: Decodable {
let message: String
}
_ = try! JSONDecoder().decode(Response.self, from: response.data(using: .utf8)!)
It’s actually Foundation that adds the localizedDescription property on an error. For example,
this fails in a repl:
enum MyError: Error {
case foo
}
do {
throw MyError.foo
} catch {
print(error.localizedDescription)
}
but if I import Foundation
first, it works as expected.
To modify the error description, use this:
import Foundation
enum MyError: LocalizedError {
case foo
var errorDescription: String? {
switch self {
case .foo:
return "My description"
}
}
}
do {
throw MyError.foo
} catch {
print(error.localizedDescription) // prints 'My description'
}
URLComponents are not needed to get out the domain and path.
let url = URL(string: "https://api.aiproxy.pro/foo/bar")!
Get the domain
url.host
Get the path
url.path
Error handling in Swift resembles exception handling in other languages, with the use of the
try, catch and throw keywords. Unlike exception handling in many languages — including
Objective-C — error handling in Swift doesn’t involve unwinding the call stack, a process that
can be computationally expensive. As such, the performance characteristics of a throw statement
are comparable to those of a return statement.
From: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/errorhandling/
// A.swift
struct A {
}
// A+B.swift
extension A {
struct B {
}
}
I think it’s more common to use this pattern for protocol conformance, but I like it for nested
structs too. I searched around for examples of Apple code that uses this pattern. It’s in
swift-foundation:
https://developer.apple.com/documentation/foundation/date/iso8601formatstyle
https://github.com/apple/swift-foundation/blob/main/Sources/FoundationEssentials/Formatting/Date%2BISO8601FormatStyle.swift#L24
f
and g
are equivalent here:
protocol P {}
struct S: P {}
func f<T>(_ t: T) where T: P {}
func g<T: P>(_ t: T) {}
f(S())
g(S())
Apple calls the first syntax a ‘generic where clause’ and the second a ‘generic parameter
clause’. The only different, as far as I can tell, is the where clause can support constraints
on a protocol’s associated types, e.g. where S1.Iterator.Element == S2.Iterator.Element
If I am not working with PATs, I prefer the syntax func g<T: P>(_ t: T)
struct S<T> {
let s: T
}
extension S where T == Int {
func plusOne() -> Int {
return self.s + 1
}
}
let x = S<Int>(s: 1)
print(x.plusOne()) // prints 2
Instead of
"{\"x\":\"y\"}"
Use
#"{"x":"y"}"#
For a multiline string that I don’t want to escape quotes or slashes:
#"""
This is
"cool"
"""#
See the ‘future proof’ section here: https://www.donnywals.com/writing-custom-json-encoding-and-decoding-logic/
Always name the associated value, otherwise you get a 0: "whatever"
in your encoded json.
The fact that I need to introduce type erasure for a concept this simple is still wild to me:
let dict = ["A": "x", "B": 2]
try? JSONEncoder().encode(dict) // Won't work
We had this with obj-c out of the box.
Redditor to the rescue: https://www.reddit.com/r/swift/comments/1ecv7dw/wrangling_json_in_swift/
let dict: [String: Any] = ["A": "x", "B": 2]
let data = try? JSONSerialization.data(withJSONObject: dict)
String(data: data!, encoding: .utf8)
Swift doesn’t have a sum or union. What do people use instead? Enum with associated values
Use the +
overload:
Data() + Data()
import Foundation
UUID().uuidString
FatalError
for broken invariants. Crash hard.AssertionError
and return nil[1,2,3].map { x in x + 1 }
[1,2,3].map { $0 + 1 }
RunLoop.current.run()
Codable is defined as
public typealias Codable = Decodable & Encodable
If I only want to create types that are encodable, use struct MyMessage: Encodable
import Foundation
struct S: Encodable {
let x: String
let y: Int
}
let s = S(x: "hello", y: 123)
let jsonData = JSONEncoder().encode(s)
if let jsonStr = String(data: jsonData, encoding: .utf8) {
print(jsonStr)
// prints {"x":"hello","y":123}
}
There is no guarantee of key order when structs are encoded. For unit tests,
create the encoder with the following for predictable serialization results:
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
Don’t put Tasks everywhere. Try to stay structured
(swift, stringify without using codable)
private func stringify(_ args: [String: Any]) -> Data? {
if JSONSerialization.isValidJSONObject(args) {
return try? JSONSerialization.data(withJSONObject: args, options: [])
}
return nil
}
(swift, remove from dictionary)
var x = ["a": 1]
x.removeValue(forKey: "a")
x #=> [:]
(swift, tuple destructuring)
var x = (1, "hello")
var (a, b) = x
a #=> 1
b #=> "hello"
(swift, special case for simulator)
#if targetEnvironment(simulator)
// Do something special
#endif
#if os(macOS)
print("mac")
#else
print("not mac")
#endif
(swift, dictionary, increment key, ruby ||=)
var x = String: Int
x[“a”, default: 0] += 1 // a maps to 1
x[“a”, default: 0] += 1 // a maps to 2
(swift async streams, continuations)
- AsyncStream conforms to AsyncSequence
- See the docstring on AsyncThrowingStream for modifying an existing
closure-based callback earthquake monitor into an async/await-compatible interface
- See continuation
reference in wwdc/modern_crash_course_takeaways.txt
- Sendable
is “A type whose values can safely be passed across concurrency domains by copying.”
- See ~/dev/ContinuationExperiment
(swift get type of instance)
type(of: myInstance)
(swift regex, repl)
Start the repl with:
swift repl -enable-bare-slash-regex
> var a = "a"
> let re = /a/
> a.replace(re) { match -> String in "b" }
> a
// Outputs "b"
What most languages call ‘lazy’ (i.e. ? after a pattern), swift calls ‘reluctant’:
See Swift Regex: Beyond the basics, 12:35
(swift regex, SPM)
Modify the target sections of Package.swift to look like this:
.target(
name: "AIProxySwift",
swiftSettings: [
.unsafeFlags(["-Xfrontend", "-enable-bare-slash-regex"])
]
),
.testTarget(
name: "AIProxySwiftTests",
dependencies: ["AIProxySwift"],
swiftSettings: [
.unsafeFlags(["-Xfrontend", "-enable-bare-slash-regex"])
]
),
@globalActor actor MyStickerActor {
static let shared = MyStickerActor()
}
// Then...
@MyStickerActor
func createSticker(_ prompt: String) async throws -> UIImage
(gcd notes, libdispatch notes, use it right, serial queue)
https://gist.github.com/tclementdev/6af616354912b0347cdf6db159c37057
(swift data)
How to create a model container outside of SwiftUI context:
https://developer.apple.com/documentation/swiftdata/preserving-your-apps-model-data-across-launches
References to organize:
// Meet SwiftData: https://developer.apple.com/videos/play/wwdc2023/10187/
// Build an App with Swift Data: https://developer.apple.com/videos/play/wwdc2023/10154
// https://developer.apple.com/xcode/swiftdata/
// bug: https://developer.apple.com/forums/thread/732788
//
// Swipe to delete: https://developer.apple.com/documentation/swiftdata/deleting-persistent-data-from-your-app
//
(swift, start repl)
swift repl
// Interpolation
let a = “a”
let a_ = “(a)”
a == a_ # => true
// Concatenation
a + “b” == “ab” # => true
// Printing multiple strings
print(a, “b”) # => prints “a b”
// Printing floats
import Foundation
let a = 1.2345
String(format: “%.2f”, a) # => 1.23
// Separating string variables from the string template:
import Foundation
String(format: “%@ %@”, “hello”, “world”)
Minute 27 of “modern swift API design”
(smells, protocol, overuse, protocols aren’t mixins)
Add to my talk: minute 13 of wwdc “modern swift API design”.
If conformer calls protocol definitions, you’re using it wrong.
(swift add convenience initializer to struct, keep memberwise initializer)
Add the initializer in an extension to avoid losing the default memberwise initializer
(swift demangle, stack trace, demangle)
xcrun swift-demangle
(assembly, swift)
For anyone curious how to look at assembly for snippets of swift, this is what
I ran: xcrun -sdk iphoneos swiftc -target arm64-darwin-ios11 -O -S -o - input.swift | xcrun swift-demangle | less
(repl, swift, lookup, type)
$ swift repl
> :type lookup String
Use this to search the api with fuzzy search (ctrl+6 once open):
printf “import Foundation:type lookup String” | swift > /tmp/foo.swift && open -a /Applications/Xcode.app /tmp/foo.swift
(repl, spm, swift, package, import module)
Start repl with:
$ swift build && swift -I.build/debug -L.build/debug -lBluetoothService
> import BluetoothService
(swift compiler, swiftc)
Additional help:
swiftc -help-hidden
(switch swift version)
$ sudo xcode-select -switch /Applications/Xcode-beta.app
$ xcrun swift –version
(swift, get type)
“foo”.dynamicType
(swift, initializers)
http://stackoverflow.com/questions/29956195/idiomatic-pattern-to-initialize-uiviews-in-swift
Will be able to find *-Swift.h in:
~/Library/Developer/Xcode/DerivedData/
Run with:
xcrun swift
or yosemite:
swift
or:
lldb –repl
When modifying previously entered input: insert newline with opt+enter
Type :help to get list of commands
:quit to quit
To set a breakpoint from within repl, create a function called go, then set a
breakpoint in the body with:
:breakpoint set –line
Then call go()
Emit debug information:
$ swiftc filename.swift -g
Run program with lldb:
$ lldb -f filename
Set breakpoint:
(lldb) breakpoint set –file filename.swift –line 1
Run:
(lldb) run
Print lots of information:
(lldb) fr v -R
Change the variable to nil:
(lldb) p x = nil
Dump it again:
(lldb) fr v -R x
(lldb) expression -l objc -O – (id) 0xaddressofobject