• Stars
    star
    211
  • Rank 180,839 (Top 4 %)
  • Language
    Swift
  • License
    Apache License 2.0
  • Created about 6 years ago
  • Updated about 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An ORM for Swift, built on Codable

Kitura

APIDoc Build Status - Master macOS Linux Apache 2 Slack Status

Swift-Kuery-ORM

Swift-Kuery-ORM is an ORM (Object Relational Mapping) library built for Swift. Using it allows you to simplify persistence of model objects with your server.

Swift-Kuery-ORM is built on top of Swift-Kuery, which means that its possible to use Swift-Kuery to customize SQL queries made to the database, if the functionality of the ORM is insufficient.

The Model Protocol

The key component of Swift-Kuery-ORM is the protocol Model.

Let's propose a struct to use as an example. We can declare an object that looks like so:

struct Grade: Codable {
  var course: String
  var grade: Int
}

Thanks to Codable Routing in Kitura 2.0, we declare our struct to be Codable to simplify our RESTful routes for these objects on our server. The Model protocol extends what Codable does to work with the ORM. In your server application, you would extend your object like so:

extension Grade: Model { }

Now that your Grade struct conforms to Model, after you have set up your database connection pool and created a database table, you automatically have access to a slew of convenience functions for your object.

Need to retrieve all instances of Grade? You can implement:

Grade.findAll()

Need to add a new instance of Grade? Here's how:

grade.save()

The Model protocol is the key to using the ORM. Let's walk through how to fully set up an application to make use of the ORM.

Example

Follow Getting Started to create a Kitura server. In this example you'll be using the Swift Kuery PostgreSQL plugin, so you will need PostgreSQL running on your local machine, which you can install with brew install postgresql. The default port for PostgreSQL is 5432.

Update your Package.swift file

Add Swift-Kuery-ORM and Swift-Kuery-PostgreSQL to your application's Package.swift. Substitute "x.x.x" with the latest Swift-Kuery-ORM release and the latest Swift-Kuery-PostgreSQL release.

dependencies: [
    ...
    // Add these two lines
    .package(url: "https://github.com/Kitura/Swift-Kuery-ORM.git", from: "x.x.x"),
    .package(url: "https://github.com/Kitura/Swift-Kuery-PostgreSQL.git", from: "x.x.x"),
  ],
  targets: [
    .target(
      name: ...
      // Add these two modules to your target(s)
      dependencies: [..., "SwiftKueryORM", "SwiftKueryPostgreSQL"]),
  ]

Let's assume you want to add ORM functionality to a file called Application.swift. You'll need to add the following import statements at the top of the file:

import SwiftKueryORM
import SwiftKueryPostgreSQL

Create Your Database

As mentioned before, we recommend you use Homebrew to set up PostgreSQL on your machine. You can install PostgreSQL and set up your table like so:

brew install postgresql
brew services start postgresql
createdb school

Initialize your database in your Application.swift file:

let pool = PostgreSQLConnection.createPool(host: "localhost", port: 5432, options: [.databaseName("school")], poolOptions: ConnectionPoolOptions(initialCapacity: 10, maxCapacity: 50, timeout: 10000))
Database.default = Database(pool)

Set Up Your Object

Like before, assume you will work with a struct that looks like so:

struct Grade : Codable {
  var course: String
  var grade: Int
}

In your Application.swift file, extend Grade to conform to Model

extension Grade : Model {
    // here, you can add any server-side specific logic to your object
}

Now, you need to create your table. If you are configuring your database while you start up your server, you can use createTableSync(), which runs synchronously. If you want to use an asynchronous function, you can use createTable() elsewhere. You can implement either of these functions like so:

do {
  try Grade.createTableSync()
} catch let error {
  // Error
}

It's important to point out that if you've already created your table, this will throw an error here.

Your application is now ready to make use of all the functions available in the Model protocol. If you'd like to see a fully working example of the ORM using Codable Routing, visit our FoodTracker example.

Let's cover all the functionality you have available to you now.

Saving

If you'd like to save a new object to your database, you have to create the object and use the save() function:

let grade = Grade(course: "physics", grade: 80)
grade.save { grade, error in
  ...
}

You also optionally have the ability to pass the ID of the newly saved object into your closure. Add it to the collection of parameters like so:

grade.save { (id: Int?, grade: Grade?, error: RequestError?) in
  ...
}

Updating

If you have the id for an existing record of your object, and you'd like to update the record with an object, you can use the update() function to do so:

let grade = Grade(course: "physics", grade: 80)
grade.course = "maths"

grade.update(id: 1) { grade, error in
  ...
}

Retrieving

If you'd like to find a specific object, and you have its id, you can use the find() function to retrieve it:

Grade.find(id: 1) { result, error in
  ...
}

If you'd like to retrieve all instances of a particular object, you can make use of findAll() as a static function on the type you are trying to retrieve:

Grade.findAll { (result: [Grade]?, error: RequestError?) in
  ...
}

You also have the ability to form your results in different ways and formats, like so:

Grade.findAll { (result: [(Int, Grade)]?, error: RequestError?) in
  ...
}

Grade.findAll { (result: [Int: Grade]?, error: RequestError?) in
  ...
}

Deleting

If you'd like to delete an object, and you have its id, you can use the delete() function like so:

Grade.delete(id: 1) { error in
  ...
}

If you're feeling bold, and you'd like to remove all instances of an object from your database, you can use the static function deleteAll() with your type:

Grade.deleteAll { error in
  ...
}

Customizing your Model

The ORM defines an extension to Model which provides a number of public static executeQuery(…) functions. These can be used to create custom functions within your model that perform more complex database operations. The example below defines a Person model and with a custom function that will retrieve all records which have age > 20:

// define the Person struct
struct Person: Codable {
    var firstname: String
    var surname: String
    var age: Int
}

// extend Person to conform to model and add overTwenties function
extension Person: Model {

    // Define a synchronous function to retrieve all records of Person with age > 20
    public static func getOverTwenties() -> [Person]? {
        let wait = DispatchSemaphore(value: 0)
        // First get the table
        var table: Table
        do {
            table = try Person.getTable()
        } catch {
            // Handle error
        }
        // Define result, query and execute
        var overTwenties: [Person]? = nil
        let query = Select(from: table).where("age > 20")

        Person.executeQuery(query: query, parameters: nil) { results, error in
            guard let results = results else {
                // Handle error
            }
            overTwenties = results
            wait.signal()
            return
        }
        wait.wait()
        return overTwenties
    }
}

Alternatively you can define and asynchronous getOverTwenties function:

public static func getOverTwenties(oncompletion: @escaping ([Person]?, RequestError?)-> Void) {
    var table: Table
    do {
        table = try Person.getTable()
    } catch {
        // Handle error
    }
    let query = Select(from: table).where("age > 20")
    Person.executeQuery(query: query, parameters: nil, oncompletion)
}

which can be called in a fashion similar to the following:

Person.getOverTwenties() { result, error in
    guard let result = result else {
        // Handle error
    }
    // Use result
}

If you'd like to learn more about how you can customize queries, check out the Swift-Kuery repository for more information.

Model Identifiers

The ORM has several options available for identifying an instance of a model.

Automatic ID assignment

If you define your Model without specifying an ID property, either by using the idColumnName property or the default name of id, then the ORM will create an auto-incrementing column named id in the database table for the model, eg.

struct Person: Model {
    var firstname: String
    var surname: String
    var age: Int
}

The model does not contain a property for the ID. The ORM provides a specific save API that will return the ID that was assigned. It is important to note the ORM will not link the returned ID to the instance of the Model in any way; you are responsible for maintaining this relationship if necessary. Below is an example of retrieving an ID for an instance of the Person model defined above:

let person = Person(firstname: "example", surname: "person", age: 21)
person.save() { (id: Int?, person, error) in
    guard let id = id, let person = person else{
        // Handle error
        return
    }
    // Use person and id
}

The compiler requires you to declare the type of the ID received by your completion handler; the type should be Int? for an ID that has been automatically assigned.

Manual ID assignment

You can manage the assignment of IDs yourself by adding an id property to your model. You may customise the name of this property by defining idColumnName. For example:

struct Person: Model {
    var myIDField: Int
    var firstname: String
    var surname: String
    var age: Int

    static var idColumnName = "myIDField"
    static var idColumnType = Int.self
}

When using a Model defined in this way, you are responsible for the assignment and management of IDs. Below is an example of saving an instance of the Person model defined above:

let person = Person(myIDField: 1, firstname: "example", surname: "person", age: 21)
person.save() { (person, error) in
    guard let person = person else {
        // Handle error
        return
    }
    // Use newly saved person
}

Using optional ID properties

Declaring your ID property as optional allows the ORM to assign the ID automatically when the model is saved. If the value of ID is nil, the database will assign an auto-incremented value. At present this is only support for an Int? type.

You may instead provide an explicit value, which will be used instead of automatic assignment.

Optional IDs must be identified by defining the idKeypath: IDKeyPath property, as in the example below:

struct Person: Model {
    var id: Int?
    var firstname: String
    var surname: String
    var age: Int

    static var idKeypath: IDKeyPath = \Person.id
}

In the example above, the Model is defined with an ID property matching the default idColumnName value, but should you wish to use an alternative name you must define idColumnName accordingly.

Below is an example of saving an instance of the Person defined above, both with an explicitly defined ID and without:

let person = Person(id: nil, firstname: “Banana”, surname: “Man”, age: 21)
let specificPerson = Person(id: 5, firstname: “Super”, surname: “Ted”, age: 26)

person.save() { (savedPerson, error) in
        guard let newPerson = savedPerson else {
            // Handle error
        }
        print(newPerson.id) // Prints the next value in the databases identifier sequence, eg. 1
}

specificPerson.save() { (savedPerson, error) in
        guard let newPerson = savedPerson else {
            // Handle error
        }
        print(newPerson.id) // Prints 5
}

NOTE - When using manual or optional ID properties, you should be prepared to handle violation of unique identifier constraints. These can occur if you attempt to save a model with an ID that already exists, or in the case of Postgres, if the auto-incremented value collides with an ID that was previously inserted explicitly.

Alternative encoding for Date properties

By default any property on your Model that is declared as a Date will be encoded and decoded as a Double.

You can change this behaviour by overriding the default value of the property dateEncodingStrategy. The dateEncodingStrategy will apply to all Date properties on your Model.

The example below defines a model which will have its Date properties encoded and decoded as a timestamp:

struct Person: Model {

    static var dateEncodingFormat: DateEncodingFormat = .timestamp

    var firstname: String
    var surname: String
    var age: Int
    var dob: Date
}

List of plugins

API Documentation

For more information visit our API reference.

Community

We love to talk server-side Swift, and Kitura. Join our Slack to meet the team!

License

This library is licensed under Apache 2.0. Full license text is available in LICENSE.

More Repositories

1

Kitura

A Swift web framework and HTTP server.
Swift
7,616
star
2

BlueSocket

Socket framework for Swift using the Swift Package Manager. Works on iOS, macOS, and Linux.
Swift
1,392
star
3

Swift-JWT

JSON Web Tokens in Swift
Swift
539
star
4

Swift-Kuery

SQL database abstraction layer
Swift
427
star
5

Swift-SMTP

Swift SMTP client
Swift
249
star
6

BlueCryptor

Swift cross-platform crypto library using CommonCrypto/libcrypto
Swift
189
star
7

HeliumLogger

A lightweight logging framework for Swift
Swift
175
star
8

swift-html-entities

HTML5 spec-compliant character encoder/decoder for Swift
Swift
165
star
9

swift-ubuntu-docker

🚫 This repo is deprecated - please use the images here: https://hub.docker.com/_/swift
Vim Script
155
star
10

BlueRSA

RSA public/private key encryption, private key signing and public key verification in Swift using the Swift Package Manager. Works on iOS, macOS, and Linux (work in progress).
Swift
128
star
11

SwiftyRequest

SwiftyRequest is an HTTP networking library built for Swift.
Swift
108
star
12

Kitura-net

Kitura networking
Swift
103
star
13

BlueSSLService

SSL/TLS Add-in for BlueSocket using Secure Transport and OpenSSL
Swift
96
star
14

Kitura-redis

Swift Redis library
Swift
94
star
15

BlueECC

Elliptic-curve cryptography for Swift
Swift
92
star
16

BlueSignals

Generic Cross Platform Signal Handler
Swift
92
star
17

Kitura-Sample

A sample application that shows how to use various features of Kitura
Swift
80
star
18

Configuration

Hierarchical configuration manager for Swift applications
Swift
78
star
19

Kitura-WebSocket

WebSocket support for Kitura
Swift
66
star
20

Swift-Kuery-PostgreSQL

PostgreSQL plugin for Swift-Kuery framework
Swift
61
star
21

OpenSSL

Swift modulemaps for libSSL and libcrypto
C
58
star
22

SwiftKafka

Swift SDK for Apache Kafka
Swift
58
star
23

KituraKit

Swift client library for using Codable routes with Kitura
Swift
58
star
24

Kitura-CouchDB

CouchDB adapter for Kitura
Swift
50
star
25

CircuitBreaker

A Swift Circuit Breaker library – Improves application stability and reliability.
Swift
45
star
26

Kitura-Credentials

A pluggable framework for validating user credentials in a Swift server using Kitura
Swift
40
star
27

Kitura-NIO

A networking library for Kitura, based on SwiftNIO
Swift
37
star
28

Kitura-OpenAPI

OpenAPI support for Kitura
Swift
36
star
29

TypeDecoder

A Swift library to allow the runtime inspection of Swift language native and complex types.
Swift
35
star
30

SwiftKueryMySQL

MySQL plugin for Swift-Kuery framework
Swift
34
star
31

Package-Builder

Build and utility scripts used for continuous integration builds for Swift Package Manager projects on the Travis CI environment
Shell
33
star
32

CCurl

Modulemap for the libcurl library
Objective-C
30
star
33

Kitura-StencilTemplateEngine

Stencil templating for Kitura
Swift
26
star
34

kitura.dev

http://www.kitura.dev
JavaScript
26
star
35

LoggerAPI

Logger protocol
Swift
25
star
36

Kitura-Markdown

Templating engine for Kitura that uses Markdown based templates
C
24
star
37

Health

An application health library for Swift.
Swift
21
star
38

Kitura-Session

A pluggable framework for managing user sessions in a Swift server using Kitura
Swift
18
star
39

Kitura-WebSocket-NIO

A SwiftNIO based implementation of WebSocket for Kitura
Swift
17
star
40

CommonCrypto

CommonCrypto Module Map
Swift
17
star
41

FileKit

Swift
16
star
42

Swift-Kuery-SQLite

An SQLite plugin for the Swift-Kuery framework
Swift
16
star
43

Kitura-TemplateEngine

Kitura Template Engine protocol
Swift
15
star
44

Kitura-CredentialsHTTP

A plugin for the Kitura-Credentials framework that authenticates using HTTP Basic and Digest authentication
Swift
15
star
45

kitura-cli

⌨️ Kitura command-line interface
Go
13
star
46

KituraContracts

A library containing type definitions shared by client and server Kitura code.
Swift
12
star
47

CZlib

Module map for Zlib library
Swift
11
star
48

CloudEnvironment

Convenience Swift package for accessing environment variables, credentials.
Swift
11
star
49

Kitura-CredentialsFacebook

A plugin for the Kitura-Credentials framework that authenticates using the Facebook web login
Swift
9
star
50

Kitura-CORS

Kitura CORS middleware
Swift
9
star
51

Kitura-Cache

Kitura cache
Swift
9
star
52

Kitura-CredentialsGoogle

A plugin for the Kitura-Credentials framework that authenticates using the Google web login
Swift
8
star
53

Swift-cfenv

Easy access to Cloud Foundry application environment for Swift Packages.
Swift
8
star
54

Kitura-Compression

Kitura compression middleware
Swift
6
star
55

CEpoll

A modulemap file and include to help Swift code use epoll on Linux
Swift
5
star
56

Kitura-WebSocket-Client

A WebSocket client based on SwiftNIO
Swift
5
star
57

Kitura-CredentialsGitHub

A plugin for the Kitura-Credentials framework that authenticates using the GitHub web login
Swift
4
star
58

Kitura-MustacheTemplateEngine

Adapter of GRMustache Template Engine to Kitura Template Engine
Swift
4
star
59

CHTTPParser

Modulemap for the http-parser library
C
4
star
60

Kitura-WebSocket-Compression

A WebSocket compression library based on SwiftNIO
Swift
3
star
61

Kitura-Session-Redis

Kitura-Session store using Redis as the backing store
Swift
3
star
62

generator-swiftserver-projects

Autogenerated Kitura projects
Shell
2
star
63

Kitura-CredentialsJWT

A plugin for the Kitura-Credentials framework that supports JWT authentication.
Swift
2
star
64

homebrew-kitura

Homebrew tap
Ruby
2
star
65

Kitura-Benchmarks

Benchmarks for Kitura
Swift
2
star
66

anapistula

Simple standalone web server in swift
Swift
1
star
67

CLibpq

PostgreSQL wrapper
Swift
1
star
68

ShellToolKit

Utility classes to help with common system/shell actions in Swift
Swift
1
star