构建iOS网络应用程序的最佳架构方法(REST客户端)

本文翻译自:Best architectural approaches for building iOS networking applications (REST clients)

I'm an iOS developer with some experience and this question is really interesting to me. 我是一名具有一定经验的iOS开发人员,这个问题对我来说非常有趣。 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? iOS网络应用程序的最佳架构是什么? 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. 我的意思是基本的抽象框架,模式,它适合每个网络应用程序,无论它是一个只有少量服务器请求的小应用程序还是复杂的REST客户端。 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. Apple建议将MVC用作所有iOS应用程序的基本架构方法,但MVC和更现代的MVVM模式都不能解释网络逻辑代码的放置位置以及如何组织它。

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? 我是否需要开发类似MVCSS for Service )的东西,并在此Service层中放置所有API请求和其他网络逻辑,从视角来看可能真的很复杂? 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). 这里建议为Web服务API (如LoginRequest类或PostCommentRequest类等)的每个网络请求创建一个单独的类,它们都继承自基本请求抽象类AbstractBaseRequest ,另外还创建一些封装的全局网络管理器常见的网络代码和其他首选项(可能是AFNetworking自定义或RestKit调优,如果我们有复杂的对象映射和持久性,甚至是自己的网络通信实现与标准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? 另一种方法是在第一种方法中使用一些单独的API调度程序或管理器类, 但不是为每个请求创建类,而是将每个请求封装为此管理器类的实例公共方法,如: fetchContactsloginUser方法等。那么,最好和最正确的方法是什么? 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 eg Model ? 我是否应该为所有这些网络内容创建另一层,如Service ,或NetworkProvider层或者我的MVC架构之上的任何内容,或者该层应该集成(注入)到现有的MVC层,例如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? 我知道存在很好的方法,或者像Facebook客户端或LinkedIn客户端这样的移动怪物如何应对指数级增长的网络逻辑复杂性?

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 . 这个问题的目标是从经验丰富的iOS开发人员那里收集最有趣的方法 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. 我想了解iOS中网络应用程序的基本,抽象和正确的架构方法。 I hope for detailed explanation from experienced developers. 我希望有经验的开发人员提供详细解释。


#1楼

参考:https://stackoom.com/question/1dNeV/构建iOS网络应用程序的最佳架构方法-REST客户端


#2楼

Because all iOS apps are different, I think there are different approaches here to consider, but I usually go this way: 由于所有iOS应用程序都不同,我认为这里有不同的方法可以考虑,但我通常会这样:
Create a central manager (singleton) class to handle all API requests (usually named APICommunicator) and every instance method is an API call. 创建一个中央管理器(单例)类来处理所有API请求(通常命名为APICommunicator),每个实例方法都是一个API调用。 And there is one central (non-public) method: 还有一种中心(非公开)方法:

- (RACSignal *)sendGetToServerToSubPath:(NSString *)path withParameters:(NSDictionary *)params; - (RACSignal *)sendGetToServerToSubPath:(NSString *)path withParameters:(NSDictionary *)params;

For the record, I use 2 major libraries/frameworks, ReactiveCocoa and AFNetworking. 为了记录,我使用了两个主要的库/框架,ReactiveCocoa和AFNetworking。 ReactiveCocoa handles async networking responses perfectly, you can do (sendNext:, sendError:, etc.). ReactiveCocoa可以完美地处理异步网络响应,您可以这样做(sendNext:,sendError:等)。
This method calls the API, gets the results and sends them through RAC in 'raw' format (like NSArray what AFNetworking returns). 此方法调用API,获取结果并以“原始”格式通过RAC发送(如NSArray AFNetworking返回的内容)。
Then a method like getStuffList: which called the above method subscribes to it's signal, parses the raw data into objects (with something like Motis) and sends the objects one by one to the caller ( getStuffList: and similar methods also return a signal that the controller can subscribe to). 然后像getStuffList:这样调用上面方法的方法订阅它的信号,将原始数据解析成对象(类似于Motis)并将对象逐个发送给调用者( getStuffList:和类似的方法也返回一个信号,即控制器可以订阅)。
The subscribed controller receives the objects by subscribeNext: 's block and handles them. 订阅控制器通过subscribeNext:的块接收对象并处理它们。

I tried many ways in different apps but this one worked the best out of all so I've been using this in a few apps recently, it fits both small and big projects and it's easy to extend and maintain if something needs to be modified. 我在不同的应用程序中尝试了很多方法,但是这个方法最好用,所以我最近在一些应用程序中使用它,它适用于小型和大型项目,如果需要修改某些内容,它很容易扩展和维护。
Hope this helps, I'd like to hear others' opinions about my approach and maybe how others think this could be maybe improved. 希望这有帮助,我想听听别人对我的方法的看法,也许其他人认为这可能会有所改善。


#3楼

I want to understand basic, abstract and correct architectural approach for networking applications in iOS : there is no "the best", or "the most correct" approach for building an application architecture. I want to understand basic, abstract and correct architectural approach for networking applications in iOS没有 “最好的”或“最正确”的方法来构建应用程序架构。 It is a very creative job. 这是一项非常有创意的工作。 You should always choose the most straightforward and extensible architecture, which will be clear for any developer, who begin to work on your project or for other developers in your team, but I agree, that there can be a "good" and a "bad" architecture. 您应该始终选择最直接和可扩展的体系结构,这对于任何开始处理项目或开发团队中其他开发人员的开发人员来说都是明确的,但我同意,可能存在“好”和“坏” “建筑。

You said: collect the most interesting approaches from experienced iOS developers , I don't think that my approach is the most interesting or correct, but I've used it in several projects and satisfied with it. 你说: collect the most interesting approaches from experienced iOS developers ,我不认为我的方法是最有趣或最正确的,但我已经在几个项目中使用它并对它感到满意。 It is a hybrid approach of the ones you have mentioned above, and also with improvements from my own research efforts. 它是您上面提到的那种混合方法,也是我自己研究工作的改进。 I'm interesting in the problems of building approaches, which combine several well-known patterns and idioms. 我对构建方法的问题感兴趣,它结合了几种众所周知的模式和习语。 I think a lot of Fowler's enterprise patterns can be successfully applied to the mobile applications. 我认为很多福勒的企业模式都可以成功应用于移动应用程序。 Here is a list of the most interesting ones, which we can apply for creating an iOS application architecture ( in my opinion ): Service Layer , Unit Of Work , Remote Facade , Data Transfer Object , Gateway , Layer Supertype , Special Case , Domain Model . 这是一个最有趣的列表,我们可以申请创建iOS应用程序架构( 在我看来 ): 服务层工作单元远程门面数据传输对象网关层超类型特殊情况域模型 You should always correctly design a model layer and always don't forget about the persistence (it can significantly increase your app's performance). 您应始终正确设计模型层,并始终不要忘记持久性(它可以显着提高应用程序的性能)。 You can use Core Data for this. 您可以使用Core Data进行此操作。 But you should not forget, that Core Data is not an ORM or a database, but an object graph manager with persistence as a good option of it. 但是你不应该忘记, Core Data不是ORM或数据库,而是具有持久性的对象图管理器作为它的一个很好的选择。 So, very often Core Data can be too heavy for your needs and you can look at new solutions such as Realm and Couchbase Lite , or build your own lightweight object mapping/persistence layer, based on raw SQLite or LevelDB . 因此, Core Data通常对您的需求来说过于沉重,您可以查看RealmCouchbase Lite等新解决方案,或者基于原始SQLite或LevelDB构建自己的轻量级对象映射/持久层。 Also I advice you to familiarize yourself with the Domain Driven Design and CQRS . 另外,我建议您熟悉域驱动设计CQRS

At first, I think, we should create another layer for networking, because we don't want fat controllers or heavy, overwhelmed models. 首先,我认为,我们应该为网络创建另一层,因为我们不需要胖控制器或沉重,不堪重负的模型。 I don't believe in those fat model, skinny controller things. 我不相信那些fat model, skinny controller东西。 But I do believe in skinny everything approach, because no class should be fat, ever. 但我相信 skinny everything方法,因为任何阶级都不应该胖。 All networking can be generally abstracted as business logic, consequently we should have another layer, where we can put it. 所有网络通常都可以抽象为业务逻辑,因此我们应该有另一层,我们可以把它放在哪里。 Service Layer is what we need: 服务层是我们需要的:

It encapsulates the application's business logic,  controlling transactions 
and coordinating responses in the implementation of its operations.

In our MVC realm Service Layer is something like a mediator between domain model and controllers. 在我们的MVC领域中, Service Layer就像是域模型和控制器之间的中介。 There is a rather similar variation of this approach called MVCS where a Store is actually our Service layer. 这种方法有一种相似的变体,称为MVCS ,其中Store实际上是我们的Service层。 Store vends model instances and handles the networking, caching etc. I want to mention that you should not write all your networking and business logic in your service layer. Store模型实例并处理网络,缓存等。我想提一下,您不应该在服务层中编写所有网络和业务逻辑。 This also can be considered as a bad design. 这也可以被认为是一个糟糕的设计。 For more info look at the Anemic and Rich domain models. 有关更多信息,请查看AnemicRich域模型。 Some service methods and business logic can be handled in the model, so it will be a "rich" (with behaviour) model. 一些服务方法和业务逻辑可以在模型中处理,因此它将是一个“丰富”(带行为)模型。

I always extensively use two libraries: AFNetworking 2.0 and ReactiveCocoa . 我总是广泛使用两个库: AFNetworking 2.0ReactiveCocoa I think it is a must have for any modern application that interacts with the network and web-services or contains complex UI logic. 我认为这对于任何与网络和Web服务交互或包含复杂UI逻辑的现代应用程序都是必须的

ARCHITECTURE 建筑

At first I create a general APIClient class, which is a subclass of AFHTTPSessionManager . 首先,我创建了一个通用的APIClient类,它是AFHTTPSessionManager的子类。 This is a workhorse of all networking in the application: all service classes delegate actual REST requests to it. 这是应用程序中所有网络的主力:所有服务类都将实际的REST请求委托给它。 It contains all the customizations of HTTP client, which I need in the particular application: SSL pinning, error processing and creating straightforward NSError objects with detailed failure reasons and descriptions of all API and connection errors (in such case controller will be able to show correct messages for the user), setting request and response serializers, http headers and other network-related stuff. 它包含HTTP客户端的所有自定义,我需要在特定的应用程序中:SSL固定,错误处理和创建简单的NSError对象,其中包含详细的故障原因以及所有API和连接错误的描述(在这种情况下,控制器将能够显示正确的用户的消息),设置请求和响应序列化程序,http标头和其他与网络相关的东西。 Then I logically divide all the API requests into subservices or, more correctly, microservices : UserSerivces , CommonServices , SecurityServices , FriendsServices and so on, accordingly to business logic they implement. 然后,我在逻辑上将所有的API请求转换的子服务,或者更确切地说,是微服务UserSerivcesCommonServicesSecurityServicesFriendsServices等,因此业务逻辑它们实现。 Each of these microservices is a separate class. 这些微服务中的每一个都是一个单独的类。 They, together, form a Service Layer . 它们一起构成了Service Layer These classes contain methods for each API request, process domain models and always returns a RACSignal with the parsed response model or NSError to the caller. 这些类包含每个API请求的方法,进程域模型,并始终将带有已解析响应模型的RACSignalNSError返回给调用者。

I want to mention that if you have complex model serialisation logic - then create another layer for it: something like Data Mapper but more general eg JSON/XML -> Model mapper. 我想提一下,如果你有复杂的模型序列化逻辑 - 那么为它创建另一个层:像Data Mapper,但更通用的例如JSON / XML - > Model mapper。 If you have cache: then create it as a separate layer/service too (you shouldn't mix business logic with caching). 如果你有缓存:那么也将它创建为一个单独的层/服务(你不应该将业务逻辑与缓存混合)。 Why? 为什么? Because correct caching layer can be quite complex with its own gotchas. 因为正确的缓存层可能与其自身的陷阱相当复杂。 People implement complex logic to get valid, predictable caching like eg monoidal caching with projections based on profunctors. 人们实现复杂的逻辑以获得有效的,可预测的缓存,例如基于profunctors的投影的monoidal缓存。 You can read about this beautiful library called Carlos to understand more. 您可以阅读有关这个​​名为Carlos的美丽图书馆以了解更多信息。 And don't forget that Core Data can really help you with all caching issues and will allow you to write less logic. 并且不要忘记Core Data可以真正帮助您解决所有缓存问题,并且可以让您编写更少的逻辑。 Also, if you have some logic between NSManagedObjectContext and server requests models, you can use Repository pattern, which separates the logic that retrieves the data and maps it to the entity model from the business logic that acts on the model. 此外,如果NSManagedObjectContext和服务器请求模型之间存在某种逻辑, NSManagedObjectContext可以使用存储库模式,该模式将检索数据的逻辑与从模型上的业务逻辑映射到实体模型的逻辑分开。 So, I advice to use Repository pattern even when you have a Core Data based architecture. 因此,即使您拥有基于Core Data的架构,我也建议使用Repository模式。 Repository can abstract things, like NSFetchRequest , NSEntityDescription , NSPredicate and so on to plain methods like get or put . 存储库可以将诸如NSFetchRequestNSEntityDescriptionNSPredicate类的NSFetchRequest抽象为诸如getput类的简单方法。

After all these actions in the Service layer, caller (view controller) can do some complex asynchronous stuff with the response: signal manipulations, chaining, mapping, etc. with the help of ReactiveCocoa primitives , or just subscribe to it and show results in the view. 在Service层中的所有这些操作之后,调用者(视图控制器)可以使用响应执行一些复杂的异步操作:信号操作,链接,映射等,在ReactiveCocoa原语的帮助下,或者只是订阅它并在结果中显示结果视图。 I inject with the Dependency Injection in all these service classes my APIClient , which will translate a particular service call into corresponding GET , POST , PUT , DELETE , etc. request to the REST endpoint. 我在所有这些服务类中注入了依赖注入我的APIClient ,它将特定的服务调用转换为对应的GETPOSTPUTDELETE等请求到REST端点。 In this case APIClient is passed implicitly to all controllers, you can make this explicit with a parametrised over APIClient service classes. 在这种情况下, APIClient会隐式传递给所有控制器,您可以通过APIClient服务类的参数化使其显式化。 This can make sense if you want to use different customisations of the APIClient for particular service classes, but if you ,for some reasons, don't want extra copies or you are sure that you always will use one particular instance (without customisations) of the APIClient - make it a singleton, but DON'T, please DON'T make service classes as singletons. 如果您想对特定服务类使用不同的APIClient自定义,这是APIClient的,但如果您出于某些原因,不想要额外的副本,或者您确定总是使用一个特定的实例(没有自定义) APIClient - 使它成为单身人士,但请不要,请不要将服务类别作为单身人士。

Then each view controller again with the DI injects the service class it needs, calls appropriate service methods and composes their results with the UI logic. 然后,每个视图控制器再次使用DI注入所需的服务类,调用适当的服务方法并使用UI逻辑组合其结果。 For dependency injection I like to use BloodMagic or a more powerful framework Typhoon . 对于依赖注入,我喜欢使用BloodMagic或更强大的框架Typhoon I never use singletons, God APIManagerWhatever class or other wrong stuff. 我从不使用单身,上帝APIManagerWhatever阶级或其他错误的东西。 Because if you call your class WhateverManager , this indicates than you don't know its purpose and it is a bad design choice . 因为如果你打电话给你的班WhateverManager ,这表明你不知道它的目的,这是一个糟糕的设计选择 Singletons is also an anti-pattern, and in most cases (except rare ones) is a wrong solution. 单身人士也是一种反模式,在大多数情况下(罕见的除外)是一种错误的解决方案。 Singleton should be considered only if all three of the following criteria are satisfied: 只有满足以下所有三个标准时,才应考虑单身人士:

  1. Ownership of the single instance cannot be reasonably assigned; 单个实例的所有权无法合理分配;
  2. Lazy initialization is desirable; 延迟初始化是可取的;
  3. Global access is not otherwise provided for. 不提供全球访问权限。

In our case ownership of the single instance is not an issue and also we don't need global access after we divided our god manager into services, because now only one or several dedicated controllers need a particular service (eg UserProfile controller needs UserServices and so on). 在我们的情况下,单个实例的所有权不是问题,在我们将神经理分成服务之后我们也不需要全局访问,因为现在只有一个或几个专用控制器需要特定服务(例如, UserProfile控制器需要UserServices等等上)。

We should always respect S principle in SOLID and use separation of concerns , so don't put all your service methods and networks calls in one class, because it's crazy, especially if you develop a large enterprise application. 我们应该始终尊重SOLID中的 S原则并使用关注点分离 ,因此不要将所有服务方法和网络调用放在一个类中,因为它很疯狂,尤其是在开发大型企业应用程序时。 That's why we should consider dependency injection and services approach. 这就是我们应该考虑依赖注入和服务方法的原因。 I consider this approach as modern and post-OO . 我认为这种方法是现代的和后OO In this case we split our application into two parts: control logic (controllers and events) and parameters. 在这种情况下,我们将应用程序分为两部分:控制逻辑(控制器和事件)和参数。

One kind of parameters would be ordinary “data” parameters. 一种参数是普通的“数据”参数。 That's what we pass around functions, manipulate, modify, persist, etc. These are entities, aggregates, collections, case classes. 这就是我们传递函数,操作,修改,持久等等。这些是实体,聚合,集合,案例类。 The other kind would be “service” parameters. 另一种是“服务”参数。 These are classes which encapsulate business logic, allow communicating with external systems, provide data access. 这些类封装了业务逻辑,允许与外部系统通信,提供数据访问。

Here is a general workflow of my architecture by example. 以下是我的架构的一般工作流程。 Let's suppose we have a FriendsViewController , which displays list of user's friends and we have an option to remove from friends. 假设我们有一个FriendsViewController ,它显示用户朋友列表,我们可以选择从朋友中删除。 I create a method in my FriendsServices class called: 我在FriendsServices类中创建了一个名为的方法:

- (RACSignal *)removeFriend:(Friend * const)friend

where Friend is a model/domain object (or it can be just a User object if they have similar attributes). 其中Friend是一个模型/域对象(如果它们具有相似的属性,它可以只是一个User对象)。 Underhood this method parses Friend to NSDictionary of JSON parameters friend_id , name , surname , friend_request_id and so on. Underhood这个方法将Friend解析为JSON参数friend_idnamesurnamefriend_request_id等的NSDictionary I always use Mantle library for this kind of boilerplate and for my model layer (parsing back and forward, managing nested object hierarchies in JSON and so on). 我总是将Mantle库用于这种模板和我的模型层(前后解析,在JSON中管理嵌套对象层次结构等)。 After parsing it calls APIClient DELETE method to make an actual REST request and returns Response in RACSignal to the caller ( FriendsViewController in our case) to display appropriate message for the user or whatever. 在解析之后,它调用APIClient DELETE方法来发出一个实际的REST请求,并将RACSignal中的Response返回给调用者(在我们的例子中是FriendsViewController ),以便为用户或其他任何东西显示相应的消息。

If our application is a very big one, we have to separate our logic even clearer. 如果我们的应用程序是一个非常大的应用程序,我们必须更清楚地分离我们的逻辑。 Eg it is not always good to mix Repository or model logic with Service one. 例如,将Repository或模型逻辑与Service one混合并不总是好的。 When I described my approach I had said that removeFriend method should be in the Service layer, but if we will be more pedantic we can notice that it better belongs to Repository . 当我描述我的方法时,我曾说过removeFriend方法应该在Service层中,但如果我们更迂腐,我们可以注意到它最好属于Repository Let's remember what Repository is. 让我们记住存储库是什么。 Eric Evans gave it a precise description in his book [DDD]: Eric Evans在他的书[DDD]中给出了一个精确的描述:

A Repository represents all objects of a certain type as a conceptual set. 存储库将特定类型的所有对象表示为概念集。 It acts like a collection, except with more elaborate querying capability. 除了具有更精细的查询功能外,它的作用就像一个集合。

So, a Repository is essentially a facade that uses Collection style semantics (Add, Update, Remove) to supply access to data/objects. 因此, Repository本质上是一个使用集合样式语义(添加,更新,删除)来提供对数据/对象的访问的外观。 That's why when you have something like: getFriendsList , getUserGroups , removeFriend you can place it in the Repository , because collection-like semantics is pretty clear here. 这就是为什么当你有类似的东西: getFriendsListgetUserGroupsremoveFriend你可以将它放在Repository ,因为类似集合的语义在这里很清楚。 And code like: 代码如下:

- (RACSignal *)approveFriendRequest:(FriendRequest * const)request;

is definitely a business logic, because it is beyond basic CRUD operations and connect two domain objects ( Friend and Request ), that's why it should be placed in the Service layer. 绝对是一个业务逻辑,因为它超出了基本的CRUD操作并连接了两个域对象( FriendRequest ),这就是它应该放在Service层中的原因。 Also I want to notice: don't create unnecessary abstractions . 另外我想注意: 不要创建不必要的抽象 Use all these approaches wisely. 明智地使用所有这些方法。 Because if you will overwhelm your application with abstractions, this will increase its accidental complexity, and complexity causes more problems in software systems than anything else 因为如果你用抽象的方式压倒你的应用程序,这将增加其意外的复杂性,并且复杂性导致软件系统中的问题比其他任何问题都多

I describe you an "old" Objective-C example but this approach can be very easy adapted for Swift language with a lot more improvements, because it has more useful features and functional sugar. 我向您描述了一个“旧的”Objective-C示例,但这种方法可以非常容易地适应Swift语言并进行更多改进,因为它具有更多有用的功能和功能性糖。 I highly recommend to use this library: Moya . 我强烈建议使用这个库: Moya It allows you to create a more elegant APIClient layer (our workhorse as you remember). 它允许您创建一个更优雅的APIClient图层(我们记住你的主力)。 Now our APIClient provider will be a value type (enum) with extensions conforming to protocols and leveraging destructuring pattern matching. 现在,我们的APIClient提供程序将是一个值类型(枚举),其扩展符合协议并利用解构模式匹配。 Swift enums + pattern matching allows us to create algebraic data types as in classic functional programming. Swift枚举+模式匹配允许我们像经典函数式编程一样创建代数数据类型 Our microservices will use this improved APIClient provider as in usual Objective-C approach. 我们的微服务将使用这种改进的APIClient提供程序,就像通​​常的Objective-C方法一样。 For model layer instead of Mantle you can use ObjectMapper library or I like to use more elegant and functional Argo library. 对于模型层而不是Mantle您可以使用ObjectMapper库或者我喜欢使用更优雅和功能性的Argo库。

So, I described my general architectural approach, which can be adapted for any application, I think. 因此,我想到了我的一般架构方法,可以适用于任何应用程序。 There can be a lot more improvements, of course. 当然,还有很多改进。 I advice you to learn functional programming, because you can benefit from it a lot, but don't go too far with it too. 我建议你学习函数式编程,因为你可以从中受益很多,但也不要太过分了。 Eliminating excessive, shared, global mutable state, creating an immutable domain model or creating pure functions without external side-effects is, generally, a good practice, and new Swift language encourages this. 消除过度,共享,全局可变状态,创建不可变域模型或创建没有外部副作用的纯函数通常是一种很好的做法,新的Swift语言鼓励这一点。 But always remember, that overloading your code with heavy pure functional patterns, category-theoretical approaches is a bad idea, because other developers will read and support your code, and they can be frustrated or scary of the prismatic profunctors and such kind of stuff in your immutable model. 但是请记住,使用繁重的纯函数模式重载代码,类别理论方法是一个主意,因为其他开发人员将阅读并支持您的代码,并且他们可能会对prismatic profunctors以及类似的东西感到沮丧或害怕你的不可变模型。 The same thing with the ReactiveCocoa : don't RACify your code too much , because it can become unreadable really fast, especially for newbies. ReactiveCocoa :不要过多地 RACify你的代码进行RACify ,因为它很快就会变得RACify ,特别是对于新手来说。 Use it when it can really simplify your goals and logic. 当它可以真正简化您的目标和逻辑时使用它。

So, read a lot, mix, experiment, and try to pick up the best from different architectural approaches . 所以, read a lot, mix, experiment, and try to pick up the best from different architectural approaches It is the best advice I can give you. 这是我能给你的最佳建议。


#4楼

We use a few approaches depending on the situation. 我们根据具体情况采用一些方法。 For most things AFNetworking is the simplest and most robust approach in that you can set headers, upload multipart data, use GET, POST, PUT & DELETE and there are a bunch of additional categories for UIKit which allow you to for example set an image from a url. 对于大多数事情来说,AFNetworking是最简单和最强大的方法,你可以设置标题,上传多部分数据,使用GET,POST,PUT和DELETE,还有一堆额外的UIKit类别允许你设置一个图像来自一个网址。 In a complex app with a lot of calls we sometimes abstract this down to a convenience method of our own which would be something like: 在一个有很多调用的复杂应用程序中,我们有时会将它抽象为我们自己的方便方法,如下所示:

-(void)makeRequestToUrl:(NSURL *)url withParameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;

There are a few situations where AFNetworking isn't appropriate however such as where you are creating a framework or other library component as AFNetworking may already be in another code base. 在某些情况下,AFNetworking不合适,例如您在创建框架或其他库组件的位置,因为AFNetworking可能已经在另一个代码库中。 In this situation you would use an NSMutableURLRequest either inline if you are making a single call or abstracted into a request / response class. 在这种情况下,如果要进行单个调用或将其抽象为请求/响应类,则可以使用内联NSMutableURLRequest。


#5楼

I avoid singletons when designing my applications. 我在设计应用程序时避免使用单例。 They are a typical go to for a lot of people but I think you can find more elegant solutions elsewhere. 它们是很多人的典型代表,但我认为你可以在其他地方找到更优雅的解决方案。 Typically what I do is a build out my entities in CoreData and then put my REST code in an NSManagedObject category. 通常,我所做的是在CoreData中构建我的实体,然后将我的REST代码放在NSManagedObject类别中。 If for instance I wanted to create and POST a new User, I'd do this: 例如,如果我想创建并发布一个新用户,我会这样做:

User* newUser = [User createInManagedObjectContext:managedObjectContext];
[newUser postOnSuccess:^(...) { ... } onFailure:^(...) { ... }];

I use RESTKit for the object mapping and initialize it at start up. 我使用RESTKit进行对象映射并在启动时初始化它。 I find routing all of your calls through a singleton to be a waste of time and adds a lot of boilerplate that isn't needed. 我发现通过单例将所有调用路由为浪费时间并添加了许多不需要的样板。

In NSManagedObject+Extensions.m: 在NSManagedObject + Extensions.m中:

+ (instancetype)createInContext:(NSManagedObjectContext*)context
{
    NSAssert(context.persistentStoreCoordinator.managedObjectModel.entitiesByName[[self entityName]] != nil, @"Entity with name %@ not found in model. Is your class name the same as your entity name?", [self entityName]);
    return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context];
}

In NSManagedObject+Networking.m: 在NSManagedObject + Networking.m中:

- (void)getOnSuccess:(RESTSuccess)onSuccess onFailure:(RESTFailure)onFailure blockInput:(BOOL)blockInput
{
    [[RKObjectManager sharedManager] getObject:self path:nil parameters:nil success:onSuccess failure:onFailure];
    [self handleInputBlocking:blockInput];
}

Why add extra helper classes when you can extend the functionality of a common base class through categories? 为什么在可以通过类别扩展公共基类的功能时添加额外的辅助类?

If you're interested in more detailed info on my solution let me know. 如果您对我的解决方案的更详细信息感兴趣请告诉我。 I'm happy to share. 我很高兴分享。


#6楼

I use the approach that I've gotten from here: https://github.com/Constantine-Fry/Foursquare-API-v2 . 我使用的方法是从这里得到的: https//github.com/Constantine-Fry/Foursquare-API-v2 I've rewritten that library in Swift and you can see the architectural approach from these parts of the code: 我在Swift中重写了这个库,你可以从代码的这些部分看到架构方法:

typealias OpertaionCallback = (success: Bool, result: AnyObject?) -> ()

class Foursquare{
    var authorizationCallback: OperationCallback?
    var operationQueue: NSOperationQueue
    var callbackQueue: dispatch_queue_t?

    init(){
        operationQueue = NSOperationQueue()
        operationQueue.maxConcurrentOperationCount = 7;
        callbackQueue = dispatch_get_main_queue();
    }

    func checkIn(venueID: String, shout: String, callback: OperationCallback) -> NSOperation {
        let parameters: Dictionary <String, String> = [
            "venueId":venueID,
            "shout":shout,
            "broadcast":"public"]
        return self.sendRequest("checkins/add", parameters: parameters, httpMethod: "POST", callback: callback)
    }

    func sendRequest(path: String, parameters: Dictionary <String, String>, httpMethod: String, callback:OperationCallback) -> NSOperation{
        let url = self.constructURL(path, parameters: parameters)
        var request = NSMutableURLRequest(URL: url)
        request.HTTPMethod = httpMethod
        let operation = Operation(request: request, callbackBlock: callback, callbackQueue: self.callbackQueue!)
        self.operationQueue.addOperation(operation)
        return operation
    }

    func constructURL(path: String, parameters: Dictionary <String, String>) -> NSURL {
        var parametersString = kFSBaseURL+path
        var firstItem = true
        for key in parameters.keys {
            let string = parameters[key]
            let mark = (firstItem ? "?" : "&")
            parametersString += "\(mark)\(key)=\(string)"
            firstItem = false
        }
    return NSURL(string: parametersString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding))
    }
}

class Operation: NSOperation {
    var callbackBlock: OpertaionCallback
    var request: NSURLRequest
    var callbackQueue: dispatch_queue_t

    init(request: NSURLRequest, callbackBlock: OpertaionCallback, callbackQueue: dispatch_queue_t) {
        self.request = request
        self.callbackBlock = callbackBlock
        self.callbackQueue = callbackQueue
    }

    override func main() {
        var error: NSError?
        var result: AnyObject?
        var response: NSURLResponse?

        var recievedData: NSData? = NSURLConnection.sendSynchronousRequest(self.request, returningResponse: &response, error: &error)

        if self.cancelled {return}

        if recievedData{
            result = NSJSONSerialization.JSONObjectWithData(recievedData, options: nil, error: &error)
            if result != nil {
                if result!.isKindOfClass(NSClassFromString("NSError")){
                    error = result as? NSError
            }
        }

        if self.cancelled {return}

        dispatch_async(self.callbackQueue, {
            if (error) {
                self.callbackBlock(success: false, result: error!);
            } else {
                self.callbackBlock(success: true, result: result!);
            }
            })
    }

    override var concurrent:Bool {get {return true}}
}

Basically, there is NSOperation subclass that makes the NSURLRequest, parses JSON response and adds the callback block with the result to the queue. 基本上,有NSOperation子类生成NSURLRequest,解析JSON响应并将带有结果的回调块添加到队列中。 The main API class constructs NSURLRequest, initialises that NSOperation subclass and adds it to the queue. 主API类构造NSURLRequest,初始化NSOperation子类并将其添加到队列中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值