ExistAPI 0.0.16

ExistAPI 0.0.16

Belle B. Cooper维护。



ExistAPI 0.0.16

ExistAPI

一个在 iOS 上与 Exist API 交互的框架。

ExistAPI 使用 PromiseKit 将 Exist API 封装起来(通过 promise),以便更容易以命令式的方式处理 API 响应并链接 API 调用。它还将所有 API 响应反序列化为强类型的 Decodable 模型。

安装

通过 Cocoapods

pod 'ExistAPI', '~> 0.0.13'

快速示例

从 Exist API 获取令牌以授权您的用户(有关详细信息,请参阅存在 API 文档中的OAuth2 认证)。然后创建 API 实例

let token = getTokenFromAuthProcess()
let existAPI = ExistAPI(token: token)

获取过去一周的用户的属性

existAPI.attributes()
	.done { attributes, response in
	// handle attribute models here
	}.catch { error in
	// deal with errors
	}

获取带有某些参数的属性

existAPI.attributes(names: ["steps", "mood"], limit: 12)
	.done { attributes, response in
	// handle data here
	}.catch { error in
	// handle error here
	}

获取属性

existAPI.acquire(names: ["steps"])
	.done { attributeResponse, urlResponse in
	// deal with data here
	}.catch { error in
	// handle error
	}

更新一些属性的属性值

let steps = IntAttributeUpdate(name: "steps", date: Date(), value: 3158)
let distance = FloatAttributeData(name: "steps_distance", date: Date(), value: 1.2)
existAPI.update(attributes: [steps, distance])
	.done { attributeResponse, urlResponse in
	// if some attributes failed but some succeeded, check failures here
	}.catch { error in
	// handle error
	}

使用方法

ExistAPI 使用 PromiseKit 处理异步网络任务。如果您以前未使用过 Promise,PromiseKit 文档非常好,可以帮助您了解基本用法。基本上,Promise 允许您对异步任务的结果进行操作,就像它是同步的,使您的代码更容易编写、阅读和维护。

ExistAPI类中可用的每个公共函数都返回一个Promise。您可以将它们一起链接,但最简单的用法是使用.done闭包来处理结果,以及使用.catch闭包来处理错误。

请参阅下面的示例,了解如何更有效地使用ExistAPI的Promise。

需求

  • Swift 4.0
  • iOS 12
  • Exist 账户
  • Cocoapods

开始使用

首先,您需要创建一个Exist开发者客户端。阅读这篇博客文章可以详细了解如何创建开发者客户端。

为了快速开始,您可以将从开发者客户端详情页复制的个人认证令牌粘贴进来。这将使您能够立即使用Exist API进行测试,并可以将构建用户授权流程的工作留到稍后。

重要说明

  • 在使用DateExistAPI配合使用时,请始终使用本地设备时间。

创建ExistAPI实例

使用您的令牌创建一个ExistAPI实例,并可选地为网络请求设置特定的超时时间。

let existAPI = ExistAPI(token: yourToken, timeout: 40)

GET请求

func attributes(names: [String]?, limit: Int?, minDate: Date?, maxDate: Date?) -> Promise<(attributes: [Attribute], response: URLResponse)>

返回一个Attribute模型的数组。

struct Attribute: AttributeValues {
    let name: String
    let label: String
    let group: AttributeGroup
    let priority: Int
    let service: String
    let valueType: ValueType
    let valueTypeDescription: String
    private let values: [AttributeData]
}

public protocol AttributeValues {
    func getIntValues() throws -> [IntValue]
    func getStringValues() throws -> [StringValue]
    func getFloatValues() throws -> [FloatValue]
}

每个属性都使用 AttributeValues 协议以 IntStringFloat 形式返回其值。您必须根据所处理的属性的值类型选择正确的对应函数。所有可用的属性及其类型可以在 Exist API 文档中找到

值类型模型如下

public protocol ValueObject {
    associatedtype ValueType
    var value: ValueType? { get }
    var date: Date { get }
}

public struct IntValue: ValueObject {
    public typealias ValueType = Int
    public var value: Int?
    public var date: Date
}

public struct StringValue: ValueObject {
    public typealias ValueType = String
    public var value: String?
    public var date: Date
}

public struct FloatValue: ValueObject {
    public typealias ValueType = Float
    public var value: Float?
    public var date: Date
}
func insights(limit: Int?, pageIndex: Int?, minDate: Date?, maxDate: Date?) -> Promise<(insights: InsightResponse, response: URLResponse)>

返回一个 InsightResponse

struct InsightResponse {
    let count: Int
    var next: String?
    var previous: String?
    let results: [Insight]
}

struct Insight: Codable {
    let created: Date
    let targetDate: Date?
    let type: InsightType
    let html: String
    let text: String
}
func averages(for attribute: String?, limit: Int?, pageIndex: Int?, minDate: Date?, maxDate: Date?) -> Promise<(averages: [Average], response: URLResponse)>

返回一个 Average 模型数组

class Average: Codable {
    let attribute: String
    let date: Date
    let overall: Float
    let monday: Float
    let tuesday: Float
    let wednesday: Float
    let thursday: Float
    let friday: Float
    let saturday: Float
    let sunday: Float
}
func correlations(for attribute: String?, limit: Int?, pageIndex: Int?, minDate: Date?, maxDate: Date?, latest: Bool?) -> Promise<(correlations: [Correlation], response: URLResponse)>

返回一个 Correlation 模型数组

class Correlation: Codable {
    let date: Date
    let period: Int
    let attribute: String
    let attribute2: String
    let value: Float
    let p: Float
    let percentage: Float
    let stars: Int
    let secondPerson: String
    let secondPersonElements: [String]
    let strengthDescription: String
    let starsDescription: String
    let description: String?
    let occurrence: String?
    let rating: CorrelationRating?
}
func user() -> Promise<(user: User, response: URLResponse)>

返回一个 User 模型

class User: Codable {
    let id: Int
    let username: String
    let firstName: String
    let lastName: String
    let bio: String
    let url: String
    let avatar: String
    let timezone: String
    let imperialUnits: Bool
    let imperialDistance: Bool
    let imperialWeight: Bool
    let imperialEnergy: Bool
    let imperialLiquid: Bool
    let imperialTemperature: Bool
    let trial: Bool
    let delinquent: Bool
}

POST 请求

属性必须在服务中专属之后才能被更新。要拥有一个属性,请使用 acquire 函数。尝试更新不属于您的客户端的属性将会失败。请参阅 Exist API 文档中的属性专属说明

public func acquire(names: [String]) -> Promise<(attributeResponse: AttributeResponse, response: URLResponse)>

返回一个 AttributeResponse

public struct AttributeResponse: Codable {
    var success: [SuccessfulAttribute]?
    var failed: [FailedAttribute]?
}

public struct SuccessfulAttribute: Codable {
    var name: String
    var active: Bool?
}

public struct FailedAttribute: Codable {
    var name: String
    var errorCode: String
    var error: String
}

要停止拥有一个属性,请使用 release 函数

public func release(names: [String]) -> Promise<(attributeResponse: AttributeResponse, response: URLResponse)>

在调用 update 函数之前,请确保您已经阅读了 关于属性获取和更新的 Exist API 文档

public func update<T: AttributeUpdate>(attributes: [T]) -> Promise<(attributeResponse: AttributeUpdateResponse, response: URLResponse)>

接受符合 AttributeUpdate 协议的对象数组

public protocol AttributeUpdate: Codable {
    associatedtype Value: Codable
    var name: String { get }
    var date: Date { get }
    var value: Value { get }
    func dictionaryRepresentation() throws -> [String: Any]?
}

存在三种 AttributeUpdate 的具体实现

public struct StringAttributeUpdate: AttributeUpdate {
    public typealias Value = String
}

public struct FloatAttributeUpdate: AttributeUpdate {
    public typealias Value = Float
}

public struct IntAttributeUpdate: AttributeUpdate {
    public typealias Value = Int
}

update 函数返回一个 AttributeUpdateResponse

public struct AttributeUpdateResponse: Codable {
    var success: [SuccessfullyUpdatedAttribute]?
    var failed: [FailedToUpdateAttribute]?
}

public struct SuccessfullyUpdatedAttribute: Codable {
    var name: String
    var date: Date
    var value: String?
}

public struct FailedToUpdateAttribute: Codable {
    var name: String?
    var date: Date?
    var value: String?
    var errorCode: String
    var error: String
}

链式操作

由于 Exist API 使用 PromiseKit,您可以连续调用多个方法。以下是一些示例

由于我们需要先获取用户 Exist 账户中的属性,然后才能更新这些属性,因此第一次尝试使用数据更新属性时,我们可以将这些步骤链在一起

existAPI.acquire(names: ["weight"]
	.then { attributeResponse, urlResponse in
     	guard let success = attributeResponse.success,
        	success.contains("weight") else { return }
     	let update = FloatAttributeUpdate(name: "weight", date: Date(), value: 65.8)
		existAPI.update(attributes: updates)
    		.done { attributeUpdateResponse, urlResponse in
        		// handle success
    		}.catch { error in
        		// handle error
    		}

PromiseKit 允许我们使用 when 来在所有承诺都被完成时才执行某个动作,并在这个承诺链中只处理一次错误。使用 when 可以一次性调用 Exist API 的一组方法并处理所有返回的数据

let insightsPromise = existAPI.insights(days: 30)
let averagesPromise = existAPI.averages(limit: 30)
let correlationsPromise = existAPI.correlations

when(fulfilled: [insightsPromise, averagesPromise, correlationsPromise])
	.done { insights, averages, correlations in
	// handle data
	}.catch { error in
	// handle errors just once for all these promises
	}

构建应用程序

使用 Xcode 10 克隆源代码并构建。

运行测试

ExistAPITests内部创建一个名为TestConstants.swift的新文件,并添加一个名为TEST_TOKEN的字符串常量,如下所示

let TEST_TOKEN = "your_token_string_here"

没有这个,测试将失败。您可以通过在Exist账户中创建开发者客户端来获取访问令牌。

待办

  • GET请求
  • POST请求
  • 创建一个便捷的func来访问只有今天的属性
  • 支持附加自定义标签
  • 为包括请求中的查询添加测试