Статьи

Проверка данных с помощью основных данных: расширенные ограничения

В предыдущем уроке вы узнали, как определять общие ограничения в модели данных. В этом уроке я покажу вам, как вы можете определить более сложные ограничения в коде.

Загрузите проект, который мы создали в предыдущем уроке с GitHub, и откройте его в Xcode. Откройте AppDelegate.swift и обновите реализацию application(_:didFinishLaunchingWithOptions) как показано ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    if let entity = NSEntityDescription.entityForName(«User», inManagedObjectContext: self.managedObjectContext) {
        // Create Managed Object
        let user = NSManagedObject(entity: entity, insertIntoManagedObjectContext: self.managedObjectContext)
 
        // Populate Managed Object
        user.setValue(44, forKey: «age»)
        user.setValue(«Bart», forKey: «first»)
        user.setValue(«Jacobs», forKey: «last»)
        user.setValue(«[email protected]», forKey: «email»)
 
        do {
            try user.validateForInsert()
 
        } catch {
            let validationError = error as NSError
             
            print(validationError)
        }
         
    }
     
    return true
}

Если вы запускаете приложение в симуляторе или на физическом устройстве, никаких ошибок не должно быть. Другими словами, управляемый объект, который мы создаем в application(_:didFinishLaunchingWithOptions) проходит проверку для вставки в постоянное хранилище приложения.

Чтобы проверить значение атрибута в коде, нам нужно создать подкласс NSManagedObject . Выберите New> File … из меню File Xcode и выберите шаблон Core Data> NSManagedObject из списка шаблонов.

Подклассы NSManagedObject

Выберите модель данных проекта и проверьте сущности, для которых вы хотите создать подкласс NSManagedObject .

Подклассы NSManagedObject
Подклассы NSManagedObject

Установите флажок Использовать скалярные свойства для примитивных типов данных , сообщите XCode, где вы хотите сохранить файлы для подклассов, и нажмите « Создать» .

Подклассы NSManagedObject

Чтобы проверить свойство объекта, вы реализуете метод в следующем формате: validate<PROPERTY>(_:) . Метод должен быть методом метания. Если проверка не пройдена, выдается ошибка, уведомляющая Базовые данные о том, что значение свойства недопустимо.

В приведенном ниже примере я реализовал метод проверки для first свойства объекта « Пользователь» . Добавьте следующий фрагмент в User.swift .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import CoreData
import Foundation
 
class User: NSManagedObject {
 
    let errorDomain = «UserErrorDomain»
 
    enum UserErrorType: Int {
        case InvalidFirst
    }
 
    func validateFirst(value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
        var error: NSError?
 
        if let first = value.memory as?
            if first == «» {
                let errorType = UserErrorType.InvalidFirst
                error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : «The first name cannot be empty.» ] )
            }
 
        } else {
            let errorType = UserErrorType.InvalidFirst
            error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : «The first name cannot be blank.» ] )
        }
 
        if let error = error {
            throw error
        }
    }
 
}

Метод проверки принимает один параметр типа AutoreleasingUnsafeMutablePointer<AnyObject?> . Это что? Не позволяйте типу параметра вас пугать. Как указывает название типа, структура AutoreleasingUnsafeMutablePointer является изменяемым указателем. Это указывает на ссылку на объект. Мы можем получить доступ к значению, на которое указывает указатель, через его свойство memory .

Как я уже упоминал, метод validateFirst(_:) выбрасывает. Если переданное нам значение недопустимо, мы выдаем ошибку. Сгенерировав ошибку, мы сообщаем Core Data, что управляемый объект недействителен.

В следующем примере я реализую метод проверки для свойства email класса User . Мы используем регулярное выражение для проверки значения свойства email .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
func validateEmail(value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
    var error: NSError?
 
    if let email = value.memory as?
        let regex = «^.+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2}[A-Za-z]*$»
        let predicate = NSPredicate(format: «SELF MATCHES %@», regex)
 
        if !predicate.evaluateWithObject(email) {
            let errorType = UserErrorType.InvalidEmail
            error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : «The email address is invalid.» ] )
        }
 
    } else {
        let errorType = UserErrorType.InvalidEmail
        error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : «The email address is invalid.» ] )
    }
 
    if let error = error {
        throw error
    }
}

Даже если вы можете изменить значение свойства в методе проверки, Apple настоятельно не рекомендует это. Если вы измените значение, переданное вам в методе валидации, управление памятью может оказаться бесполезным. Имея это в виду, процесс проверки данных становится очень простым. Проверьте значение свойства и выведите ошибку, если оно недопустимо. Это так просто.

Измените значения атрибутов в application(_:didFinishLaunchingWithOptions) и запустите приложение в симуляторе. Если введенные вами значения недействительны, выдается ошибка.

Класс NSManagedObject предоставляет три дополнительных подкласса ловушек, которые можно переопределить для проверки данных:

  • validateForInsert()
  • validateForUpdate()
  • validateForDelete()

Эти методы вызываются Core Data на управляемом объекте перед вставкой, обновлением или удалением соответствующей записи в постоянном хранилище. Каждый из этих методов является броском. Если выдается ошибка, соответствующая вставка, обновление или удаление отменяется.

Несмотря на то, что преимущество, которое эти хуки имеют по сравнению с методами валидации свойств, которые мы обсуждали ранее, может быть не сразу очевидным, они неоценимы в нескольких сценариях. Представьте, что запись пользователя не может быть удалена, если с ней связана одна или несколько записей заметок. В этом случае вы можете переопределить метод validateForDelete() в классе User .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
override func validateForDelete() throws {
    try super.validateForDelete()
 
    var error: NSError?
 
    if let notes = notes {
        if notes.count > 0 {
            let errorType = UserErrorType.OneOrMoreNotes
            error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : «A user with notes cannot be deleted..» ] )
        }
    }
 
    if let error = error {
        throw error
    }
}

Обратите внимание, что мы используем ключевое слово override и вызываем реализацию validateForDelete() суперкласса вверху. Если запись пользователя привязана к одной или нескольким записям заметок, выдается ошибка, препятствующая удалению записи пользователя.

Проверка данных является важным аспектом каждого приложения, которое работает с данными. Core Data предоставляет разработчикам несколько API-интерфейсов для реализации проверки данных. Но вам может быть интересно, какой вариант или опции использовать в вашем приложении.

Это зависит от ваших предпочтений и требований проекта. Для простой модели данных с общими ограничениями может быть достаточно вариантов, предлагаемых моделью данных. При этом некоторые разработчики предпочитают сохранять логику проверки в классах модели, то есть в подклассах NSManagedObject . Преимущество состоит в том, что логика для определенного класса модели находится в одном месте.

Для более сложной логики проверки рекомендуются пользовательские методы проверки свойств или хуки validateForInsert() , validateForUpdate() и validateForDelete() . Они добавляют мощность и гибкость к проверке объекта, и вы также получаете преимущество, заключающееся в том, что уровень модели включает логику проверки.

Важно понимать, что проверка данных состоит из двух аспектов: логики проверки данных и того, когда должна выполняться проверка данных. Слой модели отвечает за проверку данных. Контроллер отвечает за решение, когда следует проводить проверку данных, например, когда пользователь нажимает кнопку для создания учетной записи. Это тонкое, но важное отличие.

И последнее, но не менее важное: избегайте размещения логики проверки данных на уровне контроллера. Это излишне загромождает контроллеры вашего проекта и обычно приводит к дублированию кода.

Базовые данные делают проверку данных простой и понятной. Модель данных помогает вам с общими ограничениями, но платформа также предоставляет несколько более продвинутых API для проверки пользовательских данных.