将Swift编译时安全变成用户安全

发生了什么?

从技术上讲,今天没有任何事情可以阻止您作为开发人员在未经用户确认的情况下无意或无意进行不可逆的更改(例如删除用户数据)。 当然,我们尝试尽可能地减轻这种风险,在各处编写UIAlertController代码(或者为此目的甚至制作基于可爱的便捷闭包的函数),但是我们编写的API并没有采取任何措施来防止这种情况的发生。

例如,假设我们有一个类Images ,它直接注入到我们的视图控制器中

 最终课程图片{ 

私人var图片:[UIImage]

func图片(位于索引处:Int)-> UIImage {
返回图片[索引]
}

func add(_ image:UIImage){
images.append(image)
}

func delete(imageAt index:Int){
images.remove(at:index)
}

}

当用户点击“删除”按钮时,我们可以轻松地编写以下代码:

  @objc func didPressDeleteButton(sender:UIButton){ 
images.delete(imageAt:currentImageIndex)
}

没有编译时错误(很明显),没有警告,什么都没有-因此,我们只是删除了一部分用户数据而没有任何确认。 这是不好的。

常规的解决方案是什么?

大多数时候,开发人员会尝试在视图层解决此问题,也就是在didPressDeleteButton UIAlertController那里创建并显示didPressDeleteButton 。 当然,这通常可以解决问题,但是它有明显的缺点:

  • 开发人员可以忘记在某些地方编写此“安全措施”代码。
  • 尽管如此,开发人员仍然可以不小心删除图像,而不是在视图层,而是在业务逻辑中的某个位置删除图像,而无需三思而后行。

那么什么是“正确”的解决方案?

我们真正想要的是我们的Images类,根本不允许未经用户确认而执行删除操作。 为了帮助我们实现该功能,让我们创建一个UserConfirmationRequired结构:

  struct UserConfirmationRequired { 

私人让performDestructiveAction:()->()

init(destructiveAction:@escaping()->()){
self.performDestructiveAction =破坏性动作
}

func performWithUserConfirmation(alertTitle:字符串,alertMessage:字符串,alertDestructiveActionTitle:字符串,完成:@转义(Bool)->()){

//检索视图控制器以显示警报
 保护让窗口= UIApplication.shared.delegate?.window else { 
打印(“无窗口”)
完成(false)
返回
}
后卫让viewController = window?.rootViewController else {
打印(“无视图控制器”)
完成(false)
返回
}

//创建并显示警报
 让警报= UIAlertController(标题:alertTitle,消息:alertMessage,preferredStyle:.actionSheet) 

让cancel = UIAlertAction(标题:“取消”,样式:.cancel,处理程序:{_ incomplete(false)})

let destructive = UIAlertAction(title:alertDestructiveActionTitle,style:.destructive,handler:{_ in
self.performDestructiveAction()
完成度(true)
})

alert.addAction(取消)
alert.addAction(破坏性的)
viewController.present(警告,动画:true)
}

}

ew,这里发生了很多事情。 因此, UserConfirmationRequired基本上是() -> ()闭包的包装,这使得无法在没有先获得用户确认的情况下执行操作。 我们从UIApplication.shared获取视图控制器的部分看起来有点单调,但这在这里确实有意义。

然后,让我们修改我们的Images类:

  func deleteAction(ofImageAt索引:Int)-> UserConfirmationRequired { 
return UserConfirmationRequired(destructiveAction:{
self.images.remove(at:index)
})
}

我们返回UserConfirmationRequired实例,而不是立即删除图像,因此调用者将需要获得用户确认才能执行破坏性操作。

现在,如果我们尝试像以前那样写,就会收到警告: