测试已测试 | ✓ |
Lang语言 | SwiftSwift |
许可证 | MIT |
发布最后发布 | 2017年10月 |
SwiftSwift版本 | 3.0 |
SPM支持SPM | ✗ |
由Mark Woollard维护。
苹果平台上的UrbanThings API是一个现代、高度可配置的SDK,它除了使用简单外,还提供了类型安全和可测试性。
SDK是用Swift开发的,应与Swift一起使用。这是为了保证结果数据类型安全,并且任何数据元素的含义都没有歧义。如果需要,结果数据可以提供给Objective-C代码。
SDK与iOS(包括扩展)、OS X(包括扩展)、tvOS和watchKit兼容。它使用Xcode 8.3.3 / Swift 3.1构建。支持的部署版本如下
平台 | 最小支持版本 |
---|---|
iOS | 8.0 |
tvOS | 9.0 |
watchKit | 2.0 |
OS X | 10.9 |
SDK是UrbanThings生态系统的一个核心组件,也是UrbanThings在苹果产品软件内部使用的相同框架。
SDK已公开给开源社区,我们欢迎反馈和改进功能或修复任何发现的问题的pull请求。反馈可以通过GitHub问题跟踪器提交,通过电子邮件发送至[email protected]。或者,对于真正的编码高手,如果您分支了这个项目,我们非常愿意查看您的Pull Requests。
我们支持多种方法将框架包含到您的项目中。

Swift包管理器目前只支持OS X和Linux,不在Xcode中。我们将添加支持,一旦它完全支持所有苹果平台。
如果您不想使用包管理器,请克隆存储库,使用Xcode打开UrbanThingsAPI.xcodeproj
项目,为所需平台构建目标。然后您可以将框架添加到您的项目中。或者,您可以将UrbanThingsAPI.xcodeproj
添加为项目内的项目。
要从服务器请求数据,请实例化一个UrbanThingsAPI
类的实例。
import UTAPI
let service = UTService(endpoint: "https://bristol.api.urbanthings.io/api",
version: "2.0",
key: "A valid API key")
let api = UrbanThingsAPI(service: service)
单个实例可以在应用程序的生命周期内安全地用于所有请求,但您可以根据需要创建和销毁实例,并同时维护多个实例。
请求通过调用 sendRequest
并传递一个 Request
对象来实现。基本使用非常简单,只需传入提供的其中一个 UT...Request
对象,所有这些对象都实现了所需的协议。例如
api.send(request: UTImportSourcesRequest()) { data, error in
}
数据以异步方式作为尾部闭包的组成部分返回(见下文)。
您可以使用所有请求的预提供默认实现,或者高级用户可以创建自己的实现,通过实现适当的协议。
许多API请求需要传入输入数据来指定所需的参数。例如,让我们请求UrbanThings办公室半径为500米内的地点集合
let myOffice = CLLocationCoordinate2D(latitude: 51.5291205, longitude:-0.0802295)
api.send(request: UTPlacePointsRequest(center:myOffice, radius:500)) { data, error in
}
在上述示例中,我们使用了 UTPlacePointsRequest
,这是 PlacePointsRequest
协议的提供实现。它具有初始化器来处理各种允许的输入组合;我们传入了一个 radius
为 500 以及与我们的办公室对应的 center
位置。
这种方式,SDK 设计阻止构建许多无效的请求,并确保提供所有必需的信息。这是通过这些初始化器的编译时要求来实现的。
send
方法是异步的,在调用时提供闭包,以便在结果可用时传递该调用的结果。completionHandler
闭包接收可选参数:data
(如果请求成功则填充)和 error
(如果请求失败则填充)。
data
参数的类型是响应预期的数据类型。SDK 定义了一套完整的协议,适用于API返回的所有数据对象。内部,响应 JSON 被解析为这些协议的实现,然后传递给完成处理闭包。这意味着在您的代码中,您始终使用正确类型的对象,因此不会出现数据误解释的情况。所有这些都通过编译时强制。
让我们将上面的示例扩展到包括一些响应处理
api.send(request: UTPlacePointsRequest(center:office, radius:500)) { data, error in
if let data = data {
for placePoint in data.placePoints {
print("\(placePoint.name)")
}
} else {
print("Error - \(error)")
}
}
在这种情况下数据是 PlacePointList?
,这是对协议 PlacePointList
的可选对象。我们可以通过访问协议定义的属性来简单迭代包含在这些点中的内容。
让我们检查一个替代请求,列出导入源
api.send(request: UTImportSourcesRequest()) { data, error in
if let data = data {
for importSource in data {
print("\(importSource.importSourceID) - \(importSource.name)")
}
} else {
print("Error - \(error)")
}
这里响应数据类型为 [ImportSource]?
,这是对包含 ImportSource
协议实例数组的可选对象。同样,我们可以通过协议访问这些数据,具有完整的类型安全性。
有关通过API可能接收到的完整协议集合的详细信息,请参阅在线文档。
定义了以下当前请求协议及其提供实现的完整集合
协议 | 提供实现 | 用途 |
---|---|---|
DirectionsRequest | UTDirectionsRequest | 用于在两个地点之间请求路线。请求有多个选项来细化路由,例如可达性、运输模式等。 |
ImportSourcesRequest | UTImportSourcesRequest | 用于请求导入源列表。此请求没有额外的输入参数。 |
PlacePointsRequest | UTPlacePointsRequest | 用于请求属于地理区域内的一组地点。该区域可以定义为圆形或矩形,并提供其他选项以细化请求。 |
PlacesListRequest | UTPlacesListRequest | 用于请求与(包含)搜索字符串匹配的地点列表。 |
RealtimeReportRequest | UTRealtimeReportRequest | 用于请求实时到站和离站信息,适用于数据处理目的。 |
RealtimeResourcesStatusRequest | UTRealtimeResourcesStatusRequest | 用于请求单个交通站点的资源状态,例如停车场或自行车租赁码头。 |
RealtimeResourceStatusRequest | UTRealtimeResourceStatusRequest | 用于请求多个交通站点的资源状态。 |
RealtimeStopboardRequest | UTRealtimeStopboardRequest | 用于请求适合展示目的的实时到达和出发信息。 |
TransitAgenciesRequest | UTTransitAgenciesRequest | 用于从一个单一导入来源请求交通机构列表。 |
TransitAgencyRequest | UTTransitAgencyRequest | 用于请求单个交通机构。 |
TransitRoutesByImportSourceRequest | UTTransitRoutesByImportSourceRequest | 用于请求可用的外部导入数据集。 |
TransitRoutesByLineNameRequest | UTTransitRoutesByLineNameRequest | 用于请求搜索匹配特定参数的公交线路/火车线路/其他方式线路。例如,匹配指定线路名称的附近公交线路。列表可以根据营运机构或数据导入来源进一步筛选。 |
TransitRoutesByStopRequest | UTTransitRoutesByStopRequest | 用于请求搜索由特定站点服务的公交线路。 |
TransitStopCallsRequest | UTTransitStopCallsRequest | 用于请求指定TransitStop的调度时间表 - 也就是说,在指定时间段内指定日期内预计停靠在该站点的所有车辆的完整列表。 |
TransitStopsRequest | UTTransitStopsRequest | 用于请求匹配的TransitStop对象 - 诸如公交车站、火车站、停车场等。 |
TransitTripGroupsRequest | TransitTripGroupsRequest | 用于请求沿特定路线的行程,按运行日(即时间表)或其他特定指标分组。 |
TransitTripsRequest | UTTransitTripsRequest | 用于获取特定行程的详细信息,即沿已知路线的特定旅程。可以按其ID检索单个行程,也可以检索特定路线的所有行程。 |
作为一个设计决策,响应数据定义的协议故意不与Objective-C兼容。
响应数据定义的协议故意不与Objective-C兼容,这样做限制了可以定义的属性类型。具体来说,我们希望强制使用可选属性的字段,以及始终存在的非可选字段。对于不是NSObject子类的值,如果支持Objective-C兼容性,则无法实现。例如,《Enum》类型,数值类型如《Int》、《UInt》、《Double》和《Bool》。
由于Objective-C兼容性,数值类型不再强制执行正确的数值类型,NSNumber实例可能包含任何值。使用此结构时,也不清楚在NSNumber实例中预期包含哪种数值类型。
protocol SwiftOnly {
var intOptional:Int? { get }
var uintOptional:Unt? { get }
}
@objc protocol ObjcEquivalent {
var intOptional: NSNumber? { get }
var uintOptional: NSNumber? { get }
}
由于以上模糊不清,在Swift中工作时,我们决定不直接提供Objective-C兼容性。
let swift:SwiftOnly = ASwiftVersion()
let objc:ObjcEquivalent = AnObjCVersion()
if let n = swift.intOptional {
// n is of type Int
}
if let nsNumber = objc.intOptional {
// How shoud we use nsNumber?
let n = nsNumber.integerValue
let f = nsNumber.floatValue
let b = nsNumber.boolValue
}
// The following are all valid as well and will
// provide n, b and f as their respective types.
if let n = objc.intOptional as? Int {
}
if let b = objc.intOptional as? Bool {
}
if let f = objc.intOptional as? Float {
}
如何在Objective-C中工作
要在Objective-C中使用数据,需要将其转换为与Objective-C兼容的类。虽然你可以自由实现以满足你的需求,但我们提供了一个单独的框架 UTAPIObjCAdapter 。
该框架提供了一整套可以将纯Swift响应数据结构转换为在Objective-C中可用的对象。所有这些都接受实现对应Swift响应协议的对象,并实现了相应的Objective-C协议。同样,这种基于协议的方法允许你提供自己的Objective-C数据响应协议实现,如果你不想使用提供的实现。
例如,假设我们有一个处理《PlacePoint》对象列表的Objective-C类
import <UTAPIObjCAdapter/UTAPIObjCAdapter.h>
@interface PlacePointProcessor : NSObject
- (void)process:(NSArray<PlacePoint *> * _Nonnull)points;
@end
@implementation PlacePointProcessor
- (void)process:(NSArray<PlacePoint *> * _Nonnull)points {
// Do some processing...
}
@end
上述Objective-C代码片段假定已配置必要的桥接头和Xcode选项。有关更多详细信息,请参阅相关的Apple文档。
现在我们可以编写一些Swift代码来请求Objective-C代码处理的数据
import UTAPI
import UTAPIObjCAdapter
let api = UrbanThingsAPI(apiKey:"A VALID API KEY")
let processor = PlacePointProcessor()
api.sendRequest(UTPlacePointsRequest(center:office, radius:500)) { data, error in
if let data = data {
// Map the array of Swift objects to adapted Objective-C objects
let adaptedData = data.points.map { UTPlacePoint(adapt:$0) }
// Can now pass this to the Objective-C method
processor.process(adaptedData)
} else {
print("Error - \(error)")
}
}
在 Swift 和 Objective-C 之间可桥接的项目实现了此协议。然而,目前此协议文档不足。我们将探讨这是一种简化互操作性的方法,尽管框架中存在一些如何干净地实现这一点的复杂性。但是,如果您能解决这个问题,那么您将��得一套 Objective-C 对象/适配器,并可以使用类型转换进行转换。
// Get objc is instance of ObjcAdapter for ASwiftVersion instance
let objc: ObjcAdapter = ASwiftVersion()
// Similar for arrys
let objcArray:[ObjcAdapter] = swiftArray
请关注更多关于 Swift / Objective-C 互操作性的信息,并且不要犹豫为讨论做出贡献。
对于那些不想在项目中使用任何 Swift 代码的用户,我们将继续提供遗留 Objective-C API。
我们包含了一个 Google 轨线解析器,它可以将 Google 轨线字符串转换为 CLLocationCoordinate2D
实例的序列,或从字符串构建 MKPolyline
。
// A valid polyline
let polyline = "_p~iF~ps|U_ulLnnqC_mqNvxq`@"
// Iterate over sequence of CLLocationCoordinate2D values
for point in try polyline.asCoordinateSequence() {
print("lat=\(point.latitude), lng=\(point.longitude)")
}
// Get a MKPolyline instance
let mkPolyline = try polyline.asMKPolyline()
UrbanThings API 框架对 Apple 平台的使用受Apache 许可证 2.0 版本的约束。请参阅 LICENSE 文件以了解详细信息。