2️⃣ WWDC 2025 -SwiftUI 7.0- iOS 26
✅ FoundationModels (SDK)
(온디바이스 인텔리전스)
Xcode 26에는 이제 FoundationModels SDK가 포함되어, 온디바이스 인텔리전스 모델을 활용할 수 있습니다. 이 모델들은 디바이스 내에서 동작하기 때문에, 디바이스가 오프라인 상태여도 기능을 수행할 수 있습니다. 이제 이 새로운 SDK의 핵심적인 활용 사례들을 살펴보겠습니다.
⭐️ iPhone 15 이상 또는 M1 칩이 달린 iPad / Mac 이후 기종 만 지원됨
+. streamResponse 코드 몇 줄만으로도 우리는 이 새로운 SDK를 이용해 AI 채팅을 만들 수 있습니다. 하지만 답변이 너무 오래 걸린다는 것을 눈치채셨나요?
네, 그 이유는 제가 한 번에 전체 결과를 요청하고 있기 때문입니다. 하지만 부분적인 반복 결과(Partial Iterative Results)를 받을 수 있는 방법이 있습니다. 이 방식은 요즘 AI 채팅에서 가장 흔히 사용되고 있죠.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import FoundationModels
struct ContentView: View {
@State private var prompt: String = ""
@State private var answer: String = ""
@State private var disableControls: Bool = false
var body: some View {
NavigationStack {
ScrollView(.vertical) {
Text(answer)
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
.padding(15)
}
.safeAreaBar(edge: .bottom) {
HStack(spacing: 10) {
TextField("Prompt", text: $prompt)
.padding(.horizontal, 15)
.padding(.vertical, 10)
.glassEffect(.regular, in: .capsule)
Button {
Task {
guard !prompt.isEmpty else { return }
do {
let session = LanguageModelSession()
disableControls = true
let response = session.streamResponse(to: prompt)
for try await chunk in response {
self.answer = chunk.content
}
disableControls = false
}catch {
disableControls = false
print(error.localizedDescription)
}
}
} label: {
Image(systemName: "paperplane.fill")
.frame(width: 30, height: 30)
}
.buttonStyle(.glass)
}
.disabled(disableControls)
.padding(25)
}
.navigationTitle("Foundataion Model")
}
}
}
✅ FoundationModels (@Generable)
FoundationModels에는 @Generable 이라는 매크로가 포함되어 있으며, 이를 통해 LanguageModelSession이 지정된 모델을 위한 데이터를 생성할 수 있습니다.
이 예시에서는 Todo 라는 이름의 모델을 생성할 것입니다.
이 매크로를 사용하면, 언어 모델에게 무작위 할 일(todo) 목록을 생성하도록 요청할 수 있습니다.
비록 이것이 실질적인 사용 사례는 아닐 수 있지만, 다른 많은 시나리오에서 유용하게 활용될 수 있습니다!
+. @Guide()
이것은 LanguageModel에 이러한 속성을 채우는 데 필요한 컨텍스트를 제공합니다. 또한, @Generable에도 이러한 설명을 제공할 수 있습니다!
@Guide() 적용을 하면 아래 사진 처럼 Contenxt 가 @Guide(description: “칫솔질”)에 따라 비슷한 형식으로 변경된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import SwiftUI
import FoundationModels
struct ContentView: View {
@State private var todos: [Todo] = []
@State private var isWriting: Bool = false
var body: some View {
NavigationStack {
List {
ForEach(todos) { todo in
Text(todo.task)
}
}
.navigationTitle("Todo")
.toolbar(content: {
ToolbarItem(placement: .topBarTrailing) {
Button("", systemImage: "apple.intelligence") {
let propmt = "Create 10 todo list items in Korean"
Task {
do {
let session = LanguageModelSession()
let response = session.streamResponse(generating: [Todo].self) {
propmt
}
isWriting = true
for try await chunkTodos in response {
self.todos = chunkTodos.content.compactMap({
if let id = $0.id, let task = $0.task {
return .init(id: id, task: task)
}
return nil
})
}
isWriting = false
}catch {
isWriting = false
print(error.localizedDescription)
}
}
}
.disabled(isWriting)
}
})
}
}
}
@Generable
struct Todo: Identifiable {
var id: String
@Guide(description: "칫솔질") //어떤 내용이 올지 샘플을 제공함
var task: String
}
✅ scrollEdgeEffectStyle()
기본적으로, List, Navigation, 그리고 다른 UI 컴포넌트들은 이제 안전 영역(safe areas)에 부드러운 블러 효과(Progressive Blurs라고도 불림)를 갖습니다. 그러나 SwiftUI는 이러한 효과를 제어할 수 있는 간단한 수정자(modifier)를 제공합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import SwiftUI
import FoundationModels
struct ContentView: View {
@State private var todos: [Todo] = []
@State private var isWriting: Bool = false
var body: some View {
NavigationStack {
List {
ForEach(todos) { todo in
Text(todo.task)
}
}
.navigationTitle("Todo")
.toolbar(content: {
ToolbarItem(placement: .topBarTrailing) {
Button("", systemImage: "apple.intelligence") {
let propmt = "Create 10 todo list items in Korean"
Task {
do {
let session = LanguageModelSession()
let response = session.streamResponse(generating: [Todo].self) {
propmt
}
isWriting = true
for try await chunkTodos in response {
self.todos = chunkTodos.content.compactMap({
if let id = $0.id, let task = $0.task {
return .init(id: id, task: task)
}
return nil
})
}
isWriting = false
}catch {
isWriting = false
print(error.localizedDescription)
}
}
}
.disabled(isWriting)
}
})
.scrollEdgeEffectStyle(.hard, for: .top)
}
}
}
✅ backgroundExtensionEffect()
작은 이미지를 사용할 때 잠금 화면 상단에서 확장된 블러 효과가 나타나는 것을 모두 본 적이 있을 것입니다.
그런데 이제 이 기능이 SwiftUI에서 modifier(수정자) 로 제공됩니다!
이 수정자는 뷰를 사용 가능한 안전 영역(safe areas)까지 확장하며, 그 영역에 은은한 블러 효과를 적용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import SwiftUI
struct ContentView: View {
var body: some View {
GeometryReader {
let size = $0.size
Image(.pic2)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: size.width, height: size.height)
.clipped()
.backgroundExtensionEffect()
}
}
}
✅ Rich TextEditor
마침내, TextEditor가 이제 AttributedString 을 Binding으로 지원합니다. 또한 기본 제공 TextEditor에는 몇 가지 유용한 리치 텍스트 편집 기능 옵션도 포함되어 있습니다!
1
2
3
4
5
6
7
8
9
10
import SwiftUI
struct ContentView: View {
@State private var richText = AttributedString()
var body: some View {
TextEditor(text: $richText)
.frame(height: 300)
.padding(15)
}
}
✅ Native WebView
SwiftUI가 이제 Native WebView 를 지원합니다. 스크롤 위치 추적, 스크롤 위치 업데이트, 특정 제스처 비활성화 등 다양한 기본 내장 기능들이 포함되어 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
import SwiftUI
import WebKit
struct ContentView: View {
var body: some View {
WebView(url: url)
}
var url: URL {
URL(string: "https://developer.apple.com")!
}
}
✅ WebPage()
WebPage를 사용하여 페이지를 불러올 수도 있습니다. 이렇게 하면 웹 페이지를 보다 프로그래밍적으로 제어할 수 있습니다!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import SwiftUI
import WebKit
struct ContentView: View {
@State private var page = WebPage()
var body: some View {
WebView(page)
.webViewMagnificationGestures(.disabled)
.onAppear {
page.load(URLRequest(url: url))
}
}
var url: URL {
URL(string: "https://developer.apple.com")!
}
}
✅ TabView Customizations
기본적으로, SwiftUI는 새로운 iOS 26 글로시 탭 바(glossy tab bar) 에 자동으로 적용됩니다. (단, 앱이 이전에 네이티브 탭 바를 사용했을 경우이며, 커스텀 탭 바를 사용한 경우는 제외됩니다.)
iOS 26에서는 검색(Search) 역할의 탭 아이템이 탭 바에서 제외되어, 탭 바와는 별도로 표시됩니다!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
Tab.init("Home", systemImage: "house.fill") {
Text("Home")
}
Tab.init("Favorite", systemImage: "suit.heart.fill") {
Text("Favorite")
}
Tab.init("Settings", systemImage: "gearshape.fill") {
Text("Settings")
}
Tab.init("Search", systemImage: "magnifyingglass", role: .search) {
Text("Search")
}
}
}
}
+. tabViewBottomAccessory()
iOS 26에서는 TabView 가 이제 탭 바 위에 액세서리 뷰를 추가하는 것을 지원합니다.
(Apple Music 앱과 동일한 방식입니다.)
1
2
3
4
5
6
7
8
9
10
11
12
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {...}
.tabViewBottomAccessory {
Text("Custom Music Player!")
.padding(.horizontal, 15)
}
}
}
+. tabBarMinimizeBehaviour()
Apple Music 앱과 마찬가지로, 이제 탭 바는 스크롤을 내리거나(Scrolled Down) 올릴 때(Scrolled Up) 최소화(minimizing)를 지원합니다.
스크롤을 내리거나 올리면, 검색(Search) 역할의 탭 아이템은 오른쪽으로 이동하고, 탭 바는 최소화되어 왼쪽으로 밀립니다.
또한, 액세서리 뷰는 중앙으로 이동하게 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
Tab.init("Home", systemImage: "house.fill") {
ScrollView(.vertical) {
Text("Home")
.containerRelativeFrame([.horizontal])
.containerRelativeFrame(.vertical) { value, _ in
value * 3
}
}
}
Tab.init("Favorite", systemImage: "suit.heart.fill") {
Text("Favorite")
}
Tab.init("Settings", systemImage: "gearshape.fill") {
Text("Settings")
}
Tab.init("Search", systemImage: "magnifyingglass", role: .search) {
Text("Search")
}
}
.tabViewBottomAccessory {
Text("Custom Music Player!")
.padding(.horizontal, 15)
}
.tabBarMinimizeBehavior(.onScrollDown)
}
}
📒 tabBarAccessoryPlacement 환경(environment)을 사용하여 탭 바 액세서리 뷰를 상황에 맞게 업데이트할 수 있습니다!
✅ ToolBarSpacer
기본적으로, 모든 ToolBar 아이템은 iOS 26에서 함께 그룹화됩니다. 그러나 경우에 따라 버튼을 그룹에서 분리해야 할 필요가 있을 수 있습니다. 이 새로운 modifier는 ToolBar 아이템을 분리하거나, 여러 개의 ToolBar 아이템 그룹을 만드는 데 특히 유용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationStack {
List {
}
.navigationTitle("Todo's")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
HStack {
Button("", systemImage: "suit.heart.fill") {
}
Button("", systemImage: "magnifyingglass") {
}
}
}
ToolbarSpacer(.fixed, placement: .topBarTrailing)
ToolbarItem(placement: .topBarTrailing) {
Button("", systemImage: "person.fill") {
}
}
}
}
}
}
소스정리
출처
Kavsoft 영상을 공부하면서 정리한 내용