WWDC23 - Platforms State of the Union 2023

백상휘·2023년 6월 6일
0

6월 2주째는 WWDC23 이 열리는 주간이다. 이번 WWDC 에서 흥미로운 세션이나 주제를 선정해 연속적으로 포스팅을 진행할 예정이다.

https://developer.apple.com/news/?id=57gsb3bt

Learn about the latest tools, technologies, and advancemenets to help you create even better apps across Apple platforms, including the all-new visionOS.

Platforms State of the Union 이 뭔지 몰라서 아주 잠깐 찾아보니 애플 개발 생태계의 최신 현황을 선보임 이라고 할 수 있을 것 같다.

이번 포스팅에서는 이 발표 중 iOS 개발자를 포함한 애플 생태계에 포함된 개발자들이 알아 놓으면 좋을 것 같은 주제들만 선정해서 정리하고자 한다. 물론 발표의 대부분이 포함될 것이지만 빠지는 부분도 분명히 있을 것이다.

Overview

발표자인 Darin Adler 라는 사람의 말에 따르면 이 발표에는 다음의 내용을 다룰 것이라고 얘기한다.

  • Experiences - 사용자 경험을 얘기함
  • Hardware - 앱이 하드웨어에 접근하는 권한에 대해 얘기함
  • Values - 사용자에게 전달할 가치를 얘기하는 듯
  • Tools - 개발 툴을 얘기함
  • visionOS - 새로 나온 그녀석

Swift

Swift Macros

Swift 의 boiler-plate 코드를 줄이기 위한 API 이다.

어노테이션 혹은 해시(#) 기반의 문법을 갖고 있으며, 이 매크로를 통해 전달된 값이나 구조를 매크로에 의해 정해진 구조로 일관되게 변경한다.

신기한 점은 이 매크로가 컴파일 타임에 유효성 검사를 거치게 된다. 어떤 기준으로? 이는 커스텀 피드백을 설정했을 때 가능한 일이다.

간단한 예시로 URL 의 Macro 를 소개한다. String 을 URL 로 변경해준다.

var url: URL {
	switch self {
    case .diversity: #URL("http://www.swift.org/diversity")
    // 매크로 변경 후 URL(String: "http://www.swift.org/diversity")!
    case .mentorship: #URL("http://www.swift.org/mentorship")
    }
}

#URL 에 오른쪽 클릭을 해서 "Expand Macro" 를 선택하면 실제 실행되는 코드도 볼 수 있다.

다른 예시를 보자

struct  SwiftWebsite {
	@AddAsync
    func fetchContent(_ topic: Topic, completion: @escaping (Result<String, Error>) -> Void) {
		let task = URLSession.shared.dataTask(with: topic.url) {•••} 
        task.resume ()
	}
	
    /// 매크로가 생성하는 코드
	/// func fetchContent(_ topic: Topic) async throws -> String {
	///		try await withCheckedThrowingContinuation { continuation in
	///			fetchContent (topic) { returnValue in
	///				switch returnValue {
	///				case .success(let value):
	///					continuation.resume(returning: value)
	///				case .failure(let error):
	///					continuation.resume (throwing: error)
	///				}
	///			}
    ///    	}
    /// }
}

let website = SwiftWebsite ()
let task = Task {
	let content = try? await website.fetchContent (.diversity)

	if let content {
		print (content)
	}
}

await task.value

실제로 매크로가 생성하는 코드는 디버깅을 통해 확인이 가능하다.

Interoperability with C++

이제는 C++ 을 Swift 내에서 사용할 수 있다고 한다. 원래는 bridging layer 가 따로 필요했지만 이런 과정이 생략된다.

struct Proposal {
	std::string id;
    std::vector<Person> authors;
};

extension Proposal {
	var authorNames: [std.string] {
    	authors.map(\.name)
    }
}

사실 이건 할 말이 없다. C++ 을 알아야지 원...

SwiftUI

AnimationPhase

SwiftUI 의 좋은 점, 발전한 점 들을 쭉 나열하다가 올해는 애니메이션에 집중하기로 했다고 한다.

확실히 발표에 나온 것처럼 애니메이션은 사용자에게 결과를 알려주기에 너무나도 좋은 물건이다.

struct NewBirdIndicator: View {
	var body: some View {
		PhaseAnimator (IndicatorPhase.allCases) { phase in
			Image (systemName: "bird.fill")
				.foregroundStyle(.white)
                .scaleEffect(phase == .highlighted ? 1.2 : 1) // Animation
                .rotationEffect(phase == .highlighted ? .degree(-10) : .zero) // Animation
				•padding ()
				.background (in: circle)
				.backgroundStyle (.teal.gradient)
		}
	}
	enum IndicatorPhase: Caselterable {
		case idle
        case highlighted
    }
}

PhaseAnimator 가 보이는가? 저기에 파라미터로 전해준 selection 에 따라 애니메이션 실행되는 것이다.

Keyframe

Data flow

지금까지 SwiftUI 자체적으로 제공하는 property wrapper 를 살펴보자. 이들을 통해 데이터를 전달했을 것이다.

  • @State
  • @StateObject
  • @ObservedObject
  • @Environment
  • @EnvironmentObjects

여기서 두 가지로 줄여버리겠다고 한다.

  • @State
  • @Environment
/// Original
class Bird: Observableobject {
	@Published var name: String
	@Published var species: Species
	@Published var songs: [Song]
}

/// New - Using Swift Macros
@Observable
class Bird {
	var name: String
    var species: Species
    var songs: [Song]
}

struct BirdDetail: View {
	var bird: Bird // No need @ObservedObject
	
    var body: some View {
		VStack {
			Text (bird.name )
			SpeciesDetail(bird.species)
        }
    }
}

신기하면서도 주의해야 할 수도 있는 (뷰 레이아웃 관점에서) 점은 @Observable 은 필드 단위로 SwiftUI 를 업데이트 한다는 것이다. 이는 효율적인 앱을 위해서는 반드시 필요한 기능이지만, 오류가 발생할 때 모른다면 상당히 안타까운 상황이 벌어질 수도 있다.

struct BirdDetail: View {
	var bird: Bird // No need @ObservedObject
	
    var body: some View {
		VStack {
			Text(bird.name) // #1
			SpeciesDetail(bird.species) // #2
        }
    }
    
    // Only update #1
    func changeBirdName() {
    	bird.name = "randoms"
    }
    
    // Only update #2
    func changeBirdSpecies() {
    	bird.species = .houseFinch
    }
}

SwiftData

Core Data has long provided tools for data management, but its design was born of the era of Objective-C, and it doesn't take full advantage of everything that Swift has to offer.

내 이럴 줄 알았지...

SwiftData 는 데이터 모델링, 관리를 Swift 의 일급 객체로 사용할 수 있도록 만들어진 프레임워크이다.

만약 CoreData 로 데이터를 저장하고 싶다면 이렇게 했을 것이다.

  1. 저장하고 싶은 데이터의 구조체 혹은 클래스를 정의한다.
  2. CoreData Entity 를 생성하고 구조체와 맞춘다.

SwiftData 는 이렇게 하면 된다.

  1. 저장하고 싶은 구조체나 클래스에 @Model 을 붙인다. Entity 는 필요없다.
@Model
class Bird {
	var name: String
    var species: Species
    var songs: [Song]
}

여기서 @Model 은 Persistence, Schema modeling, Lightweight migration, Relationship management, iCloud synchronization, Spotlight search, Undo/Redo 기능을 모두 지원한다. CoreData 에서 들어봤던 얘기들이다.

애플에서는 데모 앱을 제공해주었는데 매우 길어서 매우 생략하도록 하겠다.

import SwiftUI
import Foundation
import SwiftData

// MARK: - SwiftData Model setting

@Model
final class Backyard {
	...
}

@Model
final class Bird {
	...
}

// MARK: - Set up the SwiftData container

@main
struct BackyardBirdsApp: App {
	var body: some Scene {
    	WindowGroup {
        	NewBackyardForm()
        }
        .modelContainer(for: Backyard.self, isUndoEnabled: true) // #1 
    }
}

// MARK: - Insert model context for persistent

struct NewBackyardForm: View {
	@Environment(\.modelContext) private var modelContext
    
	var body: some View {
		VStack {
        	BackyardList()
        	Button("insert") {
            	modelContext.insert(Backyard()) // #1
            }
        }
    }
}

// MARK: - Querying persisted data

struct BackyardList: View {
	@Query(sort: \.name)
    private var backyards: [Backyard]
    
    var body: some View {
    	List(try! backyards.first(currentPredicate)) {
        	BackyardListRow(backyard: $0)
        }
    }
}

WidgetKit

iOS 17 StandBy and Widget

iOS 17 이라니 아쉽구나 아쉬워...

iPhone 14 의 AOS (Always-On-Display) 를 통해 StandBy 를 소개한 바가 있다. 한마디로 잠금화면에서도 앱을 실행해 놓을 수 있다는 것이다. 타이머 앱이나 시계 앱을 항시 표시하고 싶을 때 쓰는 기능인 것이다.

위젯의 사용 범위가 넓어졌다는 얘기를 하는데, 단순히 데이터를 변경하는 것 뿐 아니라 위에서 말한 StandBy 에서도 위젯을 사용하는 것이 가능하다.

사실 이 기능이 WidgetKit 때문에 가능했다는 것이다.

위젯의 코드는 비동기적으로 컨텐츠를 생성하는데 SwiftUI 의 뷰가 미리 저장 된 아카이브에 필요할 때마다 불러와서 background 에서 렌더링 하는 것이다.

렌더링 관련하여서는 여러 기능을 제공하는데 쉽게 말해 실제 앱처럼 구현 가능하다.

TipKit

이번에 새로 나온 프레임워크라고 한다. 사용자에게 앱의 팁을 주기 쉽게 여러 템플릿을 제공한다는 것이다.

AirDrop

이번에 공유 탭을 호출하지 않고도 에어드랍 처럼 데이터를 주고받을 수 있는 기능인 NameDrop 이 나왔다. 이는 위에서 말한 ShareLink 로 인해 가능한 것이다.

ShareLink(
	item: Image("bird"),
    previews: SharePreview("1 Photo")
)

Apple Watch

Vertical Tabviews

세로 탭 뷰로 세로 페이징을 구현할 수 있는데 여러 방식으로 사용 가능하다.

  • 탭 뷰를 이용한 페이징으로 컨텐츠를 페이징 할 수 있도록 한다.
TabView {
	ForEach(articles) { article in
    	ScrollView {
        	NewsArticle(article)
        }
    }
}.tabViewStyle(.verticalPage) // #1
  • 탭 뷰를 이용한 페이징으로 뷰 배경 색을 컨트롤 할 수 있다.
//...
.containerBackground(metricColor.gradient, for: .tabView)

탭 뷰는 각 Section 혹은 List 별로 페이지를 구별한다. 이를 잘 활용하면 UI/UX 적으로 훌륭한 앱을 만드는 것이 가능하다. (WathchOS 10 에서...)

Accessibility, Privacy

여러 문제들에 대해 다뤄 주었지만 각각의 API 가 필요할 때마다 가져다 쓰면 될 것 같아서 넘어간다.

하지만, 접근성과 프라이버시에 대한 문제는 항상 염두에 두고 있는 사항이다.

visionOS

Vision Pro 는 우선 신기하다. 이번 발표에서는 그 기초에 대해 설명해주고 있다.

우선, 어떻게 사용자 공간에 앱이 '띄워져' 있을 수 있을까?

Shared Space 라는 공간이 있다. 앱이 존재할 수 있는 공간을 뜻하는데, 여러 앱을 동시에 실행시킨 것과 같다.

여기에는 여러 Elements 들이 있다. 이 Elements 들을 통해 Vision Pro 가 추구하는 Spatial Computing 을 수행할 수 있는 것이다.

Window

SwiftUI Scene 과 비슷한 기능이다. 즉 visionOS 내에서 여러 개 띄울 수 있는 창을 말한다.

여기에는 우리가 흔히 봐 왔던 뷰, 컨트롤부터 3D 객체, RealityViews 를 넣을 수 있다.

Volume

3D 객체에게는 꼭 필요한 Volume 도 있다. Volume 을 가진 객체는 visionOS 내에서 모든 방향을 볼 수 있고 이동할 수 있다.

(Full) Spaces

(Full) Space 는 사용자가 집중(비디오 시청, 게임, 프레젠테이션 상황)할 수 있는 공간을 만들기 위해 사용하는 것이다. 전체 배경이 산으로 바뀌는 등 완전히 다른 공간에 있는 것처럼 만드는 것이다.

Extending your apps (into visionOS)

우선 visionOS SDK 의 구조를 보자.

SwiftUI, ARKit, RealityKit 을 이용한 앱이 필요한 것으로 판단된다.

개발하는 앱을 visionOS 에서 실행할 수 있도록 변경하려면 프로젝트 파일의 타겟 설정에서 Supported Destinations 설정에 visionOS 를 추가한다.

SwiftUI (UIKit 도 가능하나 주구장창 SwiftUI 얘기만 한다는 건 알아주길 바란다) 는 Window 의 dept, Volume 을 생성하는 데 아주 유용하다.

ForEach(Fruit.allCases) { fruit in
	.offset(z: selectedFruit == fruit ? 100 : 0)
}

Modal 처럼 depth 를 이용해 이런 뷰를 만들 수 있다.

RealityKit 은 Volume 을 위해 필요하다. 사용자가 물체를 집어 움직이고 돌리고 할 수 있는 것이다.

RealityKit

애플의 3D 공간 렌더링 엔진이다.

visionOS 의 window, volume, space 를 만드는 도구이기도 한 것이다.

ARKit

ARKit 은 사용자의 환경을 해석하는 프레임워크이다.

공간과 상호작용하는 것을 뜻 하는데 공을 땅에 내리 꽂으면 튀어오르는 현상, 물을 벽에 뿌렸을 때 나오는 효과 등을 구현한다.

Accessibility

눈의 움직임, 특정 손가락, 머리 움직임 등으로 입력을 대체할 수 있다고 한다.

Developer Tools

Xcode Preview 가 3D, 2D 를 모두 지원할 것이다. 물론 Simulator 도 공간을 제공하고 앱을 띄울 수 있도록 기능을 제공할 것이다.

앱의 window, volume 과 관련한 상호작용을 디버깅하기 위해 충돌, 겹침, 3D 정보를 디버깅 메뉴를 통해 확인할 수 있다.

visionOS 앱을 실제 공간에서 테스트할 수 있도록 Reality Composer Pro 가 제공된다. 공간을 만들 수 있는 것이다.

Vision Pro 에서 실행할 수 있는 앱은 TestFlight 에서도 이용할 수 있을 것이다.

시작하는 법

우선 이번 여름에 업데이트 될 Xcode 15 가 필요하다. Xcode 업데이트를 할 때 필요한 SDK 도 함께 설치될 것이다.

HIG 도 업데이트 될 예정이며, 이번 여름에 Vision Pro 기기를 테스트해 볼 수 있는 Vision Pro Developer Labs 를 운영할 예정이라고 한다. (런던, 뮌헨, 샹하이, 싱가폴, 도쿄, 그리고 쿠퍼티노)

References

profile
plug-compatible programming unit

0개의 댓글