influxdb-client-swift

CircleCI codecov Platforms License Documentation GitHub issues GitHub pull requests Slack Status

This repository contains the reference Swift client for the InfluxDB 2.0.

Documentation

This section contains links to the client library documentation.

Features

InfluxDB 2.0 client consists of two packages

  • InfluxDBSwift
    • Querying data using the Flux language
    • Writing data
      • batched in chunks on background
      • automatic retries on write failures
  • InfluxDBSwiftApis
    • provides all other InfluxDB 2.0 APIs for managing
      • health check
      • sources, buckets
      • tasks
      • authorizations
    • built on top of InfluxDBSwift

Supported Platforms

This package requires Swift 5 and Xcode 12.

  • iOS 14.0+
  • macOS 11.0+
  • tvOS 14.0+
  • watchOS 7.0+
  • Linux

Installation

Swift Package Manager

Add this line to your Package.swift :

  1. // swift-tools-version:5.3
  2. import PackageDescription
  3. let package = Package(
  4. name: "MyPackage",
  5. dependencies: [
  6. .package(name: "influxdb-client-swift", url: "https://github.com/influxdata/influxdb-client-swift", from: "1.1.0"),
  7. ],
  8. targets: [
  9. .target(name: "MyModule", dependencies: [
  10. .product(name: "InfluxDBSwift", package: "influxdb-client-swift"),
  11. // or InfluxDBSwiftApis for management API
  12. .product(name: "InfluxDBSwiftApis", package: "influxdb-client-swift")
  13. ])
  14. ]
  15. )

Usage

Important: You should call close() at the end of your application to release allocated resources.

Creating a client

Specify url and token via parameters:

  1. let client = InfluxDBClient(url: "http://localhost:8086", token: "my-token")
  2. ...
  3. client.close()

Client Options

OptionDescriptionTypeDefault
bucketDefault destination bucket for writesStringnone
orgDefault organization bucket for writesStringnone
precisionDefault precision for the unix timestamps within the body line-protocolTimestampPrecisionns
timeoutIntervalForRequestThe timeout interval to use when waiting for additional data.TimeInterval60 sec
timeoutIntervalForResourceThe maximum amount of time that a resource request should be allowed to take.TimeInterval5 min
enableGzipEnable Gzip compression for HTTP requests.Boolfalse
Configure default Bucket, Organization and Precision
  1. let options: InfluxDBClient.InfluxDBOptions = InfluxDBClient.InfluxDBOptions(
  2. bucket: "my-bucket",
  3. org: "my-org",
  4. precision: .ns)
  5. let client = InfluxDBClient(url: "http://localhost:8086", token: "my-token", options: options)
  6. ...
  7. client.close()

InfluxDB 1.8 API compatibility

  1. client = InfluxDBClient(
  2. url: "http://localhost:8086",
  3. username: "user",
  4. password: "pass",
  5. database: "my-db",
  6. retentionPolicy: "autogen")
  7. ...
  8. client.close()

Writes

The WriteApi supports asynchronous writes into InfluxDB 2.0. The results of writes could be handled by (response, error), Swift.Result or Combine.

The data could be written as:

  1. String that is formatted as a InfluxDB’s Line Protocol
  2. Data Point structure
  3. Tuple style mapping with keys: measurement, tags, fields and time
  4. Array of above items

The following example demonstrates how to write data with Data Point structure. For further information see docs and examples.

  1. import ArgumentParser
  2. import Foundation
  3. import InfluxDBSwift
  4. struct WriteData: ParsableCommand {
  5. @Option(name: .shortAndLong, help: "The name or id of the bucket destination.")
  6. private var bucket: String
  7. @Option(name: .shortAndLong, help: "The name or id of the organization destination.")
  8. private var org: String
  9. @Option(name: .shortAndLong, help: "Authentication token.")
  10. private var token: String
  11. @Option(name: .shortAndLong, help: "HTTP address of InfluxDB.")
  12. private var url: String
  13. public func run() {
  14. // Initialize Client with default Bucket and Organization
  15. let client = InfluxDBClient(
  16. url: url,
  17. token: token,
  18. options: InfluxDBClient.InfluxDBOptions(bucket: self.bucket, org: self.org))
  19. //
  20. // Record defined as Data Point
  21. //
  22. let recordPoint = InfluxDBClient
  23. .Point("demo")
  24. .addTag(key: "type", value: "point")
  25. .addField(key: "value", value: .int(2))
  26. //
  27. // Record defined as Data Point with Timestamp
  28. //
  29. let recordPointDate = InfluxDBClient
  30. .Point("demo")
  31. .addTag(key: "type", value: "point-timestamp")
  32. .addField(key: "value", value: .int(2))
  33. .time(time: .date(Date()))
  34. client.makeWriteAPI().write(points: [recordPoint, recordPointDate]) { result, error in
  35. // For handle error
  36. if let error = error {
  37. self.atExit(client: client, error: error)
  38. }
  39. // For Success write
  40. if result != nil {
  41. print("Written data:\n\n\([recordPoint, recordPointDate].map { "\t- \($0)" }.joined(separator: "\n"))")
  42. print("\nSuccess!")
  43. }
  44. self.atExit(client: client)
  45. }
  46. // Wait to end of script
  47. RunLoop.current.run()
  48. }
  49. private func atExit(client: InfluxDBClient, error: InfluxDBClient.InfluxDBError? = nil) {
  50. // Dispose the Client
  51. client.close()
  52. // Exit script
  53. Self.exit(withError: error)
  54. }
  55. }
  56. WriteData.main()

Queries

The result retrieved by QueryApi could be formatted as a:

  1. Lazy sequence of FluxRecord
  2. Raw query response as a Data.

Query to FluxRecord

  1. import ArgumentParser
  2. import Foundation
  3. import InfluxDBSwift
  4. struct QueryCpu: ParsableCommand {
  5. @Option(name: .shortAndLong, help: "The bucket to query. The name or id of the bucket destination.")
  6. private var bucket: String
  7. @Option(name: .shortAndLong,
  8. help: "The organization executing the query. Takes either the `ID` or `Name` interchangeably.")
  9. private var org: String
  10. @Option(name: .shortAndLong, help: "Authentication token.")
  11. private var token: String
  12. @Option(name: .shortAndLong, help: "HTTP address of InfluxDB.")
  13. private var url: String
  14. public func run() {
  15. // Initialize Client with default Organization
  16. let client = InfluxDBClient(
  17. url: url,
  18. token: token,
  19. options: InfluxDBClient.InfluxDBOptions(org: self.org))
  20. // Flux query
  21. let query = """
  22. from(bucket: "\(self.bucket)")
  23. |> range(start: -10m)
  24. |> filter(fn: (r) => r["_measurement"] == "cpu")
  25. |> filter(fn: (r) => r["cpu"] == "cpu-total")
  26. |> filter(fn: (r) => r["_field"] == "usage_user" or r["_field"] == "usage_system")
  27. |> last()
  28. """
  29. print("\nQuery to execute:\n\n\(query)")
  30. client.queryAPI.query(query: query) { response, error in
  31. // For handle error
  32. if let error = error {
  33. self.atExit(client: client, error: error)
  34. }
  35. // For Success response
  36. if let response = response {
  37. print("\nSuccess response...\n")
  38. print("CPU usage:")
  39. do {
  40. try response.forEach { record in
  41. print("\t\(record.values["_field"]!): \(record.values["_value"]!)")
  42. }
  43. } catch {
  44. self.atExit(client: client, error: InfluxDBClient.InfluxDBError.cause(error))
  45. }
  46. }
  47. self.atExit(client: client)
  48. }
  49. // Wait to end of script
  50. RunLoop.current.run()
  51. }
  52. private func atExit(client: InfluxDBClient, error: InfluxDBClient.InfluxDBError? = nil) {
  53. // Dispose the Client
  54. client.close()
  55. // Exit script
  56. Self.exit(withError: error)
  57. }
  58. }
  59. QueryCpu.main()

Query to Data

  1. import ArgumentParser
  2. import Foundation
  3. import InfluxDBSwift
  4. struct QueryCpuData: ParsableCommand {
  5. @Option(name: .shortAndLong, help: "The bucket to query. The name or id of the bucket destination.")
  6. private var bucket: String
  7. @Option(name: .shortAndLong,
  8. help: "The organization executing the query. Takes either the `ID` or `Name` interchangeably.")
  9. private var org: String
  10. @Option(name: .shortAndLong, help: "Authentication token.")
  11. private var token: String
  12. @Option(name: .shortAndLong, help: "HTTP address of InfluxDB.")
  13. private var url: String
  14. public func run() {
  15. // Initialize Client with default Organization
  16. let client = InfluxDBClient(
  17. url: url,
  18. token: token,
  19. options: InfluxDBClient.InfluxDBOptions(org: self.org))
  20. // Flux query
  21. let query = """
  22. from(bucket: "\(self.bucket)")
  23. |> range(start: -10m)
  24. |> filter(fn: (r) => r["_measurement"] == "cpu")
  25. |> filter(fn: (r) => r["cpu"] == "cpu-total")
  26. |> filter(fn: (r) => r["_field"] == "usage_user" or r["_field"] == "usage_system")
  27. |> last()
  28. """
  29. client.queryAPI.queryRaw(query: query) { response, error in
  30. // For handle error
  31. if let error = error {
  32. self.atExit(client: client, error: error)
  33. }
  34. // For Success response
  35. if let response = response {
  36. let csv = String(decoding: response, as: UTF8.self)
  37. print("InfluxDB response: \(csv)")
  38. }
  39. self.atExit(client: client)
  40. }
  41. // Wait to end of script
  42. RunLoop.current.run()
  43. }
  44. private func atExit(client: InfluxDBClient, error: InfluxDBClient.InfluxDBError? = nil) {
  45. // Dispose the Client
  46. client.close()
  47. // Exit script
  48. Self.exit(withError: error)
  49. }
  50. }
  51. QueryCpuData.main()

Parameterized queries

InfluxDB Cloud supports Parameterized Queries that let you dynamically change values in a query using the InfluxDB API. Parameterized queries make Flux queries more reusable and can also be used to help prevent injection attacks.

InfluxDB Cloud inserts the params object into the Flux query as a Flux record named params. Use dot or bracket notation to access parameters in the params record in your Flux query. Parameterized Flux queries support only int , float, and string data types. To convert the supported data types into other Flux basic data types, use Flux type conversion functions.

Parameterized query example:

⚠️ Parameterized Queries are supported only in InfluxDB Cloud, currently there is no support in InfluxDB OSS.

  1. import ArgumentParser
  2. import Foundation
  3. import InfluxDBSwift
  4. struct ParameterizedQuery: ParsableCommand {
  5. @Option(name: .shortAndLong, help: "The bucket to query. The name or id of the bucket destination.")
  6. private var bucket: String
  7. @Option(name: .shortAndLong,
  8. help: "The organization executing the query. Takes either the `ID` or `Name` interchangeably.")
  9. private var org: String
  10. @Option(name: .shortAndLong, help: "Authentication token.")
  11. private var token: String
  12. @Option(name: .shortAndLong, help: "HTTP address of InfluxDB.")
  13. private var url: String
  14. public func run() {
  15. // Initialize Client with default Organization
  16. let client = InfluxDBClient(
  17. url: url,
  18. token: token,
  19. options: InfluxDBClient.InfluxDBOptions(org: self.org))
  20. // Flux query
  21. let query = """
  22. from(bucket: params.bucketParam)
  23. |> range(start: duration(v: params.startParam))
  24. |> filter(fn: (r) => r["_measurement"] == "cpu")
  25. |> filter(fn: (r) => r["cpu"] == "cpu-total")
  26. |> filter(fn: (r) => r["_field"] == "usage_user" or r["_field"] == "usage_system")
  27. |> last()
  28. """
  29. // Query parameters [String:String]
  30. let queryParams = [ "bucketParam":"\(self.bucket)", "startParam":"-10m" ]
  31. print("\nQuery to execute:\n\n\(query)\n\n\(queryParams)")
  32. client.queryAPI.query(query: query, params: queryParams) { response, error in
  33. // For handle error
  34. if let error = error {
  35. self.atExit(client: client, error: error)
  36. }
  37. // For Success response
  38. if let response = response {
  39. print("\nSuccess response...\n")
  40. print("CPU usage:")
  41. do {
  42. try response.forEach { record in
  43. print("\t\(record.values["_field"]!): \(record.values["_value"]!)")
  44. }
  45. } catch {
  46. self.atExit(client: client, error: InfluxDBClient.InfluxDBError.cause(error))
  47. }
  48. }
  49. self.atExit(client: client)
  50. }
  51. // Wait to end of script
  52. RunLoop.current.run()
  53. }
  54. private func atExit(client: InfluxDBClient, error: InfluxDBClient.InfluxDBError? = nil) {
  55. // Dispose the Client
  56. client.close()
  57. // Exit script
  58. Self.exit(withError: error)
  59. }
  60. }
  61. ParameterizedQuery.main()

Delete data

The DeleteAPI supports deletes points from an InfluxDB bucket. Use the DeletePredicateRequest identifies which points to delete.

  1. import ArgumentParser
  2. import Foundation
  3. import InfluxDBSwift
  4. struct DeleteData: ParsableCommand {
  5. @Option(name: .shortAndLong, help: "Specifies the bucket name to delete data from.")
  6. private var bucket: String
  7. @Option(name: .shortAndLong,
  8. help: "Specifies the organization name to delete data from.")
  9. private var org: String
  10. @Option(name: .shortAndLong, help: "Authentication token.")
  11. private var token: String
  12. @Option(name: .shortAndLong, help: "HTTP address of InfluxDB.")
  13. private var url: String
  14. @Option(name: .shortAndLong, help: "InfluxQL-like delete predicate statement.")
  15. private var predicate: String
  16. public func run() {
  17. // Initialize Client with default Organization
  18. let client = InfluxDBClient(
  19. url: url,
  20. token: token,
  21. options: InfluxDBClient.InfluxDBOptions(org: self.org))
  22. // Create DeletePredicateRequest
  23. let predicateRequest = DeletePredicateRequest(
  24. start: Date(timeIntervalSince1970: 0),
  25. stop: Date(),
  26. predicate: predicate)
  27. client.deleteAPI.delete(predicate: predicateRequest, bucket: bucket, org: org) { result, error in
  28. // For handle error
  29. if let error = error {
  30. self.atExit(client: client, error: error)
  31. }
  32. // For Success Delete
  33. if result != nil {
  34. print("\nDeleted data by predicate:\n\n\t\(predicateRequest)")
  35. // Print date after Delete
  36. queryData(client: client)
  37. }
  38. }
  39. // Wait to end of script
  40. RunLoop.current.run()
  41. }
  42. private func queryData(client: InfluxDBClient) {
  43. let query = """
  44. from(bucket: "\(self.bucket)")
  45. |> range(start: 0)
  46. |> filter(fn: (r) => r["_measurement"] == "server")
  47. |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
  48. """
  49. client.queryAPI.query(query: query) { response, error in
  50. // For handle error
  51. if let error = error {
  52. self.atExit(client: client, error: error)
  53. }
  54. // For Success response
  55. if let response = response {
  56. print("\nRemaining data after delete:\n")
  57. do {
  58. try response.forEach { record in
  59. let provider = record.values["provider"]!
  60. let production = record.values["production"]
  61. let app = record.values["app"]
  62. return print("\t\(provider),production=\(production!),app=\(app!)")
  63. }
  64. } catch {
  65. self.atExit(client: client, error: InfluxDBClient.InfluxDBError.cause(error))
  66. }
  67. }
  68. self.atExit(client: client)
  69. }
  70. }
  71. private func atExit(client: InfluxDBClient, error: InfluxDBClient.InfluxDBError? = nil) {
  72. // Dispose the Client
  73. client.close()
  74. // Exit script
  75. Self.exit(withError: error)
  76. }
  77. }
  78. DeleteData.main()

Management API

The client supports following management API:

API docs
AuthorizationsAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Authorizations
BucketsAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Buckets
DBRPsAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/DBRPs
HealthAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Health
PingAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Ping
LabelsAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Labels
OrganizationsAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Organizations
ReadyAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Ready
ScraperTargetsAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/ScraperTargets
SecretsAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Secrets
SetupAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Tasks
SourcesAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Sources
TasksAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Tasks
UsersAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Users
VariablesAPIhttps://docs.influxdata.com/influxdb/v2.0/api/#tag/Variables

The following example demonstrates how to use a InfluxDB 2.0 Management API to create new bucket. For further information see docs and examples.

  1. import ArgumentParser
  2. import Foundation
  3. import InfluxDBSwift
  4. import InfluxDBSwiftApis
  5. struct CreateNewBucket: ParsableCommand {
  6. @Option(name: .shortAndLong, help: "New bucket name.")
  7. private var name: String
  8. @Option(name: .shortAndLong, help: "Duration bucket will retain data.")
  9. private var retention: Int = 3600
  10. @Option(name: .shortAndLong, help: "The ID of the organization.")
  11. private var orgId: String
  12. @Option(name: .shortAndLong, help: "Authentication token.")
  13. private var token: String
  14. @Option(name: .shortAndLong, help: "HTTP address of InfluxDB.")
  15. private var url: String
  16. public func run() {
  17. // Initialize Client and API
  18. let client = InfluxDBClient(url: url, token: token)
  19. let api = InfluxDB2API(client: client)
  20. // Bucket configuration
  21. let request = PostBucketRequest(
  22. orgID: self.orgId,
  23. name: self.name,
  24. retentionRules: [RetentionRule(type: RetentionRule.ModelType.expire, everySeconds: self.retention)])
  25. // Create Bucket
  26. api.bucketsAPI.postBuckets(postBucketRequest: request) { bucket, error in
  27. // For error exit
  28. if let error = error {
  29. self.atExit(client: client, error: error)
  30. }
  31. if let bucket = bucket {
  32. // Create Authorization with permission to read/write created bucket
  33. let bucketResource = Resource(
  34. type: Resource.ModelType.buckets,
  35. id: bucket.id!,
  36. orgID: self.orgId
  37. )
  38. // Authorization configuration
  39. let request = Authorization(
  40. description: "Authorization to read/write bucket: \(self.name)",
  41. orgID: self.orgId,
  42. permissions: [
  43. Permission(action: Permission.Action.read, resource: bucketResource),
  44. Permission(action: Permission.Action.write, resource: bucketResource)
  45. ])
  46. // Create Authorization
  47. api.authorizationsAPI.postAuthorizations(authorization: request) { authorization, error in
  48. // For error exit
  49. if let error = error {
  50. atExit(client: client, error: error)
  51. }
  52. // Print token
  53. if let authorization = authorization {
  54. let token = authorization.token!
  55. print("The token: '\(token)' is authorized to read/write from/to bucket: '\(bucket.id!)'.")
  56. atExit(client: client)
  57. }
  58. }
  59. }
  60. }
  61. // Wait to end of script
  62. RunLoop.current.run()
  63. }
  64. private func atExit(client: InfluxDBClient, error: InfluxDBError? = nil) {
  65. // Dispose the Client
  66. client.close()
  67. // Exit script
  68. Self.exit(withError: error)
  69. }
  70. }
  71. CreateNewBucket.main()

Advanced Usage

Default Tags

Sometimes is useful to store same information in every measurement e.g. hostname, location, customer. The client is able to use static value or env variable as a tag value.

The expressions:

  • California Miner - static value
  • ${env.HOST_NAME} - environment property

Example

  1. client = InfluxDBClient(
  2. url: "http://localhost:8086",
  3. token: "my-token",
  4. options: InfluxDBClient.InfluxDBOptions(bucket: "my-bucket", org: "my-org"))
  5. let tuple: InfluxDBClient.Point.Tuple
  6. = (measurement: "mem", tags: ["tag": "a"], fields: ["value": .int(3)], time: nil)
  7. let records: [Any] = [
  8. InfluxDBClient.Point("mining")
  9. .addTag(key: "sensor_state", value: "normal")
  10. .addField(key: "depth", value: .int(2)),
  11. tuple
  12. ]
  13. let defaultTags = InfluxDBClient.PointSettings()
  14. .addDefaultTag(key: "customer", value: "California Miner")
  15. .addDefaultTag(key: "sensor_id", value: "${env.SENSOR_ID}")
  16. let writeAPI = client.makeWriteAPI(pointSettings: defaultTags)
  17. writeAPI.writeRecords(records: records) { _, error in
  18. if let error = error {
  19. print("Error: \(error)")
  20. return
  21. }
  22. print("Successfully written default tags")
  23. }
Example Output
  1. mining,customer=California\ Miner,sensor_id=123-456-789,sensor_state=normal depth=2i
  2. mining,customer=California\ Miner,sensor_id=123-456-789,sensor_state=normal pressure=3i

Proxy and redirects

⚠️ The connectionProxyDictionary cannot be defined on Linux. You have to set HTTPS_PROXY or HTTP_PROXY system environment.

You can configure the client to tunnel requests through an HTTP proxy by connectionProxyDictionary option:

  1. var connectionProxyDictionary = [AnyHashable: Any]()
  2. connectionProxyDictionary[kCFNetworkProxiesHTTPEnable as String] = 1
  3. connectionProxyDictionary[kCFNetworkProxiesHTTPProxy as String] = "localhost"
  4. connectionProxyDictionary[kCFNetworkProxiesHTTPPort as String] = 3128
  5. let options: InfluxDBClient.InfluxDBOptions = InfluxDBClient.InfluxDBOptions(
  6. bucket: "my-bucket",
  7. org: "my-org",
  8. precision: .ns,
  9. connectionProxyDictionary: connectionProxyDictionary)
  10. client = InfluxDBClient(url: "http://localhost:8086", token: "my-token", options: options)

For more info see - URLSessionConfiguration.connectionProxyDictionary, Global Proxy Settings Constants.

Redirects

Client automatically follows HTTP redirects. You can disable redirects by an urlSessionDelegate configuration:

  1. class DisableRedirect: NSObject, URLSessionTaskDelegate {
  2. func urlSession(_ session: URLSession,
  3. task: URLSessionTask,
  4. willPerformHTTPRedirection response: HTTPURLResponse,
  5. newRequest request: URLRequest,
  6. completionHandler: @escaping (URLRequest?) -> Void) {
  7. completionHandler(nil)
  8. }
  9. }
  10. let options = InfluxDBClient.InfluxDBOptions(
  11. bucket: "my-bucket",
  12. org: "my-org",
  13. urlSessionDelegate: DisableRedirect())
  14. client = InfluxDBClient(url: "http://localhost:8086", token: "my-token", options: options)

For more info see - URLSessionDelegate.

Contributing

If you would like to contribute code you can do through GitHub by forking the repository and sending a pull request into the master branch.

Build Requirements:

  • swift 5.3 or higher

Build source and test targets:

  1. swift build --build-tests

Run tests:

  1. ./Scripts/influxdb-restart.sh
  2. swift test

Check code coverage:

  1. ./Scripts/influxdb-restart.sh
  2. swift test --enable-code-coverage

You could also use a docker-cli without installing the Swift SDK:

  1. make docker-cli
  2. swift build

License

The client is available as open source under the terms of the MIT License.