You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中CoreData UserEntity'unrecognized selector'错误排查与解决

问题描述

开发集成CoreData与登录功能的App时,通过LoginView和LoginViewModel将用户信息传递至ContentView,运行时触发崩溃:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[abcText2Speech.UserEntity firstname]: unrecognized selector sent to instance 0x6000017f14c0'

相关代码片段

LoginViewModel中的signup方法

func signup()-> (UserEntity, Int){
    @StateObject var dataController = DataController.shared

    buttonState = .loading(title: "Loading", systemImage: "person")
    
    DispatchQueue.main.asyncAfter(deadline: .now()+1.5){
        if self.isValidEmail() && self.isValidPassword() && self.isValidName(){
            self.isLoggedIn = true
            dataController.addUser(firstname: self.fname, lastname: self.lname, email: self.email, password: self.password)
            
            print("signed in")
            print(dataController.savedEntities[(dataController.savedEntities.count)-1].email ?? "email")
            print(self.isLoggedIn)
            self.buttonState = .enabled(title: "Login", systemImage: "checkmark.circle")
        } else{
            self.buttonState = .disabled(title: "Fill out all fields to log in", systemImage: "exclamationmark.circle")
        }
    }
    print("Test")
    print(dataController.savedEntities[(dataController.savedEntities.count)-1].email ?? "email")
        
    return (dataController.savedEntities[(dataController.savedEntities.count)-1], 1)
}

注:此方法内的打印内容均正常输出。

SignUpView相关代码

ActionButton(state: $model.buttonState, onTap:{
    signup = model.signup()
    user = signup.0

                    }, backgroundColor: .primary , foregroundColor: Color(UIColor.systemBackground)
                    )
                    NavigationLink (destination: LoginView()){
                        Text("Already have a profile? Click here to Log in")
                    }
                }
            } label: {
                Label("Welcome!", systemImage: "hand.wave")
            }
            .padding()
            .textFieldStyle(.plain)
            let keyboard = abcTextViewModel()
            
            NavigationLink (destination: ContentView(viewModel: keyboard, currentUser: user) , isActive: $model.isLoggedIn){
                
                EmptyView()
            }

ContentView触发错误的代码

Text(currentUser.firstname ?? "name")

DataController代码

import Foundation
import CoreData

class DataController: ObservableObject{
static let shared = DataController()

    let container: NSPersistentContainer
    @Published var savedEntities: [UserEntity] = []
    
    init(){
        container = NSPersistentContainer(name: "Model")
        container.loadPersistentStores { description, error in
            if let error = error {
                print("Core Data failed to load: \(error.localizedDescription)")
            }
        }
        fetchUsers()
    }
    
    func fetchUsers() {
        let request = NSFetchRequest<UserEntity>(entityName: "UserEntity")
        
        do {
            savedEntities = try container.viewContext.fetch(request)
        } catch let error {
            print("Error while fetching: \(error)")
        }
    }
    
    func addUser(firstname: String, lastname: String, email: String, password: String) {
        let newUser = UserEntity(context: container.viewContext)
        newUser.firstname = firstname
        newUser.lastname = lastname
        newUser.email = email
        newUser.id = password
        saveData()
    }
    
    func saveData(){
        do {
            try container.viewContext.save()
            fetchUsers()
        } catch let error {
            print("Error saving: \(error)")
        }
    } }

崩溃触发位置(abcText2SpeechApp结构体)

import SwiftUI

@main
struct abcText2SpeechApp: App {
    var body: some Scene {
        WindowGroup{
            SignUpView()
                .environment(\.managedObjectContext, DataController.shared.container.viewContext)
            
                
        }
        
    }
}

问题原因及解决方案

核心问题1:异步操作导致返回的UserEntity无效

signup()方法内通过DispatchQueue.main.asyncAfter延迟执行用户创建逻辑,但方法本身会立即返回当前的最后一个UserEntity——此时新用户尚未创建,返回的是旧数据(甚至可能触发数组越界)。异步代码执行完成后,传递给ContentView的仍是之前返回的无效实体,调用firstname时触发找不到方法的崩溃。

核心问题2:@StateObject声明位置错误

@StateObject var dataController = DataController.shared写在方法内部是错误的,@StateObject仅能用于View或ObservableObject的属性中,方法内声明会导致CoreData上下文混乱。

修复步骤

  1. 重构LoginViewModel的signup方法
    移除方法返回值,通过@Published属性传递最新用户:

    class LoginViewModel: ObservableObject {
        // 保留原有属性(fname、lname、email、password、isLoggedIn、buttonState等)
        @Published var currentUser: UserEntity?
        let dataController = DataController.shared // 移至属性位置,使用let而非@StateObject
    
        func signup() {
            buttonState = .loading(title: "Loading", systemImage: "person")
            
            DispatchQueue.main.asyncAfter(deadline: .now()+1.5){
                guard self.isValidEmail(), self.isValidPassword(), self.isValidName() else {
                    self.buttonState = .disabled(title: "Fill out all fields to log in", systemImage: "exclamationmark.circle")
                    return
                }
                
                self.dataController.addUser(firstname: self.fname, lastname: self.lname, email: self.email, password: self.password)
                // 取最新创建的用户
                if let newUser = self.dataController.savedEntities.last {
                    self.currentUser = newUser
                }
                self.isLoggedIn = true
                self.buttonState = .enabled(title: "Login", systemImage: "checkmark.circle")
                
                print("signed in")
                print(self.dataController.savedEntities.last?.email ?? "email")
                print(self.isLoggedIn)
            }
        }
    }
    
  2. 修改SignUpView的跳转逻辑
    绑定ViewModel的currentUser,确保仅当用户实体存在时才跳转:

    struct SignUpView: View {
        @StateObject var model = LoginViewModel()
        @State private var keyboard = abcTextViewModel()
    
        var body: some View {
            // 原有视图代码...
            
            ActionButton(state: $model.buttonState, onTap:{
                model.signup()
            }, backgroundColor: .primary , foregroundColor: Color(UIColor.systemBackground))
            
            NavigationLink(
                destination: ContentView(viewModel: keyboard, currentUser: model.currentUser!),
                isActive: $model.isLoggedIn,
                label: { EmptyView() }
            )
            .disabled(model.currentUser == nil) // 防止用户未创建完成时跳转
            
            NavigationLink(destination: LoginView()){
                Text("Already have a profile? Click here to Log in")
            }
        }
    }
    
  3. 校验CoreData实体属性
    确认CoreData模型文件中,UserEntity确实存在名为firstname的属性(注意大小写匹配,若模型中为firstName,需同步修改代码中的调用为currentUser.firstName)。

  4. 消除数组越界风险
    替换dataController.savedEntities[(dataController.savedEntities.count)-1]为dataController.savedEntities.last,并添加可选绑定判断,避免数组为空时的崩溃。

内容的提问来源于stack exchange,提问作者Jaanvi chirimar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 13:55:58