Use Xcode for server-side development
Developer Tools 进阶 20m

使用 Xcode 进行服务端开发

Use Xcode for server-side development

2022年6月6日

在 Apple 官方观看视频

一句话判断

苹果终于正式拥抱了 Server-side Swift——用 Xcode 同时开发客户端和服务端,共享数据模型,Food Truck 示例项目就是这套工作流的最佳示范。

这场 Session 讲了什么

苹果在 WWDC 2022 上正式展示了如何用 Xcode 进行服务端 Swift 开发。这不是一个全新的概念——Vapor 和 Kitura 等框架已经存在多年——但苹果这次提供了官方的工作流指导和一个完整的示例项目 Food Truck。

Session 的核心思路是:用一个 Xcode Workspace 同时管理客户端(iOS App)和服务端(Swift 后端)两个 target。共享的数据模型放在一个独立的 Swift Package 里,两端都依赖它。这样你修改一个数据结构,客户端和服务端会同时更新,再也不用手动同步 API 定义。

Food Truck 示例展示了完整的开发流程:Vapor 框架搭建的 REST API 服务端、SwiftUI 构建的 iOS 客户端、共享的 FoodTruckModel 数据模型。你可以在 Xcode 中直接调试服务端代码,设置断点、查看变量,和调试客户端完全一样。

值得深挖的点

共享数据模型的工程价值。 在传统的客户端+服务端开发中,API 接口的同步是一个持续的痛点。Swift 的 Package 系统提供了一个优雅的解决方案:把 DTO(Data Transfer Object)和 API 路由定义放在一个共享的 Package 里。客户端和服务端都依赖这个 Package,编译器会帮你确保两端的类型定义一致。这比 OpenAPI/Swagger 生成代码的方式更轻量,也更符合 Swift 的类型系统。

Xcode 作为全栈 IDE 的可能性。 Session 展示了 Xcode 同时运行 iOS 模拟器和本地服务器的能力。你在客户端发起的网络请求可以直接在同一个 Xcode 实例中调试服务端的处理逻辑——包括断点、变量检查、甚至 Instruments 性能分析。这种体验对于习惯了切换 IDE 的全栈开发者来说是巨大的效率提升。

代码片段

// 共享的数据模型 Package
// Package.swift
// Targets: FoodTruckModel (shared), FoodTruckClient, FoodTruckServer

// 共享的数据模型
public struct Donut: Codable, Identifiable {
    public let id: UUID
    public var name: String
    public var flavor: String
    public var price: Double
    
    public init(name: String, flavor: String, price: Double) {
        self.id = UUID()
        self.name = name
        self.flavor = flavor
        self.price = price
    }
}

// 服务端路由(Vapor)
import Vapor
import FoodTruckModel

func routes(_ app: Application) throws {
    app.get("donuts") { req async throws -> [Donut] in
        // 直接返回共享的 Donut 类型
        try await DonutController().getAll()
    }
    
    app.post("donuts") { req async throws -> Donut in
        let newDonut = try req.content.decode(Donut.self)
        return try await DonutController().create(newDonut)
    }
}

// 客户端直接使用相同的 Donut 类型
import FoodTruckModel

class DonutService {
    func fetchDonuts() async throws -> [Donut] {
        let (data, _) = try await URLSession.shared.data(from: donutsURL)
        return try JSONDecoder().decode([Donut].self, from: data)
    }
}

最佳实践

  • 使用 Swift Package 来组织共享代码,不要把模型文件复制粘贴到两个 target 里。
  • 在 Xcode Scheme 中设置好服务端的启动参数和端口号,确保客户端的 API 地址和本地服务端一致。
  • async/await 统一客户端和服务端的异步代码风格,减少心智负担。
  • 服务端的错误类型也放到共享 Package 里,这样客户端可以直接处理服务端的错误码。

还有什么值得关注

  • 苹果虽然没有推出自己的服务端框架,但明确推荐了 Vapor 作为首选方案。
  • Food Truck 示例项目的代码在苹果的示例代码库中可以找到,值得下载学习。
  • 服务端的 Swift 代码也可以用 Instruments 做性能分析——包括内存泄漏、CPU 占用等。
  • 如果你做的是内部工具或原型开发,这种”一个 Xcode 搞定前后端”的方式效率极高。但对于大型生产级服务端,你仍然需要考虑容器化部署、日志系统、监控等运维问题。
WWDC 2022