ios 讲控制器传给子视图_快速使用子视图控制器提供强大的新闻源

ios 讲控制器传给子视图

You open your favourite news app and start reading. What makes you stay there for longer? A front page! If an interesting teaser catches your eyes, it’s more likely you will enter an article to read more.

您打开自己喜欢的新闻应用程序并开始阅读。 是什么让您在那呆了更长的时间? 头版! 如果有趣的预告片引起了您的注意,那么您更有可能输入一篇文章来阅读更多内容。

A front-page is the most important part of news apps and websites, just as it has been with classic newspapers. A group of editors and journalists work constantly to provide the best reading experience. Not only people are involved in this process, but also special algorithms and AI do their job to serve tailored content for users. It’s not only about the front page though, there are also section listings that present teasers from one specific category.

就像经典报纸一样,首页是新闻应用程序和网站最重要的部分。 一群编辑和新闻工作者不断工作,以提供最佳的阅读体验。 不仅人们参与了此过程,而且特殊的算法和AI都在为用户提供量身定制的内容。 但是,不仅是首页,还列出了特定类别的预告片。

From the presentation layer perspective e.g. iOS app, a section is a similar concept to a front-page: the goal is to present a list of teasers with a given layout and allow navigation to a corresponding article on tapping them.

从表示层的角度(例如iOS应用),一个部分的概念类似于首页:目标是显示具有给定布局的预告片列表,并允许在点击它们时导航到相应的文章。

听起来很简单,但有时可能会充满挑战…… (Sounds easy, but could be challenging sometimes…)

In the Core News Product App team, we are developing mobile apps for several news publishers from Norway and Sweden. In the moment of writing, we have 7 apps in App Store:

在核心新闻产品应用团队中,我们正在为来自挪威和瑞典的多家新闻发布商开发移动应用。 在撰写本文时,App Store中有7个应用程序:

Norway: Aftenposten, Stavanger Aftenblad, Fædrelandsvennen, Bergens Tidende, Verdens Gang

挪威:Aftenposten,斯塔万格Aftenblad,Fædrelandsvennen,卑尔根Tidende,Verdens Gang

Sweden: Aftonbladet, Sportbladet

瑞典:Aftonbladet,Sportbladet

Image for post
Aftonbladet iOS app. Aftonbladet iOS应用程序的首页和经济部分。
Image for post
VG iOS app. VG iOS应用中的首页,VG +和“体育”部分。

The main principle for our team is to share as much code as possible. Instead of writing 7 very similar apps, most of the code is put in the core module, which serves as a base for every app we have. Having a shared codebase and dealing with requirements that often are different among publishers is the most tricky part of our job. Imagine you have seven front pages and more than a hundred section listings counting all the apps. Here are the challenges we face every day:

我们团队的主要原则是共享尽可能多的代码。 无需编写7个非常相似的应用程序,而是将大多数代码放在核心模块中,该模块是我们拥有的每个应用程序的基础。 拥有共享的代码库并处理发布者之间通常不同的需求是我们工作中最棘手的部分。 想象一下,您有七个首页,并且有一百多个部分列出了所有应用程序。 以下是我们每天面临的挑战:

后端方面,表示性API: (Backend aspect, presentational API:)

In Schibsted, we have a main backend service shared among publishers that apps communicate directly with. However, this is not the case for every feed. There are some feeds that differ in a data format. Some of them are consumed from custom or legacy endpoints. That means the architecture needs to be flexible enough to present basically any data that can be resolved to article teasers.

在Schibsted,我们有一个主要的后端服务,与应用程序直接通信的发布者之间共享。 但是,并非每种提要都如此。 有些提要在数据格式上有所不同。 其中一些是从自定义或旧式终结点使用的。 这意味着体系结构必须足够灵活,以基本呈现可以解析为商品预告片的任何数据。

原生与网页内容 (Native vs. web content)

Most of the feeds in our apps are natively rendered but there are some exceptions. If a publisher needs to render one of the feeds in a webview, we need to handle that. What is more, some feeds require to be dynamically switched between native and web versions based on current newsroom decisions. That means even more complex behaviour.

我们应用中的大多数提要都是本地渲染的,但也有一些例外。 如果发布者需要在Web视图中呈现提要之一,则需要进行处理。 而且,某些提要需要根据当前新闻编辑室的决定在本机版本和Web版本之间动态切换。 这意味着更加复杂的行为。

提要不是简单的清单 (Feeds are not a simple list)

We can’t forget about the rest of components a typical news feed contains:

我们不能忘记典型的新闻提要包含的其他组件:

  • advertisements

    广告
  • carousel or list widget (list of teasers embedded into a widget)

    轮播或列表小部件(嵌入到小部件中的预告片列表)
  • location aware widget (weather forecast, local news)

    位置感知小部件(天气预报,本地新闻)
  • web rendered teasers

    网络渲染的预告片

共同状态 (Common states)

Every feed has the same possible states:

每个提要具有相同的可能状态:

  • loading, an activity indicator is shown

    加载中,显示活动指示器
  • presenting content

    展示内容
  • error, showing error message to user and giving the chance to retry by tapping a button

    错误,向用户显示错误消息,并提供通过点击按钮重试的机会

不同品牌,不同款式 (Different brands, different styling)

Having 7 different brands onboard requires seven different stylings. Besides each app having its own look, teasers have different layouts, usually resembling classic newspapers. In general, styling is a too large topic to cover in this article. What you will see is a simplification that focuses only on architectural aspects.

船上拥有7个不同的品牌需要7种不同的样式。 除了每个应用都有自己的外观外,预告片还具有不同的布局,通常类似于经典报纸。 通常,样式是一个太大的主题,无法在本文中介绍。 您将看到的是仅专注于架构方面的简化。

提要加载抽象 (Feed loading abstraction)

The goal of the blogpost is to present an architectural concept of a reusable container view controller that serves all feeds and deals with mentioned challenges.

博客文章的目的是提出一种可重用的容器视图控制器的体系结构概念,该控制器可为所有提要提供服务并应对上述挑战。

The concept consist of two main parts:

该概念包括两个主要部分:

  • an abstraction of the loading process that allows to deal with different data formats and helps to determine what type of content is needed to load

    加载过程的抽象,它允许处理不同的数据格式,并有助于确定需要加载哪种类型的内容
  • a container view controller and its child view controllers presenting a current state of the feed

    容器视图控制器及其子视图控制器呈现提要的当前状态

I will focus on the architectural part. Therefore, I’ll simplify data models, UI part, and networking logic to focus on the core of the issue here.

我将专注于架构部分。 因此,在这里我将简化数据模型,UI部分和网络逻辑,以关注问题的核心。

模型 (Model)

Let’s start by defining a model for a single element in the native feed. A list might contain elements of different types such as various article teaser types, several ad types, and widgets that can include teasers or location-based news. To reflect that, let’s use an enum with three simple cases. Of course, in the real app this model would be substantially more complex.

首先,为本机供稿中的单个元素定义一个模型。 列表可能包含不同类型的元素,例如各种文章预告片类型,几种广告类型以及可包含预告片或基于位置的新闻的小部件。 为了反映这一点,让我们使用带有三个简单案例的枚举。 当然,在实际的应用程序中,此模型将更加复杂。

Let’s create a new enum and call it FeedItem.

让我们创建一个新的枚举,并将其FeedItem

Now, we need a type that describes a feed page. As you remember, feed can be rendered natively or web-rendered. Sounds like another enum. There could be some additional pieces of information that are shared and don’t depend on a specific way of rendering. It could be a title of the feed, tracking and styling properties, or basically any metadata connected to the feed. Let’s use a title as an example of such data.

现在,我们需要一种描述提要页面的类型。 您还记得,提要可以本地渲染或通过Web渲染。 听起来像另一个枚举。 可能还有一些其他信息可以共享,而不依赖于特定的呈现方式。 它可以是提要,跟踪和样式属性的标题,或者基本上是与提要连接的任何元数据。 让我们以标题作为此类数据的示例。

Let’s create a struct Feedthat includes those requirements.

让我们创建一个包含那些需求的结构Feed

Again, we leverage the usage of enum. For native feeds, we need to hold an array of feed items. If a feed is web-rendered, we need the URL of the webpage. In both cases, a feed might have a title if it’s a section listing. The title will be then shown in a navigation bar.

同样,我们利用枚举的用法。 对于本地提要,我们需要保存一系列提要项目。 如果提要是通过网络渲染的,则我们需要网页的URL。 在这两种情况下,如果提要都是节列表,则提要都可能带有标题。 标题将显示在导航栏中。

As we’re done with the basic model types, let’s move further.

完成基本模型类型的介绍后,让我们继续进行。

如何加载所有可能的东西? (How to load all the things possible?)

As I said, there is no single specific data format for every feed in our apps. Newsroom content APIs and their format are evolving and changing over the years. Remember that our apps serve 7 different Schibsted newspapers. While most feeds are consumed in a unified format, some sections still use legacy endpoints.

正如我所说,我们的应用程序中的每个提要都没有单一的特定数据格式。 多年来,Newsroom内容API及其格式在不断发展和变化。 请记住,我们的应用程序可服务7种不同的Schibsted报纸。 尽管大多数提要以统一格式使用,但某些部分仍使用旧版端点。

Besides that there are also feeds that are always web rendered and feeds that are rendered based on the flag in the response that tells whether to render natively or fallback to web. For those reasons, we need an abstraction that allows us to load feeds in a flexible way and pass information how to render it.

除此之外,还有始终由Web渲染的提要和基于响应中的标志渲染的提要,这些标记告知是本地渲染还是回退到Web。 出于这些原因,我们需要一种抽象方法,允许我们以灵活的方式加载提要,并传递有关如何呈现提要的信息。

Let’s define a protocol that will describe that.

让我们定义一个描述它的协议。

Protocol consists of a function that returns AnyPublisherfrom Combine framework. For those who are not familiar with it, check out the docs: https://developer.apple.com/documentation/combine/anypublisher

协议包含一个从Combine框架返回AnyPublisher的函数。 对于那些不熟悉它的人,请查看以下文档: https : //developer.apple.com/documentation/combine/anypublisher

Since loading is an asynchronous process, function needs also return a value in an asynchronous manner. Feed includes information about the content, so we’re good in that aspect. Loading is a process that can fail, so we specify Error type as a failure type.

由于加载是异步过程,因此函数还需要以异步方式返回值。 Feed包含有关内容的信息,因此我们在这方面很出色。 加载是一个可能失败的过程,因此我们将错误类型指定为失败类型。

Now, let’s move on to the specific usage of the created protocol, based on the real challenges we faced during development. Imagine we have three feed pages that we would like to load in the app:

现在,基于开发过程中面临的实际挑战,让我们继续创建的协议的特定用法。 假设我们有三个供稿页面,我们希望在应用程序中加载它们:

  • Economy section from service X. It represents economic article teasers and it has its own data structure that includes everything that we need including ads and widgets. This feed can be rendered only natively.

    服务X的“经济”部分。它代表经济文章预告片,并且具有自己的数据结构,其中包含我们需要的所有内容,包括广告和小部件。 此提要只能在本地呈现。
  • Lifestyle section from service Y. It contains lifestyle and nutrition related teasers. This time, however, data structure is different from economy feed and it doesn’t include any information about ads or widgets. They need to be injected client-side. This feed is to be rendered natively or as a website depending on renderAsWebboolean value.

    服务Y的“生活方式”部分。其中包含与生活方式和营养相关的预告片。 但是,这次的数据结构与经济Feed不同,它不包含有关广告或小部件的任何信息。 他们需要在客户端注入。 此feed将根据renderAsWeb布尔值本地显示或作为网站显示。

  • Sport section from service Z. It contains sport teasers which can be only web rendered.

    服务Z的“运动”部分。它包含只能通过Web渲染的运动预告片。

Let’s deal with each of them one by one. Our goal at this point is to create loaders that are able to load mentioned sections. They will be key things to use at the later point.

让我们一个一个地处理它们。 我们目前的目标是创建能够加载上述部分的加载器。 它们将是稍后使用的关键内容。

经济课 (Economy section)

First of all, let’s examine the data structure of the economy section. As mentioned, it includes teasers, ads, and widgets. Here’s a sample idea what a response might look like:

首先,让我们检查经济部分的数据结构。 如前所述,它包括预告片,广告和小部件。 这是一个示例想法,响应可能是什么样的:

We need to define a decodable struct that will be used to parse the response:

我们需要定义一个可解码的结构,用于解析响应:

One tricky thing here is that items array is a heterogeneous collection. As you can see in the JSON file, it can be a teaser, an ad, or a widget. That means, it will be the best to define an item as an enum.

一件棘手的事情是,items数组是一个异构集合。 正如您在JSON文件中看到的那样,它可以是预告片,广告或小部件。 这意味着,最好将项目定义为枚举。

Unfortunately, Swift cannot automatically synthesize the implementation of Decodablefor such enum. It means that it needs to be implemented manually. I will not go into details here, you can check out all of the source code by entering the link posted at the end of this article.

不幸的是,Swift无法自动为此类枚举综合Decodable的实现。 这意味着它需要手动实现。 我不会在这里详细介绍,您可以通过输入本文结尾处发布的链接来签出所有源代码。

Now, it’s time to conform to FeedLoadable protocol. Let’s create a struct called EconomyLoader.

现在,该符合FeedLoadable协议了。 让我们创建一个名为EconomyLoader的结构。

As you can see, we transform an economy feed page to Feed. As requirement says, this feed is always natively rendered. To make things simple, response is loaded from a locally stored JSON file instead of requesting a real service.

如您所见,我们将经济Feed页转换为Feed 。 如需求所述,此提要始终是本地呈现的。 为简单起见,响应是从本地存储的JSON文件加载的,而不是请求真正的服务。

Now, we need to follow similar steps for two more sections keeping their requirements in mind.

现在,我们需要针对另外两个部分执行类似的步骤,同时牢记它们的要求。

生活方式专区 (Lifestyle section)

This time the section has a different data structure that contains no ads or widgets. However, we still want to show them at some predefined spots in the feed. For that reason, we’ll need to inject additional items client-side. Even more interestingly, the section needs to be natively or web rendered according to the renderAsWebflag in the response. Based on its value, a proper Feedobject will be created.

这次,该部分具有不同的数据结构,其中不包含广告或小部件。 但是,我们仍然希望在提要中的一些预定义位置显示它们。 因此,我们需要在客户端注入其他项。 更有趣的是,该部分需要根据响应中的renderAsWeb标志以本机方式或通过Web呈现。 根据其值,将创建适当的Feed对象。

As before, let’s start with the page model. Checkout the JSON file of the sample lifestyle section response:

和以前一样,让我们​​从页面模型开始。 检出样例生活节响应的JSON文件:

As no manual decoding is needed in this case, the model struct is simple:

由于在这种情况下不需要手动解码,因此模型结构很简单:

The only new things are two properties renderAsWeband renderAsWebUrl. We will make use of these during the loading process:

唯一的新事物是两个属性renderAsWebrenderAsWebUrl 。 我们将在加载过程中利用这些:

As you now see, the enum Feedshows its power. This time we don’t hardcode the specific case. We can dynamically choose whether our feed will be rendered natively or web.

如您现在所见,枚举Feed显示了其功能。 这次我们不对具体情况进行硬编码。 我们可以动态选择我们的提要是本地呈现还是通过Web呈现。

However, we still have one more requirement to meet. We want to inject ads and widgets. Loader’s responsibility is to deliver a final model to the view model. It will be the best approach to make any additional changes here, in loader.

但是,我们还有另一个要满足的要求。 我们想注入广告和小部件。 加载程序的责任是将最终模型交付给视图模型。 这是在加载程序中进行任何其他更改的最佳方法。

We also need to remember that we live in a reality of several newspapers where the code is shared among apps. Thus, we need to be flexible here and cannot simply inject ads and widget at hardcoded indices. Each newspaper wants to have different types of ads in different places in the feed and some of them may or may not want to have widgets there. For that reason, we need to pass information about additional elements from outside by adding a new closure:

我们还需要记住,我们生活在几家报纸的现实中,其中的代码在应用之间共享。 因此,我们在这里需要保持灵活性,不能简单地在硬编码索引处插入广告和小部件。 每个报纸都希望在Feed中的不同位置投放不同类型的广告,其中某些报纸可能会或可能不会希望在其中放置小部件。 因此,我们需要通过添加一个新的闭包从外部传递有关其他元素的信息:

Closure transformallows us to transform FeedItem array into a new one. This way, we can do any operation on collection we want for any publisher. Of course, this applies only to a natively rendered feed. If it’s to be web rendered, we don’t do any transformations.

闭合transform允许我们将FeedItem数组转换为一个新数组。 这样,我们可以对任何发布者想要的集合执行任何操作。 当然,这仅适用于本地渲染的提要。 如果要进行网络渲染,则不进行任何转换。

Let’s move on to the last and easiest section - sport.

让我们继续最后一个最简单的部分-运动。

运动部分 (Sport section)

In this case, the section is always web-rendered and we know it upfront. It means we don’t need to deal with any parsing or transformations. We just need a simple loader that can return Feedwith web content for a given url. Because of that, let’s give it a generic name WebLoader.

在这种情况下,该部分始终是通过网络渲染的,因此我们是预先知道的。 这意味着我们不需要处理任何解析或转换。 我们只需要一个简单的加载程序即可为给定的URL返回Feed和Web内容。 因此,我们给它一个通用名称WebLoader

In this case, what load method does s simply returning immediately with a success value of Feedwith a web content. As you can see, the WebLoader is not coupled with any particular section, it can be used for any web feed.

在这种情况下,加载方法的作用只是简单地立即返回带有Web内容的Feed的成功值。 如您所见, WebLoader并未与任何特定部分耦合,它可用于任何Web feed。

Now, we’re done with all three loaders and it’s time to move on to the second part of our feed architecture.

现在,我们已经完成了所有三个加载器的工作,是时候继续进行Feed结构的第二部分了。

自定义容器视图控制器 (Custom Container View Controller)

Loaders alone have no purpose. We need a way to plug them into a view controller. The goal is to implement a custom container view controller, but let me draw a bigger picture here.

装载机本身没有任何目的。 我们需要一种将它们插入视图控制器的方法。 目标是实现自定义容器视图控制器,但让我在这里画出更大的图景。

As Apple documentation states:

如Apple文档所述:

Container view controllers are a way to combine the content from multiple view controllers into a single user interface.

容器视图控制器是一种将来自多个视图控制器的内容组合到单个用户界面中的方法。

What are multiple view controllers in our case? Let’s think of states a feed can have:

在我们的案例中,什么是多视图控制器? 让我们考虑一下提要可以具有的状态:

  • loading

    装货
  • presenting native content

    呈现原生内容
  • presenting web content

    展示网页内容
  • error

    错误

By translate these into Swift by creating an enum State:

通过创建枚举State将它们转换为Swift:

What’s interesting here is by using an associated value of type Feed we can nicely hold information about a type of the feed that is currently presented in the feed.

这里有趣的是,通过使用Feed类型的关联值,我们可以很好地保存有关Feed中当前显示的feed类型的信息。

查看模型 (View model)

According to MVVM principles, we’d like to scope down any business logic in the view controllers. Let’s create a FeedViewModeland make it responsible for computing the current state of the feed. How to achieve that? By using loaders from the recent chapter!

根据MVVM原则,我们希望缩小视图控制器中的所有业务逻辑。 让我们创建一个FeedViewModel并使其负责计算提要的当前状态。 如何实现呢? 通过使用最近一章中的加载器!

State is defined as CurrentValueSubject<State, Never>. It’s a Combine type that allows us to store a single value and communicate any changes to subscribers. You can read about it more here: https://developer.apple.com/documentation/combine/currentvaluesubject.

状态定义为CurrentValueSubject<State, Never> 。 这是一种组合类型,它使我们可以存储单个值并将任何更改传达给订户。 您可以在这里了解更多信息: https : //developer.apple.com/documentation/combine/currentvaluesubject

We used State and Never as Output and Failure types because we want to store Statevalue and we never want it to error out as we handle errors in one of enum cases.

我们将StateNever用作输出和失败类型,因为我们要存储State值,并且在处理枚举情况之一时,我们永远不希望它出错。

As you see, we inject loaders in init. By using a protocol FeedLoadablewe can inject any loader we want and our FeedViewModelstill remains the same! Let’s use the loader to implement the load method.

如您所见,我们在init中注入了加载器。 通过使用协议FeedLoadable我们可以注入所需的任何加载程序,并且FeedViewModel仍然保持不变! 让我们使用加载器来实现load方法。

What we do here, we simply ask a loader to load a feed and we translate a result to the corresponding state. We don’t really care what type of feed it is at this point, because the view model’s responsibility is only to expose a current state to the view controller.

在这里,我们只要求加载器加载提要,然后将结果转换为相应的状态。 现在我们真的不在乎它是什么类型的提要,因为视图模型的责任只是将当前状态公开给视图控制器。

Let’s see the full view model code:

让我们看看完整视图模型代码:

Additionally, by conforming to the FeedViewModelType protocol, we make the code testable.

此外,通过遵循FeedViewModelType协议,我们使代码可测试。

子视图控制器 (Child view controllers)

Now, let’s prepare the remaining parts for our container view controller.

现在,让我们为容器视图控制器准备其余部分。

As mentioned before, a current state will be represented by using different view controllers. Here are suggested names:

如前所述,当前状态将通过使用不同的视图控制器来表示。 这是建议的名称:

  • loading -> LoadingViewController

    加载-> LoadingViewController

  • presenting native feed -> NativeFeedViewController

    呈现本机提要-> NativeFeedViewController

  • presenting web feed -> WebFeedViewController

    呈现Web WebFeedViewController > WebFeedViewController

  • error -> ErrorViewController

    错误-> ErrorViewController

The idea is to add a view controller corresponding to the current state as a child view controller of our container view controller.

这个想法是添加一个与当前状态相对应的视图控制器作为我们容器视图控制器的子视图控制器。

Let’s start building our container view controller and call it FeedViewController. First, we need a property that holds current child view controller:

让我们开始构建容器视图控制器,并将其FeedViewController 。 首先,我们需要一个拥有当前子视图控制器的属性:

By using didSet observer, we can swap child view controllers in a convenient way. How to implement updateChildren? We need to remember about a few steps when adding and removing a child view controller.

通过使用didSet观察器,我们可以方便地交换子视图控制器。 如何实现updateChildren ? 在添加和删除子视图控制器时,我们需要记住一些步骤。

添加子视图控制器 (Adding child view controller)

Let’s create an extension to UIViewController to show what needs to be done.

让我们为UIViewController创建一个扩展,以显示需要完成的工作。

  1. Method addChild creates a parent-child relationship between two view controllers which is necessary for a custom container view controller. This method tells UIKit that your container view controller is now managing the view of the child view controller.

    方法addChild在两个视图控制器之间创建父子关系,这对于自定义容器视图控制器是必需的。 此方法告诉UIKit,您的容器视图控制器现在正在管理子视图控制器的视图。

  2. We add the child’s view as a subview of the container’s view.

    我们将子视图添加为容器视图的子视图。
  3. didMove(toParent:)method is required to inform the child that it has a parent view controller now.

    didMove(toParent:)方法来告知孩子它现在具有父视图控制器。

删除子视图控制器 (Removing child view controller)

By analogy, let’s examine the process of removing.

以此类推,让我们研究一下删除过程。

  1. We tell UIKit that the view controller loses its parent.

    我们告诉UIKit,视图控制器失去了其父级。
  2. We remove the child from its parent.

    我们将孩子从父母那里移除。
  3. Child’s view is unlinked from the superview’s view.

    子视图与超级视图的视图没有关联。

With these extensions, updateChildrenmethod becomes really simple:

通过这些扩展, updateChildren方法变得非常简单:

After checking if the new child is not the old child, we simply remove the old one and add the new one if it’s not nil. By creating helpers add(_ child:) and remove(), UIKit internal logic was hidden and code became readable and easy. Last thing we need to do is to position the new child’s view. In this case, we pin it to super view using auto-layout.

在检查新孩子不是旧孩子之后,我们只需删除旧孩子,然后将新孩子添加为nil即可。 通过创建助手add(_ child:)remove() ,UIKit内部逻辑被隐藏,代码变得易读易懂。 我们需要做的最后一件事是放置新孩子的视图。 在这种情况下,我们使用自动布局将其固定到超级视图。

As we already know how to swap child view controllers, let’s move to the last part: observing FeedViewModel’s state by FeedViewController.

正如我们已经知道如何交换子视图控制器一样,让我们​​进入最后一部分:通过FeedViewController观察FeedViewModel的状态。

对状态变化做出React (Reacting to state changes)

Now, we need to ensure that every state change is reflected by adding a proper child view controller and removing old one. Since we use CurrentValueSubject this job is easy. Let’s subscribe to changes in viewDidLoad:

现在,我们需要通过添加适当的子视图控制器并删除旧的子视图控制器来确保反映每个状态更改。 由于我们使用CurrentValueSubject因此此工作很容易。 让我们订阅viewDidLoad中的更改:

As we deal with UIKit code when adding child view controllers, we need to be sure that it’s done on the main thread. We can do it by calling .receive(on: DispatchQueue.main)just before .sink. Also, we don’t need to be worried about subscription erroring out and terminating because Failure type of state subject was set to Never.

当我们在添加子视图控制器时处理UIKit代码时,我们需要确保它在主线程上完成。 我们可以通过在.sink之前.sink .receive(on: DispatchQueue.main) .sink 。 另外,我们也不必担心订阅错误和终止,因为状态主体的“失败”类型设置为“从不”。

Finally, let’s configure current child view controller based on feed:

最后,让我们基于feed配置当前的子视图控制器:

By using an enum for FeedViewModel.State, we have a nice, readable and predictable code. Every time state changes, the change is reflected in FeedViewController.

通过为FeedViewModel.State使用枚举,我们可以得到一个不错的,可读且可预测的代码。 每次状态更改时,更改都会反映在FeedViewController

It’s likely that you feel now that this code might be shared among different newspapers and feeds. If a new publisher comes in, we just can just use one of existing loaders or create a new custom one, without a need to recreate the whole view controller and view model machinery.

您现在可能会觉得此代码可以在不同的报纸和提要之间共享。 如果有新的发布者进来,我们可以只使用现有的一个加载器或创建一个新的自定义加载器,而无需重新创建整个视图控制器和视图模型机器。

Image for post
Economy section
经济课

In case of lifestyle section, in order to simulate renderAsWeb changing across responses, I used Bool.random() to compute its value. This way we can see that the feed nicely respects it and renders natively or web depending on the flag.

对于“生活方式”部分,为了模拟renderAsWeb在响应renderAsWeb变化,我使用Bool.random()来计算其值。 通过这种方式,我们可以看到提要很好地尊重了提要,并根据标志以本机或Web方式进行渲染。

Image for post
Lifestyle section
生活方式专区
Image for post
Sport section
运动部分

If you’re interested in full implementation I invite you to check out the whole sample project: https://github.com/marcin-mucha/RobustFeeds

如果您对全面实施感兴趣,请邀请您查看整个示例项目: https : //github.com/marcin-mucha/RobustFeeds

结论 (Conclusion)

First of all, thanks for the long journey!

首先,感谢您的漫长旅程!

You’ve learned some of the specifics of the news apps. And you’ve found out that a list of teasers you see in your favourite app might be a little bit tricky under the hood.

您已经了解了新闻应用程序的一些细节。 而且您发现,在最喜欢的应用程序中看到的挑逗列表在后台可能会有些棘手。

By using a real project use case, I wanted to show you a bigger picture of challenges we face in our everyday work. You saw how to create an abstraction over loading process with unspecified data structure and how to present such data to the user by using a custom container view controller. I’m sure that at least some parts of the sample project can be useful also in your apps.

通过使用真实的项目用例,我想向您展示我们在日常工作中面临的挑战的更广阔的前景。 您已经了解了如何使用未指定的数据结构来创建加载过程的抽象,以及如何使用自定义容器视图控制器将此类数据呈现给用户。 我确信示例项目的至少某些部分在您的应用程序中也会有用。

P.S. Credits to all CNP App iOS team members for collaboration to this solution in our apps!

PS致谢所有CNP App iOS团队成员,以便在我们的应用程序中与该解决方案进行协作!

翻译自: https://medium.com/schibsted-tech-polska/robust-news-feeds-using-child-view-controllers-in-swift-a32f6eaf35eb

ios 讲控制器传给子视图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值