App Intents và Siri Shortcuts: tự động hóa iOS nâng cao 2026
Hướng dẫn đầy đủ về App Intents và Siri Shortcuts cho iOS 18+. Tạo hành động Siri tùy chỉnh, tích hợp Apple Intelligence và tự động hóa ứng dụng Swift trong năm 2026.

Năm 2026, với Apple Intelligence và framework App Intents, ứng dụng iOS bước vào kỷ nguyên mà ý định người dùng quan trọng hơn giao diện đồ họa. Các ứng dụng không phơi bày intents trở nên vô hình trong một hệ điều hành lấy AI làm trung tâm. App Intents tạo nên nền tảng cho phép Siri, Spotlight, widget và Action Button tương tác với chức năng của ứng dụng.
Bài viết trình bày toàn bộ quá trình tạo App Intents và Siri Shortcuts cho iOS 18+, từ các khái niệm cơ bản cho đến tích hợp Apple Intelligence và App Intent Domains.
Hiểu framework App Intents
Framework App Intents, ra mắt cùng iOS 16, hiện đại hóa việc xây dựng intents trong Swift bằng cách thay thế framework SiriKit Intents cũ. Kiến trúc khai báo này cho phép tạo các hành động được hệ thống khám phá: Spotlight, ứng dụng Phím tắt, Siri và Action Button.
import AppIntents
// Một AppIntent đại diện cho hành động mà người dùng có thể thực hiện
struct CreateTaskIntent: AppIntent {
// Tiêu đề hiển thị trong Phím tắt và Siri
static var title: LocalizedStringResource = "Tạo nhiệm vụ"
// Mô tả cho khả năng truy cập và đề xuất
static var description = IntentDescription(
"Tạo nhiệm vụ mới trong ứng dụng."
)
// Tham số có xác thực tự động
@Parameter(title: "Tiêu đề nhiệm vụ")
var taskTitle: String
// Tham số tùy chọn với giá trị mặc định
@Parameter(title: "Mức độ ưu tiên", default: .medium)
var priority: TaskPriority
// Thực thi hành động
func perform() async throws -> some IntentResult & ReturnsValue<TaskEntity> {
// Tạo nhiệm vụ qua service
let task = TaskService.shared.createTask(
title: taskTitle,
priority: priority
)
// Trả về entity đã tạo để chaining
return .result(value: TaskEntity(task: task))
}
}Intent khai báo các tham số qua property wrapper @Parameter, cho phép Siri yêu cầu các giá trị thiếu. Phương thức perform() thực thi logic nghiệp vụ và trả về kết quả đã định kiểu.
Định nghĩa App Entities cho dữ liệu
App Entities đại diện cho "danh từ" của ứng dụng: các đối tượng mà intents thao tác trên đó. Chúng cho phép Siri hiểu và xử lý dữ liệu ứng dụng.
import AppIntents
// Mô hình dữ liệu nội bộ
struct Task: Identifiable, Codable {
let id: UUID
var title: String
var priority: TaskPriority
var isCompleted: Bool
var dueDate: Date?
}
// Entity được phơi bày cho hệ thống
struct TaskEntity: AppEntity {
// Định danh duy nhất bắt buộc
var id: UUID
// Các thuộc tính có thể hiển thị
var title: String
var priority: TaskPriority
var isCompleted: Bool
// Cấu hình hiển thị trong hệ thống
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Nhiệm vụ"
// Biểu diễn trực quan của instance
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(title)",
subtitle: "\(priority.rawValue)",
image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle")
)
}
// Query mặc định để tìm kiếm entity
static var defaultQuery = TaskEntityQuery()
// Initializer từ mô hình nội bộ
init(task: Task) {
self.id = task.id
self.title = task.title
self.priority = task.priority
self.isCompleted = task.isCompleted
}
}
// Query để tìm kiếm và lọc entity
struct TaskEntityQuery: EntityQuery {
// Tìm kiếm theo định danh
func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
TaskService.shared.fetchTasks()
.filter { identifiers.contains($0.id) }
.map { TaskEntity(task: $0) }
}
// Đề xuất hiển thị trong giao diện
func suggestedEntities() async throws -> [TaskEntity] {
TaskService.shared.fetchTasks()
.filter { !$0.isCompleted }
.prefix(5)
.map { TaskEntity(task: $0) }
}
}EntityQuery định nghĩa cách hệ thống tìm kiếm và đề xuất entity. Các phương thức entities(for:) và suggestedEntities() cung cấp cho các giao diện Siri và Phím tắt.
Sử dụng AppEnum cho các kiểu có tập giá trị cố định (mức độ ưu tiên, trạng thái), và AppEntity cho các kiểu động được người dùng tạo (nhiệm vụ, ghi chú, danh bạ).
Tạo App Enums cho giá trị cố định
App Enums phơi bày các kiểu liệt kê cho hệ thống, cho phép Siri đưa ra các lựa chọn theo ngữ cảnh.
import AppIntents
// Enum được phơi bày cho hệ thống
enum TaskPriority: String, AppEnum, Codable {
case low
case medium
case high
// Tên kiểu hiển thị
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Mức độ ưu tiên"
// Biểu diễn từng trường hợp
static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [
.low: DisplayRepresentation(
title: "Thấp",
image: .init(systemName: "arrow.down.circle")
),
.medium: DisplayRepresentation(
title: "Trung bình",
image: .init(systemName: "minus.circle")
),
.high: DisplayRepresentation(
title: "Cao",
image: .init(systemName: "exclamationmark.circle")
)
]
}
// Enum cho trạng thái nhiệm vụ
enum TaskStatus: String, AppEnum, Codable {
case pending
case inProgress
case completed
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Trạng thái"
static var caseDisplayRepresentations: [TaskStatus: DisplayRepresentation] = [
.pending: "Đang chờ",
.inProgress: "Đang tiến hành",
.completed: "Hoàn thành"
]
}Các biểu diễn trực quan (icon SF Symbols) làm phong phú hiển thị trong Phím tắt và đề xuất Siri.
Triển khai AppShortcutsProvider
AppShortcutsProvider phơi bày App Shortcuts cho hệ thống, làm chúng có sẵn ngay lập tức mà không cần cấu hình của người dùng. Các phím tắt này xuất hiện trong Spotlight, Siri và Action Button.
import AppIntents
// Provider khai báo tất cả phím tắt của ứng dụng
struct TaskAppShortcutsProvider: AppShortcutsProvider {
// Tối đa 10 phím tắt mỗi ứng dụng
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
// Phím tắt để tạo nhiệm vụ
AppShortcut(
intent: CreateTaskIntent(),
phrases: [
// Placeholder .applicationName là BẮT BUỘC
"Tạo nhiệm vụ với \(.applicationName)",
"Nhiệm vụ mới trong \(.applicationName)",
"Thêm nhiệm vụ vào \(.applicationName)"
],
shortTitle: "Tạo nhiệm vụ",
systemImageName: "plus.circle"
)
// Phím tắt để liệt kê nhiệm vụ
AppShortcut(
intent: ListTasksIntent(),
phrases: [
"Hiển thị nhiệm vụ của tôi trong \(.applicationName)",
"Hiển thị nhiệm vụ từ \(.applicationName)",
"Nhiệm vụ của tôi là gì \(.applicationName)"
],
shortTitle: "Nhiệm vụ của tôi",
systemImageName: "list.bullet"
)
// Phím tắt với tham số động
AppShortcut(
intent: CompleteTaskIntent(),
phrases: [
"Hoàn thành \(\.$taskName) trong \(.applicationName)",
"Đánh dấu \(\.$taskName) là xong với \(.applicationName)"
],
shortTitle: "Hoàn thành nhiệm vụ",
systemImageName: "checkmark.circle"
)
}
}Các cụm từ giọng nói phải bao gồm placeholder \(.applicationName) để Siri xác định ứng dụng đích. Các tham số động như \(\.$taskName) cho phép các lệnh theo ngữ cảnh.
Một ứng dụng có thể khai báo tối đa 10 App Shortcuts. Nên ưu tiên các hành động thường xuyên và hữu ích nhất cho người dùng.
Intents với tham số phức tạp
Intents có thể chấp nhận các tham số phức tạp, bao gồm entity và tùy chọn cấu hình.
import AppIntents
struct CompleteTaskIntent: AppIntent {
static var title: LocalizedStringResource = "Hoàn thành nhiệm vụ"
static var description = IntentDescription(
"Đánh dấu nhiệm vụ là đã hoàn thành."
)
// Tham số entity với tìm kiếm tự động
@Parameter(title: "Nhiệm vụ")
var task: TaskEntity
// Tham số tùy chọn với ngày
@Parameter(title: "Ngày hoàn thành")
var completionDate: Date?
// Cấu hình hộp thoại Siri
static var parameterSummary: some ParameterSummary {
Summary("Hoàn thành \(\.$task)") {
\.$completionDate
}
}
func perform() async throws -> some IntentResult & ProvidesDialog {
// Cập nhật nhiệm vụ
TaskService.shared.completeTask(
id: task.id,
completionDate: completionDate ?? Date()
)
// Phản hồi giọng nói cho Siri
return .result(
dialog: "Nhiệm vụ \(task.title) đã được đánh dấu là hoàn thành."
)
}
}
// Intent trả về danh sách entity
struct ListTasksIntent: AppIntent {
static var title: LocalizedStringResource = "Liệt kê nhiệm vụ"
// Bộ lọc tùy chọn
@Parameter(title: "Trạng thái", default: nil)
var statusFilter: TaskStatus?
@Parameter(title: "Mức độ ưu tiên", default: nil)
var priorityFilter: TaskPriority?
func perform() async throws -> some IntentResult & ReturnsValue<[TaskEntity]> {
var tasks = TaskService.shared.fetchTasks()
// Áp dụng bộ lọc
if let status = statusFilter {
tasks = tasks.filter {
switch status {
case .completed: return $0.isCompleted
case .pending, .inProgress: return !$0.isCompleted
}
}
}
if let priority = priorityFilter {
tasks = tasks.filter { $0.priority == priority }
}
let entities = tasks.map { TaskEntity(task: $0) }
return .result(value: entities)
}
}ParameterSummary định nghĩa cách Siri trình bày intent trong khi thực thi bằng giọng nói, mang lại phản hồi tự nhiên.
Sẵn sàng chinh phục phỏng vấn iOS?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Tích hợp với Apple Intelligence
iOS 18 giới thiệu App Intent Domains, các tập hợp API được thiết kế cho các chức năng cụ thể. Các domain này cho phép Apple Intelligence hiểu và thực thi các hành động với độ chính xác cao hơn.
import AppIntents
// Tuân thủ domain Bookmarks để tích hợp Apple Intelligence
struct SaveBookmarkIntent: AppIntent {
static var title: LocalizedStringResource = "Lưu dấu trang"
// Tham số URL với xác thực tự động
@Parameter(title: "URL")
var url: URL
@Parameter(title: "Tiêu đề", default: nil)
var title: String?
@Parameter(title: "Thư mục", default: nil)
var folder: BookmarkFolderEntity?
// Mở ứng dụng nếu cần
static var openAppWhenRun: Bool = false
func perform() async throws -> some IntentResult & ProvidesDialog {
let bookmark = BookmarkService.shared.save(
url: url,
title: title,
folder: folder?.id
)
return .result(
dialog: "Dấu trang đã lưu: \(bookmark.title)"
)
}
}
// Intent với khả năng nhận biết nội dung màn hình (iOS 18.4+)
struct AnalyzeScreenContentIntent: AppIntent {
static var title: LocalizedStringResource = "Phân tích nội dung"
// Truy cập ngữ cảnh màn hình qua Apple Intelligence
@Parameter(title: "Ngữ cảnh")
var screenContext: String?
func perform() async throws -> some IntentResult & ProvidesDialog {
guard let context = screenContext else {
return .result(dialog: "Không có nội dung để phân tích.")
}
// Xử lý nội dung được Apple Intelligence trích xuất
let analysis = ContentAnalyzer.analyze(context)
return .result(dialog: analysis.summary)
}
}Các domain được định nghĩa trước (Books, Camera, Spreadsheets) cho phép Siri đáp ứng các yêu cầu chính xác nhờ các mô hình được huấn luyện trên các tác vụ cụ thể này.
Hành động Siri với xác nhận của người dùng
Đối với các hành động nhạy cảm, hệ thống có thể yêu cầu xác nhận trước khi thực thi.
import AppIntents
struct DeleteTaskIntent: AppIntent {
static var title: LocalizedStringResource = "Xóa nhiệm vụ"
@Parameter(title: "Nhiệm vụ")
var task: TaskEntity
// Yêu cầu xác nhận của người dùng
static var isDiscoverable: Bool = true
func perform() async throws -> some IntentResult & ProvidesDialog {
// Yêu cầu xác nhận qua hộp thoại
try await requestConfirmation(
result: .result(
dialog: "Bạn có chắc muốn xóa \(task.title) không?"
)
)
// Xóa sau khi xác nhận
TaskService.shared.deleteTask(id: task.id)
return .result(
dialog: "Nhiệm vụ \(task.title) đã được xóa."
)
}
}
// Intent với nhiều bước hộp thoại
struct ScheduleTaskIntent: AppIntent {
static var title: LocalizedStringResource = "Lập lịch nhiệm vụ"
@Parameter(title: "Nhiệm vụ")
var task: TaskEntity
@Parameter(title: "Ngày")
var scheduledDate: Date
@Parameter(title: "Lời nhắc", default: true)
var setReminder: Bool
static var parameterSummary: some ParameterSummary {
When(\.$setReminder, .equalTo, true) {
Summary("Lập lịch \(\.$task) cho \(\.$scheduledDate) kèm lời nhắc")
} otherwise: {
Summary("Lập lịch \(\.$task) cho \(\.$scheduledDate)")
}
}
func perform() async throws -> some IntentResult & ProvidesDialog {
TaskService.shared.schedule(
taskId: task.id,
date: scheduledDate,
reminder: setReminder
)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .short
let formattedDate = dateFormatter.string(from: scheduledDate)
return .result(
dialog: "Nhiệm vụ đã được lập lịch cho \(formattedDate)."
)
}
}Phương thức requestConfirmation tạm dừng thực thi cho đến khi người dùng xác nhận, bảo vệ khỏi các hành động vô ý.
Intents trong widget tương tác
App Intents tích hợp tự nhiên với WidgetKit để tạo các widget tương tác trên iOS 17+.
import AppIntents
import WidgetKit
// Intent được tối ưu cho widget (thực thi nhanh)
struct ToggleTaskFromWidgetIntent: AppIntent {
static var title: LocalizedStringResource = "Chuyển đổi nhiệm vụ"
@Parameter(title: "ID nhiệm vụ")
var taskID: String
init() {}
init(taskID: UUID) {
self.taskID = taskID.uuidString
}
// Không hộp thoại cho widget
func perform() async throws -> some IntentResult {
guard let uuid = UUID(uuidString: taskID) else {
return .result()
}
TaskService.shared.toggleCompletion(taskId: uuid)
// Làm mới widget tức thì
WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")
return .result()
}
}
// View widget với nút tương tác
import SwiftUI
struct TaskWidgetView: View {
let task: Task
var body: some View {
Button(intent: ToggleTaskFromWidgetIntent(taskID: task.id)) {
HStack {
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundStyle(task.isCompleted ? .green : .secondary)
Text(task.title)
.strikethrough(task.isCompleted)
}
.padding()
}
.buttonStyle(.plain)
}
}Widget sử dụng cú pháp Button(intent:) để kết nối tương tác trực tiếp với App Intent mà không mở ứng dụng.
Cấu hình Action Button
Action Button trên iPhone 15 Pro và mới hơn có thể kích hoạt App Shortcuts trực tiếp.
import AppIntents
// Intent được tối ưu cho Action Button
struct QuickCaptureIntent: AppIntent {
static var title: LocalizedStringResource = "Ghi nhận nhanh"
static var description = IntentDescription(
"Tạo nhanh nhiệm vụ với tiêu đề."
)
// Mở ứng dụng để nhập
static var openAppWhenRun: Bool = true
func perform() async throws -> some IntentResult & OpensIntent {
// Thông báo để mở màn hình ghi nhận nhanh
NotificationCenter.default.post(
name: .quickCaptureTriggered,
object: nil
)
return .result(opensIntent: ShowQuickCaptureViewIntent())
}
}
// Khai báo trong AppShortcutsProvider cho Action Button
extension TaskAppShortcutsProvider {
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
// ... các phím tắt khác
AppShortcut(
intent: QuickCaptureIntent(),
phrases: [
"Ghi nhận nhanh \(.applicationName)",
"Ghi chú nhanh \(.applicationName)"
],
shortTitle: "Ghi nhận",
systemImageName: "bolt.circle"
)
}
}Người dùng có thể cấu hình Action Button để kích hoạt phím tắt này qua Cài đặt > Action Button.
iOS 18 giới thiệu các macro @DeferredProperty và @ComputedProperty để giảm boilerplate. App Intents cũng có thể nằm trong Swift Packages để dùng lại đa nền tảng.
Intents trong Swift Packages
App Intents có thể được định nghĩa trong Swift Packages để chia sẻ giữa iOS, macOS và watchOS.
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "TaskIntents",
platforms: [
.iOS(.v17),
.macOS(.v14),
.watchOS(.v10)
],
products: [
.library(name: "TaskIntents", targets: ["TaskIntents"])
],
dependencies: [],
targets: [
.target(
name: "TaskIntents",
dependencies: []
)
]
)
// Sources/TaskIntents/SharedIntents.swift
import AppIntents
// Intent dùng chung đa nền tảng
public struct SharedCreateTaskIntent: AppIntent {
public static var title: LocalizedStringResource = "Tạo nhiệm vụ"
@Parameter(title: "Tiêu đề")
public var taskTitle: String
public init() {}
public func perform() async throws -> some IntentResult {
// Logic dùng chung
await TaskRepository.shared.create(title: taskTitle)
return .result()
}
}
// Mở rộng dành riêng cho nền tảng trong ứng dụng
#if os(iOS)
extension SharedCreateTaskIntent {
// Hành vi đặc thù iOS
static var openAppWhenRun: Bool = false
}
#endifKiến trúc này duy trì một mã nguồn duy nhất cho intents trong khi điều chỉnh hành vi theo nền tảng.
Kiểm thử App Intents
App Intents có thể được kiểm thử đơn vị giống như bất kỳ mã Swift nào.
import XCTest
import AppIntents
@testable import TaskApp
final class TaskIntentTests: XCTestCase {
override func setUp() {
super.setUp()
// Đặt lại service cho các test cô lập
TaskService.shared.reset()
}
func testCreateTaskIntent() async throws {
// Given
var intent = CreateTaskIntent()
intent.taskTitle = "Nhiệm vụ kiểm thử"
intent.priority = .high
// When
let result = try await intent.perform()
// Then
let tasks = TaskService.shared.fetchTasks()
XCTAssertEqual(tasks.count, 1)
XCTAssertEqual(tasks.first?.title, "Nhiệm vụ kiểm thử")
XCTAssertEqual(tasks.first?.priority, .high)
}
func testCompleteTaskIntent() async throws {
// Given
let task = TaskService.shared.createTask(
title: "Nhiệm vụ cần hoàn thành",
priority: .medium
)
var intent = CompleteTaskIntent()
intent.task = TaskEntity(task: task)
// When
_ = try await intent.perform()
// Then
let updatedTask = TaskService.shared.fetchTask(id: task.id)
XCTAssertTrue(updatedTask?.isCompleted ?? false)
}
func testEntityQuery() async throws {
// Given
let task1 = TaskService.shared.createTask(title: "Nhiệm vụ 1", priority: .low)
let task2 = TaskService.shared.createTask(title: "Nhiệm vụ 2", priority: .high)
let query = TaskEntityQuery()
// When
let entities = try await query.entities(for: [task1.id, task2.id])
// Then
XCTAssertEqual(entities.count, 2)
}
}Các test xác minh hành vi của intent độc lập với giao diện hệ thống.
Thực hành tốt nhất và tối ưu
Một số mẫu đảm bảo App Intents hiệu năng cao và trải nghiệm người dùng tối ưu.
import AppIntents
// 1. Cụm từ được bản địa hóa
struct LocalizedTaskIntent: AppIntent {
static var title: LocalizedStringResource = "task.create.title"
static var description = IntentDescription(
"task.create.description",
categoryName: "task.category"
)
@Parameter(title: "task.parameter.title")
var taskTitle: String
func perform() async throws -> some IntentResult {
// ...
return .result()
}
}
// 2. Xử lý lỗi đúng cách
enum TaskIntentError: Error, CustomLocalizedStringResourceConvertible {
case taskNotFound
case invalidInput
case serviceUnavailable
var localizedStringResource: LocalizedStringResource {
switch self {
case .taskNotFound:
return "Không tìm thấy nhiệm vụ."
case .invalidInput:
return "Dữ liệu không hợp lệ."
case .serviceUnavailable:
return "Dịch vụ tạm thời không khả dụng."
}
}
}
struct RobustTaskIntent: AppIntent {
static var title: LocalizedStringResource = "Intent vững chắc"
@Parameter(title: "ID")
var taskId: String
func perform() async throws -> some IntentResult & ProvidesDialog {
guard let uuid = UUID(uuidString: taskId) else {
throw TaskIntentError.invalidInput
}
guard let task = TaskService.shared.fetchTask(id: uuid) else {
throw TaskIntentError.taskNotFound
}
return .result(dialog: "Đã tìm thấy nhiệm vụ: \(task.title)")
}
}
// 3. Tối ưu EntityQuery
struct OptimizedTaskQuery: EntityStringQuery {
// Tìm kiếm văn bản được tối ưu
func entities(matching string: String) async throws -> [TaskEntity] {
// Tìm kiếm phía service có giới hạn
TaskService.shared.search(query: string, limit: 10)
.map { TaskEntity(task: $0) }
}
// Đề xuất giới hạn để tối ưu hiệu năng
func suggestedEntities() async throws -> [TaskEntity] {
TaskService.shared.fetchRecentTasks(limit: 5)
.map { TaskEntity(task: $0) }
}
}
// 4. Focus Filter cho chế độ Tập trung
struct TaskFocusFilter: SetFocusFilterIntent {
static var title: LocalizedStringResource = "Lọc nhiệm vụ"
@Parameter(title: "Chỉ hiển thị ưu tiên cao")
var showHighPriorityOnly: Bool
func perform() async throws -> some IntentResult {
TaskService.shared.setFocusFilter(highPriorityOnly: showHighPriorityOnly)
return .result()
}
}Các mẫu này đảm bảo tích hợp mượt mà với hệ thống đồng thời duy trì hiệu năng tối ưu.
Kết luận
App Intents và Siri Shortcuts thay đổi cách người dùng tương tác với ứng dụng iOS. Năm 2026, với Apple Intelligence, việc phơi bày intents không còn là tùy chọn mà là điều thiết yếu để mang đến trải nghiệm hiện đại và trực quan.
Danh sách kiểm tra App Intents iOS 18+
- ✅ Tạo AppIntents cho các hành động chính của ứng dụng
- ✅ Định nghĩa AppEntities cho dữ liệu có thể thao tác
- ✅ Sử dụng AppEnum cho các kiểu liệt kê
- ✅ Triển khai AppShortcutsProvider với cụm từ giọng nói
- ✅ Tuân thủ giới hạn tối đa 10 App Shortcuts
- ✅ Bao gồm
\(.applicationName)trong tất cả cụm từ - ✅ Cấu hình ParameterSummary để có phản hồi Siri
- ✅ Tích hợp các domain Apple Intelligence khi phù hợp
- ✅ Kiểm thử intents bằng test đơn vị
- ✅ Bản địa hóa tiêu đề và mô tả
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Thẻ
Chia sẻ
Bài viết liên quan

WidgetKit iOS 17+: Widget Tương Tác với App Intents
Hướng dẫn đầy đủ tạo widget iOS tương tác với WidgetKit và App Intents. Nút bấm, công tắc, hoạt ảnh và thực hành tốt nhất cho iOS 17+ vào năm 2026.

Combine vs async/await trong Swift: Mẫu Hình Di Cư Tiến Bộ
Hướng dẫn đầy đủ về di cư từ Combine sang async/await trong Swift: chiến lược tiến bộ, mẫu hình bắc cầu và sự cùng tồn tại của các mô hình trong codebase iOS.

Câu hỏi phỏng vấn về khả năng tiếp cận iOS năm 2026: VoiceOver và Dynamic Type
Chuẩn bị phỏng vấn iOS với những câu hỏi then chốt về khả năng tiếp cận: VoiceOver, Dynamic Type, các trait ngữ nghĩa và kiểm thử.