Làm thế nào để áp dụng kiểu cho một phiên bản NSFetchRequest?


102

Trong Swift 2, mã sau đã hoạt động:

let request = NSFetchRequest(entityName: String)

nhưng trong Swift 3 nó báo lỗi:

Không thể suy ra tham số chung "ResultType"

bởi vì NSFetchRequestbây giờ là một loại chung chung. Trong tài liệu của họ, họ đã viết thế này:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

vì vậy nếu lớp kết quả của tôi là ví dụ, Levellàm thế nào tôi nên yêu cầu chính xác?

Bởi vì điều này không hoạt động:

let request: NSFetchRequest<Level> = Level.fetchRequest

1
liên kết đến các tính năng mới, nơi tôi tìm thấy mã: developer.apple.com/library/prerelease/content/releasenotes/...
Deniss

1
Đó là một phương pháp, vì vậy nó phải đượclet request: NSFetchRequest<Level> = Level.fetchRequest()
Sulthan

5
Hoặc chỉlet request = Level.fetchRequest()
Martin R

2
@MartinR Điều đó sẽ không vượt qua quá trình biên dịch bởi vì nó lớn.
Sulthan

6
@MartinR có vẻ như các thành viên tràn ngăn xếp yêu bạn rất nhiều. Họ sẽ ủng hộ bạn một cách mù quáng. : P
BangOperator

Câu trả lời:


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

hoặc là

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

tùy thuộc vào phiên bản bạn muốn.

Bạn phải chỉ định kiểu chung vì nếu không thì lời gọi phương thức là không rõ ràng.

Phiên bản đầu tiên được xác định cho NSManagedObject, phiên bản thứ hai được tạo tự động cho mọi đối tượng sử dụng tiện ích mở rộng, ví dụ:

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

    @NSManaged var timeStamp: NSDate?
}

Toàn bộ vấn đề là loại bỏ việc sử dụng các hằng chuỗi.


1
Vậy đối với mọi thực thể, tôi có cần thêm mã mở rộng không? Hay điều đó xảy ra tự động? Vì vậy, nếu tôi có thực thể "Chó" và thực thể "Mèo", tôi có cần "tiện ích mở rộng Chó {@nonobjc ...}" và "tiện ích mở rộng Cat {@nonobjc ...}" không?
Dave G

@DaveG Phần mở rộng đó được tạo tự động cho bạn.
Sulthan

1
Được rồi, ty, nhưng tôi hơi bối rối vì tôi đã thử 'let fetchRequest = NSFetchRequest <myEntityName> (entityName: "myEntityName")' và tôi gặp lỗi "Sử dụng loại không khai báo" myEntityName "
Dave G

4
Lưu ý: Phương thức fetchRequest () chỉ khả dụng trong iOS 10
dzensik

@Sulthan Xin chào, Khi tôi thử với mã của bạn, lỗi sau xảy ra. Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
Svetoslav Atanasov

56

Tôi nghĩ rằng tôi đã làm cho nó hoạt động bằng cách làm điều này:

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

ít nhất nó lưu và tải dữ liệu từ DataBase.

Nhưng có vẻ như nó không phải là một giải pháp thích hợp, nhưng nó hoạt động cho bây giờ.


Tôi thích giải pháp này hơn, vì tôi đã từng có một phương thức duy nhất lấy tên thực thể làm tham số và chỉ trả lại một mảng NSManagedObjects.
n_b

Cũng thích điều này vì nó không yêu cầu tạo một lớp tùy chỉnh. Chỉ có thể sử dụng tên thực thể!
Liam Bolling

Câu trả lời bị đánh giá thấp. Cảm ơn!
David Chelidze

33

Cấu trúc đơn giản nhất mà tôi thấy hoạt động trong 3.0 như sau:

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

trong đó Loại thực thể dữ liệu là Quốc gia.

Tuy nhiên, khi cố gắng tạo một BatchDeleteRequest dữ liệu cốt lõi, tôi thấy rằng định nghĩa này không hoạt động và có vẻ như bạn sẽ cần phải đi với biểu mẫu:

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

mặc dù các định dạng ManagedObject và FetchRequestResult được cho là tương đương nhau.


1
Cấu trúc đầu tiên đề cập trong câu trả lời này là cách duy nhất tôi có thể hiện được điều này để biên dịch với lấy kết quả điều khiển của tôi trên Swift3 / iOS 10 / Xcode 8.
David L

Đó là kinh nghiệm của tôi sau khi thử nhiều hình thức khác nhau. Họ có đề cập đến bất kỳ hình thức nào khác trong bản trình bày CoreData không? Kế hoạch để kiểm tra xem nó ra ngày mai ...
Ron Diel

Ví dụ đầu tiên là cách đơn giản nhất mà tôi đã tìm thấy mà không cần phải sử dụng if #available(iOS 10.0) { ... }điều kiện
djv

12

Dưới đây là một số phương pháp CoreData chung có thể trả lời câu hỏi của bạn:

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)")
    }
}

Giả sử rằng có một thiết lập NSManagedObject cho Liên hệ như sau:

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

Các phương pháp này có thể được sử dụng theo cách sau:

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()

Sạch sẽ và thanh lịch. Ước gì tôi có thể bỏ phiếu này lên 100! Một lần liên lạc, không biết bạn nghĩ gì, tôi đã gói mỗi phương thức với ngữ cảnh? .Perform ({}) để đảm bảo an toàn cho chuỗi. Đây là khuyến nghị của Apple.
Tinkerbell

Không OO lắm. Trừ khi bạn có thể viết chúng dưới dạng một phần mở rộng cho NSManagedObjectContect, thì đó sẽ là một giải pháp tốt.
Muz rìu

1
Chỉ cần lưu ý - để đếm tất cả các bản ghi bạn đang truy xuất chúng và sau đó đếm số mục nhập mảng - điều này thực sự không hiệu quả. Bạn có thể muốn mở rộng chức năng recordsInTable sử dụng context.count (theo yêu cầu)
Muz rìu

Đây là những bổ sung hay và nên có nhiều phiếu bầu hơn, nhưng có lẽ không phải vì nó lạc đề khỏi câu hỏi chính (mặc dù nó hữu ích). Một cái gì đó tôi muốn đề xuất khó thay đổi với chức năng xóa, đó là xóa bằng chức năng NSManagedObjectIDthay thế. Vì vậy, trước khi context.delete(record)thêm vào let record = context.object(with: record.objectID)và sử dụng đối tượng bản ghi đó để xóa.
PostCodeism

6

Đây là cách đơn giản nhất để chuyển sang Swift 3.0, chỉ cần thêm <Country>

(đã thử nghiệm và làm việc)

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

0

Tôi cũng có "ResultType" không thể suy ra lỗi. Chúng bị xóa sau khi tôi xây dựng lại mô hình dữ liệu, cài đặt Codegen của mỗi thực thể thành "Định nghĩa lớp". Tôi đã viết một bản ghi ngắn gọn với hướng dẫn từng bước ở đây:

Tìm kiếm một hướng dẫn rõ ràng về NSPers phù hợp có sửa đổi trong Xcode 8 với Swift 3

Bằng cách "xây dựng lại", ý tôi là tôi đã tạo một tệp mô hình mới với các mục nhập và thuộc tính mới. Một chút tẻ nhạt, nhưng nó đã hoạt động!


0

Điều tốt nhất cho tôi cho đến nay là:

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

0

Tôi gặp sự cố tương tự và tôi đã giải quyết nó bằng các bước sau:

  • Chọn tệp xcdatamodeld của bạn và đi tới Trình kiểm tra mô hình dữ liệu
  • Chọn Thực thể đầu tiên của bạn và chuyển đến lớp Phần
  • Đảm bảo rằng Codegen "Class Definition" được chọn.
  • Xóa tất cả các tệp Đối tượng đã tạo của bạn. Bạn không cần chúng nữa.

Sau khi làm điều đó, tôi phải xóa / viết lại tất cả các lần xuất hiện của fetchRequest vì XCode dường như bằng cách nào đó trộn lẫn với phiên bản được tạo mã.

HTH


0

Swift 3.0 Điều này sẽ hoạt động.

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.