Posted on 

Core Data with SwiftUI Tutorial by Paul Hudson

请注意

技术类文章并不一定会进行中文翻译。如此文章无中文翻译,请移步英文界面阅读。

Attention

This is a learning note, which means that there may be faults and misunderstanding in it. You are more than welcome to correct me by commenting.

L1: Why does .self work for ForEach?

Hashable is essential for SwiftUI to differentiate and locate each view component.

For more infomation: HASH是什么?“哈希值校验”又是怎么回事儿?

L2: Creating NSManagedObject subclasses

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
extension Movie {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Movie> {
return NsFetchReauest<Movie>(entityName: "Movie")
}
// We can make this not optional by simply deleting question mark
@NSManaged public var title: String?
@NSManaged public var director: String
@NSManaged public var year: Int16

// In case of nil collapsing, we can add
public var wrappedTitle:String {
title ?? "Unknown Title"
}
}

extension MovieIdentifiable {
}

Core data only load data when is actually needed (lazy load). This is good.

L3: Conditional saving of NSManagedObjectContext

1
2
3
4
5
6
7
8
9
10
11
struct ContentView: View {
@Environment(\. managedobjectContext) var moc
var body:some View {
Button("Save"){
// moc.hasChanges help us be sure about saving data only when changed
if moc.hasChanges {
try? moc.save()
}
}
}
}

L4: Ensuring Core Data objects are unique using constraints

To make sure some specific attributes have unique values, add attribute name to Constraints in the right column.

L5: Filtering @FetchRequest using NSPredicate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"universe == 'Star Wars' ")) var ships: FetchedResults<Ship>

@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"universe == %@ ", "Star Wars")) var ships: FetchedResults<Ship>

// Find all data whose inital character is before F in alphabet
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"universe < %@ ", "F")) var ships: FetchedResults<Ship>

// IN
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"universe IN %@ ", ["Aliens","Firefly"])) var ships: FetchedResults<Ship>

// BEGINSWITH is Case sensitive, while BEGINSWITH[c] is not
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"name BEGINSWITH %@ ", "E")) var ships: FetchedResults<Ship>
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"name BEGINSWITH %@ ", "e")) var ships: FetchedResults<Ship>
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"name BEGINSWITH[c] %@ ", "e")) var ships: FetchedResults<Ship>

// CONTAINS
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"name CONTAINS[c] %@ ", "e")) var ships: FetchedResults<Ship>

// NOT
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format:"NOT name CONTAINS[c] %@ ", "e")) var ships: FetchedResults<Ship>

// AND, OR

L6: Dynamically filtering @FetchRequest with SwiftUI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Parent View 
import CoreData
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@State private var lastNameFilter = "A"

var body:some View {
VStack {
FilteredList(filter: lastNameFilter)

Button("Change filter to S"){
lastNameFilter = "S"
}
}
}
}

// Child View
import SwiftUI
struct FilteredList: View {
@FetchRequest var fetchRequest: FetchedResults<Singer>

var body: some View {
List(fetchRequest, id:\.self){singer in
Text("\(singer.wrappedFirstName) \(singer. wrappedLastName)")
}

init(filter: String){
// The underscore is very essential
_fetchRequest = FetchRequest<Singer> (sortDescriptors: [], predicate:NSPredicate(format: "lastName BEGINSWITH %@",filter))
}
}

To handle any kind of Entity and filters, use following codes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Parent View 
import CoreData
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@State private var lastNameFilter = "A"

var body:some View {
VStack {
FilteredList(filterKey:"lastName", filterValue: lastNameFilter){ (singer:Singer) in
Text("\(singer.wrappedFirstName) \(singer. wrappedLastName)")

}

Button("Change filter to S"){
lastNameFilter = "S"
}
}
}
}

// Child View
import CoreData
import SwiftUI
struct FilteredList<T: NSManagedObject, Content: View>: View {
@FetchRequest var fetchRequest: FetchedResults<T>
let content: (T) -> Content

var body: some View {
List(fetchRequest, id:\.self){item in
self.content(item)
}
}
init(filterKey:String, filterValue: String, @ViewBuilder content: @escaping (T) -> Content){
// The underscore is very essential
_fetchRequest = FetchRequest<T> (sortDescriptors: [], predicate:NSPredicate(format: "%K BEGINSWITH %@",filterKey, filterValue))
}
}

L7: One-to-many relationships with @FetchRequest and SwiftUI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Core Data files 
extension Country{
// Omit some codes here

// shortName must be unique in order to specify the relationship
@NSManaged public var shortName:String?
@NSManaged public var candy: NSSet?

public var candyArray: [Candy]{
let set = candy as? Set<Candy> ?? []

return set.sorted{
// Remember to declare `wrappedName` in `Candy`
$0.wrappedName < $1.wrappedName
}
}
}

// View
// Please follow the video step by step

Reference

[Core data swiftui tutorial](https://www.youtube.com/@twostraws/search?query="core data swiftui”)

  1. Why does .self work for ForEach?
  2. Creating NSManagedObject subclasses
  3. Conditional saving of NSManagedObjectContext
  4. Ensuring Core Data objects are unique using constraints
  5. Filtering @FetchRequest using NSPredicate
  6. Dynamically filtering @FetchRequest with SwiftUI
  7. One-to-many relationships with Core Data, SwiftUI, and @FetchRequest Challenges
  8. Core Data One to Many Relationship & Core Data Delete Rules : SwiftUI

本站由 @deskside 使用 Stellar 主题创建。
This site is created with love by @deskside , powered by Hexo theme Stellar.
本博客所有文章除特别声明外,均采用 署名-非商业性使用-相同方式共享 4.0 国际 许可协议,转载请注明出处。
All articles, unless otherwise stated, are in CC BY-NC-SA 4.0 license agreement. Please indicate the source when reproduced.
由于中国大陆网络政策的限制,部分图片可能无法顺利显示。
Due to the restrictions of Chinese mainland’s network policy, some pictures may not be displayed smoothly.