Swift 관련 업데이트를 확인해 보세요. 일상적인 인체공학, 향상된 동시성, 더 안전한 고성능 코드를 위한 업데이트 등 언어 관련 최신 혁신 기술을 알아보세요. Embedded Swift의 워크플로와 언어 상호 운용성 개선 사항 및 업데이트를 살펴보세요.
MutableRef는 더 나은 방법을 제공합니다 이제 루프 시작 전에 한 번 딕셔너리 조회에서 MutableRef를 만들 수 있습니다 루프가 시작되기 전에요 딕셔너리 항목을 변형할 때 그것을 사용하면 됩니다 Ref는 탈출 불가능하므로 Swift는 접근이 끝나는 시점을 알 수 있습니다 변수가 범위를 벗어날 때요 이 새로운 소유권 기능들이 결합되어 성능에 가장 민감한 코드의 속도를 높이는 것이 그 어느 때보다 안전하고 쉬워졌습니다 소유권 시스템만이 진행 중인 작업이 아닙니다 Evan에게 돌아가서 Swift의 미래에 대해 알아봅시다 오늘 다룬 기능들은 오픈 소스로 개발됐으며 전반적인 경험을 개선했습니다 Apple OS들, Linux, Windows, 그 이상에서요 오픈 소스 커뮤니티에서 일어나고 있는 미래 개발 사항들이 더 있습니다 여러분들이 참여하실 수 있습니다 지난해 Xcode의 빌드 시스템인 Swift Build를 오픈 소스로 공개한다고 발표했습니다 Swift Build는 이제 Swift Package Manager의 기본 빌드 시스템 백엔드입니다 Swift Package 빌드와 Xcode에서 보는 것 사이의 일관성을 개선합니다 늘어나는 워크그룹 목록에 빌드 및 패키징 워크그룹이 합류했습니다 Swift 커뮤니티의 빌드 및 패키징 요구 사항을 다루는 그룹입니다 네트워킹 워크그룹은 차세대 크로스 플랫폼 네트워킹 API를 설계합니다 그리고 Windows 워크그룹은 Windows에서의 Swift 경험을 개선합니다 올해 Android 워크그룹이 Swift 6.3의 일부로 Android용 첫 번째 Swift SDK를 출시했습니다 이제 Android와 iOS 앱 사이에서 Swift 코드를 공유할 수 있게 됐습니다 Swift 생태계 개발에 참여하고 싶다면 forums.swift.org의 Swift 포럼에 참여해 주세요 여러분만의 소중한 피드백을 기다리겠습니다 오늘 Swift의 새로운 기능에 대해 함께 알아봐 주셔서 감사합니다 버그 리포트를 제출하셨든 풀 리퀘스트를 올리셨든 이벤트에 참여하셨든 포럼에 참여하셨든 여러분의 기여가 Swift의 미래를 만들어 갑니다 모든 사람이 더 안전하고 쉽게 프로그래밍할 수 있도록 만들어 줍니다 감사합니다!
1:12 - Better Swift Concurrency diagnostics (catching in the task)
Task {
do {
try lander.fly(to: moon)
}
catch {
lander.abort()
}
}
1:21 - Better Swift Concurrency diagnostics (saving the task for later)
let landingTask = Task {
try lander.fly(to: moon)
}
defer {
await orbiter.rendezvous(with: lander)
}
try await orbiter.justHangOut(waitingFor: landingTask)
1:27 - Better 'Sendable' conformances
final class Spacecraft: Sendable {
...
weak let dockedAt: SpaceStation?
...
}
class Mission: ~Sendable { ... }
class CrewedMission: Mission, @unchecked Sendable { ... }
1:48 - More accessible memberwise initializers
struct Briefing {
internal var topic: String
internal var scheduledAt: Date
private var attendees: [Person] = []
}
// Generated memberwise initializers:
// extension Briefing {
// private init(topic: String, scheduledAt: Date, attendees: [Person] = []) {
// self.topic = topic
// self.scheduledAt = scheduledAt
// self.attendees = attendees
// }
//
// internal init(topic: String, scheduledAt: Date) {
// self.topic = topic
// self.scheduledAt = scheduledAt
// self.attendees = []
// }
// }
2:03 - 'anyAppleOS' availability (before)
extension Mission {
@available(macOS 27, iOS 27, watchOS 27, tvOS 27, visionOS 27, *)
func showStatus() { ... }
@available(macOS 27, iOS 27, watchOS 27, visionOS 27, *)
@available(tvOS, unavailable)
func launch() { ... }
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS)
func makeLiveActivityWidget() -> some Widget { ... }
#endif
}
2:17 - 'anyAppleOS' availability (after)
extension Mission {
@available(anyAppleOS 27, *)
func showStatus() { ... }
@available(anyAppleOS 27, *)
@available(tvOS, unavailable)
func launch() { ... }
#if os(anyAppleOS)
func makeLiveActivityWidget() -> some Widget { ... }
#endif
}
2:40 - Controlling warnings with '@diagnose'
(DeprecatedDeclaration, as: ignored, reason: "Flying with surplus hardware")
func makeApolloSoyuzMission() -> Mission {
CrewedMission(
rocket: makeSaturnIRocket(),
payload: makeApolloCSM(),
crew: [.daniellePoole, .nathanMorrison]
)
}
(StrictMemorySafety, as: warning)
func uplinkCommand(from receiver: inout Receiver, to computer: inout Computer) {
let commandSize = receiver.receiveInt()
receiver.withReceivedData(byteCount: commandSize) {
computer.receiveUplinkedCommand($0)
}
}
(ErrorInFutureSwiftVersion, as: error)
func fetchPosition() -> (x: Double, y: Double, z: Double) {
return self.rotation
}
3:47 - Clarifying code with module selectors
import Rocket
import GiftShopToys
let rocket1 = SaturnV() // could mean `Rocket::SaturnV` or `GiftShopToys::SaturnV`
let rocket2 = Rocket.SaturnV() // prefers `Rocket::Rocket.SaturnV`
let rocket3 = Rocket::SaturnV() // correctly finds `Rocket::SaturnV`
5:00 - Clarifying code with module selectors (module selectors work on members, too)
//
// Module Chemistry
//
public protocol Flammable { ... }
extension Flammable {
/// Set `self` on fire.
public func fire() { ... }
}
//
// Module HumanResources
//
import Chemistry
public protocol Employee { ... }
extension Employee {
/// Remove `self` from job.
public func fire() { ... }
}
public class LaunchPadTechnician: Employee, Flammable { ... }
//
// Module main
//
import HumanResources
import Chemistry
let launchPadTechnician = LaunchPadTechnician(...)
launchPadTechnician.HumanResources::fire()
6:26 - Task cancellation
// Radio for help
extension Radio {
func send(_ data: [UInt8] {
if Task.isCancelled { return }
// ...
}
}
extension EmergencyTransponder {
func sendSOS() {
radio.send(makeSOSPacket())
}
}
6:40 - Task cancellation shield
// Radio for help
extension Radio {
func send(_ data: [UInt8] {
if Task.isCancelled { return }
// ...
}
}
extension EmergencyTransponder {
func sendSOS() {
withTaskCancellationShield {
radio.send(makeSOSPacket())
}
}
}
6:53 - Constructing a new dictionary
// Map values with keys
func makeCalendarDisplayNames(for missions: [Mission: LaunchWindow]) -> [Mission: String] {
let new: [Mission: String] = .init(
uniqueKeysWithValues: missions.lazy.map { mission, launchWindow in
(mission, makeDisplayName(for: mission, in: launchWindow))
}
)
return new
}
7:06 - Dictionary.mapKeyedValues
// Map values with keys
func makeCalendarDisplayNames(for missions: [Mission: LaunchWindow]) -> [Mission: String] {
missions.mapKeyedValues { mission, launchWindow in
makeDisplayName(for: mission, in: launchWindow)
}
}
7:14 - The new FilePath type
// FilePath handling macOS-named resources
var path: FilePath = "/var/www/static"
path.components.append("WWDC")
print(path.components)
// [ "var", "www", "static", "WWDC" ]
var path: FilePath = "/var/www/static/..namedresource/rsrc"
print(path.components)
// [ "var", "www", "static" ]
7:41 - Issue Severity
// Issue severity
(arguments: allRockets)
func testBurn(rocket: Rocket) throws {
rocket.burn(for: .seconds(150))
let remaining = rocket.propellantKg / rocket.totalPropellantKg
if remaining < 0.10 {
Issue.record(
"\(rocket.name) remaining fuel is below 10% reserve target",
severity: .warning
)
}
#expect(remaining > 0.02, "\(rocket.name) propellant critically low - abort")
}
7:52 - Test Cancellation
// Test Cancellation
(arguments: allRockets)
func testBurn(rocket: Rocket) throws {
// solid-fuel rocket engines can't be stopped
if rocket.engineType == .solid {
try Test.cancel("\(rocket.name) has solid fuel")
}
rocket.burn(for: .seconds(150))
let remaining = rocket.propellantKg / rocket.totalPropellantKg
if remaining < 0.10 {
Issue.record(
"\(rocket.name) remaining fuel is below 10% reserve target",
severity: .warning
)
}
#expect(remaining > 0.02, "\(rocket.name) propellant critically low - abort")
}
8:34 - XCTest interoperability: Using XCTest from Swift Testing
// XCTest interoperability: Using XCTest from Swift Testing
func checkedTransmitAndReceive(on radio: Radio,
packet: Packet,
expectedByteCount: Int) throws -> [UInt8] {
try radio.transmit(bytes: packet.data)
let bytes = try radio.receive()
XCTAssertEqual(bytes.count, expectedByteCount)
return bytes
}
func pingTest() throws {
let radio = Radio()
let bytes = try checkedTransmitAndReceive(on: radio, packet: .ping, expectedByteCount: 8)
#expect(bytes == [0x00, 0x00, 0xf0, 0x37, 0x0f, 0xc7, 0x00, 0x01])
}
8:48 - XCTest interoperability: Using Swift Testing from XCTest
// XCTest interoperability: Using Swift Testing from XCTest
class RadioTests: XCTestCase {
func testPingPacketTransmission() {
let radio = Radio()
let bytes = try checkedTransmitAndReceive(on: radio,
packet: .ping,
expectedByteCount: 8)
#expect(bytes == [0x00, 0x00, 0xf0, 0x36, 0x0f, 0xc7, 0x00, 0x02])
}
}
10:01 - Subprocess Output Stream
// Subprocess output streaming
let result = try await Subprocess.run(.name("ls"),
input: .none,
output: .sequence,
error: .string(limit:4096)) { execution in
execution.standardOtput.strings().filter { $0.hasSuffix(".obj") }
}
for try await objectFiles in result.closureOutput {
print("Object file: \(objectFile)")
}
10:37 - Progress Manager - Concurrency
// Progress reporting - Concurrency
let manager = ProgressManager(totalCount: 100)
try await rocket.launch(mission.subprogress(assigningCount: 100))
extension Rocket {
func launch(_ progress: consuming Subprogress? = nil) async throws {
let stage = progress?.start(totalCount: 3)
try await ignite(); stage?.complete(count: 1)
try await liftoff(); stage?.complete(count: 1)
try await stageSeparation(); stage?.complete(count: 1)
}
}
10:37 - Progress Manager - progress reporting
// Progress reporting - progress reporting
let manager = ProgressManager(totalCount: 100)
try await rocket.launch(mission.subprogress(assigningCount: 100))
Task {
for await update in Observations({ mission.fractionCompleted }) {
print("🚀 Mission \(Int(update * 100))%")
}
}
10:37 - Progress reporting - metadata
// Progress reporting - metadata
extension Rocket {
func ascend(_ progress: consuming Subprogress) async throws {
let stage = progress.start(totalCount: 3)
stage.detlaV = 3_400; try await burn(); stage.complete(count: 1)
stage.detlaV = 2_100; try await stageSeparation(); stage.complete(count: 1)
stage.detlaV = 1_800; try await coast(); stage.complete(count: 1)
}
}
print("Δv to orbit: \(mission.summary(of: \.deltaV)) m/s")
20:56 - Directly control inlining (source code)
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = makeInts(randomized: false)
for value in values {
result[Int(value)] += 1
}
return result
}
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
}
21:01 - Directly control inlining (inlined, but not optimized)
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = if false { //
InlineArray { _ in Int.random(in: (.min)...(.max)) } //
} else { // Inlined code
InlineArray(repeating: 0) //
} //
for value in values {
result[Int(value)] += 1
}
return result
}
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
}
21:07 - Directly control inlining (inlined and optimized)
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = InlineArray(repeating: 0) // Inlined and optimized code
for value in values {
result[Int(value)] += 1
}
return result
}
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
}
21:30 - Directly control inlining (preventing inlining)
(never)
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
}
21:39 - Directly control inlining (forcing inlining)
(always)
func makeInts(randomized: Bool) -> [256 of Int] {
if randomized {
InlineArray { _ in Int.random(in: (.min)...(.max)) }
} else {
InlineArray(repeating: 0)
}
}
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = makeInts(randomized: false)
for value in values {
result[Int(value)] += 1
}
return result
}
// Note: Specialized function doesn't actually have a directly callable name.
func `histogram of [UInt8]`(of values: [UInt8]) -> [256 of Int] { //
var result = makeInts(randomized: false) //
//
for value in values { //
result[Int(value)] += 1 // Specialized code
} //
//
return result //
} //
(where Values == [UInt8])
func histogram<Values>(of values: Values) -> [256 of Int] where Values: Sequence<UInt8> {
var result = makeInts(randomized: false)
for value in values {
result[Int(value)] += 1
}
return result
}
// Note: Specialized function doesn't actually have a directly callable name.
func `histogram of [UInt8]`(of values: [UInt8]) -> [256 of Int] { //
var result = makeInts(randomized: false) //
//
for value in values { //
result[Int(value)] += 1 // Specialized code
} //
//
return result //
} //
25:46 - Associated types can be '~Copyable' and '~Escapable'
protocol Iterable<Element, Failure>: ~Copyable, ~Escapable {
associatedtype Element: ~Copyable
associatedtype IterableIterator: IterableIteratorProtocol<Element, Failure>, ~Copyable, ~Escapable
associatedtype Failure: Error = Never
func makeIterableIterator() -> IterableIterator
var underestimatedCount: Int { get }
}
protocol IterableIteratorProtocol<Element, Failure>: ~Copyable, ~Escapable {
associatedtype Element: ~Copyable
associatedtype Failure: Error = Never
mutating func nextSpan(maximumCount: Int) throws(Failure) -> Span<Element>
mutating func skip(by maximumOffset: Int) throws(Failure) -> Int
}
27:28 - The problem with existing accessors
public struct UniqueBox<Value>: ~Copyable {
private let valuePointer: UnsafeMutablePointer<Value>
public init(_ value: consuming Value) {
valuePointer = UnsafeMutablePointer.allocate(capacity: 1)
valuePointer.initialize(to: value)
}
public var value: Value {
get { valuePointer.pointee }
set { valuePointer.pointee = newValue }
}
deinit {
valuePointer.deinitialize(count: 1)
valuePointer.deallocate()
}
}
28:19 - 'borrow' and 'mutate' accessors
public struct UniqueBox<Value: ~Copyable>: ~Copyable {
private let valuePointer: UnsafeMutablePointer<Value>
public init(_ value: consuming Value) {
valuePointer = UnsafeMutablePointer.allocate(capacity: 1)
valuePointer.initialize(to: value)
}
public var value: Value {
borrow { valuePointer.pointee }
mutate { &valuePointer.pointee }
}
deinit {
valuePointer.deinitialize(count: 1)
valuePointer.deallocate()
}
}
30:14 - Using 'MutableRef' to eliminate repeated accesses (with un-hoisted access)
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
for set in sets {
if set.contains(key) {
counts[key, default: 0] += 1
}
}
}
30:34 - Using 'MutableRef' to eliminate repeated accesses (hoisted by 'inout' parameter)
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
func updateCountImpl(count: inout Int) {
for set in sets {
if set.contains(key) {
count += 1
}
}
}
updateCountImpl(count: &counts[key, default: 0])
}
30:41 - Using 'MutableRef' to eliminate repeated accesses (hoisted by 'MutableRef')
func updateCount<Key: Hashable>(
for key: Key,
from sets: [Set<Key>],
in counts: inout [Key: Int]
) {
var countRef = MutableRef(&counts[key, default: 0])
for set in sets {
if set.contains(key) {
countRef.value += 1
}
}
}
A preview of the session's four topics: language improvements, library updates, cross-platform support, and performance tuning in Swift 6.3 and 6.4.
Quality-of-life changes land in Swift 6.4, including optional parentheses removal, concurrency task warnings, weak let, ~Sendable, and a new memberwise initializer.
Swift now lets you condense multi-platform availability attributes into a single anyAppleOS condition, reducing boilerplate across iOS, macOS, watchOS, and more.
The new @diagnose attribute gives fine-grained control over warnings within a specific declaration, letting you suppress, enable, or promote them to errors.
Swift 6.3 introduces double-colon module selector syntax to unambiguously reference a type or member from a specific module when name conflicts arise.
Updates across four key libraries: the standard library, Swift Testing, Subprocess, and Foundation.
New additions include a task cancellation shield, mapKeyedValues for dictionaries, and a cross-platform FilePath type.
Swift Testing gains configurable issue severity, dynamic test cancellation, flaky test repetition, and improved two-way interoperability with XCTest.
Subprocess reaches 1.0 with a refined API, improved error handling, convenient line-by-line output streaming, and expanded cross-platform support.
Foundation gains a new ProgressManager type and continues its Swift migration, with performance improvements to Data, NSURL, and CFURL.
Swift 6.4 extends language interoperability and broadens its reach to new environments including web, Android, and embedded devices.
The new @C attribute lets you expose Swift functions directly to C, enabling safe, incremental migration of C codebases to Swift.
The Swift-Java package now supports async and throwing Swift functions from Java, constrained extensions, and conforming Java classes to Swift protocols.
The Swift VSCode extension adds Swiftly integration for toolchain management and is now available on the OpenVSX marketplace for editors like Cursor and VSCodium.
Swift can now compile to WebAssembly, with JavascriptKit improvements delivering up to 40x faster safe bridging between Swift and JavaScript.
Embedded Swift expands its language subset with existential types, untyped throws, and improved DWARF debug info for coredump debugging on constrained hardware.
Two areas of advanced performance work: explicit optimizer control and extensions to the ownership system to prevent unnecessary copying.
New @inline(always) and @specialized attributes give you direct control over the compiler's inlining and generic specialization decisions.
Equatable, Comparable, Hashable, and associated types now work with noncopyable and non-escapable types, broadening the ownership system's reach.
A new Iterable protocol enables efficient borrow-based for loops over noncopyable elements, while new borrow and mutate accessors eliminate costly copies in computed properties.
The standard library gains UniqueBox, UniqueArray, Continuation, and the new Ref/MutableRef types for safe, high-performance ownership patterns.
Highlights open-source progress including Swift Build, new workgroups for build, networking, Windows, and Android, and an invitation to participate at forums.swift.org.