测试已测试 | ✗ |
语言语言 | SwiftSwift |
LICENSE | MIT |
发布时间最后发布 | 2017年12月 |
Swift 版本Swift 版本 | 4 |
SPM支持 SPM | ✗ |
由 Brendan Conron 维护。
FeathersSwift 是一个用于与 FeathersJS 后端交互的 Cocoa 网络库。为什么你会想使用它?
如果你使用 FeathersJS(你应该使用),FeathersSwift 是你的不二之选。无需再处理 HTTP 请求或套接字客户端。一个简单的接口就可以统治一切,在黑暗中,统一它们
FeathersSwift 分散在多个仓库中,以确保您只添加所需的组件,不再添加其他组件。有两个 Feathers 提供者,分别是 feathers-swift-rest 和 feathers-swift-socketio。按照它们的各自 README 上的说明安装任何一个提供者。
您安装了一个提供者之后,无论 rest 还是 socketio,都可以抓取一个与 Feathers
应用相关联的实例。
let feathersRestApp = Feathers(RestProvider(baseURL: URL(string: "https://myserver.com")))
然后抓取一个服务
let userService = feathersRestApp.service("users")
最后发送请求
service.request(.find(parameters: ["name": "Waldo"]))
.on(value: { response in
print(response)
})
.start()
FeathersSwift 的 API 完全使用 ReactiveSwift 构建,这是一个绝佳的功能反应式库。由于 Swift 中没有承诺,我们必须找到一个提供类似用法模式的替代方案。通过这样做,我们可以避免灾难金字塔和无休止的回调嵌套,相反,我们可以提供一个简化的反应式 API。
您可以使用对应于 Feathers 服务方法的六种类型的请求
public enum Method {
case find(query: Query?)
case get(id: String, query: Query?)
case create(data: [String: Any], query: Query?)
case update(id: String?, data: [String: Any], query: Query?)
case patch(id: String?, data: [String: Any], query: Query?)
case remove(id: String?, query: Query?)
}
使用 .update
、.patch
和 .remove
时,您可以在删除实体列表时传入 nil 的 id。实体列表由您传入的查询确定。
默认情况下,FeathersSwift 将返回一个 ProviderService
的实例,它将应用程序的传输提供者在服务中包装起来。但是,您也可以注册自己的服务
feathers.use("users-local", CoreDataService())
所有自定义服务都必须符合 ServiceType
。幸运的是,由于 FeathersSwift 提供的 Service
类,这使得符合变得很容易,这个类处理诸如钩子存储和必需方法的空实现等问题。
一个简单的自定义服务可能看起来像
class FileService: Service {
public override func request(_ method: Service.Method) -> SignalProducer<Response, FeathersError> {
let fileManager = FileManager.default
switch method {
case let .create(data, _):
guard let id = data["id"] else { break }
let fileData = NSKeyedArchiver.archiveData(withRootObject: data)
fileManager.createFile(atPath: "\(path)/\(id)", contents: fileData, attributes: nil)
default: break
}
}
}
虽然这是一个小型示例,但自定义服务可以是无限复杂的,并且在钩子过程中的任何地方都可以使用。只需调用 hookObject.app.service("my-custom-service").request(.create(data: [:], parameters: nil))
即可。
你可能注意到,与通过请求传递参数字典不同,FeathersSwift 使用一个 Query
对象。Query
类有一个简单且可组合的 API,可以表示复杂的查询,而无需与字典纠缠。它支持所有 FeathersJS 用户熟悉和喜爱的查询,例如 ne
或 or
,只是以更简化和类型安全的方式进行。
要创建一个查询
let query = Query()
.ne("age", 50)
.limit(25)
.skip(5)
let service = feathers.service("users")
service.request(.find(query)).start()
那些担心自己的字典格式是否正确的日子已经过去了,Query
知道如何序列化自身并为您处理这些问题。
为了使用您的 Feathers 后端对应用程序进行身份验证
feathersRestApp.authenticate([
"strategy": "facebook-token",
"access_token": "ACCESS_TOKEN"
])
.start()
身份验证返回一个 JWT 有效负载,该有效负载由应用程序缓存并用于发出后续请求。目前还没有重新认证机制,但请查看即将推出的版本。
要注销,只需调用
feathersRestApp.logout().start()
当使用套接字提供程序时,您不仅可以使用它来调用 Feathers 服务方法,还可以监听实时事件。只需使用 feathers-swift-socketio 并使用 SocketProvider
实例创建一个 feathers 应用程序,然后通过在您的服务上使用 .on
注册事件。
有四种不同的实时事件
public enum RealTimeEvent: String {
case created
case updated
case patched
case removed
}
您可以使用这些事件来进行诸如动态更新 UI、将实体保存到数据库或仅记录事件发生等操作。
let feathersSocket = Feathers(provider: SocketProvider(baseURL: URL(string: "https://myserver.com")!, configuration: []))
let userService = feathersSocket.service(path: "users")
userService.on(.created)
.observeValues { entity in
print(entity) // Prints the object that was just created
}
当你完成后,务必调用 .off
以注销事件。否则,提供商将保留您的完成块。
userService.off(.created)
还有一个巧妙的 .once
函数,它会执行您期望的操作;您只需监听一个事件,仅一个事件。
与 FeathersJS 类似,您可以在请求发出时注册 before
、after
和 error
钩子。可能的用例可能包括使用 before 钩子进行单元测试占位或简单的记录。
要创建一个钩子,创建一个符合 Hook
的对象
public protocol Hook {
func run(with hookObject: HookObject) -> Promise<HookObject>
}
可能看起来像这样的日志所有 create
事件的钩子
struct CreateLogHook: Hook {
func run(with hookObject: HookObject) -> Promise<HookObject> {
var object = hookObject
if object.method == .create {
print("create happened")
}
return Promise(value: object)
}
}
或者您可以进行更复杂的操作,例如网络呼叫
struct FetchAssociatedUserHook: Hook {
func run(with hookObject: HookObject) -> Promise<HookObject> {
var object = hookObject
guard object.app.service.path == "groups" else {
return Promise(value: hookObject)
}
guard case var .get(id, parameters) = object.method else {
return Promise(value: hookObject)
}
guard let userIdentifier = parameters["user_id"] as? String else {
return Promise(error: .myCustomError("no associated user found when expected to exist"))
}
return object.app.service("users").request(.get(parameters: ["id": userIdentifier])).then { response in
if case let .jsonObject(object) = response.data {
parameters["user_id"] = object["id"]
object.method = .get(id, parameters)
}
return Promise(value: object)
}
}
}
需要注意的是,var object = hookObject
。Swift 函数参数是 let
常量,因此您必须首先复制对象才能对其进行修改。
钩子对象通过钩子在后续步骤中传递。该接口与 JS 紧密对应
/// Hook object that gets passed through hook functions
public struct HookObject {
/// Represents the kind of hook.
///
/// - before: Hook is run before the request is made.
/// - after: Hook is run after the request is made.
/// - error: Runs when there's an error.
public enum Kind {
case before, after, error
}
/// The kind of hook.
public let type: Kind
/// Feathers application, used to retrieve other services.
public let app: Feathers
/// The service this hook currently runs on.
public let service: Service
/// The service method.
public var method: Service.Method
/// Error that can be set which will stop the hook processing chain and run a special chain of error hooks.
public var error: FeathersError?
/// Result of a successful method call, only in after hooks.
public var result: Response?
public init(
type: Kind,
app: Feathers,
service: Service,
method: Service.Method) {
self.type = type
self.app = app
self.service = service
self.method = method
}
}
所有 var
声明都是可变的,您可以在钩子中根据需要设置和修改它们,包括 .method
(如果您想进行诸如替换参数或完全更改方法调用(例如,将 .get
更改为 .find
)等操作)。
关于钩子对象需要注意的一些重要事项
error
将导致钩子处理链停止并立即运行任何错误钩子。如果在 before
钩子中发生这种情况,请求也将被跳过。before
钩子中将 result
设置为某个 Response
值将跳过请求,实质上是进行占位。要注册您的钩子,您首先必须创建一个 Service.Hooks
对象
let beforeHooks = Service.Hooks(all: [LogResultHook()], create: [AppendUserIdHook()]])
let afterHooks = Service.Hooks(find: [SaveInRealmHook()])
let errorHooks = Service.Hooks(all: [LogErrorHook(destination: "log.txt")])
注册钩子同样简单
let service = app.service("users")
service.before(beforeHooks)
service.after(afterHooks)
service.error(errorHooks)
重要:首先运行注册为 all
的钩子,然后运行特定服务方法的钩子。
如果您在任何时候需要检查您的钩子,也可以使用 .hooks
来实现。
let beforeHooks = service.hooks(for: .before)
有问题?开启一个问题!有想法?提交一个拉取请求!
如果你喜欢这个库,请
FeathersSwift有详细的文档,所以如果你对某个功能的实现有任何疑问,请查看源代码。你还可以在Feathers的Slack团队中向我提问 @brendan。
干杯!