스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
Xcode 16의 새로운 기능
Xcode 16의 생산성 및 성능 관련 최신 개선 사항을 알아보고, 코드 완성, 진단 및 Xcode 미리보기 관련 개선 사항도 알아보세요. 빌드의 업데이트와 디버깅 및 Instruments의 개선 사항을 확인해 보세요.
챕터
- 0:00 - Introduction
- 0:29 - Updates in editing
- 0:33 - Code completion
- 1:01 - Adopting Swift 6 data-race safety guarantees
- 2:49 - Improvements to Previews
- 6:22 - Updates in builds
- 6:26 - Explicit modules
- 7:15 - Package resolution
- 7:15 - Package resolution
- 8:30 - What's new in debugging
- 8:35 - Build process and debugging
- 9:07 - Thread performance checker
- 9:23 - The organizer
- 12:16 - The RealityKit debugger
- 12:55 - Meet Swift Testing
- 18:42 - What's new in Instruments
- 19:44 - Meet the flame graph
- 21:38 - Wrap up
리소스
관련 비디오
WWDC24
-
다운로드
안녕하세요, 저는 Daisy입니다 동료인 Jake와 함께 Xcode 16의 몇 가지 새로운 기능을 보여 드리겠습니다 바로 시작하겠습니다 Xcode는 앱 개발의 모든 단계에서 사용할 수 있습니다 Swift 코드, 빌드 실행, 디버깅 코드 테스트 및 실적 최대화를 위한 Xcode 16의 새로운 기능을 소개해 드리겠습니다 코드를 편집할 때 사용할 수 있는 3가지 새 기능부터 알려드릴게요
첫째, 코드 완성 기능이 더 빈틈없는 코드를 추천합니다 Swift 및 Apple SDK에 맞춰 특별히 훈련된 온디바이스 코딩 모델 덕분이죠
코드 완성은 함수 이름, 주석과 같은 주변 코드 맥락을 활용하여 개발자의 아이디어를 빨리 처리하죠
이는 모두 Apple Silicon 덕분에 가능하며 macOS Sequoia에서 Xcode 16을 실행할 때 사용할 수 있습니다
이제 Swift를 살펴보겠습니다 Swift 6는 동시성 안전성을 보장하는 새 언어 모드를 지원하죠 이 모드는 일반적으로 런타임에만 발생하는 데이터 경합을 컴파일 타임 문제로 전환합니다 코드의 정확성과 안전성을 효과적으로 개선할 수 있죠 이 모드를 활용하려면 Swift 6의 언어 모드를 채택해야 합니다 하지만 출시될 각 언어 기능에 대한 경고를 점진적으로 활성화하면 바로 Xcode 16에서 모드를 사용할 수 있죠 그 방법을 알려드릴게요
저는 로봇이 식물을 심는 앱인 BOTanist를 개발하고 있습니다 이 앱을 Swift 6에서 사용하기 위해 준비하고자 합니다 Build Settings의 ‘Swift Compiler - Upcoming Features‘ 섹션으로 이동하여 시작하겠습니다
여기에서 출시될 컴파일러 기능을 하나씩 활성화할 수 있죠
‘Isolated Global Variables‘를 Yes로 설정하겠습니다
빌드하면 Xcode의 문제 탐색기에서 새 경고가 표시됩니다
전역 변수인 logger의 동시성이 안전하지 않음을 알려주죠
문제를 클릭하면 logger가 정의된 위치로 이동합니다
logger를 변경하지 않아도 되므로 var에서 let으로 변경할게요 이렇게 하면 데이터 경합으로부터 보호되죠
출시될 기능을 채택하면 Swift 6로 전환하기 전에 잠재적인 문제를 파악 및 해결할 수 있죠 오류가 경고로 표시되므로 문제를 파악할 수 있습니다
준비가 되면 Build Settings에서 Swift 언어 버전을 설정하세요
자세한 내용은 ‘Swift 6으로 앱을 마이그레이션하기’에서 확인하세요
Xcode 16의 미리보기 또한 상당히 개선되었습니다
미리보기를 사용하면 UI에서 빠르게 반복하여 멋진 사용자 인터페이스를 만들 수 있죠 작성하기 쉽고 재사용이 가능하며 모델과 더 잘 통합되는 2개의 새 API가 추가되었습니다
첫 번째는 Previewable 매크로입니다 Previewable는 State와 같은 속성 래퍼에 첨부할 수 있으므로 미리보기 블록 내에서 API를 바로 사용할 수 있습니다 더 이상 래퍼 보기를 작성하지 않아도 됩니다 앱에서 예시를 보여드리겠습니다
RobotFaceSelectorView입니다 Binding을 로봇 얼굴에 적용하죠
미리보기를 생성할 때 State를 미리보기의 본문에서 바로 정의하고 Previewable 매크로를 첨부할 수 있습니다 이렇게 하면 미리보기 시스템이 알아서 래퍼 보기를 생성해 주므로 제가 직접 만들지 않아도 됩니다
그런 다음 보기를 추가할 수 있습니다
단 몇 줄 만으로 UI를 실시간으로 볼 수 있죠
두 번째의 새 API는 PreviewModifier입니다 이를 통해 쉽게 미리보기 관련 경험 또는 데이터를 공유할 수 있죠 이렇게 하면 중복 코드를 줄이고 미리보기 시스템에서 데이터를 캐시할 수 있습니다 보여드리기 위해 다른 보기로 전환할게요 RobotNameSelectorView가 작동하려면 RobotNamer가 필요해요
이 유형은 가능한 로봇 이름을 서버에서 비동기식으로 가져옵니다 하지만 미리보기를 빌드하는 동안 서버를 계속해서 히트할 필요는 없죠 외부 서버를 히트하는 대신 PreviewModifier로 모든 미리보기를 위한 RobotNamer를 만들 수 있습니다
먼저 PreviewModifier를 준수하는 유형을 정의합니다
이 프로토콜에는 2가지 요구 사항이 있습니다 우선 makeSharedContext로 데이터를 로드 및 저장해야 합니다 이 메소드는 비동기식이며 예외 처리를 넘기므로 데이터를 비동기식으로 로드하고 오류를 처리할 수 있습니다 이 경우 로컬 파일의 이름으로 RobotNamer를 생성하여 외부 서버를 히트하지 않겠습니다
핵심은 이 메소드가 같은 유형의 모든 한정자에 대해 한 번만 호출된다는 것입니다 미리보기 시스템이 이 데이터를 캐시해 주기 때문이죠
두 번째 요구 사항은 본문 메소드입니다 본문 메소드로 미리보기를 공유된 컨텍스트로 래핑할 수 있죠 제 방식대로 environment 한정자로 RobotNamer를 전달하겠습니다
한정자를 정의했으므로 trait를 통해 미리보기에 제공하기만 하면 되죠 하지만 저는 이 한정자를 자주 사용할 예정이므로 호출할 위치에서의 코드를 줄이기 위해 PreviewTrait에 대한 확장을 정의하려고 합니다 이제 미리보기를 만드는 일만 남았습니다
이번 사례에서는 비교적 간단하지만 미리보기 간에 데이터를 공유해야 하는 경우 PreviewModifier가 특히 유용하죠 SwiftData의 ModelContainer를 사용하는 경우처럼요
API를 넘어 올해는 미리보기의 성능과 기반이 비약적으로 발전했습니다 새 실행 엔진을 통해 미리보기가 그 어느 때보다 빨라졌습니다
컴파일러, 빌드 시스템 및 운영 체제의 발전을 기반으로 하는 미리보기는 프로젝트에 동일한 빌드 제품을 사용하여 더 이상 별도의 사본을 만들 필요 없이 프로그램을 즉시 재조립합니다 미리보기는 올해 빌드에서 개선된 기능 중 하나입니다 다른 주제에 대해서는 Jake가 설명해 드리겠습니다 Daisy, 고마워요 빌드에 대해 설명해 드리겠습니다
Xcode 16은 명시적 모듈로 빌드를 상당히 개선해 줍니다 이 기능은 향상된 병렬 처리 개선된 진단 및 빠른 디버깅을 제공합니다 단 한 줄의 코드를 변경할 필요 없이요
정말 놀랍죠 어떻게 하면 사용할 수 있을까요 C 및 Objective-C에서는 명시적 모듈이 기본적으로 활성화되어 있습니다 Swift에서는 활성화해야 합니다 직접 보여드릴게요
Build Settings로 이동하겠습니다
Explicitly Built Modules를 활성화한 다음
빌드를 시작할게요
음 참고로 Xcode 16의 Swift 패키지 통합 기능이 개선되어 패키지 확인이 먼저 완료될 때까지 기다릴 필요 없이 해당 빌드를 대기열에 추가할 수 있었습니다 마음에 드네요 실제로 어떻게 작동할까요 명시적 모듈을 사용하면 Xcode가 각 컴파일 단위의 처리를 스캔, 모듈 빌드
최종적으로 원본 코드 빌드 등 세 단계로 나눕니다
이 단계 중 처음 두 단계는 이제 빌드 로그에서 ‘Scan dependencies‘ ‘Compile Clang module‘ 또는 ‘Compile Swift module‘ 명렁어로 표시되죠 이전에는 이러한 작업이 소스 파일 컴파일의 일부로 암시적으로 수행되었습니다 이제는 빌드에 대한 자세한 분석과 개선된 병렬 처리를 사용할 수 있을 뿐만 아니라 모듈 문제로 인해 빌드가 실패하는 경우 더 명확한 오류 메시지를 받을 수 있습니다
명시적 모듈도 빌드 타임라인에 반영되므로 빌드 프로세스 중 어디에 시간이 소비되고 있는지 쉽게 확인할 수 있으며 빌드를 최적화하는 데도 도움이 됩니다
자세한 내용은 ‘명시적으로 빌드된 모듈 쉽게 이해하기’에서 확인하세요
좋습니다, 코드를 작성 및 빌드했으니 디버그하죠
하지만 먼저 빌드 프로세스가 디버깅 개선에 도움이 되는 몇 가지 방법에 대해 말씀드리겠습니다 명시적 모듈을 사용하면 디버그가 빨라집니다 표현식을 평가할 때 lldb가 빌드 출력을 재사용할 수 있기 때문이죠 이제 macOS Sequoia 또는 iOS 18의 배포 대상에 대해 빌드할 때 기본 디버그 심볼 형식이 DWARF5가 됩니다 DWARF5 사용 시 dSYM 번들이 작아지며 심볼 검색이 빨라집니다
스레드 성능 검사기는 Xcode 16에서 실행될 때 더 많은 작업을 수행해요 메인 스레드 hang과 우선순위 역전을 찾는 것과 더불어 과도한 디스크 쓰기 및 느린 앱 실행 진단을 표시해 주므로 앱의 성능을 개선하는 데 도움이 됩니다
Organizer에서는 앱 실행에 대한 새 진단 로그 카테고리가 제공되죠 고객 기기에서 앱을 실행하는 데 시간이 오래 걸리는 경우 Xcode에서 가장 느린 코드 경로 서명을 표시하므로 가장 큰 영향을 미치는 문제를 우선 수정할 수 있습니다
디스크 쓰기 진단을 자주 사용한다면 이 업데이트가 마음에 드실 것입니다 이제 Organizer에서 앱의 여러 버전에서 문제의 영향이 어떻게 달라졌는지 파악할 수 있습니다
Disk Writes 보기로 이동하면 일부 서명 옆에 위쪽 화살표가 있는 것을 볼 수 있습니다 이 화살표부터 시작하는 것이 좋습니다 사용자에게 가장 큰 영향을 미치는 문제를 우선 처리할 수 있죠
코드를 디버그하는 동안 스레드 성능 검사기는 가장 심각한 문제를 런타임 문제로 표시합니다 집중해야 할 코드 행을 정확히 찾아냅니다 문제가 로컬에서 재현되지 않는 경우에도 마찬가지죠
이 예시에서 스레드 성능 검사기가 고객에게 영향을 미치는 문제를 표시했습니다 메인 스레드에서 이 동영상 애셋을 로드하면 안 된다고 알려줍니다
이 문제를 추적하기 위해 Xcode에서 중단점을 설정할게요 문제가 있는 파일을 연 다음
거터를 클릭하여 중단점을 설정한 다음 실행하겠습니다
좋습니다, 중단점에 도달했습니다 이 호출의 출처를 더 쉽게 추적할 수 있도록 디버그 막대에서 Unified Backtrace 보기를 활성화하겠습니다
이 새로운 시각화에서 아래로 스크롤하기만 하면 호출 스택을 따라가며 각 프레임의 주변 코드를 볼 수 있습니다
변수에 마우스를 올려 값을 확인할 수도 있죠
이 보기 본문 메소드에 해당 코드를 호출하는 ‘await’가 있네요
호출을 추적해 보니 앱 내부에서 발생했음을 알 수 있습니다
이 동영상 플레이어 시작 함수는 비동기식이고 SwiftUI 작업 한정자에서 호출되고 있는 것처럼 보이지만 둘러싸고 있는 SwiftUI 뷰에서 @MainActor 컨텍스트를 상속하고 메인 스레드에서 I/O를 수행합니다 nonisolated로 표시하여 이 문제를 해결하겠습니다
잘 된 것 같습니다
Unified Backtrace 보기와 스레드 성능 검사기 및 Organizer의 새로운 보고 기능 덕분에 Xcode로 작업할 때 가장 중요한 사항에 집중할 수 있습니다 공간 개발도 상당히 쉬워졌습니다 새로운 RealityKit 디버거 덕분이죠 이제 버튼을 한 번만 클릭하면 실행 중인 앱의 엔티티 계층 구조의 스냅샷을 캡처하고 Xcode 내에서 바로 3D로 탐색할 수 있습니다 이 디버거를 사용하면 엔티티와 관련 구성 요소를 확인하고 내장 및 커스텀 속성을 모두 검사할 수 있습니다
RealityKit 디버거나 기존 디버거에 대해 자세히 알아보려면 ‘RealityKit 디버거 자세히 알아보기’와 ‘실행, 분석, 검사: LLDB의 효과적인 디버깅 알아보기’를 확인하세요
이제 테스트와 관련하여 Daisy가 설명해 드리겠습니다
디버그에 대해 설명해 줘서 고마워요, Jake 개발 중에 발생하는 문제를 해결하는 데 디버그가 유용했다면 테스트는 향후 반복 작업에서 문제 및 회귀를 포착하는 데 효과적이죠
Swift Testing은 Swift 언어 기능을 활용하여 테스트를 더욱 강력하고 간결하게 표현할 수 있는 새로운 프레임워크입니다 이러한 테스트는 기존 XCTest와 함께 사용할 수 있죠 Swift Testing의 예시를 보여드리겠습니다
BOTanist에 새 기능을 추가하려고 합니다 로봇의 식물 심기 스타일과 애니메이션 상태를 도입하려고 하죠 하지만 아직 하나도 테스트하지 않았으므로 새 테스트를 추가하죠
먼저 테스트 폴더에 파일을 만들고 이름을 PlantTests로 지을게요
테스트를 만들기 위해 우선 앱과 Testing 프레임워크를 가져온 다음 plantingRoses라는 함수를 작성하겠습니다 함수 이름은 원하는 대로 지정할 수 있습니다 Swift Testing의 이점을 모두 활용하려면 Test 매크로를 추가하면 됩니다
Test 매크로를 추가하자마자 Xcode는 이것이 테스트임을 인식하고 탐색기에 표시하죠
이 테스트에서는 장미의 기본 심기 스타일이 접목인지 확인하고 싶습니다 우선 type가 rose인 Plant를 만들겠습니다
그런 다음 다시 type가 rose인 Plant를 만들되 style을 접목을 뜻하는 graft로 명시하겠습니다
좋아요, 제가 원하는 대로 되었습니다 마지막으로 expect 매크로로 두 코드가 동일한지 확인할게요 이 매크로는 모든 불리언 표현식을 인식하므로 string, float 등의 형식을 확인하는 데 사용할 수 있죠
이제 테스트를 실행해 보겠습니다
아, 안타깝게도 테스트가 실패했네요 테스트 결과 두 장미가 동일하지 않은 것으로 나타났습니다 하지만 오류 설명에 따르면 둘은 확실히 비슷합니다 둘 다 동일한 이모티콘을 표시하니까요 하지만 다른 속성은 어떻게 나타나는지 알 수 없습니다
Swift Testing을 사용하면 오류에서 ‘Show Details’를 클릭하여 각 값에 대한 추가 정보를 볼 수 있습니다 자세히 살펴보겠습니다 아, 식물 심기 스타일이 다르네요 기본값 확인을 위한 테스트를 작성했으므로 빠르게 수정할 수 있죠
.seedling을 .graft로 수정해야 합니다
Quick Actions로 테스트를 다시 실행하여 통과하는지 보죠 Quick Actions를 실행하려면 단축키인 Command Shift A를 누릅니다
그다음 ‘test again’을 입력하여 테스트를 다시 실행합니다
좋습니다, 통과했어요 test 매크로는 함수만 테스트로 표시하지 않습니다 함수에 정보를 추가하거나 함수의 작동 방식을 바꿀 수 있는 특성도 처리하죠 예로, 디스플레이 이름이나 인수를 테스트 함수에 할당할 수 있죠
이 테스트에서는 로봇의 AnimationState 머신을 검사합니다
이 상태가 celebrate로 전환되는지 확인하려고 합니다 여러 테스트 함수를 작성하는 대신 상태의 목록을 test 매크로에 제공합니다 그리고 상태를 매개변수로 받는 하나의 함수를 사용하면 됩니다
이 매개변수가 적용된 테스트를 실행하겠습니다
Swift Testing에서는 각 인수가 별도의 테스트로 동시에 실행되죠 탐색기는 각 테스트를 개별로 표시하므로 어떤 변형이 실패했는지 알 수 있습니다 식물의 AnimationState가 테스트를 실패한 것으로 보이네요 .celebrate를 유효한 상태 전환으로 포함하는 것을 잊었으니 해결할게요
실패한 테스트 옆에 있는 버튼을 클릭하여 그 테스트만 실행할게요
훌륭합니다 모든 테스트를 통과했습니다 Xcode의 Swift Testing은 또한 태그 기반 구성을 제공합니다 태그를 만들어 여러 Suite에 걸쳐 테스트를 그룹화할 수 있죠 어떤 테스트가 관련되어 있는지 알아보기 위해 이 기능을 활용해 볼게요 먼저 Tag 유형을 확장하여 planting 커스텀 태그를 포함할게요
그다음 레이블을 지정할 테스트의 Test 매크로에 태그를 추가합니다 이 Test에 추가해 보겠습니다
plantingRoses에도 추가할게요
적용된 태그가 탐색기의 태그 보기에 그룹화되어 표시되죠
태그의 모든 테스트를 실행하려면 옆에 있는 버튼을 클릭하면 됩니다 또한 태그를 통해 테스트 계획에서 테스트를 포함 및 제외할 수 있죠
탐색기 상단에서 ‘Edit Test Plan’을 선택하여 테스트 계획으로 이동할게요
새로운 식물 심기 및 애니메이션 기능은 아직 개발 중입니다 이러한 테스트 때문에 CI가 불안정해지는 것을 원치 않죠 이 기능을 활성화할 준비가 될 때까지 이 기능의 태그를 Excluded Tags 목록에 추가할게요
Swift Testing을 자세히 알아보려면 ‘Swift Testing 소개’와 ‘Swift Testing으로 테스트 심화하기’를 확인하세요
코드를 디버그하고 테스트했습니다 테스트는 빠르게 실행되지만 앱은 그렇지 않네요, Jake에게 넘길게요 Daisy, 고마워요 테스트를 통해 앱의 기능을 확인했습니다 하지만 앱을 실행하면 시작하는 데 예상보다 훨씬 많은 시간이 소요됩니다
Instruments는 성능 문제를 진단하는 데 효과적입니다 Xcode의 Profile 작업에서 바로 접근할 수 있죠 Time Profiler Instrument를 통해 Daisy가 기록한 앱 시작 추적이 있습니다 이 Instrument는 코드의 CPU 사용량을 시각화하고 앱을 실행하는 데 소요되는 시간을 측정합니다 이제 살펴보겠습니다
와 CPU를 많이 사용하네요 앱을 시작할 때 정말 긴 hang이 발생하네요, 해결해 볼게요 이 현상이 발생하는 이유를 이해하려면 Inspection Range를 Hang 간격에 설정하여 시작하겠습니다
지금 추적의 일부만 보고 있으므로 데이터를 분석해 볼게요
문제의 범위를 줄이기 위해 Instruments 16의 새로운 Flame Graph를 사용할게요 Jump Bar에서 활성화할 수 있죠 Flame Graph는 추적 실행에 대한 우수한 개요로 문제를 한눈에 파악할 수 있게 해줍니다 실행 간격은 추적에서 차지한 시간 비율에 따라 가중치가 부여되며 간격은 왼쪽에서 오른쪽으로 정렬됩니다 즉, 그래프의 왼쪽 부분은 항상 가장 많이 실행된 코드를 시각화합니다 프로그램이 무엇을 실행하고 있는지 살펴보겠습니다
이 load 함수가 실행 시간의 대부분을 차지하고 있네요
SwiftUI 보기 본문에서 호출되고 있습니다 문제가 있네요
이 프레임에서 오른쪽 클릭하고 ‘Reveal in Xcode’를 선택하면 코드로 바로 이동할 수 있습니다
어디에 문제가 있는지 알겠네요 여러 개의 에셋을 루프에서 연속적으로 로드하고 있는데 메인 스레드에서 이를 수행하고 있습니다, 좋지 않네요 이 문제를 해결하기 위해 작업 그룹으로 로드를 병렬화하여 이 작업의 실행을 백그라운드로 옮기겠습니다
이제 다시 빌드한 다음 Instruments에서 새 Time Profile을 만들어 해결되었는지 볼게요
좋아요 앱이 정말 빠르게 시작됩니다 Flame Graph를 통해 애셋이 여러 백그라운드 스레드에 분산되어 더 이상 메인 스레드를 막지 않는다는 것을 알 수 있습니다
Flame Graph는 호출 트리를 사용하는 모든 Instrument에 대해 작동합니다 호출 스택을 시각적으로 표시하므로 문제를 쉽게 찾을 수 있습니다 앱을 정기적으로 검사하는 데도 유용합니다
이는 Xcode 16의 신나는 새 기능 중 일부에 불과합니다 오늘 다룬 기능이나 빨라진 UI 미리보기, 강화된 C++ 런타임 개선된 현지화 작업 흐름과 같은 다른 기능에 대해 자세히 알아보려면 developer.apple.com/kr에서 릴리즈 노트를 확인하세요 Xcode 16을 다운로드하여 직접 사용해 보면 더욱더 좋습니다 시청해 주셔서 감사합니다 WWDC를 즐기세요
-
-
3:37 - Inline State within Preview
#Preview { @Previewable @State var currentFace = RobotFace.heart }
-
3:45 - View using Inline State
RobotFaceSelectorView(currentFace: $currentFace)
-
3:53 - Complete Preview using Previewable
#Preview { @Previewable @State var currentFace = RobotFace.heart RobotFaceSelectorView(currentFace: $currentFace) }
-
4:40 - Type Conforming to PreviewModifier
struct SampleRobotNamer: PreviewModifier { typealias Context = RobotNamer static func makeSharedContext() async throws -> Context { let url = URL(fileURLWithPath: "/tmp/local_names.txt") return try await RobotNamer(url: url) } func body(content: Content, context: Context) -> some View { content.environment(context) } }
-
5:29 - Extension on PreviewTrait
extension PreviewTrait where T == Preview.ViewTraits { @MainActor static var sampleNamer: Self = .modifier(SampleRobotNamer()) }
-
5:38 - Preview using created PreviewModifier
#Preview(traits: .sampleNamer) { RobotNameSelectorView() }
-
10:26 - AVPlayer Creation
struct BOTanistAVPlayer { func player(url: URL) throws -> AVPlayer { let player = AVPlayer(url: url) return player } }
-
11:28 - AVPlayer Call Site
self.player = try? await robotVideoAVPlayer()
-
11:57 - AVPlayer Initialization
private nonisolated func robotVideoAVPlayer() async throws -> AVPlayer? { guard let url = Bundle.main.url(forResource: RobotVideo.resource, withExtension: RobotVideo.ext) else { throw BOTanistAppError.videoNotFound(forResource: RobotVideo.resource, withExtension: RobotVideo.ext) } let avPlayer = BOTanistAVPlayer() let player = try avPlayer.player(url: url) return player }
-
13:42 - Initial Test Scaffolding
import Testing @testable import BOTanist // When using the default init Plant(type:) make sure the planting style is graft @Test func plantingRoses() { // First create the two Plant structs // Verify with #expect }
-
14:36 - Complete Test
import Testing @testable import BOTanist // When using the default init Plant(type:) make sure the planting style is graft @Test func plantingRoses() { // First create the two Plant structs let plant = Plant(type: .rose) let expected = Plant(type: .rose, style: .graft) // Verify with #expect #expect(plant == expected) }
-
17:35 - Custom Tag
extension Tag { @Tag static var planting: Self }
-
17:42 - Tag Usage in @Test
.tags(.planting)
-
20:37 - Slow Asset Loading
for asset in allAssets { asset.load() }
-
20:54 - Fast Asset Loading
await withDiscardingTaskGroup { group in for asset in allAssets { group.addTask { asset.load() } } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.