Tag: 软件开发

委派指南-Swift 4

现实世界中的代表团 一个很好的起点是通过解释现实世界中的授权。 在现实世界中,委托封装了某人(委托人)将任务(委托人)交给其他人(委托人)。 有两个名词和一个动词组成委派: 委托(动词):“将任务或责任委托给另一个人”。 委托人(名词):“委托他人的人”。 代表(名词):“被选择或当选代表他人的人”。 为了澄清-委托人(名词)会将责任(动词)委托给委托人(名词)。 现在我们有了组成委派的组件的字典定义,接下来让我们实际展示一个真实的委派示例: 在仓库中,将有一名物流经理( 代理人 ),他们将知道卡车何时到达以及何时到达。 物流经理很忙,虽然他们很擅长管理物流,但在装卸和搬运箱子时却不那么擅长。 因此,他们将这项工作( 代表–动词 )交给仓库操作员( 代表–名词 )。 后勤经理告诉仓库操作员,一辆卡车将在09.00到达,当到达09.00时,他们告诉仓库操作员该卡车已经到达。 然后,仓库操作员卸下货车并将箱子运到需要的地方。 iOS世界中的代表团 数字世界中的委派与现实世界中的委派非常相似,不同之处在于,我们拥有的不是对象而不是人,而对象在Swift中将是类,结构或枚举的实例。 让我们以对现实世界相同的方式来分解软件开发中的委托: 委托(动词):将任务或责任委托给另一个对象。 委托人(名词):将职责“移交给”另一种类型的实例(对象)的类或结构实例(对象)。 委托(名词):保证处理已委派职责的对象,即提供委派的功能。 需要澄清的是,委托是指一个对象(代理)代表另一个对象(代理)提供功能时的行为。 现在,让我们通过从参与对象的角度来考虑委派来进一步深入研究: 您创建一个待办事项列表应用程序。 您可以将UIViewController子类化,以创建名为“ ToDoListViewController ”( 委托–名词 )的视图控制器对象。 您将一个UITableView实例( 委托人 )放在ToDoListViewController 。 UITableView会显示要执行的项目并检测与之交互的时间,但会将处理这些交互( 委托–动词 )的责任移交给另一个对象。 UITableView实例( delegator )将向事件的委托对象通知其将要处理或刚刚处理的事件。 ToDoListViewController ( 委托人–名词 )声明它将提供响应UITableView处理的事件而发生的功能。 假设UITableView实例( delegator )可以讲话,该对象可能会说:“ 我是一个表视图对象,并且选择了第3行 ”。 “ ToDoListViewController […]

Swift中的设计模式:策略模式

欢迎来到一系列致力于学习设计模式的文章。 尽管许多想法与代码无关,但我们的目标是向您展示如何在Swift中实现它们(在撰写本文时为Swift 3.0)。 每个帖子彼此独立,所有项目代码都可以 在Git上找到 。 策略模式允许您在运行时更改算法的行为。 使用接口,我们能够定义一系列算法,封装每个算法,并使它们可互换,从而允许我们选择在运行时执行哪种算法。 一个例子就是游戏中的角色。 我们的角色-好人-有3种状态:奔跑,步行和站立。 我们希望将状态与其他对象(即Bad Guy角色)一起重用,因此我们希望封装它们,并能够将状态与其他对象一起使用,从而减少了代码量。 我们可以使用UML图显示Good Guy角色如何工作: 首先,在Xcode中创建一个新项目。 我们不会在UIKit中碰任何东西,因此创建一个macOS Terminal项目。 首先,我们需要定义接口/协议(这是同一件事,但是Swift使用单词protocol)。 我们的基本接口定义了一种称为enterState()的方法。 它看起来像这样: 我们可以建立Good Guy类。 此类被设置为接收我们的状态并在我们传递给它的类中执行enterState()方法。 它看起来像这样: 然后我们可以建立我们的状态类。 我们将创建步行,跑步和站立状态。 这个想法是,好家伙将使用这些特征,但坏家伙也将能够在实现时通过实现CharacterState接口来使用它们。 它们非常基础,仅运行打印命令来表明我们已进入该状态。 这是我们各州的实现: 现在,在您的主类中,我们希望能够使用内置在其中的setState函数将状态传递给您的好家伙。 它看起来像这样: 我们将不同的状态传递给我们的Good Guy类,它将执行每个不同的(尽管是基本的)算法。 点击运行,您的输出将如下所示: 进入步行状态 进入站立状态 进入运行状态 程序以退出代码结束:0 欢呼! 您已经实现了策略模式。 奖励-坏家伙 因此,如上所述,“坏人”还可以利用“好人”的功能,而不必重复任何代码(“策略”模式的一大好处)。 创建一个名为BadGuy.swift的新类。 该类看起来与Good Guy类似,但您可以向其中添加特定的Bad Guy功能(例如杀死或射击)。 坏家伙看起来像这样: 为了说明它是如何工作的,在我们的主类中,我们将创建一个Bad Guy对象,并设置状态,就像我们对Good Guy所做的一样,因此现在看起来像这样: 运行它,您将得到以下结果: 进入步行状态 进入站立状态 进入运行状态 进入步行状态 […]

带有RxSwift的MVVM

背景 在开发iOS应用程序时,您知道用于开发iOS应用程序的体系结构是MVC(模型-视图-控制器),每个应用程序组件按其职责分开。 在MVC架构上,模型负责提供数据或控制与数据访问有关的所有事物。 Person或PersonDataAccess类是模型的示例。 View负责将数据显示到用户界面。 并且,最后一个是Controller,负责更改模型并准备要呈现给View的数据。 根据Apple MVC的概念,视图将用户操作发送到控制器,然后控制器使用从视图发送的数据更新模型,然后模型将模型中的数据更改通知控制器,最后一个是控制器更新视图。 看起来很简单,对吧? 但是现实与上面的概念并不相同。 大多数iOS开发人员(包括me😂)倾向于制作Massive View Controller。 为什么将其视为问题? 因为视图和控制器紧密耦合,很难说它们是分开的。 在这种情况下, UIViewController子类被视为View。 因此,您倾向于将所有这些职责(网络调用,事件处理,数据更改,显示数据等)写入视图控制器( UIViewController子类)。 似乎易于实现,但有很多后果。 由于表示和数据操作合为一层,因此很难测试业务逻辑(视图) 难以维护 膨胀的UIViewController 继续… 由于以上所有这些问题,我开始学习一种替代的体系结构,该体系结构解决了这些问题。 认识MVVM 因此,这是视图控制器庞大性的拯救。 有一个名为ViewModel的组件。 那么,ViewModel的职责是什么? 就像MVC架构上的Controller一样,ViewModel负责更改模型并准备要呈现给视图的数据。 但是,有许多差异。 ViewModel提供了到View的数据绑定机制,因此,如果ViewModel上有数据更改,则View将自动更新,并且ViewModel没有对View的引用。 就个人而言,由于数据绑定机制,我喜欢这种体系结构,因此如果ViewModel发生更改,则View可以更新自身,并且由于职责分离,因此易于测试模型和业务逻辑。 因此,将MVVM应用于iOS开发的好处是 易于测试业务逻辑和模型 视图几乎是被动的,因为存在数据绑定,尽管如此,我们仍然使用视图控制器执行segue(作业,请分开此😂) 轻松更改UI而不弄乱业务逻辑 您可以将业务逻辑或网络调用放入ViewModel,这样视图控制器就变得不那么肿了 ViewModel的经验法则 无参考资料 没有import UIKit 没有引用任何UIKit的组件,例如UIView , UIButton , UITextField等 只是一个数据。 JSON,字典或其他数据结构 RxSwift ReactiveX提供的一种反应式编程框架。 这是Rx的Swift版本。 它依赖于可观察的模式,如果Observable发生了数据更改或事件,则观察者可以执行某些操作。 在这里可以找到RxSwift的更多详细信息。 在这里,我在MVVM体系结构上使用RxSwift提供从ViewModel到View的数据绑定,并在Model发生更改时通知ViewModel。 […]

如果您的类型名称平庸,则您的代码将构成责任

我创建代码的过程如下所示: 研究目标的细节(学习) 提出实施计划(创建) 代码(单击并键入) 当我意识到我有一个错误的假设时,请返回步骤1(遗憾) 在进行编码工作之前,正确命名类型是计划中的最后一个障碍。 就像,嗯,我知道计算机不在乎我给我命名的类型。 机器解释指令时,所有这些都将被剥夺。 我的功能不会受到影响。 为什么我花了很多时间来确定角色以及事情如何融合在一起? 因为代码不仅仅适用于机器。 适用于必须充分理解它才能正确操作它的开发人员。 该类别中包括“未来”。 要做到这一点,就需要弄清问题集,并有足够的同理心,以使听众可以预期您选择的名字的所有含义。 真名 有一个古老的想法,即如果您知道某事的“真实名称”,那么您将拥有权力。 这是幻想中的常见现象,也是宗教故事的一部分。 以伊西斯和拉的故事为例。 伊希斯(Isis)是一位出色的治疗师,但只有知道她的真实姓名时才能帮助Ra。 他试图用“小写”的名字满足她的要求,但这仅使Ra未能解决核心问题。 一旦他放弃了他的真实姓名,她就可以治愈他。 这听起来像调试我。 如果您不知道所处理事物的真正含义,就无法解决该问题。 懒惰的标签将欺骗人。 您在阅读代码时让人们做的心理锻炼越多,使用它的认知就越消耗精力。 “是的,被称为 Provider 但实际上只是转换数据。” “该 modelId 不适用于该模型,适用于其他通用模型。” 但是,如果您真正知道什么是东西,则可以轻松地对其进行操作。 这很重要,因为将来必须更改代码。 我的理想是功能与代表功能的标签之间的绝对统一。 既然是理想的,我永远都不会到达那里。 想起名字时会出现回报递减的情况,但是值得花一些时间。 在清晰度和可读性之间取得平衡 某物的最精确名称可能是…… 绝对清晰。 把它收拾好。 该名称公然忽略了所有可用的上下文。 显然,这个例子很荒谬,但是添加不必要的限定词可能很诱人。 即使您觉得这个名字不是最好的,也最好在项目内部保持一致,而不要在两个约定之间来回穿梭。 一旦人们适应了您项目的标准,他们就会知道您在说什么。 一致的信息比冲突的信息要好,即使最终效果不佳。 同样,在引入新元素时也不要害怕重命名旧元素,以澄清它们之间的差异。 我们不是用石碑编码。 有些人梦想着将来验证自己的代码并期待未来的变化。 我宁愿使代码清晰地代表当前的情况,并在反映未来的将来进行更改。 这使我省去了很多我无法控制的事情。 我们控制双方 也许您没有花时间准确地命名某件事,因为它正在做很多事情…… 再塞另一种方法 有时正确的答案可能是重构基础实现以启用更好的名称。 我们都可以控制。 […]

面向对象的校长—普通英语

这篇文章讨论了面向对象的原始概念。 在编写代码时,您知道如何将方法声明为私有方法,或者如何从类中进行子类化,而在这里,我尝试用通俗的英语解释这些概念。 这是对每种情况的简单合理解释,以防您想将其解释为六岁的孩子: 遗产: 继承允许我们从类中继承子类。 在面向对象的编程中,类都是层次结构的所有成员,并且只有在继承的帮助下才有可能。 子类从父类继承方法和属性。 这使开发人员可以轻松地重用代码。 抽象: 抽象意味着表示基本功能而不包括实现。 这意味着什么? 抽象是根据事物在继承层次结构中的位置,用更简单的术语(抽象的细节)描述事物的概念或范例。 它有助于表示基本功能,而不必担心实现。 在最抽象的级别上,没有实现细节。 最抽象的概念位于顶部,更具体的思想位于底部。 该图表示抽象概念的一般概念: 接口是一组没有实现的方法。 抽象类包含抽象方法和具体方法,它们必须被继承。 封装形式: 封装用于定义,隐藏和限制访问以外的属性和方法。 它可以防止不必要地访问方法和属性(类是封装数据的容器)。 该访问仅在需要时可用,这由访问修饰符处理: 公开:可从任何地方访问 受保护的:可在相同的包和子类中访问 默认值(无说明符):包内可访问的表格 私有:仅可在同一类中访问,子类不可见 Swift中的访问修饰符可以是一个示例。 多态性: 多态性使我们可以定义在父类中已经声明的方法(在子类中)。 通常有两种类型的多态性: 编译时多态或重载: 您可以定义多个具有相同名称的方法,唯一的区别是方法签名。 运行时多态或覆盖: 您可以使用与父类(或已实现的接口)相同的名称和签名来定义方法。 这使我们能够定义特定于特定子类的方法。

WebCash韩国的生活– Lay Bunnavitou –中

WebCash韩国的生活 我曾在一家位于韩国汉城的名为WebCash company的软件公司中居住了大约两年半。 以下是有关WebCash(http://www.webcash.co.kr/index_eng.htm)的信息: #1:前6个月担任IOS实习生 我没有与IOS技能相关的经验,但就在4个月前之前,我曾经学习过基于Java代码的AOS(Android),而该代码也确实与我学校的课程有关。 并添加在Android中用作接口的XML代码。 这意味着我从什么都不是IOS开发开始。 #1.1:从身体开始 我有一位韩国高个子的导师,戴眼镜。 他不像我那么帅。 哈哈哈… 我记得他都在谈论IOS开发,因此他给了我3本书。 他告诉我准备演示文稿,以便在每个星期五下午向他和整个团队展示。 经过4个月,我对IOS有了很多了解。 还有两个月的时间,我只是重做与IOS编程有关的旧团队项目。 #1.2:首次学习IOS时感觉还不错 您知道什么是韩国料理都很好,但是因为这是第一次体验韩国料理,所以让我非常恶心。 awww……。 有一次我永远不会忘记吃过午饭后,我会继续读书。在这段时间内,我可以看到iMac屏幕上的文字会随着海中的波浪移动。 我立刻起身去洗手间。 有时我的眼睛处于烹饪模式。 像是沸腾。 好热 我可以看到我的头发有些细线变成白色而没有突出显示颜色。 推动自己,因为没有其他人会为您做到这一点。 #2:开始加入真实项目 在6个月后,我的导师有时间允许我参与一个称为客户管理项目的真实项目(即将管理所有注册该服务的客户,并向他们提供有用的信息和一些更有用的功能)。 #2.1:不仅是iOS编程 上述项目结束后。 我对IOS开发感到很满意。 在这段时间里,我不仅在研究IOS,还研究了称为API的后端。 它允许许多平台通过一个API进行访问。 指向#2.2,您可以了解有关我的研究的更多信息。 #2.2:进行研究 在没有工作的情况下,我会对当今技术中的新事物和有用事物进行研究。 我发现一些像: 1 。 Yobi Git D2 Naver:像github这样的源代码控制器,使开发人员可以轻松地与团队合作。 注意:使用play框架安装我们的服务器 2 。 推送通知服务器:它可以在Web,aos,ios等平台上使用。 3 。 API:如何通过在每种编程语言上使用安全性库来制作安全性API。 4 。 OpenCV(图像处理):允许分析图像是哪个对象。 5 。 […]

测试,测试,1、2、3,测试

我是卫斯理大学(’18)的计算机科学和戏剧双专业,整个暑假都在Flatiron学校(FS)学习。 由于我一生都是学生,所以我习惯于进行测验,学习测验,在测验前拖延,以及受到测验的情绪影响(无论好坏)。 我认为大多数人都可以同意的事实是测试很糟糕。 但是,就编码而言,从长远来看,测试可以节省时间和金钱,并且可以产生更完善,更完美的应用程序。 要进行大量测试,对于一篇博客文章而言,测试太多了,但让我们对其进行分解。 我们都很熟悉XCode向我们提供的红色或绿色小菱形,其中包含支票或’x’。 我们都看着颜色从红色变成绿色,有时又回到红色。 最后,我们全都翻了个白眼,甚至在看到红色的眼睛时甚至撞到了墙上,而在看到绿色的眼睛时就为喜悦而跳了起来。 作为程序员,成功是保持我们编码的动力。 小小的成功是使头脑振奋值得的。 想象一下,如果等到所有编程完成后才知道代码是否按预期进行编译和运行,您会头疼。 这就是为什么测试是一种绝妙的做法。 测试使我们能够构建声音产品并对其功能进行检查。 对于新编码员,养成优雅的习惯很重要。 测试是一种习惯,可能会改变您对编程的看法。 编写测试可能有些繁琐,因此尽早开始很重要。 您越早开始编写和练习进行测试,就会越好。 测试方式 这是几个不同测试选项的简要说明。 Specta和Expecta是不错的选择,因为它们不需要您输入返回类型,并且可读性强。 “总体Specta和Expecta使得语法更加简单,易于编写和阅读。 这直接转化为开发人员的生产力” ( Harry Hornreich )。 XCode提供了一个测试构建框架,使编写测试更加容易。 XCode框架很好,但是IMO最多不过是万不得已,因为它总比没有好。 XCode框架的问题在于,无法知道您正在包括最有意义的测试。 有很多UITesting工具(例如XCode)作为唯一的测试源都不可靠。 有意义的测试 测试有各种形状和大小。 编写测试可能并不像提出测试那样困难。 例如,假设我们在Pig-Latin中编写代码字。 “ in”和“ as”之类的词保持不变。 如果程序员不熟悉该语言,则他们可能会忽略此细节,并让其代码返回“ niay”和“ saay”。 或者,他们可能会记住该规则,却忘记了以元音或以“ sh”或“ ch”开头的单词在其中辅音必须保持在一起的单词的规则。 这种代码中有太多的出错机会,这就是为什么使用测试有益的原因。 如果没有遵循的准则,没有规则,那么如何使程序员避免使程序充满错误? 测试的目的是消除运行时错误(与语法等编译器问题相对)。 测试不是为了弄清楚如何使用内存空间,也不是为了提高速度和效率。 测试以二进制形式进行:您通过或失败,直到通过。 有被迫失败的事情。 强制失败用于指示代码是否朝着不应执行的方向行进。 如果存在某种情况,您担心会发生并且不希望发生,那将是包括强制性失败的充分理由。 右BICEP Right-BICEP是我经过多年编程学习的一种助记符设备。 Right-BICEP是考虑编写代码的绝佳方法。 正确是指检查结果是否正确。 […]

Swift:常见错误无人问津-宏和指令

您好,我亲爱的开发人员, 我有个故事要告诉你。 从前我很无聊。 那时我像往常一样在课堂上玩耍,用猕猴桃为他们编写测试。 我偶然发现了一个有趣的问题,我真的很想为块模拟,以删除不必要的样板。 可悲的是,没有人开源。 所以我想,为什么不写呢? 具有一些非常有趣的功能的请求不会伤害我或整个社区。 因此,我开始实施代码,并在重构和挖掘运行时库的过程中发现了很多奇怪的事情。 可悲的是,故事并没有很好地结束,因为当我提交请求请求时,维护人员决定停止添加任何具有大型功能的新功能。 而且我就像FFFFFFUUUUUUUUUUUUUUUUUUUU…。因为在接受请求请求之前,我开始在项目中使用该Kiwi功能,这意味着我必须维护并行分支。 我的故事。 那好吧… 尽管如此,在这段旅程中,我发现代码中散布着一件有趣的事情: #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG @尝试{ #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG … #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG } @catch(NSException * exception){ KWSetExceptionFromAcrossInvocationBoundary(exception); } #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG 如果看一看,您会发现它分散在整个代码中,但是它具有清晰的模式。 对我们来说,这意味着应该删除重复数据。 为什么,因为从表面上看,这种黑客不再存在,但是将其从代码库中删除真的很困难,因为代码绝不是孤立的。 而且,从我收集到的信息来看,不再需要这种hack(我不知道它的引入原因,但是在禁用它的情况下测试不会失败,至少看起来像这样,因为我没有进行深入研究) 。 您不应该认为这是针对猕猴桃的怨言,因为事实并非如此。 Kiwi令人敬畏,它的开发人员和维护人员构建了我多年来使用的工具,并且在编写ObjC代码时仍在使用。 阅读完代码后,我决定不想自己碰到此类问题,因此我添加了一条准则,使所有特定于宏的代码隔离。 如果我们将该准则应用于我专门为您编写的错误代码: 这不是唯一迅速解决的具体问题。 它们有很多,甚至没有用#标记。 因此,对于此类代码,更好的措施是至少将其隔离为单独的功能。 但是,这也不是最好的主意。 为什么? 我们在整个代码中具有不同的特定操作,这导致我们遇到#if os(iOS)的相同问题,该问题既重复又难以重构。 因此,更好的方法是使用与注入闭包和隔离此类代码相同的方式进行操作: 您可以在以前的演讲中读到更多有关注射的信息: 没人管的常见错误– Swift扩展 最糟糕的编码方式。 […]

Swift中的泛型和JSON解码

因此,上一篇文章写了有关如何使用Alamofire进行联网的文章,这使我回想起最初开始获取数据的时间。 一团糟。 就是这样: 假设您需要执行GET请求才能说出两个端点。 您最有可能做什么? 您很可能会复制第一个GET请求,然后将其粘贴并更改一些内容,例如端点和模型等。 您可能会执行以下操作: 当您添加更多请求时,这很快会使您的代码成为噩梦,更不用说看了。 这就是Generics发挥作用的地方。 我们可以使用更简洁的代码将这两个请求合并为一个函数。 就像上次一样,我将对所有API端点使用https://jsonplaceholder.typicode.com/。 模型 我们的第一个端点是: https://jsonplaceholder.typicode.com/posts : https://jsonplaceholder.typicode.com/posts 对于/posts ,我们将获取userId , id , title , body 。 实施泛型 现在我们有了数据模型,让我们设置通用函数。 如果您阅读了我有关Alamofire的上一篇文章,您可能会注意到一些差异。 而不是返回responseJSON我们只是返回response 。 同样,我们只获得response.data而不是response.data 调用我们的函数 最后,我们在使用JSONDecoder()地方进行了do catch 。 现在我们可以调用通用函数: 很简单,但是我们只给出一个url字符串,并给它一个可解码的模型。 现在我们有了通用的网络请求,可以将其用于所需的任何端点。 我们只需要创建一个可解码的模型,然后传递url端点即可。 然后再次 让我们调用https://jsonplaceholder.typicode.com/comments /comments返回一个postId , id , name , email , body 。 现在我们要做的就是调用我们的泛型函数。 我们可以继续前进,尽管我觉得您明白了。 如果需要更多请求,只需为该端点创建一个模型,请确保您符合Decodable ,然后调用通用函数并传递url和模型。 […]

iOS应用程序要求清单

当我们开始开发新应用时,通常会经过客户定义的要求,并开始考虑概念,体系结构和估计。 这些要求的范围可以从用几句话写成的模糊概念到带有模型,用例和接受标准的详细规范(很少有)。 但是,即使定义了产品的所有功能需求,也有一些事情被假定由客户完成,或者在定义规范时甚至没有考虑。 这可能是由于缺乏技术知识,假设或认为工作量太低而无法在以后定义的想法。 不幸的是,这些事情通常不会那么小,并且可能对项目成本产生重大影响。 这篇文章将探讨几种类似的情况,因此您可以粗略地概述并核对清单,以了解在项目开始时需要询问的内容。 在没有互联网连接的情况下使用应用程序可能会引起最大的误解之一。 如今,应用程序通常连接到后端系统上的REST服务,以获取所需的数据并将其呈现给用户。 当您有互联网连接时,一切都很好。 但是,需要尽快定义应用程序在您无法访问互联网时的行为方式,因为这会对项目的时间表和成本产生重大影响。 这里有几个选项。 这是最简单,最便宜的选择-如果没有互联网连接,该应用程序将无法运行。 每当对后端的请求失败时,应用程序应显示一个弹出窗口,指出发生了错误,并且需要显示该数据的屏幕为空。 这是一个更复杂的选项-您应该在连接可用时存储最新保存(或预先捆绑)的数据,并将其呈现给用户。 在这种情况下,您可以实现文件系统缓存,使用许多数据库选项之一,或者如果数据量很小,则可以摆脱“用户默认值”。 但是,您执行的任何需要在后端进行更改的操作(例如,我要离线添加产品)都是不可能的,并且会显示一个弹出窗口,您需要具有互联网连接才能执行此操作。 这是最复杂的选项。 它支持上面的只读模式,但是即使没有互联网连接,它也支持对数据进行更改。 为此,通常,您将需要在后端的数据库与移动电话上的本地存储或数据库之间实现同步机制。 同步机制需要进一步定义,因为其各种可能性会对项目的复杂性和成本产生不同的影响。 这里最复杂的部分是当移动应用程序和后端对同一组数据进行更改时会发生什么。 执行同步时,手机和服务器上的数据之间存在冲突。 有几种解决冲突的方法。 其中之一总是胜利 例如,可以说我们的冲突解决机制是从后端(或电话)获取最新更改并仅使用这些更改。 另一侧的更改将被丢弃。 这是最简单的选择,但它会导致丢失丢弃的数据,这对用户可能至关重要。 更好的选择是保留更改的时间戳。 当发生冲突时,我们将采用较新的更改并丢弃较旧的更改。 这种方法是对前一种方法的小升级,但是仍然会丢失数据。 两种方法中的快速优势就是要求用户选择他们想要保留的数据。 最复杂的方法是合并有冲突的数据,并以一种易于理解的方式将其呈现给用户,因此他们能够根据所做的更改查看要合并的数据。 它还应允许用户放弃合并并选择一侧或另一侧的更改。 类似于git或svn冲突解决方案。 如您所见,离线使用的复杂性从非常简单到非常复杂的实现不等,可能需要数周的开发时间。 很常见的误解。 如果该应用程序可以在iPad上运行,则应提前知道。 尽管每个负责任的开发人员都应使用自动布局,大小类和所有其他UI技术来实现灵活的布局,但这种要求需要事先知道,因为这可能会影响项目的组织和工作量。 另一个问题是iPad的屏幕要大得多,这意味着如果仅将iPhone应用程序缩放到iPad屏幕,则该应用程序的UI可能会非常空白。 要求iPad设计可以帮助计划和构建项目。 这同样适用于横向支持,在这种情况下,您始终应谨慎使用自动布局约束和对应用程序进行连续测试。 也应在项目开始时阐明受支持的最低iOS版本。 例如,请考虑以下情形:您需要开发增强现实应用程序。 您选择ARKit,开发所有内容,然后将要测试的应用程序发送给客户端。 但是,他们无法安装该应用程序。 他们的设备是装有iOS 10的iPhone 6。 您可以要求他们安装iOS 11,这已经引起了人们的注意,因为客户会急于检查市场上发行的OS版本。 iOS 10才使用了2年,因此缩小了该应用程序的市场规模。 当您最终说服他们安装iOS 11时,您意识到iPhone 6S及更高版本支持ARKit。 […]