我有以下形式创建一个新的“活动”:

对此,我希望强制执行以下要求:
我编写了以下代码:
// ActivityDetailViewController.swift
import UIKit
private struct Constants {
static let activityNameMaxCharacters = 50
static let activityDescriptionMaxCharacters = 200
}
final class ActivityDetailViewController: NiblessViewController {
/* ... */
// This method is called when user taps on the "Add" bar button item
// from the navigation bar.
@objc
func save(_ sender: UIBarButtonItem) {
do {
try validateInputs()
activity.name = nameField.text ?? ""
activity.activityDescription = descriptionTextView.text
presentingViewController?.dismiss(animated: true, completion: onDismiss)
} catch let error as ErrorMessage {
present(errorMessage: error)
} catch {
// NO-OP
}
}
/* ... */
// This methods applies the input validation rules.
private func validateInputs() throws {
guard let activityName = nameField.text else {
let errorMessage = ErrorMessage(
title: "Activity Creation Error",
message: "Activity name can't be empty."
)
throw errorMessage
}
let activityDescription = descriptionTextView.text ?? ""
if activityName.count > Constants.activityNameMaxCharacters {
let errorMessage = ErrorMessage(
title: "Activity Creation Error",
message: "Activity name exceeds max characters (50)."
)
throw errorMessage
} else if activityDescription.count > Constants.activityDescriptionMaxCharacters {
let errorMessage = ErrorMessage(
title: "Activity Creation Error",
message: "Activity description exceeds max characters (200)."
)
throw errorMessage
}
}
}一些支持代码:
// ErrorMessage.swift
import Foundation
public struct ErrorMessage: Error {
// MARK: - Properties
public let id: UUID
public let title: String
public let message: String
// MARK: - Methods
public init(title: String, message: String) {
self.id = UUID()
self.title = title
self.message = message
}
}
extension ErrorMessage: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.id == rhs.id
}
}// UIViewController+ErrorPresentation.swift
import UIKit
extension UIViewController {
public func present(errorMessage: ErrorMessage) {
let errorAlertController = UIAlertController(
title: errorMessage.title,
message: errorMessage.message,
preferredStyle: .alert
)
let okAction = UIAlertAction(title: "OK", style: .default)
errorAlertController.addAction(okAction)
present(errorAlertController, animated: true, completion: nil)
}
}对于如何改进当前的解决方案,我将不胜感激。
我不喜欢的东西:
catch方法中的try-catch子句末尾添加一个空的save(_:)。否则编译器会抱怨:“从这里抛出的错误不会被处理,因为包围捕获并不是详尽无遗的。”发布于 2022-02-06 15:21:56
可以使错误符合LocalizedError:
// MARK: - ActivityCreationError
enum ActivityCreationError: Error {
case empty
case nameTooLong
case descriptionTooLong
}
// MARK: LocalizedError conformance
extension ActivityCreationError: LocalizedError {
var errorDescription: String? {
switch self {
case .empty:
return NSLocalizedString("The name cannot be empty.", comment: "MyError.empty")
case .nameTooLong:
return String.localizedStringWithFormat(
NSLocalizedString("The name is too long (max %d characters).", comment: "MyError.nameTooLong"),
Constants.activityNameMaxCharacters
)
case .descriptionTooLong:
return String.localizedStringWithFormat(
NSLocalizedString("The description is too long (max %d characters).", comment: "MyError.descriptionTooLong"),
Constants.activityDescriptionMaxCharacters
)
}
}
}
// MARK: Public interface
extension ActivityCreationError {
static let title = NSLocalizedString("Activity Creation Error", comment: "MyError.title")
}请注意,在我的错误消息中,本着保持它干燥的精神,我避免在错误消息中再次对最大字符串长度进行硬编码,而是使用localizedStringWithFormat来提取验证过程中使用的相同常量。
然后您就可以捕获它并使用它的localizedDescription:
do {
try validateInputs()
...
} catch {
presentAlert(title: ActivityCreationError.title, message: error.localizedDescription)
}其中:
extension UIViewController {
public func presentAlert(title: String?, message: String?) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let string = NSLocalizedString("OK", comment: "Alert Button")
let action = UIAlertAction(title: string, style: .default)
alert.addAction(action)
present(alert, animated: true)
}
}顺便说一句,在确认输入和确认字符串长度之前,我建议对输入进行清理,以减少空白。你不想,例如,一个用户说:“哦,这个名字是必需的?好吧,我只要在那个领域里输入一个空间就可以绕过这一问题。“您可能希望删除空格,因为您不希望,例如,使用前导或尾随空格的输入。
例如,而不是:
let activityDescription = descriptionTextView.text ?? ""使用:
let activityDescription = descriptionTextView.text.?trimmingCharacters(in: .whitespacesAndNewlines) ?? ""如果您正在寻找更高级的顾问,您可能会采用更好的职责分离,并将验证逻辑完全从视图控制器中提取出来。您可以将其放入一些人称之为“视图模型”或“演示者”或简单的“控制器”中(不要与“视图控制器”混淆),这是一个独立于UIKit的对象,您可以在其中放置业务逻辑(例如验证规则、启动与数据存储或网络管理器的交互等)。
有关更多信息,请参见:
所有这些讨论的共同主题是避免将“视图”( UIKit控件的配置和交互)与业务规则混为一谈。
https://codereview.stackexchange.com/questions/273696
复制相似问题