[WWDC] WWDC24 추천 세션들 — 2탄

얼레벌레 돌아가는 덥덥 메모장 ~.~

naljin
31 min read5 days ago

들어가기 전에

개-하! 돌아왔슴니다! 덥덥 추천 2탄!!

사실 추천이라기 보단 역시나 철저한 제 흥미 위주로 본거 정리예요 ㅋㅎ.. 아시져? ㅎㅎ 아직 보려고 하는게 세개정도 남았지만 그건 각잡고 보려면 좀 시간이 걸릴것 같아서 우선 지금까지 본것들만 들고 왔습니당!

제목 옆에 붙어있는 이모지의 분류 기준은 아래와 같아여

⭐️ 따로 정리해볼만 함. 다시 보면 좋음.

❤️ 인터레스팅

👀 그냥저냥 봄

그럼 ㄱㄱㄱ!

Run, Break, Inspect: Explore effective debugging in LLDB 👀

  • 엑코에서 crashlog 파일을 열면, lldb가 이를 바탕으로 디버깅 세션을 생성함. 이 세션은 충돌이 발생한 시점의 코드와 상태를 시각적으로 보여줌. 또한 호출 스택(백트레이스)을 재구성하여, 충돌이 발생한 함수와 호출 경로를 파악할 수 있게 도와줌.
  • breakpoint 는 여러 코드 경로를 통해 중지될 수 있음.
  • b 명령어로 중단점을 설정하고 break command add 명령어로 breakpoint action 을 추가 할 수 있음. break modify 같은 명령어도 있음. 여기서 --condition 옵션을 통해 트리거 조건을 설정할 수도 있고, --ignore-count 옵션을 통해 몇번은 무시할지도 설정할 수도 있음. (엑코 인터페이스에서도 설정할 수 있긴함)
  • apropros 명령어로 키워드와 관련된 명령어를 찾을 수 있음
  • Action 에서 p 등을 통해 어떤 값을 출력할 수도 있지만, 새로운 브레이크포인트를 생성할수도 있음. (Ex. tbreak Importer.sswift:67. 여기서 tbreak 는 temporary brakpoint 라는 뜻으로, 해당 포인트에서 한번만 멈춤)
  • raise(SIGSTOP) 을 통해 디버거가 프로그램을 일시 정지시킬 수 있음. 이 시그널을 받은 프로세스는 즉시 실행을 멈추고 정지 상태가 되며, 디버거는 이 상태를 감지하여 마치 중단점에서 멈춘 것처럼 프로그램의 상태를 검사할 수 있음.
  • p 명령어 Xcode 15부터 통합된 "do what I mean" 명령어로 다양한 도구를 결합하여 제공. 이를 통해 변수 상태를 검사하고 표현식을 평가할 수 있음. 대부분의 상황에서 사용하기 적합함
  • Swift Error breakpoint 는 에러가 발생할 때 프로그램을 중지함. 엑스코드 Breakpoint navigator 좌측 하단에 + 누르면 추가할 수 있음.
  • Swift 6에서는 새로운 @DebugDescription 매크로를 통해 소스 코드에서 직접 p 명령어와 variable viewer 의 출력을 사용자 정의할 수 있는 메커니즘을 도입. 이 매크로와 함께 debugDescription 속성도 지정해야함.
  • CustomDebugStringConvertible protocol 에서 String interpolation 과 computed property 만 사용했다면, 이 매크로로 대체할 수 있음 (원래 protocol 썼으면 디버깅할때 po 명령어 썼을것)

Demystify explicitly built modules ⭐️

  • Xcode 16에서는 Swift와 Clang의 모듈 빌드 방식을, 기존의 암시적 모듈 빌드(Implicitly Built Modules)에서 명시적 모듈 빌드(Explicitly Built Modules)로 전환하여 빌드 과정을 개선
  • 모듈이란 라이브러리나 프레임워크의 인터페이스를 설명하는 코드 배포 단위. 모듈을 통해 컴파일러는 외부 코드를 이해하고 사용할 수 있음
  • Swift 모듈: 여러 Swift 파일로 구성되며, 각 파일의 인터페이스는 접근 제한자와 함께 .swiftinterface 파일에 표현됨.
  • Objective-C 모듈: 수작업으로 작성된 헤더와 모듈 맵에 의해 정의됨. 헤더는 모듈의 인터페이스를 제공하고, 모듈 맵은 모듈의 구조를 정의. 즉, 모듈이 어떤 헤더를 포함하고 있으며 어떤 모듈을 가져올 수 있는지 설명.
  • 모듈은 컴파일러가 서로 다른 소스 파일 간에 인터페이스 파싱을 공유할 수 있게 함. 이는 프로젝트 소스를 컴파일할 때 각 모듈을 개별적으로 컴파일러가 읽을 수 있는 바이너리 파일로 변환한 후, 해당 모듈이 참조될 때마다 그 모듈의 공용 인터페이스를 가져오도록 함으로써 이루어짐. Swift에서는 이 컴파일된 형식이 *.swiftmodule 파일로 표현되며, Clang에서는 *.pcm 파일 또는 precompiled module file 로 표현됨.
  • 모듈의 재사용이 이루어지려면 먼저 해당 모듈들을 찾아서 컴파일해야 함. 이 과정은 컴파일러가 import 문을 만나면, 그 import가 어떤 모듈을 가리키는지 먼저 찾아내고, 그 모듈의 컴파일된 표현을 가져옴.
  • 예를 들어 @import UIKit 을 만나면, 먼저 SDK 내에 있는 UIKit의 모듈 맵을 찾음. UIKit 모듈이 무엇인지 알게 되면, 이제 UIKit의 컴파일된 .pcm 파일을 찾음.
  • 근데 그 파일이 기존에 존재하지 않는다면?! -> 이때 모듈을 빌드하는 방식의 차이점이 Xcode 16 에서 등장.
  • 원래는 한 컴파일 작업이 모듈을 빌드하는 동안, 그 모듈이 필요한 다른 작업이 해당 모듈이 빌드될 때까지 대기해야 했음.
  • Xcode 16은 explicitly built modules 을 통해 이 문제를 해결함. 이는 모듈을 암시적으로 빌드하는 작업을 명시적인 빌드 시스템 작업으로 변경함.
  • 명시적 모듈 빌드는 각 소스파일의 컴파일을 다음과 같은 3단계로 나눔.
  1. 스캔: 각 소스 파일을 스캔하여 모듈 그래프를 구축. 이때 여러 타겟간에도 모듈을 공유할 수 있음. 이 작업들은 프로젝트의 각 소스 파일마다 한 번 실행되며, 캐시 가능.
  2. 모듈 빌드: 모듈 그래프를 구성하는 동안 Xcode는 모듈 컴파일 작업을 시작할 수도 있음. 이러한 작업은 지정된 모듈을 컴파일된 모듈 파일로 빌드함.
  3. 원래 소스코드 빌드: 모듈이 준비된 후, 컴파일된 모듈을 추가하여 의존하는 원래 컴파일 작업을 실행
  • 이러한 모듈 빌드 방식에는 빌드 안정성, 효율성 향상, 빌드 로그 투명성 등 여러가지 이점이 있는데, 그 중 하나가 Xcode에서 디버깅할 때 Swift 모듈을 디버거로 전달할 수 있다는 것. 기존에는 Xcode 빌드와 디버거는 완전히 별개의 모듈 그래프를 가지지만, 명시적으로 빌드되는 모듈을 사용하면 디버거가 이미 빌드된 모듈을 재사용할 수 있음. 즉, 디버거가 ‘p’ 또는 ‘po’를 사용하여 표현식을 평가할 때 Swift 유형에 대해 알아야 할 때 모듈을 다시 빌드하지 않음
  • Build Settings > Explicitly Built Modules > YES 로 설정하면 됨
  • Product > Perform Action > Build with Timing Summary 를 통해 빌드 성능에 대한 추가 정보 수집 가능
  • 한 모듈이 여러 번 빌드되는 경우도 있음. 예를 들어 서로 다른 소스 파일이 호환되지 않는 빌드 설정을 가지고 있어서 모듈 변형이 발생하는 경우가 있음. 예를 들어 C 파일과 Objective-C 파일은 일부 모듈을 매우 다르게 parse 함. 빌드 로그에 ‘modules report’라고 필터 입력해서 variants 확인할 수 있음.
  • 프로젝트와 타겟의 빌드 설정을 통합하여 모듈 변형(variants)을 최소화할 수도 있는데, 이렇게 하면 모듈 빌드가 통합되어 빌드 시간이 줄어듦.

Build custom swimming workouts with WorkoutKit ❤️

  • watchOS 11 의 WorkoutKit에서 pool swimming 지원됨!!
  • distance 와 time 을 poolSwimDistanceWithTime 에 전달해서 목표 (WorkoutGoal) 를 만듦.
  • 목표가 설정된 구간에서는 거리와 간격 시간이 화면 하단에 표시되고, 목표가 달성시 체크 표시가 됨.
  • 이를 통해 사용자가 수영 훈련을 더욱 효과적으로 계획하고 관리할 수 있게 도와줌
  • Ultra 모델은 물 온도도 표시 됨
// 목표
let distance: Measurement<UnitLength> = Measurement(value: 50, unit: .meters)
let distance: Measurement<UnitDuration> = Measurement(value: 1, unit: .minutes)
let distTime: WorkoutGoal = .poolSwimDistanceWithTime(distance, time)

// 웜업
let warmupStep = WorkoutStep(goal: .distance(200, .meters), displayName: "Kickboard")

// 인터벌
var workStpe = IntervalStep(.work)
work.step.goal = distTime
work.step.displayName = "FreeStyle"
var intervalBlock = IntervalBlock(steps: [workStep], iterations: 6)

// 쿨다운
let coolDownStep = WorkoutStep(goal: .distance(200, .meters), displayName: "Free Swim")

// 커스텀 워크아웃
let swimWorkout = CustomWorkout(activity: .swimming,
displayName: "Just Keep Swimming",
warmup: warmupStep,
blocks: [intervalBlock],
cooldown: cooldownStep)

Meet Swift Testing ❤️

  • 엑코 16 부터는 Testing System 의 기본 설정이 XCTest 대신 Swift Testing 임
  • @Test 속성은 이 함수가 테스트임을 나타내며, Xcode에서 이를 인식하고 실행 버튼을 제공.
  • 글로벌 function 이나 static / instance method 에 붙을 수도 있고, asyncthrows를 사용할 수 있으며, @MainActor 같은 글로벌 액터로 격리될 수 있음.
  • #expect 매크로를 사용해 예상 조건이 true 인지 확인. 실패 시 자세한 값을 캡처.
  • #require 매크로는 필수 조건을 확인하며, 조건이 false 경우 테스트를 중단하고 오류를 던짐. 옵셔널 값을 안전하게 언랩핑하거나, 필요한 값이 없을 때 테스트를 조기 종료하는 데 유용.
  • Trait 를 통해 관련 버그를 참조하거나 (.bug), custom 태그 (.tags)를 추가할 수 있음. 혹은 테스트 실행 조건을 제어하거나 (.enabled(if:), .disabled , @available), 실행 방식을 수정 (.timeLimit, .serialized) 할 수도 있음.
  • Suite 는 연관된 테스트 함수들과 (혹은 다른 Suite 와) 그룹화 하기 위한 개념. 이는 @Suite 속성으로 명시하거나, @Test 함수 등을 가진 타입일때 암시적으로 지정됨. 여기서는 instance property 를 가질 수 있고, setup 및 tearDown 로직을 위해 init / deinit 사용 가능. 각 @Test 함수마다 별도의 Suite 인스턴스가 생성되어 의도치 않은 상태 공유를 방지함.
  • @Suite 에 .tags 를 지정하면, 이 안에 있는 @Test 들에 상속됨
  • 여러번 반복되어야 하는 테스트의 경우 arguments 를 이용해서 여러 인수로 테스트를 실행 가능.
  • Suite 의 경우, Swift Testing 에서는 @Suite 속성으로 명시 가능. XCTest 에서는 XCTestCase 의 subclass 로 표현됐음. Swift Testing 에서는 struct, actor, class 를 모두 Suite 로 사용 가능하고, nested type 을 통해 서브그룹화 가능
  • 다만 XCUIApplication과 같이 UI 자동화 API를 사용하는 테스트나, XCTMetric과 같은 성능 테스트 API를 사용하는 테스트에는 Swift Testing이 지원되지 않으므로 XCTest를 계속 사용해야함. Objective-C로만 작성할 수 있는 테스트에도 XCTest 사용 필요.

Go further with Swift Testing ❤️

  • #expect(throws:) 를 통해 에러 핸들링 테스트 더 쉽게 할 수 있음. 안에 있는 구문이 throws 에 있는 에러를 반환해야 통과함. MyError.selfMyError.typo 처럼 구체적인 에러 케이스 지정할 수도 있음.
  • 혹은 #expect {} throws: {error in ~~} 구문에서는 좀 더 자세한 error 기대값을 지정할 수도 있음.
  • 필수 조건 확인할때나 optional 값을 해제하고 싶을땐 #require 로 풀기.
  • @Test(.disable) 로 테스트 실행 막을수도 있지만, withKnownIssue 로 처리하는게 더 나은 옵션일때도 있음. 테스트자체는 실행되지만, 에러 반환할때 어차피 예상한 에러였기 때문에 이걸 fail 로 치지 않음.
  • 나중에 에러 해결되어서 더 이상 에러 반환하지 않는데, withKnownIssue 로 처리되어있으면, 이게 fail 로 처리됨.
  • CustomTestStringConvertible protocol 을 conform 하고 testDescription 작성하면 테스트용 custom description 작성 가능
  • @Test(arguments:) 로 인자 받아서 실행할 수 있음. 이때 argument 당 별도의 테스트케이스가 됨. 따라서 완전히 독립적이고, 병렬적으로 실행됨. 그래서 enum 등에서 다른 성공 케이스말고 실패한것만 실행도 가능.
  • argument 는 array, set, dictionary, range 등의 sendable collection 이면 됨.
  • @Test(arguments: Ingredient.allCases, Dish.allCases) 이런식으로 차례대로 인자를 n 개 넘길수도 있음. 그러면 a x b 만큼의 테스트 케이스가 생성.
  • @Test(arguments: zip(Ingredient.allCases, Dish.allCases)) 이런 식으로도 사용 가능. 그럼 zip 에 맞는 개수만큼만 테스트 케이스 생성.
  • @Suite 는 @Test 는 물론, 다른 @Suite 까지 포함할 수 있음.
  • @Tag 랑 @Suite 는 다른거임. Suite 는 소스 레벨에서 test function 에 구조를 정의하는거고, tag 는 다른 파일이나, suite, target 등에 있는 테스트들을 공통점으로 연관짓게 도와주는 거임.
  • @Suite(.tag:)@Test(.tag:) 처럼 태그 지정 가능. Suite 에 붙으면 그 안에 있는 Test 가 tag 를 상속함. tag 는 중복으로 지정 가능. 만약 Suite 에도 Test 에도 태그가 붙어있으면, 두가지 태그 속성 다 갖게 되는 것.
  • 태그 기준으로 그룹화된것만도 테스트 가넝. 혹은 특정 태그들만 테스트 대상에서 포함 / 제외할 수도 있음
  • 엑코 클라우드도 Swift Testing 지원을 위한 업데이트 됨
  • Swift Testing 은 병렬 테스팅을 기본으로 지원. 원래 XCTest 에서는 한 테스트가 끝나야 다른 테스트가 실행되는 serial 방식. 이는 여러 대의 프로세스가 있어야만 병렬로 작동하는 XCTest 와는 다른 점. 따라서 훨씬 빨라짐. 다만 @Suite(.serialized) 처럼 그 안에 있는 테스트의 실행 방식 지정할 수도 있음.
  • 테스트 순서는 랜덤하기 때문에 의도치 않은 디펜던시 잡을 수 있음.
  • async 함수 테스트하고 싶을때는 똑같이 await 쓰면 됨. 다만 completion handler 사용하면, 이게 트리거 되기 전에 test 자체가 끝나버림. 따라서 대부분 completion handler 함수에서는 Swift 가 async overload 를 제공하니까 이거 쓰고, 만약 이것도 사용하지 못하는 상황이라면 withCheckedContinuation 혹은 withCheckedThrowingContinuation 사용하기
  • 만약 callBack 이 여러번 실행되고, 이 안에서 바깥에 있는 변수를 수정하고자 한다면 Swift6 에서는 이런식으로 변수를 할당하는 것이 안전하지 않다고 판단 후 concurrency error 뱉을 것.
  • 따라서 테스팅 코드가 callBack 을 여러번 호출하고, 이것이 얼마나 호출되었는지 세어야한다면 confirmation 사용하기. 원래 confirmation 은 한번만 실행되는걸 기본으로 하지만, expectedCount 를 통해 지정할 수도 있음.

Consume noncopyable types in Swift 👀

  • Swift에서 Copyable 이라는 새로운 프로토콜은 타입이 자동으로 복사될 수 있음을 나타냄. Swift 에서는 기본적으로 모든 것이 Copyable 이라고 간주되기 때문에, 별도의 처리를 하지 않아도 됨.
  • 하지만 일부 상황에서는 아예 copy 불가능한 타입이 나을 때도 있음. 이를 위한 Noncopyable 등장 (: ~Copyable)
  • Noncopyable struct 라면, 할당할때 단순히 copy 되는 대신 consume 키워드 써야함. 변수를 consume (소비) 한다는 것은, 변수는 해제하지 않은채로 그 안에 값만 빼온다는 뜻임.
  • 그래서 아래 코드의 load(system) 에서는 'system' used after consume 에러 뜸.
struct FloppyDisk: ~Copyable {}

func copyFloppy() {
let system = FloppyDisk()
let backup = consume system // 여기처럼 consume 키워드 필요
load(system) // ❌ 'system' used after consume
}
  • 원래 struct 를 함수 인자로 넘길때는 복사해서 들어갈 것.
struct FloppyDisk {}
func format(_ disk: FloppyDisk) {} // 원래는 복사
  • 근데 Noncopyable 을 넘기면? 함수 인자 받는 쪽에서 어떤식으로 해당 값을 소비할지 명시해야함
  • 방법 1. consuming
  • 함수가 인자를 호출자로부터 가져간다는 의미임. 즉, 해당 인자가 호출자에게서 떨어져나와서 함수 내부로 완전히 소유됨. 이로 인해 함수는 해당 인자를 변경(mutate)할 수도 있음.
  • 하지만 아래 케이스의 경우 문제가 될 수 있음. format 함수는 반환하는 값이 없기 때문.
func format(_ disk: consuming FloppyDisk) {}

func newDisk() {
let result = FloppyDisk()
format(result)
return result // ❌ 'result' consumed more than once
}
  • 방법 2. borrowing
  • borrowing 키워드를 통해 일시적인 접근도 가능. 이는 인자에 대한 read-only 를 허용함 (let 바인딩처럼..)
  • 하지만 아래 케이스의 경우 문제가 될 수 있음. borrowing 한 인자는 consume 혹은 mutate 가 불가하기 때문.
func format(_ disk: borrowing FloppyDisk) {
var tempDisk = disk // ❌ 'disk' is borrowed and cannot be consumed
}

func newDisk() {
let result = FloppyDisk()
format(result)
return result
}
  • 방법 3. inout
  • inout 은 caller 로 넘어온 variable 에 대한 일시적인 write access 제공
  • 그러나 함수가 끝나기 전에 어느 시점에서 inout 매개변수에 대한 재초기화 필요. 왜냐하면 caller 는 return 시점에 inout paramter 에 대한 값을 기대하고 있기 때문.
func format(_ disk: inout FloppyDisk) {
var tempDisk = disk
// …
disk = tempDisk // inout parameter 에 대한 재초기화 필요
}

func newDisk() {
let result = FloppyDisk()
format(result)
return result
}
  • 아래처럼 nonCopyable 타입에 consuming 키워드 붙여서 함수 정의하면 caller 로 부터 self 에 대한 값을 뺏어감(?). 이러면 Swift 는 동일한 객체에서 해당 함수를 두번 이상 호출하지 못하게 만듦
struct BankTransfer: ~Copyable {
consuming func run() {}
}
  • 이제 noncopyable generic 도 가능
  • 일반적인 제약 조건은 허용되는 타입을 좁히고, ~ 제약 조건은 허용 범위를 넓힘. 그러니까 ~Copyable 을 보면 , "이건 copy 불가!" 라고 이해하면 안되고, "Copyable 일수도 있고~ 아닐수도 있고~" 라고 이해해야함
  • 한.. 15분쯤 부터 그림과 함께 예시를 막 들어주는데… 이해가 될듯 말듯함 (사실 안됨). 그리고 어따 쓸지 잘 감이 안와서 흥미 하락…

A Swift Tour: Explore Swift’s features and design 👀

  • Swift 의 전반적인 특징들을 훑어줘서 좋았다
  • 모듈은 함께 빌드되는 소스 파일들의 모음. 모듈 모음은 Package 로 배포될 수 있음. 한 패키지 안에 있는 모듈은 다른 패키지의 모듈에 의존성 걸려있을 수도 있음.
  • access level 중 package 는 동일 package 내 다른 모듈이 접근 가능한 정도를 나타냄
  • shared mutable state 를 사용하고 싶을때는 클래스 같은 reference type 이 필요. 혹은 동일성(identity)를 가진 object 나 상속이 필요할땐 클래스가 적합.
  • String 도 Unicode character 로 이뤄진 ordered collection임
  • 동시성의 기본 단위는 Task 로, 독립적이고 병렬적인 실행 context 를 나타냄. 가벼워서 여러개 만들어도 됨
  • 하나의 Task 가 await 로 결과 기다리는 동안 다른 Task 의 일이 실행될 수 있도록 양보함
  • data race 안정성을 달성하는 방법중 하나는 concurrency 에서 공유되는 값이 Sendable 하면 됨. Sendable value 는 동시적인 접근에서 자신의 상태를 보호함 (lock 등).
  • 하지만 직접 동기화 로직 작성하는것보다 actor 쓰는게 편함. actor 은 reference type 이고, shared mutable state 를 갖는다는 점에서 class 와 비슷하지만, 직렬화된 접근을 통해 상태를 보호한다는 점에서 차이점이 있음. actor 에서는 오직 하나의 Task 만 실행 가능. 바깥에서 actor method 를 호출하는건 비동기임.
  • @Argument property wrapper 를 통해서 tool 의 command 를 좀 더 쉽게 작성 가능
  • result builders는 복잡한 값을 선언적으로 표현할 수 있게 해줌. 이거 더 알고 싶으면 Write a DSL in Swift using result builders 보기
  • 매크로는 컴파일러 플러그인 역할을함. syntax tree를 input 으로 받아 변환된 코드를 output 으로 출력

Demystify SwiftUI containers 👀

  • what’s new in swiftUI 세션 설명한 사람들이 초대장 보내서 이야기 연결시키는거 진짜 어이없어여ㅋㅋㅋㅋ쿠ㅜ
  • ForEach(subviewOf:) api 새롭게 나옴. 여기서는 하나의 view 를 입력으로 받음. 그리고 해당 view 의 subview 들을 trailing view builder 로 전달하여 이를 다른 종류의 뷰로 변환할 수 있게 함.
  • 서브뷰는 Declared subview 와 resolved subview 로 구분 가능. declared subviews 는 resolved subviews 를 생성하는 방법을 정의함
  • 예를 들어 ForEach 나 Group View 같은 Declared View 들은 최종적인 목적이 그 안에 들어있는 resolved subview 를 만드는 거임. EmptyView 같이 resolved subview 가 없는 애들도 있음. 조건문 등으로 나누면 컨디션에 따라서 resolved subview 의 개수가 달라지기도 함
  • ForEach(subviewOf:) api 는 인자로 들어가는 뷰가 어떻게 구성되어 있든지, 그 subview 들을 iterate 함. 그래서 나는 크게 신경쓰지 않고도, 해당 subview 들만 접근 가능. (즉, ForEach 안에 감싸져 있어도 그 안에 있는 Text 들을 접근할 수 있는 것)
  • Group(subviewsOf:) api 도 등장. 얘도 ForEach(subviewOf:) 와 비슷하게 view 인자로 받아서 subview 들을 resolve 하는데, ForEach 처럼 하나씩 iterate 하는 대신, subview의 collection 을 돌려줌. subview 의 개수 등을 알고 싶을때 유용.
  • ForEach(sectionOf:) api 도 등장. ForEach(subviewOf:) 와 비슷하게 view 인자로 받음. 다만 section 을 iterate 함. 그 안에 내용은 section.headersection.content 로 접근하면 됨.
  • 특정 컨테이너 전용 modifier 를 만드는 새로운 API가 있음. 바로 container values 임.
  • container values 는 Environment 나 Preferences 와 비슷하게 키가 있는 storage
  • 하지만 Environment values 는 전체 뷰 계층 구조에 하향식으로 전달되고, Preferences 는 포함하는 모든 뷰에 상향식으로 값이 전달되는 것과 달리, resolved subviews 의 container values 는 오직 해당 서브뷰의 직접적인 컨테이너에 의해서만 접근할 수 있음. 따라서 컨테이너 전용 custom 옵션을 구현하는 데 적합.
  • container values 를 구현하기 위해서는 extension ContainerValues@Entry 매크로를 통해 값을 정의함. @Entry 매크로는 SwiftUI 의 keyed storage 타입 (environment values, focus values 등) 에 새로운 값을 추가하기 위한 편리한 syntax 를 제공. 이후, containerValue() API 를 호출해서 property 의 key path 와 새롭게 설정할 값을 전달하면 됨. 사용할땐 view.containerValues.내가정의한Entry프로퍼티 처럼 접근 가능.

SwiftUI essentials 👀

  • SwiftUI 의 전반적인 특징들을 훑어줌. 중간에 나오는 쉘든 거북이 귀 여 워
  • SwiftUI 뷰는 아래 세가지 특징 있음
  • 선언적(Declarative) — 무엇을 해야하는지 선언해두면 상태에 기반해 UI가 자동으로 갱신됨. 기대되는 결과에 좀 더 집중. 반면 Imperative (명령형) 은 객체의 상태를 직접 변경하고, 필요할 때 수동으로 UI를 갱신. 상태 변화에 효과적. 이 둘은 상호배타적인건 아님.
  • 구성적(Compositional) — 여러 뷰를 조합하여 UI를 구성
  • 상태 기반(State-driven) — 뷰의 상태가 변할 때 SwiftUI는 자동으로 UI를 업데이트. view 에서 사용하는 모든 data 는 해당 view 의 dependency 임.
  • contentTransition modifier 에 넘길 수 있는 인자로 numericText 가 있는데 (ex. Text(rating).contentTransition(.numericText(value: rating))) 짱이다.. 걍 제가 원하던 효과잖아요.. iOS 17 부터 가능했군
  • SwiftUI 는 다양한 Apple 플랫폼에 따라 자동으로 최적화되도록 도와줌.
  • searchable modifier 에 token 을 넘겨서 토큰 단위 UI 를 만들 수도 있었군
  • FlipBoard 애니메이션 어케 했어.. 하고 찾아보니까 비슷한게 github FlipClock-SwiftUI / FlipNumberView-Swift 에 있군 ㅎㅎ 나중에 시간되면 봐야징
  • SwiftUI는 UIKit 및 AppKit과 원활하게 통합됨. UIKit이나 AppKit의 뷰나 뷰 컨트롤러를 SwiftUI 에 사용할 수 있으며 (UIViewRepresentable / NSViewRepresentable), 반대로 SwiftUI의 뷰를 UIKit이나 AppKit에서 사용할 수도 있음 (UIHostingController / NSHostingController).

Xcode essentials ❤️

  • Xcode 의 전반적인 특징들을 훑어줌. 몰랐던 기능이 많았다.. 그 내용들 위주로 적음!
  • project navigator 하단에 필터링에서 타겟 단위로 필터링 할수도 있고, 최근 본 파일이나, git status 에 있는 애들만도 볼 수 있음
  • find navigator 에서 단어 검색 후, 하단 필터링 기능을 통해 파일 이름이나, 해당 라인에 있는 추가적인 단어들을 기준으로 필터링 할 수 있음.
  • find navigator 에서 command 누른채로 disclosure indicator 누르면 전체 파일 내용이 접힘
  • find navigator 에서 text > containing 옵션 외에도 class 계층을 확인하기 좋은 Descending Types 등 다양한 옵션 제공
  • command E 누르면 find navigator 에 자동 입력 되어있음
  • tab 쪽에는 permanent tab 과 implicit tab (이탤릭체로 표시) 으로 나뉘는데, 수정 없이 implicit tab 을 permanent 로 만들고 싶다면 우클릭 후 Keep Open 선택하거나 그냥 double click 하면 됨
  • tab 을 다 한번에 닫고 싶을때는, option + 탭 하나 X 버튼 누르면 그 탭 빼고 다 닫힘
  • tabbar 의 좌측에 < > 버튼은 back / forward 기능을 함. 단 꾹 누르고 있으면 full history 를 보여줘서 원하는 곳으로 한번에 이동할 수 있음.
  • < 버튼 좌측에는 related file menu 가 있음. 여기서는 현재 class 의 subclass 들이나 현재 funciton 의 caller 등을 확인 가능
  • 탭바 우측 세개의 버튼은 UI editor 설정 관련된 것. 그 중 가운데 있는건 editor layout 을 control 함. SwiftUI 프리뷰나, editor mini map, author, code coverage 등을 볼 수 있음.
  • 탭 바 밑에 jump bar 가 있음. 여기서 경로별 폴더 / 파일들 볼 수 있는데, 타이핑 하면 바로 필터링 가능.
  • 동일한 파일 / 내용 복사하고 싶으면 option + 해당 파일 클릭 & 드래그 하면 됨.
  • command x 통해서 클립보드에 저장해두고, navigator 아무곳에서나 우클릭한 후 option 을 누르면 몇몇 메뉴가 바뀜. 여기서 “New file with contents of clipboard” 로 새 파일 만들수도 있음 (흠.. 근데 난 안 바뀌는뎅..)
  • #warning("") 작성 통해 자신만의 워닝도 추가할 수 있음
  • command + shift + o 로 파일 이동 외에도 :lineNumber 붙여서 해당 라인으로 바로 이동 가능 (MyFile:21)
  • command + shift + o 로 파일 열때 새 창으로 열고 싶으면 option 누른채로 엔터치면 됨
  • command + shift + a 로 quick action 열 수 있음.
  • option + Click 은 quick help 로, 문서나 추론된 타입 보여줌. (생각해보니.. 변수 타입같은거 확인할때 우측 네비게이터 열어서 확인했는데.. 이걸로 확인했으면 됐잖아??)
  • control + m 으로 한줄에 있는 많은 코드들 다수 라인으로 재포맷팅 가능
  • 함수등에서 { 범위기 어디까지인지 알고 싶으면 더블클릭
  • option + -> 는 단어 단위 , control + -> 는 subword 단위, command + -> 는 라인 끝 단위로 이동함 (근디 나는 contorl + -> 가 화면 이동으로 설정되어있어서.. subword 단위..라..?)
  • <#placeholder#> 와 같은 형식을 통해 코드 컴플리션에서 보는 것과 동일한 placeholder 를 만들 수 있음. 이는 템플릿 같은거 만들때 유용함.
  • 엑코 16 에서는 주변 코드를 기반으로 전체 구문을 추천해줌. suggest 뜰때 tab 키 눌러서 보이는 제안을 채택할 수도 있고, option 키 눌러서 전체 제안 코드를 볼 수도 있고, tab + option 을 함께 눌러서 전체 제안을 채택할 수도 있음. 주석이나 변수명을 더 잘 해둘수록 추천되는 코드 결과가 더 좋아짐
  • completion window 의 제일 하단에서 함수의 전체 signature 를 확인할 수 있는데, 전체 argument 를 사용할거라면 option + enter 를 누르면 됨 (맨 아래까지 내렸었는데요..!)
  • Xcode 에서는 Editor > Vim Mode 를 지원하는데, 엑코 16에서부터는 vim 의 repeat command 를 지원 (multil cursor editing 같은 것)
  • 우클릭 > show last change for line 을 통해 해당 line 에 대한 마지막 commit 이력을 알 수 있음 (blame 의 focused version)
  • breakpoint 네비게이터에서 Swift Error Breakpoint 걸어두면 error throw 하는데서 멈춤
  • 브포 걸려서 초록색으로 멈춘 program counter 를 이전 라인으로 드래그 할 수도 있음. 그러면 이전의 expression 을 다시 실행하려고 시도함. 단, side effect 는 되돌려지지 않으므로 프로그램이 이상한 상태에 있을 수 있지만, 다른 유일한 방법이 실행을 중지하고 디버깅 세션을 다시 시작하는 것이라면, 이 방법을 해보는 것도 괜찮음. 왜냐면 시간 절약되니까.
  • 동일한 문제를 여러번 디버깅해야할 때가 있음. 크래시 나는 상황이라면 프로그램 변경사항도 없는데 이를 진단하기 위해 계속 빌드해야함. 이때 Command-Control-R (빌드 없이 실행 — run without build) 을 통해 빌드 단계를 건너뛰고 즉시 디버깅으로 돌아갈 수 있음 (대박적… 나는 인생을.. 낭비하고 있었다…). 코드를 변경했어도, 마지막 세션 이후로 다시 빌드하지 않았고, 이전 코드를 한 번 더 디버그하고 싶을 때에도 사용 가능.
  • Xcode 16에서는 backtrace 를 하나의 editor 에서 조각별로 모아볼 수 있음. 해당 view mode 는 debug bar 에서 memory 및 view debugger 옆에 있는 버튼으로 활성화 가능
  • print 에 #fileID , #line, #function 매크로를 사용해서 로그를 찍는 대신, 세팅한 디버그 레벨에 맞는 정보를 함께 제공해주는 os_log 사용하면 런 매크로를 더 이상 사용할 필요 없음. 이런 매크로를 사용하지 않는 이유는, 단순히 '이동' 화살표를 클릭하면 로그 메시지가 나오는 소스의 line 으로 바로 이동할 수 있기 때문.
  • command + 6 로 test navigator 바로 이동 가능. 하단 필터링 기능을 통해 fail 된것만 거를 수도 있음.
  • command + control + option + u 가 커서 위치한 단일 테스트 코드 실행시키는 것, command + control + option + g 는 이전 테스트한 케이스 재실행하는 것.
  • conmmand + control + u 를 통해 빌드 없이 테스트코드 실행 가능
  • 테스트 우클릭해서 반복적으로 실행할 횟수 정할수도 있음
  • Apple Developer Program 멤버십은 Xcode Cloud 의 월 25시간 컴퓨팅 시간을 무료로 제공. 이를 통해 클라우드 환경에서 테스트 가능
  • Test Plan은 원하는 시점에 원하는 테스트만 실행할 수 있도록 테스트를 그룹화하는 방법. 프로젝트가 성장함에 따라, 여러 다른 scheme 을 통해 테스트를 실행하거나, 빠른 unit test 만을 실행하는 그룹을 만들거나, 커밋 전에 모든 것을 테스트하는 다른 그룹을 만들고 싶어질 수도 있음. Product > Test Plan 메뉴에서 설정 가능. 이렇게 만들어진 test plan 은 여러 스킴에 Adding Existing Test Plan 을 통해 추가 가능.
  • command + 9 으로 report navigator 이동 가능
  • 실패한 테스트를 더블 클릭하면 발생한 사건들과 함께 스크린 녹화가 나란히 표시됨. 이는 테스트 진행 중 앱의 상태를 시각적으로 확인할 수 있어 정확히 무엇이 잘못되었는지를 파악하는 데 매우 유용하며, 하단의 타임라인에서 실패가 발생한 시점을 정확히 볼 수 있음. (이거.. UITest 얘기겠지..?)
  • Archive 는 컴파일된 앱의 스냅샷으로, 릴리즈 빌드를 포함함. 이 릴리즈 버전은 공간을 절약하기 위해 디버그 정보가 더 이상 포함되지 않음. 하지만 아카이브에도 debug symbol 은 포함되어 있으므로 이를 저장하면 나중에 해당 버전을 디버깅할 수 있음. (스냅샷 — 특정 시점에서 시스템의 현재 상태를 포착한 이미지나 복사본). 아카이브는 선택한 destination 에 맞춰 앱의 내용을 다시 패키징 후 배포할 수 있도록 해줌
  • 앱 배포 옵션에는 아래와 같은 옵션들이 있음
  • App Store Connect — 앱을 TestFlight 또는 App Store Connect에 업로드
  • TestFlight Internal Only — App Review를 건너뜀. 실수로 App Store에 제출하지 않도록 보호하는 기능을 포함. 내부 테스터에게만 사용할 수 있으며 외부 테스터에게는 미적용
  • Release Testing, Enterprise, Debugging — 모두 최적화된 빌드를 생성하여 포털에 등록된 장치에 설치할 수 있게 함

마무리

나머지로 보려고 째리고 있는 세션들은 요 세개인데요..!

딱 제목만 봐도 너무 중요하고… 어려워보임.. 미루고 미루는 중..☺️

이 외에도 추천할 세션 있다면 알려주세여!! 그럼 20000!

--

--