首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Swift/iOS/UIKit上进行输入验证的最佳方法

在Swift/iOS/UIKit上进行输入验证的最佳方法
EN

Code Review用户
提问于 2022-02-03 02:22:56
回答 1查看 270关注 0票数 1

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

对此,我希望强制执行以下要求:

  • 名称=> minimum # of chars: 1maximum # of chars: 50
  • 字符的描述=> maximum #:200

我编写了以下代码:

代码语言:javascript
复制
// 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
        }
    }
}

一些支持代码:

代码语言:javascript
复制
// 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
    }
}
代码语言:javascript
复制
// 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(_:)。否则编译器会抱怨:“从这里抛出的错误不会被处理,因为包围捕获并不是详尽无遗的。”
EN

回答 1

Code Review用户

回答已采纳

发布于 2022-02-06 15:21:56

可以使错误符合LocalizedError

代码语言:javascript
复制
// 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

代码语言:javascript
复制
do {
    try validateInputs()
    ...
} catch {
    presentAlert(title: ActivityCreationError.title, message: error.localizedDescription)
}

其中:

代码语言:javascript
复制
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)
    }
}

顺便说一句,在确认输入和确认字符串长度之前,我建议对输入进行清理,以减少空白。你不想,例如,一个用户说:“哦,这个名字是必需的?好吧,我只要在那个领域里输入一个空间就可以绕过这一问题。“您可能希望删除空格,因为您不希望,例如,使用前导或尾随空格的输入。

例如,而不是:

代码语言:javascript
复制
let activityDescription = descriptionTextView.text ?? ""

使用:

代码语言:javascript
复制
let activityDescription = descriptionTextView.text.?trimmingCharacters(in: .whitespacesAndNewlines) ?? ""

如果您正在寻找更高级的顾问,您可能会采用更好的职责分离,并将验证逻辑完全从视图控制器中提取出来。您可以将其放入一些人称之为“视图模型”或“演示者”或简单的“控制器”中(不要与“视图控制器”混淆),这是一个独立于UIKit的对象,您可以在其中放置业务逻辑(例如验证规则、启动与数据存储或网络管理器的交互等)。

有关更多信息,请参见:

  • 请参阅Delong的更好的MVC中包含MVC的观点,但是使用技术来保持它们的小规模和可管理性。
  • 有关从MVP、MVVM、Viper等到其他方法的回顾,请参见媒体的iOS体系结构模式
  • 关于这个主题的另一个很好的讨论,请参阅Krzysztof Zabłocki的ł。

所有这些讨论的共同主题是避免将“视图”( UIKit控件的配置和交互)与业务规则混为一谈。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/273696

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档