Networking 是 ws 项目的下一代版本。可以将其视为专为 iOS13 构建的 ws 2.0。它使用 Apple 的原生 Combine 框架,而不是 Then Promise 库,移除了 Arrow 依赖项,从而倾向于 Codable(但 Arrow 仍然可以轻松适配),并移除了 Alamofire 依赖项,从而倾向于更简单的纯原生 URLSession 实现。本质上,减少了依赖项,增加了原生内容,同时具有几乎相同的 API。如果你的应用支持 iOS13 及更高版本,强烈建议迁移到 Networking。WS 将出于向后兼容性原因进行“维护”,但从 iOS13 开始,请将其视为已弃用。
let ws = WS("http://jsonplaceholder.typicode.com")
ws.get("/users").then { json in
// Get back some json \o/
}
因为 JSON API 在 99% 的 iOS 应用程序中使用,所以它应该 简单。
我们开发人员应该 专注于我们的应用程序逻辑,而不是 样板代码。
更少的代码意味着更好的代码
ws 是 freshOS iOS 工具集的一部分。 在示例应用程序中尝试一下! 下载入门项目
通过提供一个轻量级客户端,**自动化每个人都必须编写的样板代码**。
通过公开一个 非常简单 的 API,以简单、清晰、快速的方式完成工作。
从 JSON API 获取 Swift 模型现在已成为过去的问题
import ws // Import ws at the top of your file
import Arrow // Import Arrow to get access to the JSON type
class ViewController: UIViewController {
// Set webservice base URL
let ws = WS("http://jsonplaceholder.typicode.com")
override func viewDidLoad() {
super.viewDidLoad()
// Get back some json instantly \o/
ws.get("/users").then { (json:JSON) in
print(json)
}
}
}
创建一个 User+JSON.swift
文件并将 JSON 键映射到你的模型属性
import Arrow
extension User: ArrowParsable {
mutating func deserialize(_ json: JSON) {
identifier <-- json["id"]
username <-- json["username"]
email <-- json["email"]
}
}
注意:ws
使用 Arrow
进行 JSON 解析 https://github.com/freshOS/Arrow
在这里,你将创建一个包装你的请求的函数。根据你想要返回的内容,有不同的编写该函数的方法。一个空块,JSON,模型或模型数组。
func voidCall() -> Promise<Void> {
return ws.get("/users")
}
func jsonCall() -> Promise<JSON> {
return ws.get("/users")
}
func singleModelCall() -> Promise<User> {
return ws.get("/users/3")
}
func modelArrayCall() -> Promise<[User]> {
return ws.get("/users")
}
你可以注意到,仅通过更改返回类型,ws 自动 知道该怎么做,例如,尝试将响应解析为 User
模型。
这使我们能够保持简洁,而无需编写额外的代码。 \o/
注意:ws
使用 then
进行 Promises 操作 https://github.com/freshOS/then
voidCall().then {
print("done")
}
jsonCall().then { json in
print(json)
}
singleModelCall().then { user in
print(user) // Strongly typed User \o/
}
modelArrayCall().then { users in
print(users) // Strongly typed [User] \o/
}
想要记录所有网络调用和响应吗?
ws.logLevels = .debug
想要隐藏网络活动指示器吗?
ws.showsNetworkActivityIndicator = false
想要覆盖默认会话管理器以自定义信任策略吗?
import Alamofire
ws.sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(
policies: ["myspecialhostname.com" : .disableEvaluation]
))
这是一个典型的 Articles CRUD 示例
extension Article {
static func list() -> Promise<[Article]> {
return ws.get("/articles")
}
func save() -> Promise<Article> {
return ws.post("/articles", params: ["name":name])
}
func fetch() -> Promise<Article> {
return ws.get("/articles/\(id)")
}
func update() -> Promise<Void> {
return ws.put("/articles/\(id)", params: ["name":name])
}
func delete() -> Promise<Void> {
return ws.delete("/articles/\(id)")
}
}
这是我们在代码中如何使用它的
// List Articles
Article.list().then { articles in
}
// Create Article
var newArticle = Article(name:"Cool story")
newArticle.save().then { createdArticle in
}
// Fetch Article
var existingArticle = Article(id:42)
existingArticle.fetch().then { fetchedArticle in
}
// Edit Article
existingArticle.name = "My new name"
existingArticle.update().then {
}
// Delete Article
existingArticle.delete().then {
}
当请求失败时,我们通常想知道由于 HTTP 状态码的原因。这是如何获取它的
ws.get("/users").then {
// Do something
}.onError { e in
if let wsError = e as? WSError {
print(wsError.status)
print(wsError.status.rawValue) // RawValue for Int status
}
}
你可以在这里找到完整的 WSError
枚举 -> https://github.com/freshOS/ws/blob/master/ws/WSError.swift
通常,我们处理列表以及加载更多
项目的功能。 在这里,我们将看到使用 ws
的此模式的示例实现。 这不包括在内,因为逻辑本身取决于你的后端实现。 这将为你提供一个示例,供你推出自己的版本。
import ws
import then
import Arrow
class LoadMoreRequest<T:ArrowParsable> {
var limit = 12
private var params = [String:Any]()
private var offset = 0
private var call: WSRequest!
private var canLoadMore = true
private var aCallback:((_ ts: [T]) -> [T])? = nil
init(_ aCall: WSRequest) {
call = aCall
}
func resetOffset() {
offset = 0
canLoadMore = true
}
func hasMoreItemsToload() -> Bool {
return canLoadMore
}
func fetchNext() -> Promise<[T]> {
params = call.params
params["limit"] = limit
params["offset"] = offset
call.params = params
offset += limit
return call.fetch()
.registerThen(parseModels)
.resolveOnMainThread()
}
private func parseModels(_ json: JSON) -> [T] {
let mapper = WSModelJSONParser<T>()
let models = mapper.toModels(json)
if models.count < limit {
canLoadMore = false
}
return models
}
}
正如你所看到的,我们有一个强类型的请求。
限制是可调的。
它封装了一个 WSRequest。
它处理偏移逻辑,以及是否还有更多项目要加载。
这就是我们需要的一切!
现在,这就是我们构建 LoadMoreRequest
的方式
func loadMoreUsersRequest() -> LoadMoreRequest<User> {
return LoadMoreRequest(ws.getRequest("/users"))
}
这是我们在控制器中使用它的方式
class ViewController: UIViewController {
// Get a request
let request = api.loadMoreUsersRequest()
override func viewDidLoad() {
super.viewDidLoad()
request.limit = 5 // Set a limit if needed
}
func refresh() {
// Resets the request, usually plugged with
// the pull to refresh feature of a tableview
request.resetOffset()
}
func loadMore() {
// Get the next round of users
request.fetchNext().then { users in
print(users)
}
}
func shouldDisplayLoadMoreSpinner() -> Bool {
// This asks the requests if there are more items to come
// This is useful to know if we show the "load more" spinner
return request.hasMoreItemsToload()
}
}
现在你有一种简单的方法来处理 App 🎉 中的加载更多请求
在使用 RESTFUL
API 时,我们可以玩得开心并走得更远一点。
通过引入 RestResource
协议
public protocol RestResource {
static func restName() -> String
func restId() -> String
}
我们可以有一个构建我们的 REST
URL 的函数
public func restURL<T:RestResource>(_ r:T) -> String {
return "/\(T.restName())/\(r.restId())"
}
我们将我们的 User
模型符合协议
extension User:RestResource {
static func restName() -> String { return "users" }
func restId() -> String { return "\(identifier)" }
}
我们可以实现一个 get
版本,它使用我们的 RestResource
public func get<T:ArrowParsable & RestResource>(_ restResource:T, params:[String:Any] = [String:Any]()) -> Promise<T> {
return get(restURL(restResource), params: params)
}
然后
ws.get("/users/\(user.identifier)")
可以这样写
ws.get(user)
当然,同样的逻辑可以应用于所有其他 ws 函数 (post
, put
delete
等)! 🎉
由于同时支持所有软件包管理器的挑战,SPM 支持可在单独的分支 spm-only
上使用。
在你的 Cartfile 中
github "freshOS/ws"
carthage update
ws.framework
从 Carthage/Build/iOS
拖放到 Linked Frameworks and Libraries
(“General”设置选项卡)Project
> Target
> Build Phases
+ New run Script Phase
/usr/local/bin/carthage copy-frameworks
添加输入文件
$(SRCROOT)/Carthage/Build/iOS/ws.framework
$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework
$(SRCROOT)/Carthage/Build/iOS/Arrow.framework
$(SRCROOT)/Carthage/Build/iOS/then.framework
这链接了 ws 及其依赖项。
Carthage 非常有用,因为它负责拉取依赖项,如 Arrow、then 和 Alamofire。 酷的是它确实是透明的。 我的意思是,你可以只在旁边使用 carthage 来拉取和构建依赖项,并手动将框架链接到你的 Xcode 项目。
如果没有 Carthage,我会看到 2 种解决方案:1 - 复制粘贴所有源代码:ws / then / Arrow / Alamofire,这听起来并不好玩;) 2 - 手动链接框架(ws + 依赖项),方法是 A 在每个 repo 上抓取 .frameworks 它们,或者 B 使用 Carthage 构建它们
target 'MyApp'
pod 'ws'
use_frameworks!
Swift 2 -> 版本 1.3.0
Swift 3 -> 版本 2.0.4
Swift 4 -> 版本 3.0.0
Swift 4.1 -> 版本 3.1.0
Swift 4.2 -> 版本 3.2.0
Swift 5.0 -> 版本 5.0.0
Swift 5.1 -> 版本 5.1.0
Swift 5.1.3 -> 版本 5.1.1
喜欢该项目吗? 提供咖啡或每月捐款支持我们,并帮助我们继续开展活动 :)
成为赞助商,并在我们的 Github README 上展示你的徽标,并链接到你的网站 :)