Я попытался создать приложение SwiftUI для iOS У меня есть отцовское представление, которое содержит список комнат чатов, отсортированных по дате последнего действия, когда я открываю дочернее представление (представление чата) и отправляю сообщение, которое я храню в основных данных, представление закрывается и снова открывается, я пробовал FetchRequest в представлении
Обратите внимание, в приведенном ниже примере я удалил весь ненужный код и просто показываю фрагмент кода в TextField, генерирующий ошибку. мой код
struct ChatsView: View {
@ObservedObject var chatsVM: ChatsVM = ChatsVM()
var body: some View {
GeometryReader { geometry in
NavigationView {
VStack {
List {
ForEach(self.search(self.chatsVM.rooms), id: \.self) { room in
NavigationLink(destination: ChatView(room: room)){
RoomItemView()
.environmentObject(room)
.listRowInsets(EdgeInsets())
}
}
}
}
.navigationBarTitle(Text("Chats"))
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
это вид чата
struct ChatView: View {
@FetchRequest var messages: FetchedResults<MessageCD>
init(room: RoomCD) {
self.chatVM = ChatVM(room: room)
// Do some kind of control flow for customizing the predicate here.
self._messages = FetchRequest(
entity: MessageCD.entity(),
sortDescriptors: [NSSortDescriptor(key: "createdAt", ascending: false)],
predicate: NSPredicate(format: "room == %@", room)
)
}
var body: some View {
GeometryReader { geometry in
VStack {
VStack(alignment: .center, spacing: 5) {
ZStack {
VStack(spacing: 0) {
if self.chatVM.isImageEditing {
VStack {
PreviewToSendView(inputImage: self.$chatVM.imageSelected, isEditting: self.$chatVM.isImageEditing, url: self.$chatVM.urlFile)
}
} else{
Rectangle()
.fill(Color.clear)
.frame(height: 40)
self.messagesList
if self.chatVM.urlFile != nil {
self.documentPreview
}
}
self.messageOptionsAndInput
}
VStack {
if !self.chatVM.isImageEditing {
self.countdown
}
Spacer()
}
}
.onReceive(
NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)
.receive(on: RunLoop.main),
perform: self.updateKeyboardHeight
)
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
print("Moving back to the foreground!")
}
}
}
.navigationBarHidden(self.chatVM.isImageEditing)
.navigationBarTitle("", displayMode: .inline)
.navigationBarBackButtonHidden(true)
.navigationBarItems(trailing:
HStack {
if self.chatVM.isImageEditing {
EmptyView()
} else {
self.titleBar
.frame(width: (geometry.size.width * 1) - 20)
}
}
)
.padding(.bottom, self.keyboardHeight)
.transition(.slide)
.animation(.easeIn(duration: self.keyboardAnimationDuration))
.animation(.easeOut(duration: self.keyboardAnimationDuration))
.edgesIgnoringSafeArea((self.keyboardHeight > 0) ? [.bottom] : [])
}
}
func sendMessageText() {
self.chatVM.sendMessage(text: self.typingMessage ?? "")
self.typingMessage = nil
self.chatVM.isImageEditing = false
self.endEditing(true)
}
private var messagesList: some View {
List {
ForEach(messages, id: \.self) { message in
MessageView(currentMessage: message)
.transition(.fade)
.animation(.easeIn)
.scaleEffect(x: 1, y: -1, anchor: .center)
.listRowInsets(EdgeInsets())
}
}
.listSeparatorStyleNone()
.onAppear() {
self.chatVM.seenMessages()
}
.scaleEffect(x: 1, y: -1, anchor: .center)
.listRowInsets(EdgeInsets())
.padding(5)
}
}
и ObservableObject
class ChatVM: ObservableObject {
private let managedObjectContext: NSManagedObjectContext
private let sessionService = SessionService.shared
private let socketIO = SocketIOManager.sharedInstance
@Published var room: RoomCD
// Image
@Published var imageSelected: UIImage? = nil
@Published var showImagePicker: Bool = false
@Published var isImageEditing: Bool = false
// Doc
@Published var showDoc: Bool = false
@Published var isLoading = false
@Published var urlFile:URL? = nil
// Camera
@Published var showCameraPicker: Bool = false
// IncidentView
@Published var showIncidentView: Bool = false
// Video
@State var dataFile: Data? = nil
init(room: RoomCD) {
let context = AppDelegate.shared.persistentContainer.viewContext
self.managedObjectContext = context
self.room = room
}
func sendMessage(text: String) {
let text = text.trimmingCharacters(in: .whitespacesAndNewlines)
let message = self.handleSendMessage(text: text)
self.sendToServer(text, message)
self.imageSelected = nil
self.dataFile = nil
self.urlFile = nil
}
func handleSendMessage(text: String) -> MessageCD {
let message = MessageCD(context: self.managedObjectContext)
message.text = text
message.isMyMessage = true
message.id = UUID()
message.status = 0
message.createdAt = Date()
message.room = room
if let userS = self.sessionService.user,
let userOwner = userS.getUserDB() {
message.owner = userOwner
}
room.addToMessages(message)
room.lastMessage = message
room.lastActivity = Date()
return message
}
func sendToServer(_ text: String, _ message: MessageCD) {
var param = JsonDictionary()
param["room"] = room.m_id ?? ""
param["text"] = text
DispatchQueue.global(qos: .background).async {
APICaller.shared.callSendMessage(params: param) { (isSuccess, messageStr, json) in
print("json js", json)
DispatchQueue.global(qos: .background).async {
guard let newMessageJs = json["message"] as? JsonDictionary else {
return
}
self.managedObjectContext.perform {
message.initFrom(json: newMessageJs)
message.status = 2
self.managedObjectContext.saveIfNeeded()
self.socketIO.sendMessage(dataJs: json)
}
}
}
}
}