จะใช้ประเภทนี้กับอินสแตนซ์ NSFetchRequest ได้อย่างไร


102

ใน Swift 2 รหัสต่อไปนี้ใช้งานได้:

let request = NSFetchRequest(entityName: String)

แต่ใน Swift 3 ให้ข้อผิดพลาด:

ไม่สามารถอนุมานพารามิเตอร์ทั่วไป "ResultType" ได้

เพราะNSFetchRequestตอนนี้เป็นประเภททั่วไป ในเอกสารของพวกเขาพวกเขาเขียนสิ่งนี้:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

ดังนั้นหากคลาสผลลัพธ์ของฉันเป็นตัวอย่างLevelฉันจะขออย่างถูกต้องได้อย่างไร

เนื่องจากไม่ได้ผล:

let request: NSFetchRequest<Level> = Level.fetchRequest

1
ลิงก์ไปยังคุณสมบัติใหม่ที่ฉันพบรหัส: developer.apple.com/library/prerelease/content/releasenotes/…
Deniss

1
มันเป็นวิธีการที่ควรจะเป็นlet request: NSFetchRequest<Level> = Level.fetchRequest()
Sulthan

5
หรือแค่let request = Level.fetchRequest()
Martin R

2
@MartinR นั่นจะไม่ผ่านการรวบรวมเพราะมันมีความสำคัญ
Sulthan

6
@MartinR ดูเหมือนว่าสมาชิกล้นสแต็คจะรักคุณมาก พวกเขาจะโหวตให้คุณสุ่มสี่สุ่มห้า : P
BangOperator

คำตอบ:


129
let request: NSFetchRequest<NSFetchRequestResult> = Level.fetchRequest()

หรือ

let request: NSFetchRequest<Level> = Level.fetchRequest()

ขึ้นอยู่กับเวอร์ชันที่คุณต้องการ

คุณต้องระบุประเภททั่วไปเพราะมิฉะนั้นการเรียกเมธอดจะไม่ชัดเจน

เวอร์ชันแรกถูกกำหนดไว้สำหรับNSManagedObjectเวอร์ชันที่สองถูกสร้างขึ้นโดยอัตโนมัติสำหรับทุกอ็อบเจ็กต์โดยใช้ส่วนขยายเช่น:

extension Level {
    @nonobjc class func fetchRequest() -> NSFetchRequest<Level> {
        return NSFetchRequest<Level>(entityName: "Level");
    }

    @NSManaged var timeStamp: NSDate?
}

จุดรวมคือการลบการใช้ค่าคงที่สตริง


1
ดังนั้นสำหรับทุกเอนทิตีฉันต้องเพิ่มรหัสส่วนขยายหรือไม่ หรือว่าเกิดขึ้นโดยอัตโนมัติ? ดังนั้นหากฉันมีเอนทิตี "สุนัข" และเอนทิตี "แมว" ฉันจำเป็นต้องมี "ส่วนขยายของสุนัข {@nonobjc ... }" และ "ส่วนขยายของแมว {@nonobjc ... }" หรือไม่
Dave G

@DaveG ส่วนขยายนั้นถูกสร้างขึ้นสำหรับคุณโดยอัตโนมัติ
Sulthan

1
โอเค ty แต่ฉันสับสนเล็กน้อยเพราะฉันลอง 'let fetchRequest = NSFetchRequest <myEntityName> (entityName: "myEntityName")' และฉันได้รับข้อผิดพลาด "Use of undeclared type" myEntityName "
Dave G

4
หมายเหตุ: วิธีการ fetchRequest () สามารถใช้ได้เฉพาะใน iOS 10
dzensik

@Sulthan สวัสดีเมื่อฉันลองใช้รหัสของคุณข้อผิดพลาดต่อไปนี้เกิดขึ้น Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
Svetoslav Atanasov

56

ฉันคิดว่าฉันได้ผลโดยทำสิ่งนี้:

let request:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Level")

อย่างน้อยก็บันทึกและโหลดข้อมูลจาก DataBase

แต่รู้สึกเหมือนไม่ใช่วิธีแก้ปัญหาที่เหมาะสม แต่ใช้ได้ผลในตอนนี้


ฉันชอบโซลูชันนี้ดีกว่าเนื่องจากฉันเคยมีเมธอดเดียวที่ใช้ชื่อเอนทิตีเป็นพารามิเตอร์และส่งกลับอาร์เรย์ของ NSManagedObjects
n_b

ชอบสิ่งนี้เช่นกันเพราะไม่ต้องสร้างคลาสที่กำหนดเอง สามารถใช้ชื่อนิติบุคคลได้!
Liam Bolling

คำตอบที่ไม่เหมาะสม ขอบคุณ!
David Chelidze

33

โครงสร้างที่ง่ายที่สุดที่ฉันพบว่าทำงานใน 3.0 มีดังนี้:

let request = NSFetchRequest<Country>(entityName: "Country")

โดยที่ประเภทเอนทิตีข้อมูลคือประเทศ

อย่างไรก็ตามเมื่อพยายามสร้าง Core Data BatchDeleteRequest ฉันพบว่าคำจำกัดความนี้ใช้ไม่ได้และดูเหมือนว่าคุณจะต้องใช้แบบฟอร์ม:

let request: NSFetchRequest<NSFetchRequestResult> = Country.fetchRequest()

แม้ว่ารูปแบบ ManagedObject และ FetchRequestResult จะเทียบเท่ากันก็ตาม


1
โครงสร้างแรกที่กล่าวถึงในคำตอบนี้เป็นวิธีเดียวที่ฉันสามารถรวบรวมสิ่งนี้กับตัวควบคุมผลลัพธ์ที่ดึงมาใน Swift3 / iOS 10 / Xcode 8
David L

นั่นเป็นประสบการณ์ของฉันหลังจากลองใช้รูปแบบต่างๆ พวกเขาครอบคลุมรูปแบบอื่น ๆ ในการนำเสนอ CoreData หรือไม่ วางแผนจะเช็คเอาท์พรุ่งนี้ ...
Ron Diel

ตัวอย่างแรกเป็นวิธีที่ง่ายที่สุดที่ฉันพบโดยไม่ต้องใช้if #available(iOS 10.0) { ... }เงื่อนไข
djv

12

นี่คือวิธีการทั่วไปของ CoreData ที่อาจตอบคำถามของคุณ:

import Foundation
import Cocoa

func addRecord<T: NSManagedObject>(_ type : T.Type) -> T
{
    let entityName = T.description()
    let context = app.managedObjectContext
    let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
    let record = T(entity: entity!, insertInto: context)
    return record
}

func recordsInTable<T: NSManagedObject>(_ type : T.Type) -> Int
{
    let recs = allRecords(T.self)
    return recs.count
}


func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T]
{
    let context = app.managedObjectContext
    let request = T.fetchRequest()
    do
    {
        let results = try context.fetch(request)
        return results as! [T]
    }
    catch
    {
        print("Error with request: \(error)")
        return []
    }
}

func query<T: NSManagedObject>(_ type : T.Type, search: NSPredicate?, sort: NSSortDescriptor? = nil, multiSort: [NSSortDescriptor]? = nil) -> [T]
{
    let context = app.managedObjectContext
    let request = T.fetchRequest()
    if let predicate = search
    {
        request.predicate = predicate
    }
    if let sortDescriptors = multiSort
    {
        request.sortDescriptors = sortDescriptors
    }
    else if let sortDescriptor = sort
    {
        request.sortDescriptors = [sortDescriptor]
    }

    do
    {
        let results = try context.fetch(request)
        return results as! [T]
    }
    catch
    {
        print("Error with request: \(error)")
        return []
    }
}


func deleteRecord(_ object: NSManagedObject)
{
    let context = app.managedObjectContext
    context.delete(object)
}

func deleteRecords<T: NSManagedObject>(_ type : T.Type, search: NSPredicate? = nil)
{
    let context = app.managedObjectContext

    let results = query(T.self, search: search)
    for record in results
    {
        context.delete(record)
    }
}

func saveDatabase()
{
    let context = app.managedObjectContext

    do
    {
        try context.save()
    }
    catch
    {
        print("Error saving database: \(error)")
    }
}

สมมติว่ามีการตั้งค่า NSManagedObject สำหรับ Contact ดังนี้:

class Contact: NSManagedObject
{
    @NSManaged var contactNo: Int
    @NSManaged var contactName: String
}

วิธีการเหล่านี้สามารถใช้ได้ในลักษณะต่อไปนี้:

let name = "John Appleseed"

let newContact = addRecord(Contact.self)
newContact.contactNo = 1
newContact.contactName = name

let contacts = query(Contact.self, search: NSPredicate(format: "contactName == %@", name))
for contact in contacts
{
    print ("Contact name = \(contact.contactName), no = \(contact.contactNo)")
}

deleteRecords(Contact.self, search: NSPredicate(format: "contactName == %@", name))

recs = recordsInTable(Contact.self)
print ("Contacts table has \(recs) records")

saveDatabase()

สะอาดและสง่างาม หวังว่าฉันจะโหวตได้เต็ม 100! สัมผัสเพียงครั้งเดียวสงสัยว่าคุณคิดอย่างไรฉันรวบรวมแต่ละวิธีด้วยบริบท? .perform ({}) เพื่อความปลอดภัยของเธรด แนะนำโดย Apple
Tinkerbell

ไม่มาก OO. ถ้าคุณไม่สามารถเขียนสิ่งเหล่านี้เป็นส่วนขยายไปยัง NSManagedObjectContect นั่นจะเป็นทางออกที่ดี
muz the ax

1
เพิ่งสังเกตเห็น - ในการนับระเบียนทั้งหมดที่คุณเรียกดูจากนั้นนับจำนวนรายการอาร์เรย์ - นี่ไม่มีประสิทธิภาพจริงๆ คุณอาจต้องการขยายฟังก์ชัน recordsInTable เพื่อใช้ context.count (request)
muz the ax

สิ่งเหล่านี้เป็นส่วนเสริมที่ดีและควรได้รับการโหวตมากขึ้น แต่อาจไม่ใช่เพราะเป็นการพูดนอกเรื่องไปจากคำถามหลัก (แม้ว่าจะมีประโยชน์) สิ่งที่ฉันขอแนะนำให้เปลี่ยนยากด้วยฟังก์ชันลบคือการลบด้วยไฟล์NSManagedObjectID. ดังนั้นก่อนที่จะcontext.delete(record)เพิ่ม let record = context.object(with: record.objectID)และใช้วัตถุบันทึกนั้นเพื่อลบ
PostCodeism

6

นี่เป็นวิธีที่ง่ายที่สุดในการย้ายไปใช้ Swift 3.0 เพียงแค่เพิ่ม <Country>

(ทดสอบและใช้งานได้)

let request = NSFetchRequest<Country>(entityName: "Country")

0

ฉันยังมี "ResultType" ไม่สามารถอนุมานข้อผิดพลาดได้ พวกเขาล้างเมื่อฉันสร้างแบบจำลองข้อมูลขึ้นใหม่โดยตั้งค่า Codegen ของแต่ละเอนทิตีเป็น "Class Definition" ฉันเขียนบทความสั้น ๆ พร้อมคำแนะนำทีละขั้นตอนที่นี่:

กำลังมองหาบทช่วยสอนที่ชัดเจนเกี่ยวกับ NSPersistentContainer ที่แก้ไขแล้วใน Xcode 8 พร้อม Swift 3

โดย "สร้างใหม่" หมายความว่าฉันได้สร้างไฟล์โมเดลใหม่ที่มีรายการและแอตทริบิวต์ใหม่ น่าเบื่อเล็กน้อย แต่ได้ผล!


0

สิ่งที่ดีที่สุดสำหรับฉันจนถึงตอนนี้คือ:

let request = Level.fetchRequest() as! NSFetchRequest<Level>

0

ฉันมีปัญหาเดียวกันและฉันแก้ไขด้วยขั้นตอนต่อไปนี้:

  • เลือกไฟล์ xcdatamodeld ของคุณแล้วไปที่ Data Model Inspector
  • เลือกเอนทิตีแรกของคุณและไปที่คลาสส่วน
  • ตรวจสอบให้แน่ใจว่าได้เลือก Codegen "Class Definition" แล้ว
  • ลบไฟล์เอนทิตีที่คุณสร้างขึ้นทั้งหมด คุณไม่ต้องการอีกต่อไป

หลังจากทำเช่นนั้นฉันต้องลบ / เขียนใหม่ทั้งหมดที่เกิดขึ้นของ fetchRequest เนื่องจาก XCode ดูเหมือนจะผสมกับเวอร์ชันที่สร้างโค้ด

HTH


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.