【徒然DB】気ままにUIKit-CoreData編コラム〜ちょっと気になってたのでConstraintsを検証してみた〜
概要
までで、CoreDataにConstraintsを付けた場合、FatalErrorが原因なので、
調べてみた。。。
⒈Use Core Dataチェックなしで、データモデルを追加する場合
//
// AppDelegate.swift
// TestNotUseCoreData
//
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
}
てな感じで、AppDelegateファイルにCoreData関連のメソッドがない👀
👉まあ、これはここまでの記事で想定の範囲内🕺
のまとめコードをそのまま嵌め込み
そこで、Use Core Dataにチェックを入れて作った新規プロジェクト
と、AppDelegate.swiftファイルのコードを比較すると、、、
//
// AppDelegate.swift
// TestNotUseCoreData
//
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
}
//
// AppDelegate.swift
// TestCoreData
//
import UIKit
import CoreData
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "TestCoreData")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
とCoreData周りのコードが初期から追加されてる👀
じゃあ、
SceneDelegate.swift周りはどうなの?
ってことになると思うので、こちらも比較
//
// SceneDelegate.swift
// TestNotUseCoreData
//
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
}
func sceneDidBecomeActive(_ scene: UIScene) {
}
func sceneWillResignActive(_ scene: UIScene) {
}
func sceneWillEnterForeground(_ scene: UIScene) {
}
func sceneDidEnterBackground(_ scene: UIScene) {
}
}
//
// SceneDelegate.swift
// TestCoreData
//
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
}
func sceneDidBecomeActive(_ scene: UIScene) {
}
func sceneWillResignActive(_ scene: UIScene) {
}
func sceneWillEnterForeground(_ scene: UIScene) {
}
func sceneDidEnterBackground(_ scene: UIScene) {
(UIApplication.shared.delegate as? AppDelegate)?.saveContext()
}
}
とほぼ、全く同一ってことに気づくよね👀
なので、
コンテナ名が、
let container = NSPersistentCloudKitContainer(name: "TestCoreData")
のままになっているので、
let container = NSPersistentCloudKitContainer(name: "Model")
て感じで、該当するモデル名に変更して、
プロジェクトファイル自体を開き直すと、
⒉Use Core Dataを最初からチェックした場合
ちなみに、、、
で動かなかったコードを追加して動くか検証
今回のコード(Constraints)
class IndexesCoreDataViewController: UIViewController, UITextFieldDelegate{
@IBOutlet weak var myTextField: UITextField!
@IBOutlet weak var myLabel: UILabel!
//管理オブジェクトコンテキスト
var managedContext:NSManagedObjectContext!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
do {
//管理オブジェクトコンテキストを取得する。
let applicationDelegate = UIApplication.shared.delegate as! AppDelegate
managedContext = applicationDelegate.persistentContainer.viewContext
//管理オブジェクトコンテキストからPlayerエンティティを取得する。
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Player")
let result = try managedContext.fetch(fetchRequest) as! [Player]
//すべてのPlayerエンティティの名前をラベルに表示する。
for data in result {
myLabel.text = myLabel.text! + "," + data.name!
}
//デリゲート先に自分を設定する。
myTextField.delegate = self
} catch {
//エラーメッセージをアラートで表示する。
let alert = UIAlertController(
title: "エラー",
message: String(describing: error),
preferredStyle: UIAlertController.Style.alert
)
self.present(alert, animated:true, completion:nil)
}
}
//Returnキー押下時の呼び出しメソッド
func textFieldShouldReturn(_ textField:UITextField) -> Bool {
do {
//ラベルの値にテキストフィールドの値を追記する。
myLabel.text = myLabel.text! + "," + myTextField.text!
//新しいPlayerエンティティを管理オブジェクトコンテキストに格納する。
let player = NSEntityDescription.insertNewObject(forEntityName: "Player", into: managedContext) as! Player
//Playerエンティティの名前にテキストフィールドの値を設定する。
player.name = myTextField.text
//管理オブジェクトコンテキストの中身を永続化する。
try managedContext.save()
//キーボードをしまう
self.view.endEditing(true)
} catch {
//エラーメッセージをアラートで表示する。
let alert = UIAlertController(
title: "エラー",
message: String(describing: error),
preferredStyle: UIAlertController.Style.alert
)
self.present(alert, animated:true, completion:nil)
}
return true
}
}
を組み込み〜〜〜〜
さらにちなみに、
⒈でやったUse Core Dataにチェックなしで作ったプロジェクトで
まとめ
以上から、これまでの記事で書いたとおりなんだけど、
⒈最初からUse Core Dataにチェックを入れて作っておいた方がいい。
👉後からデータモデルを追加出来なくはないが、どんな影響があるかわからない(=サイト記事当初からPersistence Containerに仕様が変わっていたりな)ので。
⒉Constraints=一意制約は、データベースの初期段階で使うなら追加する。
👉データベース全体に対する制約になるので、後から追加しようとして、既に制約に反するデータがモデル上に存在する場合、制約違反=FatalErrorになるので。
⒊100の理論や知識よりも、1の検証(実際に動かして確かめる)が大事。思い込まずに動かして確認する。
👉プログラミングにしろデータベースにしろ、機能やロジックは頭では理解してる(インターネットや本なんかに転がってるからね)と、いざ動かしてみると、想定の範囲外ってことはよくある話。
ってことがよくわかると思う。こんなことを書くと、経験狂信者の人が、
だから経験が大事だ
って言いそうなんだけど、
こうやってWEB記事で公開しても、自分の経験ではないことや、興味ないことは、経験狂信者の人ほど、
今の自分には関係ない
って他人の経験(=知識や理論)を聞こうとしない。
一番大事なのは、
💃経験したことを自分だけにとどめておくのではなく、
きちんと経験から得た知識を発信すること🕺
ここからは、余談なんだけど、
SQLとかACCESSなんかでも、Constraintsはあるけど、
制約に反するものに関しては入力を許さないなど強固なデータベースを構築する際には非常に有用。
ただ、裏を返すと後からDBの仕様変更やリレーションの変更なんかがあった場合、Constraintsが逆に邪魔をして、柔軟性がなくなる
=要求変更に対応できなくなるか、改修に余計な手間がかかったり、下手したら0から作り直した方が早いなんてことはよくある。
データベースは所詮、データを格納するテーブルの構成に過ぎず、
格納されたデータ自体を新しいテーブルに取り込みなおした方が早い
んだけどね。
そういうことに気づかずに、初心者っていうか、管理管理、安全安全ってそっちばかりに気がいってる自称、データベースエンジニアなんかで、教科書とか学校で習ったことばかりを実践しようとして、
いきなり最初からガッチガチにConstraintsを追加したがる
し、要求変更があった場合、他のDBを作り直した方が早いことでも、
それをせずに直せるのがエンジニアだ!!!
みたいな感じで、却って余計な時間ばかりかけて悦に浸ってるw🤣
の【開発哲学】すら一切読まずに、読めば数日〜数ヶ月で理解できたり改善できたりすることに数年〜十数年とか時間をかけてるからね👀
💃やり方はひとつではないのに💦🕺
知識として、テーブル、クエリ、フォーム、レポート、ビューを
頭に入れてるだけで、
リレーショナルデータベース(RDB)
CRUD(Create、Read、Update、Delete)
ACID(Atomically、Consistency、Isolation、Durability)
を概念としてすら肌感覚で身に付けてないからね。。。データベースなんて後は所詮、テーブル内にあるデータに対して、
アクセスみたいに画面上の操作でも出来るようにするか
SQLみたいにコードでやるか
だけの違いなんだけどね、、、💦🤔
CoreDataがまさに、
データモデルの中にEntityってテーブルを作って、
Attributeって各データ項目を扱ってるだけ
👉データベースでしょ?👀
それを色々な各設定項目なんかをXcodeみたいなGUIやコードを併用しながら、やりたいことをやってるだけ🕺
まだ殆ど記事にはしてないけど、実は、既にSwiftUIも
サンプルもまとめてひとつに繋ぎ終わってるし、あとは次いでに
を年内に勉強しよかなあくらいなもんなんで〜〜〜!
てことで今日はもう疲れたし、
本編はまた明日以降で気が向いたらまた書こ!!!
残り15記事くらいだし。
なんかGW明けくらいには終わってそうだね👀
この記事が気に入ったらサポートをしてみませんか?