I'm an iOS developer with some experience and this question is really interesting to me. I saw a lot of different resources and materials on this topic, but nevertheless I'm still confused. What is the best architecture for an iOS networked application? I mean basic abstract framework, patterns, which will fit every networking application whether it is a small app which only have a few server requests or a complex REST client. Apple recommends to use MVC as a basic architectural approach for all iOS applications, but neither MVC nor the more modern MVVM patterns explain where to put network logic code and how to organize it in general. Do I need to develop something like MVCS(S for Service) and in this Service layer put all API requests and other networking logic, which in perspective may be really complex? After doing some research I found two basic approaches for this. Here it was recommended to create a separate class for every network request to web-service API (like LoginRequest class or PostCommentRequest class and so on) which all inherits from the base request abstract class AbstractBaseRequest and in addition to create some global network manager which encapsulates common networking code and other preferences (it may be AFNetworking customisation or RestKit tuning, if the we have complex object mappings and persistence, or even an own network communication implementation with standard API). But this approach seems an overhead for me. Another approach is to have some singleton API dispatcher or manager class as in the first approach, but not to create classes for every request and instead to encapsulate every request as an instance public method of this manager class like: fetchContacts, loginUser methods, etc. So, what is the best and correct way? Are there other interesting approaches I don't know yet? And should I create another layer for all this networking stuff like Service, or NetworkProvider layer or whatever on top of my MVC architecture, or this layer should be integrated (injected) into existing MVC layers e.g. Model? I know there exists beautiful approaches, or how then such mobile monsters like Facebook client or LinkedIn client deal with exponentially growing complexity of networking logic? I know there are no exact and formal answer to the problem. The goal of this question is to collect the most interesting approaches from experienced iOS developers. The best suggested approach will be marked as accepted and awarded with a reputation bounty, others will be upvoted. It is mostly a theoretical and research question. I want to understand basic, abstract and correct architectural approach for networking applications in iOS. I hope for detailed explanation from experienced developers.
当前回答
我们根据具体情况使用几种方法。对于大多数事情来说,AFNetworking是最简单和最健壮的方法,因为你可以设置标题,上传多部分数据,使用GET, POST, PUT和DELETE,还有一堆附加的UIKit类别,允许你从url设置图像。在一个有很多调用的复杂应用程序中,我们有时会将其抽象为我们自己的方便方法,就像这样:
-(void)makeRequestToUrl:(NSURL *)url withParameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
有一些情况下,AFNetworking是不合适的,但是,如你正在创建一个框架或其他库组件,因为AFNetworking可能已经在另一个代码库。在这种情况下,你可以使用NSMutableURLRequest内联,如果你正在做一个单独的调用或抽象到一个请求/响应类。
其他回答
从纯类设计的角度来看,你通常会有这样的东西:
Your view controllers controlling one or more views Data model class - It really depends upon how many real distinct entities you are dealing with, and how they are related. For example, if you have an array of items to be displayed in four different representations (list, chart, graph etc), you will have one data model class for list of items, one more for an item. The list of item class will be shared by four view controllers - all children of a tab bar controller or a nav controller. Data model classes will come handy in not only displaying data, but also serializing them wherein each of them can expose their own serialization format through JSON / XML / CSV (or anything else) export methods. It is important to understand that you also need API request builder classes that map directly with your REST API endpoints. Let's say you have an API that logs the user in - so your Login API builder class will create POST JSON payload for login api. In another example, an API request builder class for list of catalog items API will create GET query string for corresponding api and fire the REST GET query. These API request builder classes will usually receive data from view controllers and also pass the same data back to view controllers for UI update / other operations. View controllers will then decide how to update Data Model objects with that data. Finally, the heart of the REST client - API data fetcher class which is oblivious to all sorts of API requests your app makes. This class will more likely be a singleton, but as others pointed out, it doesn't have to be a singleton. Note that the link is just a typical implementation and does not take into consideration scenarios like session, cookies etc, but it is enough to get you going without using any 3rd party frameworks.
我们根据具体情况使用几种方法。对于大多数事情来说,AFNetworking是最简单和最健壮的方法,因为你可以设置标题,上传多部分数据,使用GET, POST, PUT和DELETE,还有一堆附加的UIKit类别,允许你从url设置图像。在一个有很多调用的复杂应用程序中,我们有时会将其抽象为我们自己的方便方法,就像这样:
-(void)makeRequestToUrl:(NSURL *)url withParameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
有一些情况下,AFNetworking是不合适的,但是,如你正在创建一个框架或其他库组件,因为AFNetworking可能已经在另一个代码库。在这种情况下,你可以使用NSMutableURLRequest内联,如果你正在做一个单独的调用或抽象到一个请求/响应类。
我认为目前中型项目使用MVVM架构,大型项目使用VIPER架构 并努力实现
面向协议编程 软件设计模式 S.O.L.D原则 泛型编程 不要重复自己(DRY)
以及构建iOS网络应用程序的架构方法(REST客户端)
对于代码干净易读的分离问题,避免重复:
import Foundation
enum DataResponseError: Error {
case network
case decoding
var reason: String {
switch self {
case .network:
return "An error occurred while fetching data"
case .decoding:
return "An error occurred while decoding data"
}
}
}
extension HTTPURLResponse {
var hasSuccessStatusCode: Bool {
return 200...299 ~= statusCode
}
}
enum Result<T, U: Error> {
case success(T)
case failure(U)
}
依存关系反演
protocol NHDataProvider {
func fetchRemote<Model: Codable>(_ val: Model.Type, url: URL, completion: @escaping (Result<Codable, DataResponseError>) -> Void)
}
主要负责:
final class NHClientHTTPNetworking : NHDataProvider {
let session: URLSession
init(session: URLSession = URLSession.shared) {
self.session = session
}
func fetchRemote<Model: Codable>(_ val: Model.Type, url: URL,
completion: @escaping (Result<Codable, DataResponseError>) -> Void) {
let urlRequest = URLRequest(url: url)
session.dataTask(with: urlRequest, completionHandler: { data, response, error in
guard
let httpResponse = response as? HTTPURLResponse,
httpResponse.hasSuccessStatusCode,
let data = data
else {
completion(Result.failure(DataResponseError.network))
return
}
guard let decodedResponse = try? JSONDecoder().decode(Model.self, from: data) else {
completion(Result.failure(DataResponseError.decoding))
return
}
completion(Result.success(decodedResponse))
}).resume()
}
}
你会发现这里是GitHub MVVM架构与rest API Swift项目
这个问题已经有很多优秀而广泛的答案,但我觉得我必须提一下,因为没有人提过。
斯威夫特的Alamofire。https://github.com/Alamofire/Alamofire
它是由与AFNetworking相同的人创建的,但在设计时更直接地考虑了Swift。
试试https://github.com/kevin0571/STNetTaskQueue
在分开的类中创建API请求。
STNetTaskQueue将处理线程和委托/回调。
可针对不同协议进行扩展。
推荐文章
- iPhone上UIView和UILabels的渐变
- keychain上的分发证书中缺少私钥
- 在实现API时,我如何避免在块中捕获自我?
- 如何创建一个Swift Date对象?
- 如何通过windows命令行关闭TCP和UDP端口
- Xcode 4在目标设备上说“finished running <my app>”——什么都没有发生
- UDP和TCP比起来快多少?
- 从另一个应用程序打开设置应用程序
- 快速提取正则表达式匹配
- 如何应用梯度的背景视图的iOS Swift应用程序
- 图书馆吗?静态的?动态吗?或框架?另一个项目中的项目
- 如何用SwiftUI调整图像大小?
- Xcode 6 gitignore文件应该包括什么?
- 如何在iPhone/iOS上删除电话号码的蓝色样式?
- 检测视网膜显示