Tag: 移动

Swift中的App架构和对象组成

对象组合是面向对象编程的核心概念。 对象可以包含其他对象,有时会创建复杂的层次结构。 在本文中,我想展示如何设计这种架构。 在一个实际的示例中,您将看到处理复杂的层次结构很简单,而对象和层次结构可以重用。 面向对象的设计过程包括规划对象如何连接和交互以创建零件或完整系统。 我们都习惯了Model-View-Controller体系结构,该体系结构定义了包含视图和模型的控制器对象。 这些组成部分共同构成了应用程序的基础。 形成树结构的递归类型使事情变得有趣。 递归组合很简单,可以用来表示任何潜在的复杂层次结构。 组合模式是“设计模式:可重用的面向对象软件的元素”一书中描述的结构模式。 它描述了如何构建由两种对象的类组成的类层次结构:原始对象和复合对象。 将对象组合到树结构中以表示部分整个层次结构。 复合可以使客户统一对待单个对象和对象组成。 设计模式:可重用的面向对象软件的元素 复合模式定义了下一个参与者: 组件是一个抽象接口。 它声明函数并由客户端使用。 Leaf实现了由Component接口声明的功能。 复合工具组成。 它包含子组件数组。 Composite通过委派(转发)对其子级的调用来实现Component接口声明的功能。 客户端是通过Component接口使用合成的代码。 我们可以像这样实现Composite模式: 现实生活中的实现可能会略有不同。 常见的变化是Component,Leaf和Composite是同一对象。 使用树结构进行组合是一种非常强大的模式,它对应用程序体系结构具有很多好处: 将复杂的任务分解为小部分。 组件解决小任务。 组成一棵树以解决更大的任务。 与复杂结构进行交互的方式与与单个实例进行交互的方式相同。 组件树具有与单个组件相同的接口。 单一责任原则。 组件负责单个功能。 它可以是特定于域的,也可以是特定于组成的(例如按特定顺序安排孩子)。 可重用性。 组件是可重用的,可以在更大的层次结构中使用组件树来解决更大的任务。 可测试性。 由于组件和组件树是独立的,因此可以进行单元测试。 应用各种设计模式的机会。 树结构可以很好地与各种创建,结构和行为设计模式配合使用。 简单。 最后但并非最不重要的。 在特殊情况下,复合模式只需要一个类即可实现。 树和树上的操作对于开发人员是众所周知的。 难以置信? 让我们看一些例子。 UIView UIView的层次结构可能是树结构最突出的示例。 视图是应用程序用户界面的基本构建块。 可以将视图嵌套在其他视图中以创建视图层次结构。 例如,我们使用小的UIView节点构造复杂的树。 我们可以按功能对视图进行分组: 基础UIView类。 它定义了所有视图共有的行为,呈现内容以及处理与该内容的任何交互。 显示内容。 […]

在Swift中使用IBInspectables

具有可自定义细分的简单进度栏 我已经看到了很多进度条小部件,所以我决定尝试通过Interface Builder进行自定义的“ SegmentedProgressBar”。 要继续或快速将此视图添加到您自己的项目中,请在GitHub上获取它。 在深入研究代码之前,我先介绍了两个主要功能: 1)自定义细分 段应该是完全可自定义的,包括宽度,高度,背景颜色,borderWidth,borderRadius,borderColor。 同样,分隔线段的线也应可自定义:线高,线宽(线段之间的距离),线色。 2)IBDesignables IBDesignables允许Interface Builder实时呈现自定义视图的UI更改。 与IBInspectables结合使用,用户可以编辑特定的运行时变量并立即查看结果,从而节省了大量的开发时间。 入门 首先,我们需要建立一个支持IBDesignables的自定义类。 @IBDesignable标记使Interface Builder知道该类应自动呈现。 如您所见,所有定义为IBInspectable变量的属性在属性检查器中都是可见的。 现在建立我们的第一部分! 我将重写视图的onDraw()函数,以实时呈现对该类所做的所有UI更改。 每个段和线的所有自定义绘制逻辑都将在此函数内发生。 在这里,我创建了一个名为’segment1’的新UIView,该视图以父级居中居中,其宽度,高度和颜色取自可检查变量。 编辑这些变量中的任何一个都应实时更改“ segment1”的形状和颜色。 注意:如果Interface Builder似乎没有呈现任何更改,则可能必须在编辑器菜单中启用“自动刷新视图”。 建立进度条 现在我们有了第一段,但是还需要两个元素:第二段以及它们之间的连接线。 此时, draw()函数变得越来越庞大。 让我们draw()重构为drawLine()和drawSegments() ,然后制作一些实例变量来跟踪我们的视图。 我们将创建两个UIView列表,一个用于段,一个用于线,以便以后可以与每个元素进行交互。 现在添加buildOddSegments()函数。 大多数逻辑已在drawLine()和drawSegment() ,因此此实现应与buildEvenSegments()相当相似。 太好了,现在我们可以为任意数量的细分提供工作进度条! 一切看起来都不错,只是许多部分都被绘制在了线后。 让我们添加一个快速功能,在构建完所有线之后更新线段的z轴。 现在我们可以从界面构建器手动更新selectedIndex ,让我们创建一些函数以使开发人员能够以编程方式执行此操作。 最终波兰语 进度栏确实开始融合在一起,但是我们需要在其完全完成之前添加一些自定义选项。 开发人员应该能够编辑线段的cornerRadius , borderWidth和borderColor 。 此外,如果开发人员希望使所有线段都为圆形而无需计算特定的拐角半径,则应添加一个circularSegment选项。 首先让我们为cornerRadius , borderWidth , borderColor和cornerRadius添加一些新的可检查变量。 为了使线段完美地呈圆形,其宽度和高度必须相等。 在这里,我使用didSet{}来确保segmentWidth […]

通过将功能构建为框架来提高iOS团队的生产力

向现有的大型代码库中添加功能是许多工程师在其编码生涯中将面临的挑战。 如果iOS应用程序已经存在了几年以上,那么它可能已经吸引了许多开发人员为许多功能做出了贡献,从而使其成为了许多版本。 通常,结果可能是非常大的代码库,具有不同程度的模块化,较长的构建时间,而且代码不是很干净。 Scout24上的iOS应用处于不久前还处于这种状态,我想与大家分享我们如何对开发环境进行重大改进以及带来的好处。 问题 如果您曾经在一个大型项目中使用Swift进行开发,那么即使稍作改动,您可能对构建整个应用程序模块所花费的时间太熟悉了。 关于如何改善总体持续时间,有大量资源。 Swift具有许多出色的语言功能,但是这些功能是以更长的编译时间为代价的。 即使进行了少量更改,您也可能要等待几分钟才能看到结果。 这确实让人分心,效率很低! 在编码时,时间和精力是您最宝贵的资源。 如何解决 较长构建时间的答案很简单: 使编译器构建更少的代码! 听起来很合理,但是我们如何实现呢? 答案是通过使用iOS框架将我们的代码划分为更小,更离散的功能块。 在此示例中,我们将从主应用程序模块开始,并添加一个包含一些UI功能的框架,该功能构成了我们将集成到我们的应用程序中的功能。 这有什么好处? 这么小的示例可能看起来并不明显,但是以这种方式进行开发的好处是巨大的。 当您必须将功能内置到具有大量旧组件的大型应用程序中时,它们特别大。 通过将功能构建为高度独立于主应用程序模块的独立模块,您可以将更多精力放在要创建的功能上。 您会发现,您被迫以仅公开应用模块所需内容的方式来组织代码,该代码往往更加清晰和经过深思熟虑,并且不太可能发展成意大利面条式混乱,因为这样做更困难这样做。 与框架一样,构建作为框架的功能的一个基本步骤是创建一个“包装器应用程序”,就像我们在本教程中所做的那样,该程序将您创建的功能最小化地集成到一个微型应用程序中。 该应用程序无需构建主应用程序模块所需的所有其他依赖项,因此构建时间将很快缩短。 创建一个新的应用程序工作区 您将需要为您的应用程序和功能所在的框架创建一个工作区。 首先单击Xco​​de中的File-> New- > Workspace 。 在要保存工作空间的位置创建一个新文件夹。 保存后,我们现在有了一个空白工作区。 现在,单击Xco​​de中的File-> New- > Project ,然后选择“ single view app”。 将项目命名为“ SimpleCounterApp”,并确保选择“ Swift”作为应用程序语言。 之所以这样命名,是因为它将仅包含应用程序模块。 在下一步中,选择保存SimpleCounter.xcworkspace的文件夹。 在“ 添加到”下,选择SimpleCounter工作区。 必须在Xcode中打开SimpleCounter.xcworkspace才能显示此选项。 创建项目并注意,现在我们有了一个看起来很无聊的项目。 让我们开始做一个框架! 我们将在以后的步骤中将功能集成到主应用程序中,因此现在无需执行任何操作。 再次单击文件->新建->项目 ,这一次,选择“ Cocoa […]

提克特(Tickld),《大笑之旅》

基于社区的应用程序,由用户生成的内容组成; 图像,GIF,故事和视频,它们之间以及与社交媒体之间共享。 还具有典型的社交网络应用程序的功能,用户配置文件,关注者/关注者,查看/创建/发布内容,评级和评论,并使用托管在AWS S3实例上的后端构建。 语言: Objective-C 功能:推送通知,Facebook SDK,新的Relic Analytics SDK,简单的照片编辑器(裁剪,添加文本和图像叠加层) 创建了一个新设计,帮助了消费内容的流程和UI元素的更好位置,从而增加了评论和投票的使用率。 构建推送通知和自定义的简单照片编辑器,完成简单的模因创建,裁剪图像,添加图像和文本覆盖图。 我的第一个产品应用程序之一,不仅是我要构建并签署给客户,而且有机会在此过程中使用多个版本,就痛点和问题与社区进行交流,并基于分析数据。 如果您有一个必须更新或使用旧代码的应用程序,那将如何呢? 在下面的评论中让我知道。 让我们连接! 通过评论我的帖子或与我联系,不要犹豫, 电子邮件: markgage86@gmail.com 推特: markgage86 盘带: markgage86

加快您的iOS开发:模块的范例

如果我们看一下图形,这个概念很容易理解。 我们只需要将您的应用视为ViewControllers件的集合,这些ViewControllers件可能会出现在ViewControllers任何位置。 这意味着,如果我们想在不同的ViewController重用“模块1”,则它应尽可能简单。 然后,我们推断ViewController将由Modules组成 为了开始使用模块,需要一种解决方法,这就是为什么我创建了一个Cocoapod库来帮助我们实现这一奇妙 范例的原因 FTMTableSectionModules 具有TableView的可重用ViewController,分为几节 cocoapods.org FTMTableSectionModules 基本上包含2个类别: ModulesViewController是UIViewController的子类。 此类仅是已实现UITableViewDelegate 和 UITableViewDataSource所有方法的代理。 主要功能是根据所选部分将职责重定向到适当的模块。 此外,还公开了处理模块的方法,例如添加/删除/插入… TableSectionModule ,AKA 模块是NSObject的子类。 此类包含一些逻辑,以改善UITableView的生命周期 ,并使TableView的各个部分的隔离和可重用性变得更加容易。 在这里,我们描述了模块生命周期中最重要的步骤: 我想开始尝试我们已经有足够的信息,因此我们可以跳到有趣的事情……。 编码时间! 。 像往常一样,尝试开发中的新事物的最佳方法是创建一个“ Hello World!”。 第一步,创建一个HelloWorldModule作为TableSectionModule的子类 最后一步,我们错过了在MyFirstModulesVC中添加HelloWorldModule的操作 。 超类ModulesViewController自动调用一个名为createModules的方法,因此我们需要重写此方法: 模块范例的主要关键之一是将责任划分到应用程序组成的不同部分。 这可以归纳为2条规则。 模块内部发生的事情留在Module中 。 在模块外部发生的事情留在ViewController中 。 与其用无聊的理论来解释,不如让我们举一些真实的例子。 模块内部会发生什么,请留在模块中。 想象一下,我们有应用程序的典型部分,您可以在其中显示和隐藏详细信息。 正如我在开始时提到的,尝试将应用视为模块的集合是非常重要的; 在这种情况下,我们面对的是一个清晰的示例,我们需要创建一个新的模块。 在这里,我们可以看到一个示例,说明这个新模块的外观: 从GIF可以猜到,我们在扩展和折叠信息时将插入和删除UITableViewCells 。 遵循第一条规则, Module具有足够的授予权和责任如何单独实现这一目标,因此这意味着对于ViewController ,折叠和展开将是完全透明的。 在这里,您是实现新FoldUnfoldModule的代码。 加起来 ViewController仅包含Modules和Delegates如果有)的列表。 该Module包含视图(页眉/页脚/单元格)以及完全完成TableViewSection所需的每个逻辑 自从我们开始以来,已经提到过多次,这对于创建可以以最简单的方式在任何地方重复使用的组件非常重要。 […]

从Swift 4迁移到Swift 5

好极了! Swift 5终于来了。 🎊 这是Swift的下一个主要版本,并且最终带来了ABI稳定性 。 除此之外,还添加了几个新的关键功能,包括原始字符串,将来的枚举大小写,结果类型,检查多个整数等等。 稍后我们将在另一篇文章中介绍有关Swift 5的所有功能,我将尽快提供其链接。 现在,我们将讨论将现有的Swift 4代码迁移到Swift 5的问题。 迁移器将负责许多更改,但是您可能需要进行一些手动更改,才能在应用更改后构建项目。 就像您可能需要应用一些修复程序或更改某些功能及其声明,如上面概述中所述。 另外,您可能还需要编辑某些第三方库和Pod。 但不必担心,它们只是最小的变化。 耶eh! 现在您已完成迁移。 🎉 就是这样! 你完成了。

使用Bitrise上的构建缓存在构建之间传递工件

可以 在 Bitrise 上 使用 Cache:Push 和 Cache:Pull 步骤在 构件之间传递工件 ,如果要使用一个 构件 在 另一个构件中 生成的文件,这非常方便。 例如,在第一个构建中,您可以存档您的iOS项目并从xcarchive导出Development ipa文件,然后可以由测试人员进行测试。 测试人员完成后,无需再次存档项目,但是可以使用xcarchive (由第一个版本生成)来导出一个ipa文件供App Store分发并将其部署到App Store。 让我们看看如何做到这一点: 1,使用iOS项目的存储库在Bitrise上创建一个新项目。 Bitrise将对其进行扫描,并将生成包含primary工作流程和deploy工作流程的初始配置。 您需要使用配置为能够导出到Development和App Store类型的ipa文件的iOS应用。 此外,该项目需要已经部署(至少一次)到iTunesConnect,以便能够使用Bitrise自动进行后续部署。 format_version: ‘4’ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git project_type: ios trigger_map: – push_branch: “*” workflow: primary – pull_request_source_branch: “*” workflow: primary workflows: deploy: steps: – activate-ssh-key@3.1.1: run_if: ‘{{getenv “SSH_RSA_PRIVATE_KEY” | ne “”}}’ […]

移动应用程序的简单架构

在加入BigCommerce担任高级移动软件工程师之前,我很幸运在Mutual Mobile工作了五年以上,担任过iOS工程师到架构师的职位。 对于不熟悉MM的人士,他们是一家全球数字创新和新兴技术机构,总部位于德克萨斯州奥斯汀(与BC省相同)。 在代理机构中成为移动开发人员的最大好处之一就是项目范围广泛。 有一天,您可能正在开发银行应用程序。 六个月后,它可能是围绕体育社区构建的社交应用程序,健身应用程序,网状网络系统,智能家居管理器等等。 尽管客户多种多样,但他们总是有共同点,在Mutual Mobile,我们开发和开源了许多开发人员至今使用的工具和体系结构。 由Jeff Gilbert和Conrad Stoll在Mutual Mobile上开发并倡导的一种名为VIPER的体系结构在iOS社区及以后的社区中获得了广泛的关注。 最近在BigCommerce开始新的移动项目时,我考虑过回到熟悉的体系结构。 VIPER是适用于移动应用程序的Clean Architecture应用程序,专注于将逻辑与视图控制器分开的可测试代码。 在移动世界中,大多数应用程序都使用MVC架构,这通常意味着包含应用程序逻辑的视图控制器(也称为Massive视图控制器)。 通过将视图与业务逻辑分开,VIPER使类更易于理解,测试和维护。 但是最终我决定VIPER超出了我的需要。 尽管它是一种出色的,可测试的现代体系结构,但它往往需要大量文件才能正确执行。 可以使用几个开源项目来生成执行VIPER所需的文件(尽管如果像吉尔伯特那样执行TDD风格,则仅在需要时创建这些文件)。 我决定尝试一下自己的个人建筑构想。 它是最小的,不是样板化的,并且高度便携。 我已经使用它在Objective-C,Swift和JavaScript中编写了应用程序,并且没有理由说它不能在其他十几个版本中运行。 它绝对受到VIPER及其背后思想的影响。 其中心论点是:移动应用程序是由数据传递流和子流(开始,结束,前进和后退以及有时执行操作)管理的屏幕构造。 因此,我将其称为“流程体系结构”。 流架构介绍 无论是将用户(这是该架构的起源),登录服务还是仅使用带有不同屏幕导航堆栈的基于选项卡的应用程序,几乎任何应用程序过程都可以描述为屏幕流和信息。 这一切都从一个开始开始。 / **启动流程,提供可选的初始数据* / func start(带有数据:任何?) 当然,任何可以开始的流程也可以结束。 / **完成一个流程,提供可选数据* / func end(带有数据:任何?) 某些流程,例如入职或登录,可能需要前进和后退。 / **向后退,提供可选数据* / func back(带有数据:任何?) / **前进到流程中,提供可选数据* / func next(带有数据:任何?) 有时,流程可能需要执行一些不一定导致前进或后退的动作。 一个动作需要一个ID来标识它,并可能提供一些数据来为其提供上下文。 我也可能会记录一个操作,例如用于分析。 流量没有实际变化。 […]

启用幻灯片以在UITableView中删除。

我敢肯定,无论是滑动消息还是滑动电子邮件,您都已经完成了数百次。 在iOS中,许多UITableViews的外观如下: 好消息! 为此,它非常简单。 这实际上非常简单。 尽管您需要一个有效的UITableView来实现此目的,但我不会讨论如何设置UITableView 。 如果愿意,可以转到我的GitHub,下载我使用NSURLSession为获取JSON创建的项目示例,并填充UITableView第1部分。 希望到目前为止,您已经可以使用显示一些数据的UITableView了。 实施。 这只需要几行,因此您可以避免紧张。 添加此func : 在此函数内,我们需要: 检查提交的编辑类型。 从数组中删除对象 删除带有动画的行 我们可以这样做: 另一种实施方式。 这种方式要好得多,主要是因为没有if else陈述,我认为这很if else 。 因为在另一种方法中,如果要有更多选择,则需要实现if else语句来处理操作。 您还注意到,滑动时我添加了另一个选项:共享按钮。 做完了! 是的,就是这样! 很容易吧? 我希望本文能对您有所帮助,以便您现在可以继续并将其轻松实现到您的应用程序中。 像往常一样希望得到反馈。 评论和建议非常感谢。 让我知道接下来要写些什么! 谢谢。

了解Swift 4中的泛型函数和类型

编写可维护的代码应该是每位软件工程师都应该意识到的一个问题,因为从长远来看,它具有回报。 通常,为了尽快完成工作,我们倾向于重复自己。 这通常涉及复制和粘贴我们可能对函数的类似实现,并进行调整以适合我们想要的内容。 复制在某种程度上增加了复杂性并阻塞了可读性。 一个好的方法总是尽可能多地采用DRY(不要重复自己)原则。 在我们的代码中,考虑如何设计API和适应的应用架构非常重要,因为从长远来看,这会影响代码的可维护性。 因此,回到讨论主题。 Swift中的泛型。 使用通用代码,我们可以编写可重用的函数和数据类型,这些函数和数据类型可以与匹配我们定义的约束的任何类型一起使用。 目的是排除共享功能并减少样板代码。 我们有约束和不受约束的泛型。 对于不受约束的泛型,我们不能对它们做太多事情。 受约束的泛型用协议表示。 我们所知道的协议在其符合类型上声明了一组要求。 在这一点上,让我们通过具体示例更深入地探讨实际问题。 解释够了吧? 是的,让我们这样做。 我们将使用Xcode Playground。 因此,随着我们的前进,请随时在操场上测试代码。 我们将从无约束的泛型开始,但是我们不会浪费时间,因为我们不能真正地对无约束的泛型做很多事情。 上面的参数化函数可以接受任何类型的输入。 我们可以将输入值复制到局部变量,然后从函数返回它。 我们也可以将函数传递给另一个函数。 这就是我们可以使用此不受约束的泛型函数所能做的事情。 因此,现在让我们看看受约束的泛型函数。 首先,我们将在不使用泛型的情况下进行常规实现。 然后,我们将使用泛型来排除共享功能并减少样板。 我们的示例将通过decodable协议从网络中获取数据并解码json响应。 我们将从https://jsonplaceholder.typicode.com/获取数据,其中包含用于测试的虚拟数据API。 通过仔细查看上面的函数,我们可以轻松地看到fetchPosts和fetchComments函数有很多共同点。 它们之间的唯一区别在于不同的Post和Comment模型对象,这些对象符合我们传递给JSONDecoder的可解码协议。 因此,这是一个很好的地方,我们可以使用泛型来避免代码重复。 注意:我们正在从上述函数同步获取数据。 在生产代码中,建议异步获取数据,以避免在等待下载完成时冻结UI。 因此,让我们继续提取通用函数,我们可以轻松地重用它们而无需重复自己。 因此,使用泛型,我们已经能够提取两个功能之间的共享功能。 因此,使loadResources可重用。 我们将该函数限制为可解码协议,以帮助我们根据分别来自用户特定函数loadPosts和loadComments通过完成处理程序传递的模型对象类型来解码json数据。 编译器将能够在运行时根据我们传递给完成处理程序的类型来推断参数类型。 在总结泛型之前,还有一件事是看一下泛型类型或数据类型 。 我们将实现一个通用类型,以执行与从网络中获取数据相同的操作,但是这次,我们将使用 结构 。 就像函数一样,结构也可以是泛型的。 通过以上操作,我们设法减少了postResource和commentResource实例,以描述资源所在的位置。 我们已经将网络部分完全分离出来了。 因此,使用这种方法,测试变得更加简单,因为我们可以轻松地测试该结构的实例以了解其是否完整形成。 从以上代码中,我们还有两个功能,一个功能同步执行资源加载,另一个功能异步执行操作。 基本上,除了使用URLSession异步API外,两者之间的区别是在异步函数中使用了注释关键字@escaping 。 这样做是为了确保完成处理程序转义方法的范围,因为我们正在处理异步请求。 因此,最后,我们已经能够解释如何在Swift中实现泛型函数和类型。 我知道现在您对自己今天学到的东西感到高兴。 […]