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/...
데니스

1
그것은 방법, 그래서 그것을해야한다let request: NSFetchRequest<Level> = Level.fetchRequest()
Sulthan

5
아니면 그냥let request = Level.fetchRequest()
Martin R

2
@MartinR 모호하기 때문에 컴파일을 통과하지 못할 것입니다.
Sulthan

6
@MartinR은 스택 오버플로 멤버가 당신을 많이 사랑하는 것 같습니다. 그들은 당신을 맹목적으로 찬성 할 것입니다. : P
BangOperator 2016-10-23

답변:


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
따라서 모든 엔터티에 대해 확장 코드를 추가해야합니까? 아니면 자동으로 발생합니까? 따라서 "Dog"엔티티와 "Cat"엔티티가있는 경우 "extension Dog {@nonobjc ...}"및 "extension Cat {@nonobjc ...}"가 필요합니까?
Dave G

@DaveG 해당 확장은 자동으로 생성됩니다.
Sulthan 2016 년

1
나는 시도하기 때문에 좋아, 타이가,하지만 난 조금 혼란하고있어 '하자 fetchRequest = NSFetchRequest <myEntityName> (엔티티 이름 : "myEntityName")'나는 오류 myEntityName "신고되지 않은 유형의 사용" "있어요
데이브 G

4
참고 : fetchRequest () 메서드는 iOS 10에서만 사용할 수 있습니다
dzensik

@Sulthan Hi, 귀하의 코드로 시도했을 때 다음 오류가 발생합니다. Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
Svetoslav Atanasov

56

나는 이것을 수행하여 작동한다고 생각합니다.

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

적어도 데이터베이스에서 데이터를 저장하고로드합니다.

그러나 적절한 해결책이 아닌 것 같지만 지금은 작동합니다.


엔터티 이름을 매개 변수로 사용하고 NSManagedObjects 배열을 다시 전달하는 단일 메서드를 사용했기 때문에이 솔루션이 더 좋습니다.
n_b

커스텀 클래스를 만들 필요가 없었기 때문에 이것도 좋아했습니다. 엔티티 이름 만 사용할 수 있습니다!
Liam Bolling 2016

과소 평가 된 답변입니다. 감사!
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 프레젠테이션에서 다른 형식을 다루었습니까? 계획은 ... 내일 그것을 확인
론 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)")
    }
}

다음과 같이 Contact에 대한 NSManagedObject 설정이 있다고 가정합니다.

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으로 투표 할 수 있기를 바랍니다! 한 번의 터치 업으로 여러분이 어떻게 생각하는지 궁금해서 스레드 안전을 위해 각 메서드를 context? .perform ({})으로 래핑했습니다. 이것은 Apple에서 권장합니다.
Tinkerbell

그다지 OO가 아닙니다. 이것들을 NSManagedObjectContect의 확장으로 작성할 수 없다면 그것은 좋은 해결책이 될 것입니다.
도끼 MUZ

1
검색하는 모든 레코드를 세고 배열 항목의 수를 세는 것은 정말 비효율적입니다. context.count (request)를 활용하기 위해 recordsInTable 함수를 확장하고 싶을 것입니다.
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"으로 설정하는 데이터 모델을 다시 빌드하면 삭제되었습니다. 여기에 단계별 지침으로 간단한 글을 작성했습니다.

Swift 3을 사용하는 Xcode 8의 수정 된 NSPersistentContainer에 대한 명확한 튜토리얼을 찾고 있습니다.

"재 구축"이란 새 항목과 속성이있는 새 모델 파일을 만들었 음을 의미합니다. 조금 지루했지만 효과가있었습니다!


0

지금까지 나에게 가장 잘 맞는 것은 다음과 같습니다.

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

0

나는 같은 문제가 있었고 다음 단계로 해결했습니다.

  • xcdatamodeld 파일을 선택하고 데이터 모델 검사기로 이동하십시오.
  • 첫 번째 엔티티를 선택하고 섹션 클래스로 이동하십시오.
  • Codegen "Class Definition"이 선택되어 있는지 확인하십시오.
  • 생성 된 모든 엔티티 파일을 제거하십시오. 더 이상 필요하지 않습니다.

그 후 XCode가 코드 생성 버전과 섞여있는 것처럼 보이기 때문에 fetchRequest의 모든 발생을 제거 / 재 작성해야했습니다.

HTH


0

Swift 3.0 작동합니다.

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.