Swift APIs for SQLite: Type-safe down to the schema. Very, very, fast. Dependency free.

Overview

Lighter

Lighter is a set of technologies applying code generation to access SQLite3 databases from Swift, e.g. in iOS applications or on the server. Like SwiftGen but for SQLite3.

Type-safe down to the SQL schema. State of the art: Developer writes Swift structures that match a SQLite table. Enlighter reverses this, the generated Swift code reflects what the SQLite table is actually defined as. No place for mistakes. Remove a “somewhere” in the “it’s always any-o-clock somewhere in the stack”.
Very, very, fast. Lighter builds upon the database schema and hence directly knows what it looks like at compile time. For common operations no mapping is necessary at all, the generated code runs as fast (usually faster) than hand written code. It directly binds Swift structures to the SQLite API.
Dependency free. Lighter itself is a small and convenient API to access SQLite databases. Enlighter, the code generator, can however produce code that just uses the SQLite API and doesn’t require any dependencies, i.e. tech debt. Don’t ship 3rd party libraries, directly generate the necessary code into your app.

Lighter is useful for two main scenarios:

Shipping SQLite databases within your app (e.g. containing a product database).

SQLite databases are very resource efficient way to ship and access small and big amounts of data. As an alternative to bundling JSON resources files that are large and have to be parsed fully into memory on each start.

With a SQLite database only the required data needs to be loaded and the database files are extremely compact (e.g. no duplicate keys).

SQLite database are also efficient and useful for downloading data from the network!

Maintaining a fast local SQL cache or database.

If the needs are simpler than a full ORM like CoreData, Lighter can be a great way to produce neat and typesafe APIs for local caches or databases. It is basic but convenient to use and very very fast as no runtime mapping or parsing has to happen at all. The code directly binds the generated structures to the SQLite API.

Databases can be created on the fly or from prefilled database files shipped as part of the application resources.

Linux is also supported, and Lighter can be a great choice for simple servers that primarily access a readonly set or run on a single host.

Overview

Lighter works the reverse from other "mapping" tools or SQLite wrappers. Instead of writing Swift code that generates SQLite tables dynamically, Lighter generates Swift code for a SQLite database. Either literally from SQLite database files, or from SQL files that create SQLite databases.

Small Example Database (stored in either a SQLite db or created from .sql files):
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
);

Can be converted to a structure like this (in a highly configurable way):

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?
  }
}

The code generator can either generate dependency free code that only uses the raw SQLite3 API or code that uses the Lighter library. The Lighter library is not an ORM, but just a set of Swift protocols that allow for typesafe queries (and it is only intended to be used to support the code generator, not as a standalone library).

How does the code generation work?

The setup is intended to work with the new Swift Package Plugins feature of the Swift Package Manager, available since Swift 5.6 (and exposed in Xcode 14+). If SPM plugins cannot be used yet, the sqlite2swift tool can be called directly as well.
If you want to support the project, there is also the Code for SQLite3 app on the Mac AppStore. It does the same code generation as this FOSS project in a little more interactive way.

The Lighter package comes with a "build tool plugin" called Enlighter, that automatically integrates the code generation results into the build process. If it is added to a target, it'll scan for databases and SQL files and create the Swift accessors for them:

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

This variant is fully automatic, i.e. other code within the ContactsDB target has direct access to the database types (e.g. the Person struct above).

As a manual alternative the Generate Code for SQLite "command plugin" is provided. This plugin does the same generation as Enlighter, but is explicitly run by the developer using the Xcode "File / Packages" menu. It places the resulting code into the "Sources" folder of the app (where it can be inspected or modified).

Accessing a database using the higher level 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!
}
Fetching Individual Columns

One of the advantages of SQL is that individual columns can be selected and updated for maximum efficiency. Only things that are required need to be fetched (vs. full records):

// 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")
})

The references are fully typesafe down to the schema, only columns contained in the person table can be specified.

Dependency free SQLite3 API

The toolkit is also useful for cases in which the extra dependency on Lighter is not desirable. For such the generator can produce database specific Swift APIs that work alongside the regular SQLite 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)

There is another style the code generator can produce, it attaches the same functions to the generated types, e.g.:

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)

The main advantage of using the raw API is that no extra dependency is necessary at all. The generated functions are completely self-contained and can literally be copied&pasted into places where needed.

Beautiful, autogenerated DocC API Comments
The Lighter code generator can also generate API comments for the database types.

Example: Northwind Database.

Interested? 👉 Getting Started.

Who

Lighter is brought to you by Helge Heß / ZeeZide. We like feedback, GitHub stars, cool contract work, presumably any form of praise you can think of.

Want to support my work? Buy an app: Code for SQLite3, Past for iChat, SVG Shaper, Shrugs, HMScriptEditor. You don't have to use it! 😀

Comments
  • Code generated based on database creation SQL should not create `module`

    Code generated based on database creation SQL should not create `module`

    While testing the fix of #8, using develop branch (works) I noticed the following:

    I added a database creation script Li_Talents.sql but forgot to exclude it from the project.

    This created the following code within the struct LiTalents:

      public static let module : LiTalents! = {
        #if SWIFT_PACKAGE
        let bundle = Bundle.module
        #else
        final class Helper {}
        let bundle = Bundle(for: Helper.self)
        #endif
        if let url = bundle.url(forResource: "Li_Talents", withExtension: "sql") {
          return LiTalents(url: url, readOnly: true)
        }
        else {
          fatalError(#"Missing db resource Li_Talents.sql, not copied?"#)
        }
      }()
    

    Notice how this tries to use the URL to the SQL file when constructing the instance. Using a plain SQL file results in an assertion failure when using the instance:

    print(try? await LiTalents.module?.liLiTalents.fetchCount())
    // → 2022-08-24 13:33:50.896955+0200 XXX[37374:5177759] [logging] file is not a database in "SELECT COUNT(*) FROM "LiLiTalents""
    // → Lighter/SQLDatabaseOperations.swift:56: Fatal error: Failed to prepare SQL SELECT COUNT(*) FROM "LiLiTalents"
    

    The module static is not generated if the SQL file is not included in the project.

    bug 
    opened by dhoepfl 2
  • Code generated for `withOptUUIDBytes` fails to compile.

    Code generated for `withOptUUIDBytes` fails to compile.

    I tried to use Lighter plugin for a SQL that uses a UUID column type:

    CREATE TABLE Talents (
        id              UUID PRIMARY KEY NOT NULL,
        name         TEXT NOT NULL
        -- …
    );
    

    The following code is generated for withOptUUIDBytes:

      public static func withOptUUIDBytes<R>(
        _ uuid: UUID?,
        _ body: ( UnsafeRawBufferPointer ) throws -> R
      ) rethrows -> R
      {
        if let uuid = uuid { return try withUnsafeBytes(of: &uuid, body) }
        else { return try body(UnsafeRawBufferPointer(start: nil, count: 0)) }
      }
    

    It fails to compile with: Cannot pass immutable value as inout argument: 'uuid' is a 'let' constant on the call to withUnsafeBytes.

    (Using Xcode “Version 14.0 beta 5 (14A5294e)” if this matters, but I do not think so.)

    Possible fix:

        if case .some(var uuid) = uuid { return try withUnsafeBytes(of: &uuid, body) }
    
    bug 
    opened by dhoepfl 2
  • Add a separate type for

    Add a separate type for "integer pkeys" in SQLite3Schema

    Lighter currently considers all int pkeys as database generated, but that is only true if they are spelled explicitly as INTEGER (case doesn't matter). I.e. just id INT PRIMARY KEY doesn't actually work. We should replicate the SQLite behaviour and only consider a property as autogenerated if it matches the requirements.

    This probably warrants a new type or marker in SQLite3Schema.

    • https://www.sqlite.org/lang_createtable.html#rowid
    • https://www.sqlite.org/autoinc.html
    opened by helje5 1
  • Generated comment incorrectly claims

    Generated comment incorrectly claims "has default"

    When generating code for this SQL:

    CREATE TABLE Talents (
        id   UUID PRIMARY KEY NOT NULL,
        name TEXT NOT NULL
    );
    

    The DocC comment for the records is this (notice the "has default"):

    public struct Talents : Identifiable, SQLKeyedTableRecord, Codable {
      
      /// Static SQL type information for the ``Talents`` record.
      public static let schema = Schema()
      
      /// Primary key `id` (`UUID`), required (has default).
      public var id : UUID
      
      /// Column `name` (`TEXT`), required (has default).
      public var name : String
    }
    

    Neither id nor name has a database default though:

    sqlite> PRAGMA table_info(Talents);
    cid  name  type  notnull  dflt_value  pk
    ---  ----  ----  -------  ----------  --
    0    id    UUID  1                    1 
    1    name  TEXT  1                    0 
    

    Though they get default values assigned in Swift:

        public let id = MappedColumn<Talents, UUID>(
          externalName: "id",
          defaultValue: UUID(uuid: ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 )),
          keyPath: \Talents.id
        )
        public let name = MappedColumn<Talents, String>(
          externalName: "name",
          defaultValue: "",
          keyPath: \Talents.name
        )
    
    bug 
    opened by helje5 1
  • Name conflict in raw fetches (`sqlite3_talents0_fetch`)

    Name conflict in raw fetches (`sqlite3_talents0_fetch`)

    When generating a DB for this:

    CREATE TABLE Talents (
        id   UUID PRIMARY KEY NOT NULL,
        name TEXT NOT NULL
    );
    

    The result is mostly OK, but ends up with those SQLite API functions:

    public func sqlite3_talents0_fetch(...)
    

    Notice the talents0, it is probably generated by the code which makes the identifiers unique. The CUD functions are fine though:

    public func sqlite3_talents_delete(...)
    
    bug 
    opened by helje5 1
  • TBD: Add a default for SQLite generated primary keys

    TBD: Add a default for SQLite generated primary keys

    Inserts for integer primary keys use SQLite to generate the actual key, e.g.

    CREATE TABLE person (
      person_id INTEGER PRIMARY KEY NOT NULL,
      name TEXT
    );
    

    This is generated into:

    struct Person {
      var id : Int
      var name : String?
      init(id: Int, name: String? = nil) {...}
    }
    

    Which is a little inconvenient for inserts:

    var person = Person(id: 0, name: "Donald")
    person = try db.insert(person)
    

    (the return value of the insert will have copy of the record with the proper db assigned primary key).

    Maybe this should be some, ideally unlikely, obscure default value, like:

    init(id: Int = MyDB.defaultIntPrimaryKey, name: String? = nil) { ... }
    extension MyDB {
      static let MyDB.defaultIntPrimaryKey : Int = -0xDEADBEEF
    }
    

    Not quite sure whether that is actually good or not, maybe it is OK as an optional generation option.

    enhancement 
    opened by helje5 1
  • Add a default for `UUID` primary keys

    Add a default for `UUID` primary keys

    Let's say we have:

    CREATE TABLE talent (
      talent_id UUID NOT NULL,
      name      TEXT NOT NULL
    );
    

    which generates

    struct Talent {
      var id   : UUID
      var name : String
      init(id: UUID, name: String) { ... }
    }
    

    it would be nice if the init would be init(id: UUID = UUID(), name: String). As that kinda is the sane thing to do for new records.

    enhancement 
    opened by helje5 1
  • Single column views/tables produce invalid Swift code

    Single column views/tables produce invalid Swift code

    E.g.:

    CREATE VIEW fun_sum
      AS SELECT SUM(intCol) + SUM(doubleCol) AS lol FROM fun;
    

    breaks Swift with:

    Cannot create a single-element tuple with an element label on this generated code:

    public typealias PropertyIndices = ( idx_lol: Int32 )
    
    bug 
    opened by helje5 1
  • AST generation fails when a column has over 183 columns

    AST generation fails when a column has over 183 columns

    For example the CoreData database of the Apple Notes application has a table called ZICCLOUDSYNCINGOBJECT, which has 184 properties.

    Generation for this eventually crashes due to the recursion done in:

      func generateBindStatementForProperty(
             _ property : EntityInfo.Property,
             index      : Expression,
             trailer    : () -> [ Statement ] = { [] }
           ) -> ( Statement, didRecurse: Bool )
    

    Probably need to flatten that. Maybe not do recursive binds for tables with that many columns at all.

    bug 
    opened by helje5 0
  • Insufficient indentation of code generated by Enlighter

    Insufficient indentation of code generated by Enlighter

    I've got the issue that the create code generated by Enlighter does not properly indent the multi-line string literal and thus creates a compilation error:

    Insufficient indentation of next 4 lines in multi-line string literal

    Sample SQL:

    CREATE TABLE IF NOT EXISTS "TestTable" (
      "id" integer PRIMARY KEY NOT NULL,
      "someText" text(128),
      "someInt" integer(128),
      "someFloat" real(128)
    );
    

    Code generated:

    /// The SQL used to create the `TestTable` table.
        public static let create = 
          #"""
          CREATE TABLE "TestTable" (
      "id" INTEGER PRIMARY KEY NOT NULL,
      "someText" text(128),
      "someInt" integer(128),
      "someFloat" real(128)
    );
          """#
    

    Manually adding the generated code to the codebase and fixing the indentation compiles just fine.

    Lighter: 1.0.16 Xcode 14.1

    opened by fouquet 5
  • TBD: Add a change notification center

    TBD: Add a change notification center

    This can very quickly become non-Light, and maybe it shouldn't be actually done, but it might be useful to have a simple notification center that can broadcast DB change notifications. I had the feeling that it is better to let the user do this in own code as required.

    I'd probably do this as a simple closure the user can hook up to anything he wants, like:

    MyDatabase.onChange { some enum in  }
    

    It quickly becomes a Pony, users might want to know the exact tables or IDs affected and so on, which sometimes isn't even possible w/ SQL itself. Transactions also complicate the matter.

    Maybe rather something for Heavier. So a basic mechanism might be helpful.

    question 
    opened by helje5 1
  • SQLite Views loose type affinity w/ aggregates, workaround that

    SQLite Views loose type affinity w/ aggregates, workaround that

    As soon as an aggregate function is used, SQLite seems to loose the type affinity of the expression, e.g.:

    CREATE TABLE fun ( value INT );
    CREATE VIEW fun_sum AS SELECT SUM(value) AS combined FROM fun;
    

    Results in:

    /// Column `combined ` (`ANY`), optional (default: `nil`).
    public var combined : String?
    

    Which isn't great.

    Adding CASTs doesn't seem to help with that, the affinity is lost by the aggregate function. To fix that, we'd probably have to parse the SQL and follow the type-affinity in the expression AST. Possible but work.

    bug 
    opened by helje5 1
  • Add a mode in which the database is rendered as Swift statics

    Add a mode in which the database is rendered as Swift statics

    Instead of shipping the database, the database itself could be generated as Swift code, like:

    struct Person: Identifiable {
      let id : Int
      let name : String
    }
    
    let people : [ Person ] = [
      Person(id: 1, name: "Bourne"),
      Person(id: 2, name: "Jason")
    ]
    

    That might be useful sometimes? 🤔

    enhancement 
    opened by helje5 0
  • Directly parse SQL selects and generate VIEW like structs for them

    Directly parse SQL selects and generate VIEW like structs for them

    Instead of heaving to create a view like this:

    CREATE VIEW fun_sum
      AS SELECT SUM(intCol) + SUM(doubleCol) AS lol FROM fun;
    

    It would be nice if sqlite2swift could directly generate code for a SELECT expression, like:

    -- Swift: fetchSums
    SELECT SUM(intCol) + SUM(doubleCol) AS lol FROM fun; 
    

    Similar to SQLDelight.

    This has one big advantage, a binding could be specified at compile time, like:

    -- Swift: fetchSums
    SELECT SUM(intCol) + SUM(doubleCol) AS lol FROM fun WHERE year = $1; 
    

    And added to the fetch function, like:

    db.fun.fetchSums(1973)
    
    enhancement 
    opened by helje5 0
Releases(1.0.16)
  • 1.0.16(Oct 31, 2022)

    Recent Swift versions seem to be picky w/ ivar lifetimes in deinit's, when such are dispatched to different dispatch queues. An attempt to fix that. Thanks @dasdom for reporting!

    Source code(tar.gz)
    Source code(zip)
  • 1.0.14(Oct 4, 2022)

  • 1.0.12(Sep 10, 2022)

    As a convenience the simple pool now releases open SQLite connection when the application did enter background, on iOS. The pool collector now also captures the pool weakly, though that shouldn't matter much in practice.

    Also added a few docs here and there.

    Source code(tar.gz)
    Source code(zip)
  • 1.0.10(Aug 27, 2022)

    The release improves SQLite compatibility wrt "rowid" primary keys, also called "INTEGER primary keys". If a column is declared as INTEGER PRIMARY KEY, SQLite will automatically push a row-id as the primary key if no explicit value is specified. Lighter already accounted for that. It didn't distinguish that though from other integer columns, e.g. if specified as just INT. A column declared as INT PRIMARY KEY doesn't support the automatic key assignment (INTEGER must be spelled out). This is now properly implemented in the schema reflection and addressed in the generator.

    There is another fix related to the generation of plural names in the SQLite API, e.g. the generator generated names like taltents0 if the base name was plural already (and singularizeRecordNames is off, which is the default).

    Source code(tar.gz)
    Source code(zip)
  • 1.0.8(Aug 24, 2022)

    New day, new release. 1.0.8 brings a few small fixes and conveniences.

    UUID Primary Keys

    When using UUID primary keys, like:

    CREATE TABLE talent (
      talent_id UUID NOT NULL,
      name      TEXT NOT NULL
    );
    

    The generated code now generates a default UUID when none is specified, e.g.:

    var talent = Talent(name: "Jason Bourne") // <= no explicit `id` necessary anymore
    try db.insert(talent)
    

    Integer Primary Keys

    Similar to UUIDs, Integer primary keys improved and default to Int.min, e.g.:

    CREATE TABLE person (
      person_id INTEGER PRIMARY KEY NOT NULL,
      name TEXT NOT NULL
    );
    

    Doesn't require the id to be specified anymore (it isn't used for INSERTs in any case!):

    var person = Person(name: "Jason Bourne") // <= no explicit `id` necessary anymore
    try db.insert(person)
    

    Note: There is still a glitch in the form of issue #14. SQLite only does int-key things, if the column was created using INTEGER, e.g. INT doesn't work properly.

    Issues fixed

    • Issue #13 (Generated comment incorrectly claims "has default)
    • Issue #11 (Code generated based on database creation SQL should not create module)
    • Issue #10 (Add a default for SQLite generated primary keys)
    • Issue #9 (Add a default for UUID primary keys)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.6(Aug 23, 2022)

    This release fixes a set of bugs in generating UUID specific code: #8. Thanks go to @dhoepfl for reporting the issue!

    Screenshot 2022-08-23 at 17 03 49
    sqlite> .schema
    CREATE TABLE Talents (
        id   UUID PRIMARY KEY NOT NULL,
        name TEXT NOT NULL
    );
    sqlite> SELECT * FROM talents;
    Լ?4eFI????g?|Hello
    ??’??B??O}??D?|World
    

    (uses UUIDStorageStyle.blob)

    Source code(tar.gz)
    Source code(zip)
  • 1.0.4(Aug 17, 2022)

    This version fixes issue #1 which made things fail w/ tables or views that only had a single column It also fixes an issue with some deletes/updates, that may have acquired a read/only connection.

    Source code(tar.gz)
    Source code(zip)
  • 1.0.2(Aug 14, 2022)

    This release fixes an issue w/ Xcode 14b where Xcode doesn't copy resources when a build plugin claims ownership. Might be intentional or not.

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Aug 10, 2022)

    The initial release of:

    • the “Lighter” support library (only intended to be used in combination w/ Enlighter)
    • the “Enlighter” build plugin for Xcode and Swift Package Manager
    • the “Generate Code for SQLite3” command plugin for Xcode and Swift Package Manager
    • the “swift2sqlite3” tool backing both

    The Xcode plugins require Xcode 14b, the SPM 5.6+ (5.7 recommended).

    Source code(tar.gz)
    Source code(zip)
Owner
Lighter.swift
Swift APIs for SQLite: Type-safe down to the schema. Very, very, fast. Dependency free.
Lighter.swift
A Swift library to fetch the schema from a SQLite3 database.

SQLite3Schema A small library to fetch the schema of a SQLite database. Contains a SQLite3 helper module for Linux. TBD. Uses just the raw SQLite API.

Helge Heß 4 Sep 17, 2022
An elegant, fast, thread-safe, multipurpose key-value storage, compatible with all Apple platforms.

KeyValueStorage An elegant, fast, thread-safe, multipurpose key-value storage, compatible with all Apple platforms. Supported Platforms iOS macOS watc

null 3 Aug 21, 2022
YapDB is a collection/key/value store with a plugin architecture. It's built atop sqlite, for Swift & objective-c developers.

YapDatabase is a collection/key/value store and so much more. It's built atop sqlite, for Swift & Objective-C developers, targeting macOS, iOS, tvOS &

Yap Studios 3.3k Dec 29, 2022
A Swift wrapper for SQLite databases

Squeal, a Swift interface to SQLite Squeal provides access to SQLite databases in Swift. Its goal is to provide a simple and straight-forward base API

Christian Niles 297 Aug 6, 2022
A stand-alone Swift wrapper around the SQLite 3 client library.

Perfect - SQLite Connector This project provides a Swift wrapper around the SQLite 3 library. This package builds with Swift Package Manager and is pa

PerfectlySoft Inc. 47 Nov 19, 2022
A type-safe, protocol-based, pure Swift database offering effortless persistence of any object

There are many libraries out there that aims to help developers easily create and use SQLite databases. Unfortunately developers still have to get bogged down in simple tasks such as writing table definitions and SQL queries. SwiftyDB automatically handles everything you don't want to spend your time doing.

Øyvind Grimnes 489 Sep 9, 2022
A toolkit for SQLite databases, with a focus on application development

A toolkit for SQLite databases, with a focus on application development

Gwendal Roué 5.6k Jan 8, 2023
Realm is a mobile database: a replacement for Core Data & SQLite

Realm is a mobile database that runs directly inside phones, tablets or wearables. This repository holds the source code for the iOS, macOS, tvOS & wa

Realm 15.7k Jan 1, 2023
A Cocoa / Objective-C wrapper around SQLite

FMDB v2.7 This is an Objective-C wrapper around SQLite. The FMDB Mailing List: https://groups.google.com/group/fmdb Read the SQLite FAQ: https://www.s

August 13.7k Dec 28, 2022
Implement Student Admission System using SQlite

StudentAdmissionSQLiteApp Implement Student Admission System using SQlite. #Func

Hardik 2 Apr 27, 2022
BucketServer - Small API with SQLite database that saves notes for an iOS appliction called Bucket list

BucketList Server-Side Small API with SQLite database that saves notes for an iO

null 0 Dec 30, 2021
macOS Sqlite tableView 샘플 - objective c

목적 Objective C언어를 이용하여 macOS를 개발해본다. Sqlite를 이용하여 데이터를 저장하고, 불러와본다. FMDB를 이용한다. 데이터를 NSTableView를 이용하여 불러와본다. 추가적으로 NSOutlineView 구현해본다. 추가적으로 KVOCont

HyunSu Park 0 Jan 10, 2022
MagicData - A replacement of SQlite, CoreData or Realm.

MagicData A replacement of SQlite, CoreData or Realm. It is very easy to use and is a light version. Guides MagicData We use MagicData manage all the

Underthestars-zhy 20 Jul 4, 2022
A protocol-centric, type and queue safe key-value workflow.

Light-weight, strict protocol-first styled PropertyKit helps you to easily and safely handle guaranteed values, keys or types on various situations of

gitmerge 12 Feb 26, 2021
SwiftDI - A dependency injection tool for Swift

SwiftDI SwiftDI is a dependency injection tool for Swift. With it you can build well-structured and easily testable applications for iOS class Example

Satish Babariya 0 Jan 15, 2022
The hassle-free way to add Segment analytics to your Swift app (iOS/tvOS/watchOS/macOS/Linux).

Analytics-Swift The hassle-free way to add Segment analytics to your Swift app (iOS/tvOS/watchOS/macOS/Linux/iPadOS). Analytics helps you measure your

Segment 53 Dec 16, 2022
A fast, pure swift MongoDB driver based on Swift NIO built for Server Side Swift

A fast, pure swift MongoDB driver based on Swift NIO built for Server Side Swift. It features a great API and a battle-tested core. Supporting both MongoDB in server and embedded environments.

null 646 Dec 10, 2022
💾 Safe, statically-typed, store-agnostic key-value storage written in Swift!

Storez ?? Safe, statically-typed, store-agnostic key-value storage Highlights Fully Customizable: Customize the persistence store, the KeyType class,

Kitz 67 Aug 7, 2022
Safe and easy wrappers for common Firebase Realtime Database functions.

FirebaseHelper FirebaseHelper is a small wrapper over Firebase's realtime database, providing streamlined methods for get, set, delete, and increment

Quan Vo 15 Apr 9, 2022