当前位置: 首页 > 知识库问答 >
问题:

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

吴宏扬
2023-03-14

我是一个iOS开发人员,有一定的经验,这个问题对我来说真的很有趣。我看到了关于这个话题的很多不同的资源和材料,但尽管如此,我还是感到困惑。iOS联网应用的最佳架构是什么?我指的是基本的抽象框架,模式,它将适合每一个网络应用程序,无论是一个只有几个服务器请求的小应用程序,还是一个复杂的REST客户机。苹果建议将 作为所有iOS应用程序的基本架构方法,但无论是 还是更现代的 模式都没有说明将网络逻辑代码放在哪里,以及通常如何组织它。

我是否需要为 )开发类似 )的东西,并在这个 层中放置所有 请求和其他网络逻辑(从透视图来看,这可能非常复杂)?在做了一些研究之后,我发现了两种基本的方法。这里建议为每个网络请求创建一个单独的类--服务 (如 类或 类等),这些类都继承自基本请求抽象类 ,此外还要创建一些全局网络管理器,它封装了常见的网络代码和其他首选项(如果我们有复杂的对象映射和持久性,或者甚至有自己的具有标准API的网络通信实现,则可能是 定制或 调优)。但这种方法对我来说似乎是一种负担。另一种方法是像第一种方法一样,使用一些单独的 分派器或管理器类, 为每个请求创建类,而是将每个请求封装为这个管理器类的实例公共方法,例如: 方法等等。那么,什么是最好和正确的方法呢?还有其他我还不知道的有趣的方法吗?

我应该为所有这些网络东西创建另一个层,比如 ,或者 层,或者在我的 体系结构之上的任何层,或者该层应该集成(注入)到现有的 层中,例如

我知道有一些很好的方法,或者像Facebook客户端或LinkedIn客户端这样的移动怪兽是如何处理成倍增长的复杂网络逻辑的呢?

我知道这个问题没有确切而正式的答案。 。最好的建议方法将被标记为接受,并奖励与声誉赏金,其他将被支持。这主要是一个理论性和研究性的问题。我想了解iOS中网络应用程序的基本,抽象和正确的架构方法。我希望从经验丰富的开发人员的详细解释。

共有3个答案

左丘源
2023-03-14

因为所有的iOS应用程序都是不同的,所以我认为这里有不同的方法可以考虑,但我通常会这样做:
创建一个中央管理器(单例)类来处理所有的API请求(通常命名为APICommunicator),每个实例方法都是一个API调用。并且有一个中央(非公共)方法:

-Code(RACSignal*)SendGetToServerToSubPath:NSString*)带有参数的路径:NSDictionary*)Params/Code>

请注意,我使用两个主要的库/框架,reactiveCoa和afnetworking。ReactiveCoa可以完美地处理异步网络响应(sendnext:,senderror:等)。
此方法调用API,获取结果并通过RAC以'raw'格式发送它们(如NSArray what AFNetworking返回)。
然后调用上述方法的 之类的方法订阅它的信号,将原始数据解析为对象(使用类似Motis的东西),并将对象一个一个地发送给调用者(codeGetStuffList:/code>和类似的方法也返回控制器可以订阅的信号)。
订阅控制器通过
我在不同的应用程序中尝试了很多方法,但这一个效果最好,所以我最近在一些应用程序中使用了这一方法,它适用于小型和大型项目,并且如果需要修改某些内容,它很容易扩展和维护。
希望这对我有帮助,我想听听其他人对我的方法的看法,也许其他人认为这可以如何改进。

梁丘翔
2023-03-14

根据这个问题的目的,我想描述一下我们的架构方法。

我们的通用iOS应用体系结构基于以下几种模式:服务层,MVVM,UI数据绑定,依赖注入;和函数式反应编程范型。

我们可以将一个典型的面向消费者的应用程序分成以下逻辑层:

组装层是我们应用程序的一个引导点。它包含一个依赖注入容器和应用程序对象及其依赖项的声明。这一层还可能包含应用程序配置(URL,第三方服务密钥等)。为此,我们使用台风库。

模型层包含领域模型的类,验证,映射。我们使用Mantle库来映射模型:它支持序列化/反序列化到 json ode=""> 格式和 模型。对于模型的验证和表单表示,我们使用FXForms和FXModelValidation库。

服务层声明用于与外部系统交互的服务,以便发送或接收在域模型中表示的数据。因此,通常我们有与服务器API(每个实体),消息传递服务(如PubNub),存储服务(如AmazonS3)等进行通信的服务。基本上,服务包装SDK(如PubNub SDK)提供的对象或实现它们自己的通信逻辑。对于一般的网络,我们使用AFNetworking库。

存储层的目的是组织设备上的本地数据存储。我们使用核心数据或领域来实现这一点(两者都有优缺点,决定使用什么是基于具体的规格。对于核心数据设置,我们使用MDMCoreData库和一堆类-存储库(类似于服务),它们为每个实体提供对本地存储的访问。对于Realm,我们只是使用类似的存储来访问本地存储。

经理层是抽象/包装器居住的地方。

在经理角色中可以是:

因此,管理者的角色可以是实现应用程序工作所需的特定方面或关注点的逻辑的任何对象。

我们尽量避免单人,但如果需要的话,这一层是他们居住的地方。

协调器层提供依赖于其他层(服务,存储,模型)的对象的对象,以便将它们的逻辑组合到某个模块(特征,屏幕,用户故事或用户体验)所需的一个工作序列中。它通常将异步操作链化,并知道如何对它们的成功和失败案例做出反应。例如,您可以想象一个消息传递特性和相应的 对象。处理发送消息操作可能如下所示:

    null

null

null

    null

为了避免大量的视图控制器,我们使用MVVM模式并在ViewModels中实现UI表示所需的逻辑。ViewModel通常将协调器和管理器作为依赖项。ViewControllers使用的ViewModel和某些类型的视图(例如,表视图单元格)。ViewControllers和ViewModel之间的粘合剂是数据绑定和命令模式。为了使这种粘合成为可能,我们使用ReactiveCocoa库。

我们还使用ReactiveCocoa及其 概念作为所有协调器,服务,存储方法的接口和返回值类型。这允许我们链式操作,并行或串行运行它们,以及ReactiveCoa提供的许多其他有用的东西。

我们尝试以声明的方式实现我们的UI行为。数据绑定和自动布局大大有助于实现这一目标。

基础结构层包含应用程序工作所需的所有助手,扩展和实用程序。

这种方法对我们和我们通常构建的那些类型的应用程序都很有效。但是您应该明白,这只是一种主观的方法,应该根据具体的团队目的进行调整/改变。

null

此外,您可以在此博客文章iOS开发即服务中找到更多有关iOS开发过程的信息

葛景龙
2023-03-14

:构建应用程序体系结构没有“最佳”或“最正确”的方法。这是一项很有创意的工作。您应该始终选择最直接和最可扩展的架构,这对于任何一个开发人员来说都是很清楚的,他们开始在您的项目上工作,或者对于您团队中的其他开发人员来说,但我同意,架构可以有“好”和“坏”之分。 ios>

您说: ional="" approactions="" from="" headed="" ios="" developers="" ode=""> ,我不认为我的方法是最有趣或最正确的,但我已经在几个项目中使用过它,并对它感到满意。这是你上面提到的一种混合方法,同时也是我自己研究工作的改进。我对构建方法的问题很感兴趣,这些方法结合了几种著名的模式和习惯用法。我认为Fowler's的很多企业模式都可以成功地应用到移动应用中。这里列出了最有趣的一个列表,我们可以将其应用于创建iOS应用程序架构(在我看来):服务层,工作单元,远程外观,数据传输对象,网关,层超类型,特殊情况,域模型。您应该始终正确地设计一个模型层,并且始终不要忘记持久性(它可以显著地提高您的应用程序的性能)。您可以为此使用 。但是您不应该忘记, 不是ORM或数据库,而是一个对象图管理器,具有持久性是一个很好的选项。因此,通常 对于您的需求来说太重了,您可以查看新的解决方案,例如Realm和Couchbase Lite,或者基于原始SQLite或LevelDB构建您自己的轻量级对象映射/持久性层。此外,我建议您熟悉领域驱动设计和CQR。

首先,我认为,我们应该为联网创建另一层,因为我们不想要胖控制器或笨重的,不堪重负的模型。我不相信那些 的东西。但是我确实相信 方法,因为任何类都不应该是胖的。所有的网络通常都可以抽象为业务逻辑,因此我们应该有另一层,我们可以把它放在那里。服务层是我们需要的:

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

null

在我们的 领域中, 类似于域模型和控制器之间的中介。这种方法有一个相当类似的变体,称为MVCS,其中 实际上是我们的 层。 提供模型实例并处理网络,缓存等。我想提到的是,您不应该在服务层中编写所有的网络和业务逻辑。这也可以被认为是一个糟糕的设计。有关更多信息,请查看贫乏和丰富的领域模型。模型中可以处理一些服务方法和业务逻辑,因此它将是一个“丰富”(具有行为)的模型。

我总是广泛使用两个库:AFNetworking2.0和ReactiveCoA。我认为对于任何与网络和Web服务交互或包含复杂UI逻辑的现代应用程序来说,它都是必须具备的。

体系结构BR>

首先,我创建一个常规的 类,它是AFHttpSessionManager的子类。这是应用程序中所有网络的一个累赘:所有服务类都将实际的REST请求委托给它。它包含HTTP客户端的所有自定义,这是我在特定应用程序中所需要的:SSL钉扎,错误处理和创建直接的 对象,其中包含详细的故障原因和所有 和连接错误的描述(在这种情况下,控制器将能够为用户显示正确的消息),设置请求和响应序列化器,HTTP头和其他与网络相关的东西。然后,我根据它们实现的业务逻辑,将所有API请求逻辑地划分为子服务,或者更正确地说,划分为微服务: 等等。这些微服务中的每一个都是一个单独的类。它们一起构成了一个 。这些类包含每个API请求的方法,处理域模型,并总是向调用方返回带有解析响应模型的

我想提到的是,如果您有复杂的模型序列化逻辑--那么就为它创建另一个层:比如数据映射器,但更通用,例如JSON/XML-&>;模型映射器。如果您有缓存:那么也将其创建为一个单独的层/服务(您不应该将业务逻辑与缓存混合在一起)。为什么?因为正确的缓存层可能非常复杂,有它自己的gotchas。人们通过实现复杂的逻辑来获得有效的,可预测的高速缓存,例如基于Profunctor的投影的monoidal高速缓存。你可以读一下这个叫做卡洛斯的美丽图书馆来了解更多。而且不要忘记,核心数据确实可以帮助您解决所有缓存问题,并允许您编写更少的逻辑。另外,如果在 和服务器请求模型之间有一些逻辑,则可以使用Repository模式,该模式将检索数据并将其映射到实体模型的逻辑与作用于模型的业务逻辑分开。因此,我建议使用存储库模式,即使您有一个基于数据的核心架构。Repository可以将 等抽象为简单的方法,如

在服务层完成所有这些操作之后,调用者(视图控制器)可以在 原语的帮助下对响应执行一些复杂的异步操作:信号操作,链接,映射等,或者只是订阅它并在视图中显示结果。我在所有这些服务类中使用依赖项注入来注入我的 ,它将把特定的服务调用转换为相应的 等到REST端点的请求。在这种情况下, 隐式地传递给所有控制器,您可以通过参数化的 服务类来显式传递。如果您希望对特定的服务类使用不同的 自定义,那么这是有意义的,但是如果您出于某些原因不想要额外的副本,或者您确信您总是会使用 的一个特定实例(没有自定义)--使它成为一个单独的实例,但是不要,请不要使服务类成为单独的实例。

然后每个视图控制器再次使用DI注入它需要的服务类,调用适当的服务方法,并将它们的结果与UI逻辑组合在一起。对于依赖注入,我喜欢使用BloodMagic或者更强大的框架Typhoon。我从不使用单例,上帝 类或其他错误的东西。因为如果您调用您的类 ,这表明您不知道它的用途,这是一个糟糕的设计选择。Singletons也是一种反模式,在大多数情况下(除了罕见的情况)是一种错误的解决方案。仅当满足以下三个标准时,才应考虑单例:

  1. 未提供全局访问。/li>

在我们的示例中,单个实例的所有权不是问题,而且在将god manager划分为服务之后,我们也不需要全局访问,因为现在只有一个或多个专用控制器需要特定的服务(例如 控制器需要 等等)。

我们应该始终尊重 原则,并使用关注点分离,所以不要将所有的服务方法和网络调用放在一个类中,因为这太疯狂了,尤其是当您开发一个大型企业应用程序时。这就是为什么我们应该考虑依赖注入和服务方法。我认为这种方法是现代的,后面向对象的。在本例中,我们将应用程序分成两部分:控制逻辑(控制器和事件)和参数。

一种参数是普通数据参数。这就是我们传递的函数,操作,修改,持久化等等。这些是实体,聚合,集合,案例类。另一类是服务参数。这些是封装业务逻辑,允许与外部系统通信,提供数据访问的类。

null

这里通过示例给出了我的架构的一般工作流程。假设我们有一个 ,它显示用户的朋友列表,并且我们有一个从朋友中删除的选项。我在 类中创建了一个方法,名为:

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

其中 是一个Model/Domain对象(如果它们具有相似的属性,也可以只是一个 对象)。首先,该方法将 解析为JSON参数 等的 。我总是将Mantle库用于这类样板和我的模型层(前后向解析,管理JSON中的嵌套对象层次结构等)。解析之后,它调用 方法来发出实际的REST请求,并将 中的 返回给调用者(在我们的示例中为codefriendsviewcontroller/code>),以便为用户显示适当的消息或其他内容。

如果我们的应用程序是一个非常大的应用程序,我们必须把我们的逻辑分离得更清楚。例如。将 或模型逻辑与 One混合使用并不总是好的。当我描述我的方法时,我已经说过 方法应该在 层中,但是如果我们更加迂腐的话,我们可以注意到它属于 更好。让我们记住什么是存储库。Eric Evans在他的书[DDD]:

存储库将某种类型的所有对象表示为一个概念集合。它的作用类似于集合,只是具有更复杂的查询功能。

因此, 本质上是一个使用集合样式语义(添加,更新,删除)来提供对Data/Objects的访问的facade。这就是为什么当您有这样的代码: 时,您可以将其放在 中,因为这里类似集合的语义非常清楚。代码如下:

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

绝对是一个业务逻辑,因为它超出了基本的 操作,并且连接了两个域对象(codeFriend/code>和 层中的原因。另外,我想注意的是:不要创建不必要的抽象。明智地使用所有这些方法。因为如果您将用抽象来淹没您的应用程序,这将意外地增加它的复杂性,而复杂性在软件系统中引起的问题比任何其他问题都要多

我给您描述了一个“旧的”Objective-C示例,但是这种方法可以非常容易地适应Swift语言,并且有更多的改进,因为它有更多有用的特性和功能糖。我强烈推荐使用这个库:莫亚。它允许您创建一个更优雅的 层(您还记得,我们的工作人员)。现在我们的 提供程序将是一个值类型(枚举),其扩展符合协议并利用析构模式匹配。Swift枚举+模式匹配允许我们像在经典函数式编程中一样创建代数数据类型。我们的微服务将使用这种改进的 提供程序,就像通常的Objective-C方法一样。对于模型层,而不是 ,您可以使用ObjectMapper库,或者我喜欢使用更优雅和功能更强的Argo库。

因此,我描述了我的通用架构方法,我认为它可以适用于任何应用程序。当然,还可以有更多的改进。我建议您学习函数式编程,因为您可以从中受益良多,但也不要太过分。通常,消除过度的,共享的,全局可变状态,创建一个不可变的域模型或创建没有外部副作用的纯函数是一种良好的做法,新的 语言鼓励这样做。但是永远记住,用大量纯函数模式,分类理论方法重载代码是一个坏主意,因为其他开发人员将阅读并支持您的代码,他们可能会对您的不可变模型中的 和此类内容感到沮丧或害怕。与 相同的是:不要太多 您的代码,因为它可能很快变得不可读,特别是对于新手。当它能真正简化你的目标和逻辑的时候就用它。

因此,<大量阅读代码,混合,实验,并尝试从不同的架构方法中挑选出最好的代码>。这是我能给你的最好的建议。

 类似资料:
  • 我是一个有经验的iOS开发人员,这个问题对我来说真的很有趣。我看到了很多关于这个话题的不同的资源和材料,但尽管如此,我仍然感到困惑。一个iOS联网应用程序的最佳架构是什么?我指的是基本的抽象框架和模式,它适合每一个网络应用程序,无论是只有少量服务器请求的小应用程序,还是复杂的REST客户端。苹果推荐使用作为所有iOS应用程序的基本架构方法,但无论是还是更现代的模式都没有说明网络逻辑代码应该放在哪里

  • 问题内容: 我正在制作一个Android应用程序。因为它是如此简单,所以我首先想到我可以简单地消除充当中间件的服务器上对Java应用程序的需求。我尝试使用MySQL的JDBC驱动程序直接连接到数据库,但是我的程序崩溃了,所以我不确定Android是否“支持” MySQL的JDBC驱动程序。 因此,我正在考虑如何实施该应用程序。基本上,应用程序从远程MySQL数据库写入一些数据,并从远程MySQL数

  • 我的应用程序与一些外部服务集成,并向该服务请求数据。我只是想知道在客户端建模响应的最佳实践是什么,尤其是在客户端业务逻辑只需要一些数据的情况下。我正在考虑以下实施: 将JSON转换为包含所有数据/属性的类对象,并在我的代码中使用此类对象 将JSON转换为仅包含进一步处理所需的数据/属性的类对象 以上两者的组合(创建包含所有属性的类对象,然后将其转换为仅包含进一步业务逻辑所需属性的对象)

  • 问题内容: 以下是我典型的程序的整体结构。 funA funB并在用户单击按钮1、2、3时funC打开另一个Toplevel带有窗口小部件的窗口。 我想知道这是否是编写python tkinter程序的正确方法吗?当然,即使我这样写也可以,但这是最好的方法吗?听起来很愚蠢,但是当我看到其他人编写的代码时,他们的代码并没有弄乱一堆函数,而且大多数情况下都有类。 有没有作为良好实践应遵循的特定结构?开

  • 问题内容: 我将要构建一个将使用RESTful Web服务的Android应用程序。我不想自己编写REST客户端,因为我希望它尽可能有效和稳定(这是我第一次使用REST)。 我可以在项目中使用任何(免费)适用于Android / Java的框架或实用程序吗? 问题答案: Restlet是一个出色的REST框架,并且具有Android版本。

  • 问题内容: 你将如何为Java应用程序实现插件系统? 是否有一个易于使用的(对于开发人员而言)系统可以实现以下目的: 用户将其插件放入应用程序的子目录中 插件可以提供配置屏幕 如果使用框架,那么许可证是否与商业开发兼容? 问题答案: 首先,你需要一个所有插件都需要实现的接口,例如 然后,插件作者应将其插件捆绑到JAR文件中。你的应用程序将打开JAR文件,然后可以使用JAR清单中的属性或JAR文件中