Lighter

Lighter 是一套技术,它应用代码生成来从 SQLite3 数据库访问 Swift,例如在 iOS 应用程序中或在服务器上。类似于 SwiftGen,但针对的是 SQLite3。

类型安全直至 SQL 架构。最先进的技术:开发者编写与 SQLite 表匹配的 Swift 结构。Enlighter 反其道而行之,生成的 Swift 代码反映了 SQLite 表的实际定义。没有出错的余地。消除了“总有某个时间在堆栈中的某个地方”的“某个地方”。
非常,非常快速Lighter 构建于数据库模式之上,因此在编译时直接知道它的外观。对于常见操作,根本不需要映射,生成的代码运行速度与手写代码一样快(通常更快)。它直接将 Swift 结构绑定到 SQLite API。
无依赖。Lighter 本身是一个小巧方便的 API,用于访问 SQLite 数据库。但是,Enlighter,代码生成器,可以生成只使用 SQLite API 且不需要任何依赖项的代码,即没有技术债务。不要发布第三方库,直接将必要的代码生成到您的应用程序中。

Lighter 主要适用于两种场景

在您的应用程序中发布 SQLite 数据库(例如,包含产品数据库)。

SQLite 数据库是一种非常节省资源的方式来发布和访问少量和大量数据。作为捆绑 JSON 资源文件的替代方案,这些文件很大并且每次启动时都必须完全解析到内存中。

使用 SQLite 数据库,只需要加载所需的数据,并且数据库文件非常紧凑(例如,没有重复的键)。

SQLite 数据库对于从网络下载数据也非常高效和有用!

维护一个快速的本地 SQL 缓存或数据库。

如果需求比像 ORM 这样的完整对象关系映射器 (CoreData) 更简单,Lighter 可以成为为本地缓存或数据库生成简洁且类型安全的 API 的绝佳方式。它很简单但使用方便,并且非常非常快,因为根本不需要运行时映射或解析。代码直接将生成的结构绑定到 SQLite API。

数据库可以动态创建,也可以从作为应用程序资源一部分发布的预填充数据库文件创建。

也支持 Linux,Lighter 可以成为主要访问只读集或在单个主机上运行的简单服务器的绝佳选择。

概述

Lighter 的工作方式与其他“映射”工具或 SQLite 包装器相反。Lighter 不是编写动态生成 SQLite 表的 Swift 代码,而是 SQLite 数据库生成 Swift 代码。可以直接从 SQLite 数据库文件生成,也可以从创建 SQLite 数据库的 SQL 文件生成。

小型示例数据库(存储在 SQLite db 中或从 .sql 文件创建)
CREATE TABLE person (
  person_id INTEGER PRIMARY KEY NOT NULL,
  name      TEXT NOT NULL,
  title     TEXT NULL
);

CREATE TABLE address (
  address_id INTEGER PRIMARY KEY NOT NULL,
  
  street  VARCHAR NULL,
  city    VARCHAR NULL,
  
  person_id INTEGER,
  FOREIGN KEY(person_id) REFERENCES person(person_id) ON DELETE CASCADE DEFERRABLE
);

可以转换为这样的结构(以高度可配置的方式)

struct ContactsDB {

  struct Person: Identifiable, Hashable {
    var id       : Int
    var name     : String
    var title    : String?
  }

  struct Address: Identifiable, Hashable {
    var id       : Int
    var street   : String?
    var city     : String?
    var personId : Int?
  }
}

代码生成器可以生成仅使用原始 SQLite3 API 的无依赖代码,也可以生成使用 Lighter 库的代码。Lighter 库不是 ORM,而只是一组允许类型安全查询的 Swift 协议(它仅用于支持代码生成器,而不是作为独立的库)。

代码生成是如何工作的?

该设置旨在与 Swift Package Manager 的新 Swift Package Plugins 功能一起使用,该功能自 Swift 5.6 起可用(并在 Xcode 14+ 中公开)。如果尚无法使用 SPM 插件,也可以直接调用 sqlite2swift 工具。
如果您想支持该项目,Mac AppStore 上还有一个 Code for SQLite3 应用程序。它以稍微更交互的方式执行与此 FOSS 项目相同的代码生成。

Lighter 包带有一个名为 Enlighter 的“构建工具插件”,该插件自动将代码生成结果集成到构建过程中。如果将其添加到目标,它将扫描数据库和 SQL 文件,并为其创建 Swift 访问器

.target(name: "ContactsDB", dependencies: [ "Lighter" ],
        resources: [ .copy("ContactsDB.sqlite3") ],
        plugins: [ "Enlighter" ]) // <== tell SPM to use Enlighter on this target

此变体是完全自动的,即 ContactsDB 目标中的其他代码可以直接访问数据库类型(例如,上面的 Person 结构)。

作为手动替代方案,提供了 Generate Code for SQLite “命令插件”。此插件执行与 Enlighter 相同的生成,但由开发人员使用 Xcode “File / Packages”菜单显式运行。它将生成的代码放置在应用程序的“Sources”文件夹中(可以在其中检查或修改)。

使用更高级别的 Lighter API 访问数据库
// Open a SQLite database embedded in the module resources:
let db = ContactsDB.module!

// Fetch the number of records:
print("Total number of people stored:", 
      try db.people.fetchCount())

// There are various ways to filter, including a plain Swift closure:
let people = try db.people.filter { person in
  person.title == nil
}

// Primary & foreign keys are directly supported:
let person    = try db.people.find(1)
let addresses = try db.addresses.fetch(for: person)

// Updates can be done one-shot or, better, using a transaction:
try await db.transaction { tx in
  var person = try tx.people.find(2)!
  
  // Update a record.
  person.title = "ZEO"
  try tx.update(person)

  // Delete a record.
  try tx.delete(person)
  
  // Reinsert the same record
  let newPerson = try tx.insert(person) // gets new ID!
}
获取单个列

SQL 的优点之一是可以选择和更新单个列,以获得最高的效率。只需要获取需要的内容(而不是完整记录)

// Fetch just the `id` and `name` columns:
let people = try await db.select(from: \.people, \.id, \.name) {
  $0.id > 2 && $0.title == nil
}

// Bulk update a specific column:
try db.update(\.people, set: \.title, to: nil, where: { record in
  record.name.hasPrefix("Duck")
})

这些引用是完全类型安全的,直至模式,只能指定 person 表中包含的列。

无依赖的 SQLite3 API

该工具包也适用于不希望额外依赖 Lighter 的情况。对于这种情况,生成器可以生成与常规 SQLite API 并行的特定于数据库的 Swift API。

// Open the database, can also just use `sqlite3_open_v2`:
var db : OpaquePointer!
sqlite3_open_contacts("contacts.db", &db)
defer { sqlite3_close(db) }

// Fetch a person by primary key:
let person = sqlite3_person_find(db, 2)
  
// Fetch and filter people:
let people = sqlite3_people_fetch(db) {
  $0.name.hasPrefix("Ja")
}

// Insert a record
var person = Person(id: 0, name: "Jason Bourne")
sqlite3_person_insert(db, &person)

代码生成器可以生成另一种风格的代码,它将相同的功能附加到生成的类型,例如:

let people = Person.fetch(in: db) { $0.name.hasPrefix("So") }
var person = Person.find(2, in: db)

person.name = "Bourne"
person.update(in: db)
person.delete(in: db)
person.insert(into: db)

使用原始 API 的主要优点是不需要任何额外的依赖项。生成的函数是完全独立的,可以真正地复制 & 粘贴到需要的地方。

美观的,自动生成的 DocC API 注释
Lighter 代码生成器还可以为数据库类型生成 API 注释。

示例:Northwind Database

感兴趣?👉 入门

Lighter 由 Helge Heß / ZeeZide 带给您。我们喜欢反馈,GitHub 星星,酷的合同工作,大概任何你能想到的赞扬形式。

想要支持我的工作?购买一个 应用程序Code for SQLite3, Past for iChat, SVG Shaper, HMScriptEditor。你不一定要用它! 😀