influxdb-client-swift

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

    This section contains links to the client library documentation.

    Swift - 图11Features

    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

    This package requires Swift 5 and Xcode 12.

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

    Installation

    Add this line to your Package.swift :

    Swift - 图16Creating 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

    Swift - 图18Configure 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()

    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. 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()
    • sources -

    Queries

    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. url: url,
    17. token: token,
    18. options: InfluxDBClient.InfluxDBOptions(org: self.org))
    19. // Flux query
    20. let query = """
    21. from(bucket: "\(self.bucket)")
    22. |> range(start: -10m)
    23. |> filter(fn: (r) => r["_measurement"] == "cpu")
    24. |> filter(fn: (r) => r["cpu"] == "cpu-total")
    25. |> filter(fn: (r) => r["_field"] == "usage_user" or r["_field"] == "usage_system")
    26. |> last()
    27. """
    28. print("\nQuery to execute:\n\n\(query)")
    29. client.queryAPI.query(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. print("\nSuccess response...\n")
    37. print("CPU usage:")
    38. do {
    39. try response.forEach { record in
    40. print("\t\(record.values["_field"]!): \(record.values["_value"]!)")
    41. }
    42. } catch {
    43. self.atExit(client: client, error: InfluxDBClient.InfluxDBError.cause(error))
    44. }
    45. }
    46. }
    47. // Wait to end of script
    48. RunLoop.current.run()
    49. }
    50. private func atExit(client: InfluxDBClient, error: InfluxDBClient.InfluxDBError? = nil) {
    51. // Dispose the Client
    52. client.close()
    53. // Exit script
    54. Self.exit(withError: error)
    55. }
    56. }
    57. QueryCpu.main()
    • sources -

    Query to Data

    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 .

    Parameterized query example:

    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()

    The supports deletes points from an InfluxDB bucket. Use the 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. from(bucket: "\(self.bucket)")
    44. |> range(start: 0)
    45. |> filter(fn: (r) => r["_measurement"] == "server")
    46. |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
    47. """
    48. client.queryAPI.query(query: query) { response, error in
    49. // For handle error
    50. if let error = error {
    51. self.atExit(client: client, error: error)
    52. }
    53. // For Success response
    54. if let response = response {
    55. print("\nRemaining data after delete:\n")
    56. do {
    57. try response.forEach { record in
    58. let provider = record.values["provider"]!
    59. let production = record.values["production"]
    60. let app = record.values["app"]
    61. }
    62. } catch {
    63. self.atExit(client: client, error: InfluxDBClient.InfluxDBError.cause(error))
    64. }
    65. }
    66. self.atExit(client: client)
    67. }
    68. }
    69. private func atExit(client: InfluxDBClient, error: InfluxDBClient.InfluxDBError? = nil) {
    70. // Dispose the Client
    71. client.close()
    72. // Exit script
    73. Self.exit(withError: error)
    74. }
    75. }
    76. DeleteData.main()

    Swift - 图26Management API

    The client supports following management API:

    API docs
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Authorizations
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Buckets
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/DBRPs
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Health
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Ping
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Labels
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Organizations
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Ready
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/ScraperTargets
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Secrets
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Tasks
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Sources
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Tasks
    https://docs.influxdata.com/influxdb/v2.0/api/#tag/Users
    https://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 .

    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

    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

    Swift - 图29Example

    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

    Swift - 图31Proxy and redirects

    For more info see - , 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 - .

    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

    License

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