Não foi possível encontrar a subclasse específica de NSManagedObject

136

Estou trabalhando no desenvolvimento de um aplicativo com o Core Data. Quando criei uma instância usando:

let entity = NSEntityDescription.entityForName("User", inManagedObjectContext: appDelegate.managedObjectContext)
let user = User(entity: entity, insertIntoManagedObjectContext: appDelegate.managedObjectContext)

Recebi um aviso no log:

CoreData: warning: Unable to load class named 'User' for entity 'User'.  Class not found, using default NSManagedObject instead.

Como eu poderia consertar isso?

E outra pergunta, como posso definir um método de instância na subclasse NSManagedObject?

Editar:

Especifiquei a classe da entidade como na captura de tela a seguir:

insira a descrição da imagem aqui

MsrButterfly
fonte
7
Você prefixou o nome da classe de entidades com o nome do módulo, conforme documentado em Implementando subclasses de objeto gerenciado por dados principais ?
Martin R
@ Martinin: Veja a atualização da minha pergunta.
MsrButterfly
2
A classe deve ser "YourAppName.User", consulte a documentação (link no meu comentário anterior).
Martin R
@ Martinin: Obrigado pela sua ajuda. Funciona.
MsgButterfly
O melhor é excluir essas classes e recriá-las. Isso funcionou para mim
Abhishek

Respostas:

220

Atualização para o Xcode 7 (final): A adição do nome do módulo à classe (como no Xcode 6 e nas versões beta anteriores do Xcode 7) não é mais necessária. A documentação da Apple Implementando subclasses de objeto gerenciado por dados principais foi atualizada de acordo.

O inspetor de modelo de dados agora possui dois campos "Classe" e "Módulo" para uma entidade:

insira a descrição da imagem aqui

Quando você cria uma subclasse de objeto gerenciado Swift para a entidade, o campo "Módulo" é definido como "Módulo atual do produto" e, com essa configuração, a criação de instâncias funciona tanto no aplicativo principal quanto nos testes de unidade. A subclasse de objeto gerenciado não deve ser marcada com @objc(classname)(isso foi observado em https://stackoverflow.com/a/31288029/1187415 ).

Como alternativa, você pode esvaziar o campo "Módulo" (ele mostrará "Nenhum") e marcar as subclasses de objetos gerenciados com @objc(classname)(isso foi observado em https://stackoverflow.com/a/31287260/1187415 ).


Observação: Esta resposta foi originalmente escrita para o Xcode 6. Houve algumas alterações nas várias versões beta do Xcode 7 com relação a esse problema. Como é uma resposta aceita com muitos upvotes e links para ela, tentei resumir a situação da atual versão final do Xcode 7.

Eu fiz minha própria "pesquisa" e li todas as respostas para essa pergunta e para a pergunta semelhante CoreData: warning: Não é possível carregar a classe nomeada . Portanto, a atribuição é atribuída a todos eles, mesmo que eu não os liste especificamente!


Resposta anterior para o Xcode 6 :

Conforme documentado em Implementando subclasses de objeto gerenciado por dados principais , você deve prefixar o nome da classe de entidades no campo Classe no inspetor de entidades do modelo com o nome do seu módulo, por exemplo "MyFirstSwiftApp.User".

Martin R
fonte
2
Este está trabalhando para mim. Pergunta: E se os dados principais forem incluídos em uma estrutura, como prefixar o nome da subclasse ManagedObject, pois ainda não existe um aplicativo real?
Allan Macatingrao
9
@ Allan: Você pode tentar o @objc(ClassName)método sugerido na resposta de Christer.
Martin R
8
Funciona, mas depois de definir o nome da classe como "APPNAME.User" no inspetor de entidades, não consigo gerar novamente as classes do modelo: o Xcode parece estar confuso com o prefixo e gera um arquivo / classe com o nome NOME DO APLICATIVO. Estou esquecendo de algo?
Pascal Bourque 26/09
1
E se você receber esse erro no Objective-C ao usar dados principais em uma biblioteca estática?
George Taskos
1
@ Suragch: Agora, finalmente, atualizei a resposta, espero que tudo esteja correto agora.
Martin R
62

Apenas como uma nota lateral. Eu tive o mesmo problema. E tudo que eu tinha que fazer era adicionar @objc(ClassName)no meu arquivo de classe.

Exemplo:

@objc(Person)
class Person { }

E isso resolveu meu problema.

Christer
fonte
1
Você definiu typealias (<% ProjectName%>. Person -> Person) no Objective-C dessa maneira, para que funcione.
precisa saber é o seguinte
Parece que a Apple esqueceu de convertê-lo totalmente para o veloz .. não sei porquê, mas este fixa-lo para mim muito gentilmente
Jiří Zahalka
7
Isso é particularmente útil se você tiver um projeto com vários destinos, em que cada destino compartilha o modelo CoreData. Nesses casos, não é possível prefixar o nome da classe com o do identificador de destino, pois isso torna o modelo de CD útil para apenas um dos destinos.
djbp
@djbp E "por que não?" Eu gostaria de saber. Eu gosto do conceito de namespacing, mas isso me fez querer jogar meu MacBook pela janela (eu tenho um aplicativo com uma extensão e encontrei esse problema ao executá-la). Tem que haver uma maneira "certa" de fazer isso com namespaces, simplesmente não consigo descobrir.
S'pht'Kr
Yup isso funcionou para mim quando se trabalha com modelos de dados compartilhados em um espaço de trabalho
naz
31

A resposta aceita para essa pergunta me ajudou a resolver o mesmo problema, mas eu tinha uma ressalva que achava que seria útil para outras pessoas. Se o nome do seu projeto (módulo) tiver um espaço, você deverá substituir o espaço por um sublinhado. Por exemplo:

Entidade: MyEntity Classe: My_App_Name.MyClass

Mannix
fonte
Exatamente o que eu estava procurando! Felicidades!
manosim
Quando definimos o nome da classe com AppName.ClassName, o Xcode 9 remove o ponto e cria uma classe sem um ponto. Portanto, isso não está mais no Xcode 9. *.
precisa saber é
17

Lembre-se de remover seu módulo :

insira a descrição da imagem aqui

Bartłomiej Semańczyk
fonte
1
Isso corrigiu meu problema!
simme 26/07/2015
2
Isso corrigiu meu problema.
Jason Huh
9

Dependendo se você estiver executando como App vs Tests, o problema pode ser que o aplicativo está procurando <appName>.<entityName>e quando está sendo executado como teste <appName>Tests.<entityName>. A solução que eu uso no momento (Xcode 6.1) é NÃO preencher o Classcampo na interface do usuário CoreData e fazê-lo no código.

Esse código detectará se você está executando como App vs Tests e usa o nome correto do módulo e atualiza o managedObjectClassName.

lazy var managedObjectModel: NSManagedObjectModel = {
    // The managed object model for the application. This property is not optional...
    let modelURL = NSBundle.mainBundle().URLForResource("Streak", withExtension: "momd")!
    let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)!

    // Check if we are running as test or not
    let environment = NSProcessInfo.processInfo().environment as [String : AnyObject]
    let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"

    // Create the module name
    let moduleName = (isTest) ? "StreakTests" : "Streak"

    // Create a new managed object model with updated entity class names
    var newEntities = [] as [NSEntityDescription]
    for (_, entity) in enumerate(managedObjectModel.entities) {
        let newEntity = entity.copy() as NSEntityDescription
        newEntity.managedObjectClassName = "\(moduleName).\(entity.name)"
        newEntities.append(newEntity)
    }
    let newManagedObjectModel = NSManagedObjectModel()
    newManagedObjectModel.entities = newEntities

    return newManagedObjectModel
}()
Ludovic Landry
fonte
1
Tentei sua solução, mas estou recebendo "erro fatal: o elemento NSArray falhou ao corresponder ao tipo de elemento Swift Array" ao converter o NSManagedObject para sua classe real. Na verdade, não quando se projeta, mas ao tentar entrar em um loop for.
Rodrigo Ruiz
1
Esse exemplo não funciona se você tiver algum tipo de herança em seu modelo. Para cada nova entidade, você também precisa iterar sobre os subentitos e atualizá-los com as novas entidades que você criou.
Anton
8

Se você estiver usando um hífen no nome do seu projeto como "My-App", use um sublinhado em vez do hífen como "My_App.MyManagedObject". Em geral, observe o nome do arquivo xcdatamodeld e use o mesmo prefixo daquele nome. Ou seja, "My_App_1.xcdatamodeld" requer o prefixo "My_App_1"

Rien
fonte
1
Uau. Obrigado, obrigado, obrigado. Eu estaria interessado em saber onde nos docs da Apple que pequena pepita é :)
nh32rg
5

Isso pode ajudar aqueles com o mesmo problema. Eu estava, com Swift 2 e Xcode 7 beta 2.

A solução no meu caso era para comentar @objc(EntityName)no EntityName.swift.

Enreas
fonte
3

Eu tive o mesmo aviso, embora meu aplicativo parecesse funcionar bem. O problema era que, ao executar Editor> Criar subclasse NSManagedObject na última tela, usei o local padrão do grupo, sem destinos exibidos ou verificados, que salvou a subclasse no diretório MyApp superior, onde MyApp.xcodeproj estava localizado.
O aviso desapareceu quando, em vez disso, mudei o Grupo para a subpasta MyApp e verifiquei o destino MyApp.

Daniel Ford
fonte
2

As respostas acima foram úteis. Essa verificação rápida de sanidade pode poupar algum tempo. Vá para Projeto> Fases de construção> Compilar fontes e remova o xcdatamodeld e os arquivos de modelo com o botão "-" e adicione-os novamente com o botão "+". Reconstruir - isso pode cuidar disso.

Trevorgrayson
fonte
2

A propósito, tenha cuidado com o que você adiciona como prefixo: Meu aplicativo é chamado "ABC-def" e o Xcode converteu o "-" em um "_".

Para estar seguro, procure o localizador, encontre os arquivos do projeto e veja o que diz para o seu modelo de dados (por exemplo "ABC_def.xcdatamodeld") e use o que está escrito EXATAMENTE !!!

Mike
fonte
1

As respostas acima me ajudaram a resolver problemas diferentes relacionados ao Objective-C (talvez isso ajude alguém):

Se você refatorou os nomes das entidades, não se esqueça de alterar "Classe" no "Painel de Utilitários" também.

Vive
fonte
0

As respostas acima me ajudaram, mas isso pode ajudar alguém. Se, como eu, você os fez e ainda está tendo um problema, lembre-se de simplesmente 'limpar seu projeto'. Para o XCode8, Produto> Limpar. Então corra novamente.

Olaolu Akinsete
fonte