ModelActorX

Swift 6 License: MIT

ModelActorX 是一个 Swift 库,提供了自定义的宏 ModelActorXMainModelActorX,用于增强和扩展 SwiftData 的 ModelActor 的功能。 这些宏通过允许开发者控制初始化的生成以及在 actor 和类中声明额外的变量,提供了额外的灵活性。


不要错过关于 Swift、SwiftUI、Core Data 和 SwiftData 的最新更新和精彩文章。订阅 Fatbobman's Swift Weekly,直接在您的收件箱中接收每周的见解和有价值的内容。


特性

安装

Swift Package Manager

使用 Swift Package Manager 将 ModelActorX 添加到您的项目中。在您的 Package.swift 文件中,添加

dependencies: [
    .package(url: "https://github.com/fatbobman/ModelActorX.git", from: "0.1.0")
]

或者,在 Xcode 中

  1. 转到 File > Add Packages...
  2. 输入仓库 URL:https://github.com/fatbobman/ModelActorX.git
  3. 按照提示将包添加到您的项目中。

用法

ModelActorX

ModelActorX 宏用于定义具有类似于 SwiftData 的 ModelActor 功能的 actor。 关键区别在于 disableGenerateInit 参数,当设置为 true 时,它会阻止自动生成初始化器。 这允许您声明额外的变量并提供自定义初始化器。

基本用法

@ModelActorX
actor DataHandler {
    func newItem(date: Date) throws -> PersistentIdentifier {
        let item = Item(timestamp: date)
        modelContext.insert(item)
        try modelContext.save()
        return item.persistentModelID
    }

    func getTimestampFromItemID(_ itemID: PersistentIdentifier) -> Date? {
        return self[itemID, as: Item.self]?.timestamp
    }
}

自定义初始化器

@ModelActorX(disableGenerateInit: true)
actor DataHandler1 {
    let date: Date

    func newItem() throws -> PersistentIdentifier {
        let item = Item(timestamp: date)
        modelContext.insert(item)
        try modelContext.save()
        return item.persistentModelID
    }

    func getTimestampFromItemID(_ itemID: PersistentIdentifier) -> Date? {
        return self[itemID, as: Item.self]?.timestamp
    }

    init(container: ModelContainer, date: Date) {
        self.date = date
        modelContainer = container
        let modelContext = ModelContext(modelContainer)
        modelExecutor = DefaultSerialModelExecutor(modelContext: modelContext)
    }
}

MainModelActorX

MainModelActorX 宏用于生成一个在 MainActor 上运行的类。这对于 UI 更新或任何需要在主线程上执行的操作特别有用。 生成的类使用 ModelContainer 中的 mainContext

基本用法

@MainActor
@MainModelActorX
final class MainDataHandler {
    func newItem(date: Date) throws -> PersistentIdentifier {
        let item = Item(timestamp: date)
        modelContext.insert(item)
        try modelContext.save()
        return item.persistentModelID
    }

    func getTimestampFromItemID(_ itemID: PersistentIdentifier) -> Date? {
        return self[itemID, as: Item.self]?.timestamp
    }
}

自定义初始化器

@MainActor
@MainModelActorX(disableGenerateInit: true)
final class MainDataHandler1 {
    let date: Date

    func newItem() throws -> PersistentIdentifier {
        let item = Item(timestamp: date)
        modelContext.insert(item)
        try modelContext.save()
        return item.persistentModelID
    }

    func getTimestampFromItemID(_ itemID: PersistentIdentifier) -> Date? {
        return self[itemID, as: Item.self]?.timestamp
    }

    init(container: ModelContainer, date: Date) {
        self.date = date
        modelContainer = container
    }
}

将 ModelActorX 与 MainActor 结合使用

@ModelActorX 还提供了一个使用 @MainActor 声明的构造器。 当您使用此构造器生成 actor 时,它将直接利用 mainContext(视图上下文),并且整个 actor 将在主线程上运行。 与 @MainModelActorX 的主要区别在于,类型仍然是一个 actor。 这意味着基于 ModelActor 构建的现有代码不需要修改——调用仍将保留 await

在 iOS 18 解决与使用 @ModelActor 进行更新相关的响应性问题之前,此方法可能是一个理想的临时解决方案。

@ModelActorX
actor DataHandler {}

Task{ @MainActor in
   let handler = DataHandler(mainContext: container.mainContext) // Use the view context for construction
   await handler.updateItem(id: id) // Even on the main thread, you can still use `await`
}

测试示例

ModelActorXTests

struct ModelActorXTests {
    @Test func example1() async throws {
        let container = createContainer()
        let handler = DataHandler(modelContainer: container)
        let now = Date.now
        let id = try await handler.newItem(date: now)
        let date = await handler.getTimestampFromItemID(id)
        #expect(date == now)
    }

    @Test func example2() async throws {
        let container = createContainer()
        let now = Date.now
        let handler = DataHandler1(container: container, date: now)
        let id = try await handler.newItem()
        let date = await handler.getTimestampFromItemID(id)
        #expect(date == now)
    }
}

MainModelActorXTests

@MainActor
struct MainModelActorXTests {
    @Test
    func test1() async throws {
        let container = createContainer()
        let handler = MainDataHandler(modelContainer: container)
        let now = Date.now
        let id = try handler.newItem(date: now)
        let date = handler.getTimestampFromItemID(id)
        #expect(date == now)
    }

    @Test
    func test2() async throws {
        let container = createContainer()
        let now = Date.now
        let handler = MainDataHandler1(container: container, date: now)
        let id = try handler.newItem()
        let date = handler.getTimestampFromItemID(id)
        #expect(date == now)
    }
}

参数

ModelActorX 宏

MainModelActorX 宏

要求

许可

ModelActorX 根据 MIT 许可证发布。 有关详细信息,请参阅 LICENSE

鸣谢

贡献

欢迎贡献!如果您有改进的想法或发现错误,请打开一个 issue 或提交一个 pull request。

Buy Me A Coffee