В предыдущем уроке вы узнали, как определять общие ограничения в модели данных. В этом уроке я покажу вам, как вы можете определить более сложные ограничения в коде.
1. Настройка проекта
Загрузите проект, который мы создали в предыдущем уроке с 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)
проходит проверку для вставки в постоянное хранилище приложения.
2. Подклассы NSManagedObject
Чтобы проверить значение атрибута в коде, нам нужно создать подкласс NSManagedObject
. Выберите New> File … из меню File Xcode и выберите шаблон Core Data> NSManagedObject из списка шаблонов.
Выберите модель данных проекта и проверьте сущности, для которых вы хотите создать подкласс NSManagedObject
.
Установите флажок Использовать скалярные свойства для примитивных типов данных , сообщите XCode, где вы хотите сохранить файлы для подклассов, и нажмите « Создать» .
3. Проверка недвижимости
Чтобы проверить свойство объекта, вы реализуете метод в следующем формате: 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)
и запустите приложение в симуляторе. Если введенные вами значения недействительны, выдается ошибка.
4. Проверка объекта
Класс 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()
суперкласса вверху. Если запись пользователя привязана к одной или нескольким записям заметок, выдается ошибка, препятствующая удалению записи пользователя.
5. Какой вариант вы должны использовать?
Проверка данных является важным аспектом каждого приложения, которое работает с данными. Core Data предоставляет разработчикам несколько API-интерфейсов для реализации проверки данных. Но вам может быть интересно, какой вариант или опции использовать в вашем приложении.
Это зависит от ваших предпочтений и требований проекта. Для простой модели данных с общими ограничениями может быть достаточно вариантов, предлагаемых моделью данных. При этом некоторые разработчики предпочитают сохранять логику проверки в классах модели, то есть в подклассах NSManagedObject
. Преимущество состоит в том, что логика для определенного класса модели находится в одном месте.
Для более сложной логики проверки рекомендуются пользовательские методы проверки свойств или хуки validateForInsert()
, validateForUpdate()
и validateForDelete()
. Они добавляют мощность и гибкость к проверке объекта, и вы также получаете преимущество, заключающееся в том, что уровень модели включает логику проверки.
Важно понимать, что проверка данных состоит из двух аспектов: логики проверки данных и того, когда должна выполняться проверка данных. Слой модели отвечает за проверку данных. Контроллер отвечает за решение, когда следует проводить проверку данных, например, когда пользователь нажимает кнопку для создания учетной записи. Это тонкое, но важное отличие.
И последнее, но не менее важное: избегайте размещения логики проверки данных на уровне контроллера. Это излишне загромождает контроллеры вашего проекта и обычно приводит к дублированию кода.
Вывод
Базовые данные делают проверку данных простой и понятной. Модель данных помогает вам с общими ограничениями, но платформа также предоставляет несколько более продвинутых API для проверки пользовательских данных.