Tag: 错误处理

为什么您的iPhone应用程序(和网站)必须具有错误监控系统

那么什么是错误监视系统? 您可能熟悉崩溃报告,当应用崩溃时,崩溃报告会向您发送所有详细信息,以便您快速解决问题。 好吧,错误监视非常相似。 每当您的应用中发生错误时,它都会向您发送报告,而不是向您发送崩溃报告。 重要的是,让我告诉您我的一位客户。 雇用我的对话非常迅速: 首席执行官:“我有一个使用Parse的iPhone应用程序。 自从我们迁移到Parse Server以来,Facebook登录不起作用。” 我:“好吧,我最近使用解析处理了一个应用程序,因此我可能会有所帮助。 您知道您遇到什么样的错误吗?” 首席执行官:“我们的用户遍布Facebook,App Store和Twitter,因为他们无法登录。 他们不断回到签名屏幕。” 我:“好吧,您还知道其他吗?” 首席执行官:“不是,我们的CTO几个月前就离开了,我现在有一个新的CTO,但是我希望他专注于即将发布的版本。 您认为这可能需要多长时间?” 我:“不知道。 您不知道会遇到什么样的错误,因此首先我将不得不熟悉您的代码,然后找出正在发生的事情,然后找出如何解决它……这不会很便宜。” 首席执行官:“什么时候可以开始?” 我:“明天” 首席执行官:“您被录用了……” 好的,这些不是引号,关于钱的话题也很简短,但是您知道了。 因此,第二天我参加了会议。我再次尝试从首席执行官那里获得有关该问题的更多详细信息,但是我什么也没得到。 我所获得的只是访问该应用程序所有内容的凭据(github帐户,AWS帐户,crashlytics帐户等),然后我就离开了。 我先查看应用程序的代码,看看是否发现明显的问题。 您可以听到我的时钟在后台滴答作响。 然后,由于我在iPhone应用程序中没有任何运气,我跳上服务器查看日志,看看是否有任何事情发生在我身上。 仍然没有运气,我的时钟还在滴答作响。 然后,我查看了服务器代码,更多的滴答声仍然没有运气。 在这一点上, 我在该项目上花费了大约8到10个小时,但仍未接近解决方案 。 因此,我愿意做我所有项目的第一天的工作:在应用程序中安装 Rollbar (或类似的应用程序日志记录系统),以便我们可以看到用户遇到的错误,在登录流程中尽可能多地定位并将该应用发送给他的TestFlight用户。 然后等待,看看我们又得到了什么错误。 肯定在一个小时内 ,我们又得到了一些错误,并返回了准确的错误号,该错误号是Parse Server抛出的错误。 再次 , 我们终于知道我们的用户正在经历什么 。 现在请记住,这并不意味着问题已解决,这仅意味着我们知道问题所在。 因此,CEO不得不给我大约10/15个小时的报酬, 以了解我们面临的问题 。 因为他的原始团队没有养成记录那些您不想在MVP中处理的错误的习惯,所以您可以专注于快速迭代。 我对MVP的策略如下: 创建每个人都会使用的代码 仅处理您知道会发生的错误情况并检测所有错误情况(无论大小如何) 稍后,如果我发现很多用户遇到了问题,则可以编写代码来修复它 每个错误情况的检测时间约为20秒。 如果您做数学运算,那么我花了10个小时来弄清楚问题是什么,就算是检测1800个错误…… […]

自上而下的iOS错误架构

关于iOS生态系统中的错误处理的文章并不多,即使对于每个应用程序来说,这似乎都不是简单而实质的组成部分。 今天,我希望解决这个问题,并提出我的解决方案,以减少iOS应用程序中的破坏性错误处理。 初始状态 为了确保我们在同一个页面上,通过错误我将表示一个符合Swift的Error类型的实例,并且错误处理是对此的一种反应。 这些错误可能以同步方式(磁盘操作,无效参数)或异步方式 (没有Internet连接,会话令牌无效等)到达。 显然,由于Swift的标准do – catch结构,第一组更容易处理。 通常,简单的自下而上的过程就足够了,其中错误会中断执行并自动将其传递给调用方。 如果您具有带有完成处理程序的异步API,则开发人员可以自由选择众多技术(通常也遵循自下而上的模型)(提及Promise或RX流)。 通常,自下而上意味着如果我们无法完全处理错误,则会将错误传播到父层。 我发现,在许多情况下,自下而上的方法不是最佳方法,当错误必须遍历许多层以达到能够消耗它的目的时,就会导致沮丧。 举一个例子,假设APIClient收到401 Unauthorised HTTP响应,并且您的应用应返回到初始登录屏幕。 您是否熟悉ViewModel中的完成处理程序是否能够处理特定错误,是否不将其传递给其Flow Coordinator,然后将其传递给父级Flow Coordinator以最终到达通用位置,是否听起来很熟悉?您指示导航控制器移回根目录。 它最终以一个代码打开,该代码可以广泛地打开错误,例如: 如果您为不同元素之间的通信选择了委托机制,则委托协议可能会开始膨胀-每个协调器都应包括其嵌套子代的错误事件。 我观察到,在许多应用程序中,树的上层只能处理某些特定错误(例如会话无效或不受支持的API版本),并且在下面是一个聚集点,所有错误均以警报视图或类似形式呈现给UI。 )。 因此,我建议不要从底层开始,而是还原传播顺序,并从顶层开始并仅在父层无法处理时将其向下传递。 请注意,在根元素中,我们获得了应用程序中所有错误的单个聚集点,这对于分析目的可能是有用的。 因此,让我们将错误处理过程分为两个阶段: 阶段1:将事件传播到其父对象(无条件) 阶段2:逐层向下传播事件,找到可以处理事件的事件 这样的过程并不是一个全新的想法,例如JavaScript使用了一种非常类似的技术,称为冒泡事件委托,将触摸事件分配给DOM元素。 协议定义 实现自上而下的错误处理体系结构并不是一项艰巨的任务。 让我向您展示,您可以使用面向扩展开放协议的面向通用协议的解决方案轻松地在50行代码中实现它。 出于本文的目的,我将使用受Swift关键字列表( catch , throw , finally )启发的函数名称。 尽管你们中的一些人可能会觉得有点争议,但我相信这样的类比有助于理解它。 我们将定义唯一的协议ErrorHandleable ,它将用作错误处理树中的节点。 每个实例都保留对第1阶段传播中使用的父层的引用,并封装第2阶段中使用的通用闭包( HandleAction ),该闭包决定是否应将其填充(引发错误)。 此外,没有什么可以阻止在操作块中重写错误。 只需抛出一个全新的错误(例如在errorHandler#1中),所有下面的层将观察到已修改的错误。 但是,请记住,强大的力量伴随着巨大的责任,请谨慎使用此功能… 该协议API非常通用,因此我们可以添加一些扩展以方便使用: 约束将错误捕获为通用类型并透明地传递所有其他类型:

更好的表格视图数据源:捕获和传达状态

关于本机应用程序中更健壮的表和集合视图的实用指南 我构建的几乎每个本机UI都植根于表或集合视图。 我已经写了关于 表视图及其数据源的方法 ,这是高级入门。 这篇文章概述了开发人员编写代码时面临的常见问题; 倾向于将边缘案例处理推迟到以后的日期,这永远不会发生。 我关注的主要情况是加载,加载错误和无数据,但是我下面介绍的方法可以满足您的任何需要。 并非所有人都忽略了这些极端情况,但是我注意到,如果对它们进行处理,它们通常是在表视图之前处理并通过UI的生命周期进行管理(例如,当前视图控制器,启动加载,显示旋转磁盘,从DA返回,隐藏旋转磁盘,重新加载表)。 我更喜欢将加载/错误/无数据UI直接合并到表视图本身中,更重要的是,我希望数据访问和状态捕获独立于UI及其生命周期。 当您具有由依赖于可能处于不同加载状态的不同数据源的节组成的表视图时,这将带来很多好处。 当数据可用时(逐节)呈现数据通常很重要,而不是等待所有数据全部可用。 而且只有通过内联生命周期通信才有可能。 表格视图是数据访问失败和错误无声消失的地方 场景 :您出差在外的3G连接糟糕,当您意识到应用程序远离加利福尼亚州的高速LTE连接时感觉如何破损时,您会感到羞愧和厌恶。 问题 :通信基于网络的数据加载并启用故障重试对于专业构建的应用程序和UI至关重要。 请记住,这是一个面临连接挑战的移动平台。 如果您的表视图中充满了// TODO:handle error ,则可以尝试以下方法来改善它们 方法 :在数据源中创建数据类型的标准枚举,并让表视图处理每个枚举案例。 这是我的: 您的大多数数据块的类型都是“自定义”,但其他数据块则值得 当数据源通过从模型层读取数据进行组装时,它还可以向您的数据访问对象询问任何相关暂挂请求的状态。 当前正在加载东西吗? 有错误吗? 这是什么错误? 如果数据源对这些问题有任何有趣的答案,则可以使用相应的类型枚举值将数据块组装并附加到其自己的内部数据哈希中。 假设模型层为空,数据访问对象说请求仍在进行中。 数据源可以在加载块或加载错误块后附加相关的错误对象。 您会注意到,我对数据访问服务对象使用了类似的枚举,以便在状态枚举的上下文中轻松地传达状态 当向数据源请求给定索引(路径)处的数据块时,表格视图便能够快速切换所有可能的块类型,并做出相应的响应。 通过在应用程序范围内设置一些简单的加载和重试单元格,并将其注册到表格视图中,处理这些状态便成为样板代码。 也有机会自定义每种情况,但至关重要的是,它提供了一种快速一致的方式来传达这些经常被忽略的状态:加载,加载错误(具有重试能力)和无数据。 视图还可以选择完全忽略这些枚举值,并且数据源(例如,如果它不依赖于加载)可以明确表示从不使用它们。 我的视图控制器严重依赖数据管理器及其返回的块的类型 我构建的标准LoadError单元需要使用Load Error Retry Delegate进行初始化,然后视图控制器将其分派回数据管理器。 您可以看到如何将我们在cellForRowAtIndexPath中使用的相同开关逻辑应用于所有其他表视图委托和数据源协议实现方法。 在viewDidLoad中的单元格注册可实现对每个状态的良好,标准,简单的处理,它位于util中,如下所示: 将简单的可重用生命周期单元格注册到任何表视图 结论 我到处都使用这种模式。 它的维护成本低,这意味着我可以将开发工作重点放在我们要在要构建的表中呈现给用户的产品和UI上。 但这使我充满信心,在连接不畅或数据访问有问题的情况下,我正在构建的应用程序的用户将知道正在发生的事情(正在加载),并有权采取行动(重试错误) 。 永远不会出现这样的情况:他们不得不强制关闭应用程序以重试失败的数据访问,或者来回导航以尝试重新启动某些数据访问。 注意:不要在块类型切换语句中使用默认处理程序。 如果添加新的枚举值,则希望编译器向我提醒我需要添加对新状态的支持的位置,它可以帮助您建立对使用这些数据块的所有方式的信心。 […]

Swift 3中的错误处理:尝试,尝试?和尝试!

某些人可能已经熟悉使用关键字try来处理Swift中的错误。 对于那些不知道try是什么的人, try只是一种处理任何给定函数中可能出现的错误的方法。 通常,我们具有“抛出”功能,这意味着它们具有抛出错误的能力,而try是一个简洁的关键字,它使我们能够处理那些错误的可能性。 在构建我们的应用程序时,可以使用三种try形式。 尝试 尝试? 尝试! 如果您对这些变体感到恐惧,那就不要! 而且,如果您不确定要使用哪一个,请不要担心,我已为您覆盖。 正如我之前提到的, try通常与throw函数结合使用,因此我们将讨论一下throw函数,并在实际中使用try关键字。 假设我们正在尝试通过一些JSON进行解析,以便可以将这些数据带入我们的应用程序。 此数据可能由于互联网中断而无法到达我们的应用程序,或者您尝试提取的数据不再存在! 这些都是在我们尝试从API检索数据时可能出现错误的情况。 但是Swift的JSONSerialization方法具有内置函数,默认情况下throws 。 Swift知道我们检索数据的尝试可能会带来一些错误,并创建了一个函数,该函数使我们能够在发生错误的情况下解决该情况。 使用try ,我们的throw函数必须包装在do-catch语句中。 这使我们能够自定义错误处理并根据错误执行特定的操作。 下面我们将使用try来优雅地捕获任何错误。 做{ 让responseJSON =试试JSONSerialization.jsonObject(with:data,options:[])as! [[String:Any]] 完成(responseJSON) } { 打印(“嗯,这里出了点问题。请尝试连接到wifi。”) } 作为开发人员,将try与do-catch语句一起使用可使我们检查有意义的错误,以样式处理它们,并避免用户感到沮丧。 考虑一下,我们是否真的希望我们的应用程序在用户没有任何互联网连接时崩溃,还是我们要让他们知道该应用程序一旦连接到wifi就会继续运行? 在这种情况下,通知会更有帮助,而我们的do-catch语句使我们可以做到这一点。 另一方面,我们也可以使用try?处理错误try? 。 如果您有使用可选控件的任何经验,那么您很快就会意识到该try? 就是这样。 try? 让我们忽略我们的错误,并在我们的函数将其抛出时让它们变为nil。 因此,我们不必将代码包装在do-catch语句中。 但是,如果要分配try?我们是否必须解开变量try? 有价值。 让responseJSON =试试吗? JSONSerialization.jsonObject(with:data,options:[])as! [[String:Any]] 如果让responseJSON = responseJSON { print(“是的!我们刚刚包装了responseJSON!” } 上面的代码为responseJSON分配了一个可选值,并在调用的函数引发任何错误时将其设置为nil。 使用try? […]

小错误处理简介

我们在编写代码时都已经看到错误和警告,它们肯定会很痛苦,但要感谢它们在那里。 如果不存在,我们的应用程序将在用户面前崩溃,并且该应用程序肯定会在应用程序商店中获得不良评价。 没有人想要! 成为一名优秀的开发人员意味着您不仅可以编写代码,而且可以编写正确的无错误代码。 即使不是不可能,编写完全没有错误的代码也是非常困难的,但是我们可以采取步骤确保我们的代码包含尽可能少的错误。 此过程称为错误处理。 那么什么是错误? 错误是任何会产生不正确或意外结果或导致程序无人照管的行为的代码。 有两种不同类型的错误。 编译器错误和运行时错误。 编译器错误是快速编译器在解析或读取代码时引发的错误。 编译器错误也可以分解为域错误,但并非总是如此。 有些错误是编译器无法捕获的,只有在您击中特定代码行时才会发生。 这些是运行时错误。 运行时错误可以分解为两个。 第一个是可恢复的错误,第二个是不可恢复的错误。 根据是哪个错误,我们可以对如何处理错误做出不同的反应。 不可恢复的错误可能是由程序员错误引起的逻辑错误,也可能是我们无法预料到发生的普遍错误。 因为这两种情况都无法防范,我们通常会在这些情况下崩溃并以不同的方式处理它们。 我们将重点放在可以恢复的运行时错误上。 错误处理到底是什么? 苹果必须说的是: 错误处理是响应程序错误状态并从错误状态中恢复的过程。 从Apple文档中可以找到四种处理错误的方法。 您可以断言该错误不会发生。 您可以将错误作为可选处理。 或者,您可以使用throw在函数中传播错误。 您可以使用do-catch语句。 我们已经看到了其中的一些,但我将深入探讨最后两个。 首先,我们可以通过采用错误协议来对错误建模。 错误协议是一个空协议,但是它指示可以将类型用于错误处理。 枚举通常用于对我们的错误可能进行建模,因为我们可以在枚举中使用关联的值,这使我们更具描述性 现在,我们对错误进行了建模,可以编写函数了。 为了表明该函数可能引发错误,我们在函数本身中使用了关键字throws。 如您所见,throws关键字添加在function /方法参数之后和返回类型之前。 如果没有返回类型,则可以使用关键字throws。 现在在函数内部,我们必须使用关键字throw。 throw关键字是控制转移语句,它退出当前作用域,并且必须始终接受一个参数。 既然我们已经完成了该函数的编写,我们可以调用它,但是由于它是一个抛出函数,因此需要尝试实现。 我们在这里使用do – catch语句。 如果do子句中的代码引发错误,则将其与catch子句进行匹配,以确定其中哪一个可以处理该错误。 Do子句实质上会创建所谓的幸福之路,以确保一切成功。 这是错误处理的非常简短的介绍。 那里有很多不错的文章,但会更深入,但是我建议使用Apple文档。 编码愉快!

Swift的用户友好型错误消息

由Max Howell 无论您的代码多么出色,都会发生错误。 因此,良好的错误处理和消息传递对于完善的用户体验和使您的用户感到沮丧的应用程序至关重要。 NSError始终具有非常有用(且已翻译)的localizedDescription方法,该方法为您提供了有用的信息以呈现给用户。 如果错误严重困扰他们的需求,他们可以向您发送电子邮件,也可以将其发送给Google,或者自己弄清楚他们需要做什么。 那是因为可可错误。 苹果花时间使这些消息有用。 但是对于我们自己的错误,至少在Objective-C时代,很少有人愿意做Right Thing™: -(void)foo错误:(NSError **)错误{ 如果(badThing){*错误= [NSError errorWithDomain:@“ TODOland”代码:1 userInfo:nil]; 返回} // … } Swift坚持要通过try对您的应用进行更强大的编码,并具有真正出色的基于enum的自定义错误,因此我们可以轻松,准确地指定错误条件: 枚举AuthenticationError { 大小写无效密码 案例鼠标无法加入我们的服务 } 专家提示:您可以在函数本地编写错误枚举。 这通常是获得良好错误消息的最佳方法,而不必考虑错误对于全局范围的含义。 不幸的是,这些错误的localizedDescription通常为“操作无法完成”: 这违反了我们的介绍:向用户提供了信息丰富且有用的错误消息。 因此,Codebase对Error产生了以下extension : 因此, AuthenticationError.miceCannotJoinOurService的legibleDescription描述为: “AuthenticationError.miceCannotJoinOurService” 。 显然,一个好的localizedDescription会更好。 为此,使您的Error符合LocalizedError并实现errorDescription 。 但是让我们面对现实吧,现在没有时间这样做,上面的代码示例是一个很好的权宜之计。 上面的代码段经过了实战测试,并且经过了严格的测试,更加努力: 错误是来自Cocoa还是Swift枚举的NSError ? 弄清楚这一点是非常困难的:Swift在两个对象之间“有帮助地”架起了桥梁:( as? NSError和as? Error 总是正确的。我们还尝试了ObjC的isKindOfClass似乎可以工作一段时间,但是随后发现了Swift Error实例。 s变成了我们背后的NSError s,因此我们选择了上面的检查,这不是最安全的方法(长期使用的类名可能会更改),但是我们无法立即找到更好的方法,它也可以100%起作用。 如果存在根本错误,并且根本错误消息不是这样,则获取该潜在错误:“该操作无法完成。” CloudKit错误通常掩埋在潜在错误中,因此您(没有我们的代码段!)实际显示的错误是不友好。 将我们的legibleDescription代码与以下函数配对,无论屏幕上发生什么情况,该函数都会尝试显示警报控制器。 […]

Swift:错误处理

条件ต่างๆมากมายซึ่งแน่นอนในแอปพลิเคชั่นเราก็จะเต็มไปด้วย否则声明จุดประสงค์ของหัวข้อนี้จะกล่าวถึงการใช้错误处理ช่วย นการลดความซ้ำซ้อนและแยกส่วนLogicกับส่วนแสดงผล ยกตัวอย่างเงื่อนไขดังนี้ รัฐบาลกำลังจะปล่อยเงินกู้ดอกเบี้ยต่ำพิเศษให้แก่ประชาชนโดยมีเงื่อนไข 1ผู้กู้ต้องมีรายได้ขั้นต่ำ15,000บาทต่อปี 2ผู้กู้ต้องมีรายได้ไม่เกิน10,000,000ต่อปี 3มี消息แจ้งเตือน用户เมื่อไม่ผ่านเกณฑ์ที่กำหนด 5,000ยหากมีรายได้น้อยกว่า15,000ให้แจ้งข้อความว่าขออภัยรายได้ท่านน้อยกว่าเกณฑ์ที่กำหนด ,000,000ยหากมีรายได้มากกว่า10,000,000ต่อปีให้แจ้งข้อความว่า“ขออภัยรายได้ท่านมากกว่าเกณฑ์ที่กำหนด ซึ่งหากเราเขียน功能ขึ้นมาตรวจสอบช่วงของรายได้ก็จะมีหน้าตาประมาณนี้ 需求逻辑功能验证需求警报功能 handlingนี้เรามาลองใช้错误处理เข้ามาช่วยเพื่อแยกส่วน验证ออกมา 1ออกแบบรูปแบบของErrorที่ต้องการ 2สร้าง逻辑功能 3เรียกใช้功能 สิ่งที่เปลี่ยนไปคือ 1ส่วนของLogicและส่วนของ警报消息แยกส่วนกันเป็นที่เรียบร้อยแล้ว 2收入水平验证收入范围收入范围收入

Swift的错误处理

Seringkali,ketika ada error di sebuah aplikasi,ada 2 kemungkinan yang terjadi。 Pertama,崩溃tanpa permisi,lalu yang kedua aplikasi tidak崩溃,dan muncul pemberitahuan ke pengguna bahwa aplikasi sedang bermasalah。 Tentunya,北蒂达克(Kita Tidak Ingin)Kemungkinan Yang Pertama itu Terjadi kan吗? Penyebab Aplikasi撞车 Di dunia iOS开发,penyebab崩溃seringkali disebabkan oleh开发人员yang menulis kode程序yang tidak 安全 ,misalkan 强制转换,强制展开可选值, tidak sengaja membuat 循环依赖项 antar objek yang keduanya sama-sama memiliki […]

从Swift 2的ErrorType获得更好的NSErrors

自10.2.7版以来,可可就包含了NSError 。 这是在Cocoa应用程序中表达和返回错误的标准方法。 NSError不仅仅是简单的错误处理代码可以使用的对象。 它具有许多属性和相关的API,尤其是在macOS上,使其非常适合直接呈现给用户。 自从Swift 2引入以来,就可以抛出 Swift ErrorType实例,并使它们自动桥接到NSError。 不幸的是,这种桥接行为相当有限。 桥接到Objective-C API的ErrorType实例非常简单。 他们缺乏本地化的描述,故障原因,恢复建议,帮助锚等。 如果您仅开发移动应用程序,这似乎并不那么糟糕。 大多数iOS应用程序不使用错误恢复或帮助锚等功能。 如果像我们一样开发macOS应用程序,那就更成问题了。 macOS内置了使用NSResponder或NSAlert的initWithError:上的-presentError :呈现丰富错误警报的支持。 如果提供的NSError实例具有完全填充的userInfo字典,则将大大改善用户体验。 例如: 相对于: 尽管iOS并没有像macOS那样立即提供给用户呈现NSError的功能,但是您可以使用UIAlertController或一些其他自定义的瞬态或内联警报,轻松在自己的应用中为NSError呈现创建类似的系统。 我们已经在为客户开发的iOS应用程序中做到了这一点。 未来是光明的 Swift Evolution提案SE-0112描述了Swift的未来版本将如何改善Error ( 错误类型的新名称)与NSError的桥接。 该提案解决了当前桥接行为的所有局限性。 但是目前,我们必须自生自灭 但是,与此同时,我们陷入了Swift 2的困扰– Xcode 8和Swift 3的初始beta版本没有包含SE-0112的实现,但是最近它已经实现(即,Xcode 8 beta 5包含了新的Error协议)。 下面,我提供了一种扩展ErrorType的简单机制,以将更丰富的NSError行为带入您的iOS和macOS Swift 2应用程序。 NSErrorUserInfoValueProviding Itty Bitty Apps 总部位于澳大利亚墨尔本, 为大小客户提供了出色的移动和Mac软件。 这也使 揭示   -适用于iOS开发人员的功能强大的运行时视图调试。

Swift中的错误处理

错误处理是响应程序中的错误情况并从中恢复的过程。 Swift支持在运行时引发 , 捕获 , 传播和操作可恢复错误的方法。 在Swift中处理错误的四种方法是: 1.使用throwing函数传播错误:在函数的参数后的声明中使用关键字throw表示函数,方法或初始化程序可以引发错误。 标有throw的函数称为throwing函数 。 enum errorType: Error { case errorOne } var errorOne: Bool = true func throwingFunction() throws{ if errorOne { throw errorType.errorOne } } do { try throwingFunction() } 2. 使用do-catch语句处理错误: do-catch语句用于通过运行代码块来处理错误。 如果do子句中的代码引发错误,则将其与catch子句匹配,以确定其中哪个子句可以处理该错误。 do { try throwingFunction () } catch errorType.errorOne { print(“Invalid Value”) } 3. 将错误作为 […]