A PostgreSQL client library for Swift. Does not require libpq.

Overview

PostgresClientKit

PostgresClientKit provides a friendly Swift API for operating against a PostgreSQL database.

Features

  • Doesn't require libpq. PostgresClientKit implements the Postgres network protocol in Swift, so it does not require libpq.

  • Developer-friendly API using modern Swift. For example, errors are represented by instances of enum PostgresError: Error and are raised by a throw or by returning a Result<Success, Error>.

  • Safe conversion between Postgres and Swift types. Type conversion is explicit and robust. Conversion errors are signaled, not masked. PostgresClientKit provides additional Swift types for dates and times to address the impedance mismatch between Postgres types and Foundation Date.

  • Memory efficient. The rows in a result are exposed through an iterator, not an array. Rows are lazily retrieved from the Postgres server.

  • SSL/TLS support. Encrypts the connection between PostgresClientKit and the Postgres server.

  • Well-engineered. Complete API documentation, an extensive test suite, actively supported.

Sounds good? Let's look at an example.

Example

This is a basic, but complete, example of how to connect to Postgres, perform a SQL SELECT command, and process the resulting rows. It uses the weather table in the Postgres tutorial.

import PostgresClientKit

do {
    var configuration = PostgresClientKit.ConnectionConfiguration()
    configuration.host = "127.0.0.1"
    configuration.database = "example"
    configuration.user = "bob"
    configuration.credential = .scramSHA256(password: "welcome1")

    let connection = try PostgresClientKit.Connection(configuration: configuration)
    defer { connection.close() }

    let text = "SELECT city, temp_lo, temp_hi, prcp, date FROM weather WHERE city = $1;"
    let statement = try connection.prepareStatement(text: text)
    defer { statement.close() }

    let cursor = try statement.execute(parameterValues: [ "San Francisco" ])
    defer { cursor.close() }

    for row in cursor {
        let columns = try row.get().columns
        let city = try columns[0].string()
        let tempLo = try columns[1].int()
        let tempHi = try columns[2].int()
        let prcp = try columns[3].optionalDouble()
        let date = try columns[4].date()
    
        print("""
            \(city) on \(date): low: \(tempLo), high: \(tempHi), \
            precipitation: \(String(describing: prcp))
            """)
    }
} catch {
    print(error) // better error handling goes here
}

Output:

San Francisco on 1994-11-27: low: 46, high: 50, precipitation: Optional(0.25)
San Francisco on 1994-11-29: low: 43, high: 57, precipitation: Optional(0.0)

Prerequisites

  • Swift 5 or later (PostgresClientKit uses Swift 5 language features)
  • libssl-dev (only required on Linux)

PostgresClientKit is compatible with Linux, macOS, and iOS. It has been tested on:

  • Ubuntu 18.04 LTS, 20.04 LTS
  • macOS 10.14, 10.15, 11
  • iOS 12, 13, 14, 15
  • Postgres 10, 11, 12, 13, 14

Building

cd <path-to-clone>
swift package clean
swift build

Testing

Set up a Postgres database for testing. This is a one-time process.

Then:

cd <path-to-clone>
swift package clean
swift build
swift test

Using

Swift Package Manager

In your Package.swift file:

  • Add PostgresClientKit to the dependencies. For example:
dependencies: [
    .package(url: "https://github.com/codewinsdotcom/PostgresClientKit", from: "1.0.0"),
],
  • Reference the PostgresClientKit product in the targets. For example:
targets: [
    .target(
        name: "MyProject",
        dependencies: ["PostgresClientKit"]),
]

Import to your source code file:

import PostgresClientKit

CocoaPods

Add PostgresClientKit to your Podfile. For example:

target 'MyApp' do
  pod 'PostgresClientKit', '~> 1.0'
end

Then run pod install.

Import to your source code file:

import PostgresClientKit

Documentation

Additional examples

Contributing

Thank you for your interest in contributing to PostgresClientKit.

This project has a code of conduct. See CODE_OF_CONDUCT.md for details.

Please use issues to:

  • ask questions
  • report problems (bugs)
  • request enhancements

Pull requests against the develop branch are welcomed. For a non-trivial contribution (for example, more than correcting spelling, typos, or whitespace) please first discuss the proposed change by opening an issue.

License

PostgresClientKit is licensed under the Apache 2.0 license. See LICENSE for details.

Versioning

PostgresClientKit uses Semantic Versioning 2.0.0. For the versions available, see the tags on this repository.

Built with

Authors

Comments
  • Improve performance of date/time conversions

    Improve performance of date/time conversions

    As my database grows I've been noticing the performance starting to become an issue when compared with other database client implementations. I'm not sure if this is my code in particular or implementation differences.

    Consider I have a table with 7 or so columns and about 6000 rows. The columns contain simple data: last name string, some integers, a timestamp, a few other strings. I want to retrieve the entire table - all rows and columns (so a simple SELECT statement). As I read each row from the query, the columns are parsed into their native types and stored in a list of objects.

    I have a Java client using JDBC running on a laptop, for which this operation takes about 300 milliseconds. Roughly about 250 Kbytes are retrieved.

    An iPhone and iPad on the same Wi-fi network running PostgresClientKit take about 8 seconds.

    I don't think this is a network speed issue. The laptop is faster of course, but hard to believe it's that much faster. Both connections use SSL, possibly a performance issue in BlueSSLService.

    Possibly the JDBC client is pre-fetching all rows before the cursor increments to them? Is there efficiency to be gained by trying to fetch all rows at once?

    Could there be an underlying socket performance issue with blocking/non-blocking reads?

    Any thoughts appreciated.

    bug 
    opened by kkieffer 16
  • Cannot retrieved TEXT from the server

    Cannot retrieved TEXT from the server

    I am creating an application which will upload an image from iPad to postgres server and also download the image data from database and display in iPad.

    In my database, I have a column named image with TEXT data type. In my application, I converted the image to string data using base64EncodedString(options: .lineLength64Characters) method.

    The upload of the image data to database works well. However, an error occurred when I tried to select the data (including the image) from the database and here is the error message: "Response truncated; no data available" "no data available from server"

    Can anyone help with this issue?

    Thank you and good day

    opened by stevenang 10
  • Feature request: adjustable socket timeouts

    Feature request: adjustable socket timeouts

    I would like to be able to set the timeouts for connect/read/write as the defaults are kinda long. I think this is as simple as, after creating the socket calling:

    socket.setReadTimeout(value: timeout)
    socket.setWriteTimeout(value: timeout)
    

    and replacing the socket.connect() method with the overloaded one that takes a timeout.

    The timeout values could be stored in the ConnectionConfiguration struct.

    enhancement 
    opened by kkieffer 9
  • share open connection

    share open connection

    Hi there, in my swift project I call an example right at the beginning:

    do { var configuration = PostgresClientKit.ConnectionConfiguration() configuration.host = "127.0.0.1" configuration.database = "example" configuration.user = "bob" configuration.credential = .scramSHA256(password: "welcome1")

    let connection = try PostgresClientKit.Connection(configuration: configuration)
    let text = "SELECT start();"
    try connection.prepareStatement(text: text).execute()
    

    } catch { print(error) }

    I need the connection not to be terminated and continue in another function:

    func test() { do { let text = "SELECT * FROM foo();" try connection.prepareStatement(text: text).execute(). // error: Cannot find 'connection' in scope } catch { print(error) }

    My problem: I don't know how set "connection" as global var
    Can you please advise me how to solve this?

    opened by AMBIENTE1 8
  • How to configure TLS certs in client app

    How to configure TLS certs in client app

    My app can access our non-TLS, development PostgreSQL instance. But when attempting to test against a production environment, I am forced to use TLS access. So far, I've been unsuccessful in locating an example on how to configure my client cert and key .pem files. I have the correct .pem files, but I'm missing how to specify these files in the 'ConnectionConfiguration()'.

    A brief example would be greatly appreciated.

    question 
    opened by ebsanford 7
  • Feature Request: Adding Meta Data to Results

    Feature Request: Adding Meta Data to Results

    I really like the work you've been doing on this project, as this seems to be the easiest to use Postgres library for Swift so far that also works beautifully on my iOS app. Now to my request: I'm currently trying to build a new app that would allow the user to type in SQL queries himself on a custom database (so I don't know the schema upfront). The user can use that feature to generate custom reports, charts, ..., and there needs to be a way of naming things and do some introspection of query results.

    I do agree with your FAQ saying that one shouldn't use name based indices to retrieve values, but would it be possible to somehow retrieve the name of a column and it's type?

    Think about a query like this (that the user will input into a text field of the running app):

    SELECT count(*) AS population, born_at
    FROM people
    GROUP BY born_at
    

    I would like to display the label population in the graph, effectively giving the user the possibility to steer everything about the graph/report with SQL (also finding out that born_at is a date, and therefore correctly displaying it in the UI).

    Any hints or directions on this?

    enhancement 
    opened by JensRavens 7
  • SCRAM-SHA-256 Support

    SCRAM-SHA-256 Support

    Hi! Thanks for writing this driver for Swift!

    PostgreSQL has supported SCRAM-SHA-256 authentication since version 10 and the community is looking to make it the default password-based authentication method for version 13 (more than a year away, so there is some time). As such, I would strongly recommend to support it :smile:

    A list of non-libpq based implementations that are implementing SCRAM can be found here: https://wiki.postgresql.org/wiki/List_of_drivers

    I have some practice implementing the method so I'm happy to help where needed.

    enhancement 
    opened by jkatz 7
  • RowDecoder

    RowDecoder

    I've written a RowDecoder, which lets you easily convert from a PostgresClientKit.Row to a Codable struct, like

    struct User: Codable {
        let id: Int
        let name: String
    }
    

    It's working well in my project, and if you think it'd be good to add to this library, let me know and I would be happy to clean it up and pull request it.

    enhancement 
    opened by khanlou 6
  • Allow DateStyle to be

    Allow DateStyle to be "ISO, MDY", "ISO, DMY", or "ISO, YMD".

    Hallo, I would like to change parameter datestyle from ISO, MDY to ISO, DMY in Parameter.swift When I try: let text = "SET DateStyle TO 'ISO, DMY';" let cursor = try Database.connection().prepareStatement(text: text).execute()

    error: Invalid value for Postgres parameter (response.name): ISO, DMY (must be ISO, MDY); closing connection

    Please what is the best way to do this?

    opened by AMBIENTE1 6
  • Feature Request: Allow SSL Authentication with Postgres Server, optionally using embedded certificates

    Feature Request: Allow SSL Authentication with Postgres Server, optionally using embedded certificates

    Although SSL can be enabled to encrypt the connection to the server, the server is not authenticated. The client is vulnerable to a Man-In-The-Middle attack, where it unknowingly connects to a fake server impersonating the real server. Thus it is good practice for the client to authenticate the server while establishing the SSL connection.

    I propose two new features:

    First feature: allow the client to authenticate the server. Currently the SSL service uses a initializer:

    SSLService.Configuration()

    which by default allows self-signed server certificates. However by setting the self-signed parameter to false, the server must provide a certificate that can be validated by the client using the client's root certificates.

    Second feature: often times servers are using certificates that don't establish a chain of trust to a root certificate on the client. This is true if you are using a self-signed certificate on a server, for instance. You can install those public certificates on the client, but this is an onerous process and must be done by the user on iOS. A more secure approach is to embed the public server certificate in the client and authenticate directly.

    I have worked on a new feature for BlueSSLService that has recently been merged into the main trunk. This change allows the client to store several public certificates as .der files to be used to authenticate the server. See pull request here for details:

    https://github.com/IBM-Swift/BlueSSLService/pull/81

    I request that the new SSL feature be available in this library, so the Postgres client can authenticate a server using embedded certificates.

    In essence, the change to this library would be to use the following init method for SSLService, supplying the parameters to the init method from new fields in the ConnectionConfiguration.

    public init(withCipherSuite cipherSuite: String? = nil, clientAllowsSelfSignedCertificates: Bool = true, embeddedServerCertPaths : [URL]? = nil)

    Note: The embeddedServerCertPaths feature is unavailable (parameter ignored) on MacOS versions less than 10.14 and iOS < 12.0

    It is easy to embed certificates into the client. Simply add the .der file to the Xcode project and load them this way:

    let path = Bundle.main.url(forResource: "MyServerCert", withExtension: ".der")

    You can load multiple certificates if the client is connecting to several servers.

    enhancement 
    opened by kkieffer 6
  • SET Timezone

    SET Timezone

    Hello pitfield,

    In my project Postgres server is located in Europe - Prague. When I use the select now () command, Postgres returns a datetime in GMT, but I need a datetime of +2 hours. I could ask to adjust the SET TIMEZONE = 'GMT' to parameter SET TIMEZONE = + 2 or SET TIMEZONE = 'Europe / Prague' ?

    Best regards Carlos

    enhancement 
    opened by AMBIENTE1 5
  • Please migrate from BlueSocket and BlueSSLService to swift-nio

    Please migrate from BlueSocket and BlueSSLService to swift-nio

    Migrate from BlueSocket and BlueSSLService to definitely preferred Apple's open source swift-nio.

    Currently BlueSocket and BlueSSLService block modern versions of swift-argument-parser. They depend on 'swift-argument-parser' 0.4.1..<1.0.0. It's definitely wrong when networking libraries (not their applications) depend on argument parser.

    opened by sdpopov-keyvariable 2
  • Pull request discussion: Adding PostgresArray type

    Pull request discussion: Adding PostgresArray type

    I've created an extension to the library in my own code which may be useful if you'd consider a pull request. One of my data types is an array of SQL values, which there is currently no support for. The code linked here would add that type:

    https://gist.github.com/kkieffer/0adadaaa351871d59d08a95d40f67cc3

    The user can create an optionalArray and pull the values from it like this:

    let postgresArrayValues : [PostgresValue] = try columns[8].optionalArray()?.arrayValues

    Then for each value, they can extract the desired type.

    The postgresValue for PostgresArray provides a raw value like this:

    {"value1","value2","value3"}

    enhancement 
    opened by kkieffer 2
Releases(v1.5.0)
  • v1.5.0(Aug 30, 2022)

    Breaking

    • None

    Enhancements

    • #39 Support for Decodable: see Row.decodeByColumnName(_:defaultTimeZone:) and Row.decodeByColumnIndex(_:defaultTimeZone:) (thanks to @khanlou for the idea and initial code)

    Bug fixes

    • #44: Don't buffer log output
    Source code(tar.gz)
    Source code(zip)
  • v1.4.4(Jul 23, 2022)

    Breaking

    • None

    Enhancements

    • None

    Bug fixes

    • #42 : Incompatible with swift-argument-parser v1.0.0+
    • #43: Replace references to IBM-Swift with Kitura
    Source code(tar.gz)
    Source code(zip)
  • v1.4.3(Mar 19, 2022)

  • v1.4.2(Mar 18, 2022)

  • v1.4.1(Nov 11, 2021)

  • v1.4.0(Apr 3, 2021)

  • v1.3.2(Feb 23, 2021)

  • v1.3.1(Nov 13, 2020)

  • v1.3.0(Jul 24, 2020)

  • v1.2.0(May 29, 2020)

    Breaking

    • None

    Enhancements

    • New applicationName property on ConnectionConfiguration to set the Postgres application_name included in the pg_stat_activity view and displayed by pgAdmin (#21)

    • New retrieveColumnMetadata parameter on Statement.execute(...) and new columns property on Cursor to optionally retrieve metadata about the columns in the result of executing a Statement (#12, #18)

    Bug fixes

    • None
    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(Dec 27, 2019)

  • v1.1.0(Dec 26, 2019)

  • v1.0.0(Oct 4, 2019)

    Version 1.0.0!

    No API changes in this release, though. Just recognizing that PostgresClientKit is ready for production use.

    Breaking

    • None

    Enhancements

    • Tested against Swift 5.1
    • Tested against iOS 13
    • Tested against PostgreSQL 12

    Bug fixes

    • #13 Errors converting PostgresValue instances to PostgresTime, PostgresTimeWithTimeZone, PostgresTimestamp, PostgresTimestampWithTimeZone
    • #15 Issues creating PostgresTimeWithTimeZone and PostgresTimestampWithTimeZone instances from strings using Swift 5.1 on Linux
    Source code(tar.gz)
    Source code(zip)
  • v0.3.2(Jul 25, 2019)

  • v0.3.1(Jul 16, 2019)

    Swift 5.1 compatibility (based on Xcode 11.0 Beta 3)

    Breaking

    • None

    Enhancements

    • None

    Bug fixes

    • #7 Compilation Error on Xcode 11.0 beta 3 (Swift 5.1)
    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(May 24, 2019)

  • v0.2.0(May 1, 2019)

  • v0.1.0(Apr 29, 2019)

Owner
codewins.com
codewins.com
Listens to changes in a PostgreSQL Database and via websockets.

realtime-swift Listens to changes in a PostgreSQL Database and via websockets. A Swift client for Supabase Realtime server. Usage Creating a Socket co

Supabase 35 Dec 1, 2022
PostgreSQL database adapter (ORM included)

PostgreSQL PostgreSQL adapter for Swift 3.0. Conforms to SQL, which provides a common interface and ORM. Documentation can be found there. Installatio

Zewo Graveyard 91 Sep 9, 2022
Check your emails before someone else does

Checkpoint Proofread your emails before your mom does ?? Have you ever meant to

Linus Skucas 9 Nov 15, 2022
A MongoDB interface for Swift [Not under active development]

MongoDB #This library is no longer under active development. I highly recommend using the robust, pure-swift alternative MongoKitten. A Swift MongoDB

Dan Appel 266 Jan 29, 2022
Why not use UserDefaults to store Codable objects 😉

tl;dr You love Swift's Codable protocol and use it everywhere, who doesn't! Here is an easy and very light way to store and retrieve -reasonable amoun

Omar Albeik 452 Oct 17, 2022
A stand-alone Swift wrapper around the mongo-c client library, enabling access to MongoDB servers.

This package is deprecated in favour of the official Mongo Swift Driver. We advise users to switch to that pack

PerfectlySoft Inc. 54 Jul 9, 2022
A stand-alone Swift wrapper around the MySQL client library, enabling access to MySQL servers.

Perfect - MySQL Connector This project provides a Swift wrapper around the MySQL client library, enabling access to MySQL database servers. This packa

PerfectlySoft Inc. 118 Jan 1, 2023
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 Swift client for Redis.

Perfect-Redis 简体中文 Redis client support for Perfect Quick Start Get a redis client with defaults (localhost, default port): let client = RedisClient.g

PerfectlySoft Inc. 28 May 16, 2021
🧡 SQLiteOrm-Swift is an ORM library for SQLite3 built with Swift 5

?? Easy to use SQLite ORM library written with Swift

Yevgeniy Zakharov 25 Oct 6, 2022
Elegant library to manage the interactions between view and model in Swift

An assistant to manage the interactions between view and model ModelAssistant is a mediator between the view and model. This framework is tailored to

Seyed Samad Gholamzadeh 28 Jan 29, 2022
Prephirences is a Swift library that provides useful protocols and convenience methods to manage application preferences, configurations and app-state. UserDefaults

Prephirences - Preϕrences Prephirences is a Swift library that provides useful protocols and convenience methods to manage application preferences, co

Eric Marchand 557 Nov 22, 2022
Swift library that makes easier to serialize the user's preferences (app's settings) with system User Defaults or Property List file on disk.

PersistentStorageSerializable PersistentStorageSerializable is a protocol for automatic serialization and deserialization of Swift class, struct or NS

Ivan Rublev 163 Jun 3, 2021
Easiest local storage library in Swift

SundeedQLite SundeedQLite is the easiest offline database integration, built using Swift language Requirements iOS 12.0+ XCode 10.3+ Swift 5+ Installa

Nour Sandid 15 Sep 23, 2022
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
Disk is a powerful and simple file management library built with Apple's iOS Data Storage Guidelines in mind

Disk is a powerful and simple file management library built with Apple's iOS Data Storage Guidelines in mind

Saoud Rizwan 3k Jan 3, 2023
KeyPathKit is a library that provides the standard functions to manipulate data along with a call-syntax that relies on typed keypaths to make the call sites as short and clean as possible.

KeyPathKit Context Swift 4 has introduced a new type called KeyPath, with allows to access the properties of an object with a very nice syntax. For in

Vincent Pradeilles 406 Dec 25, 2022
A library that provides the ability to import/export Realm files from a variety of data container formats.

Realm Converter Realm Converter is an open source software utility framework to make it easier to get data both in and out of Realm. It has been built

Realm 212 Dec 9, 2022
TypedDefaults is a utility library to type-safely use NSUserDefaults.

TypedDefaults TypedDefaults is a utility library to type-safely use NSUserDefaults. Motivation The talk Keep Calm and Type Erase On by Gwendolyn Westo

Kazunobu Tasaka 110 Feb 6, 2022