The logic I'm trying to create for my logging in the app is:
A ScrollView with a frame to control the height and allow the user to see logs from actions in the app, the logs should be scrollable to scroll up on previous appended logs.
I've created a log view model which allows the log to be set and then appends to a log array and then get.
The logs are set through actions in callbacks from various view controllers and actions from the user.
currently I have the logs being retrieved in the
UIViewControllerRepresentable-updateUIViewControllermethod.The code works for each callback and for the user actions, the problems are: 5a. It's not scrollable to go to the top of the log messages, 5b. The log messages keep showing on the screen as
updateUIViewControlleris continuously being called.
I was trying to think of a way to empty the array after each action, but not sure the best way to go about this.
Code:
LogViewModel:
import UIKit
import SwiftUI
class LogViewModel: ObservableObject{
@Published var mTime: String = ""
@Published var id: String = "#"
@Published var mMessage: String = ""
private var fullLogMessages: [String] = [""]
func setTimeFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
return formatter
}
func setTheTime(date: Date){
self.mTime = setTimeFormatter().string(from: date)
}
func getTheTime() -> String{
return self.mTime
}
func setTheMessage(mMessage: String) {
ThreadUtil.runAsyncOnMainThread { [weak self] in
self?.mMessage = mMessage
}
}
func getTheMessage() -> String {
return self.mMessage
}
func getTheFullLogMessage() -> [String] {
let fullLog: String = getTheTime() + " - " + getTheGivenId() + " - " + getTheMessage()
self.fullLogMessages.append(fullLog)
return self.fullLogMessages
}
func setTheGivenId(id: String) {
ThreadUtil.runAsyncOnMainThread { [weak self] in
self?.id = id
}
}
func getTheGivenId() -> String {
return self.id
}
}
Controllers: In each controller I've created a method like this to set the log messages:
func setTheLogMessages(message: String) {
self.logViewModel.setTheTime(date: date)
self.logViewModel.setTheMessage(mMessage: message)
}
In the view I have the UIViewControllerRepresentable:
struct MyScreenView_UI: UIViewControllerRepresentable{
@ObservedObject var viewModel: myScreenViewModel
@ObservedObject var logViewModel: LogViewModel
@Binding var fullLogMessage: [String]
func makeUIViewController(context: Context) -> some myViewController {
print(#function)
return myViewController(viewModel: viewModel, logViewModel: logViewModel)
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
print(#function)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
fullLogMessage = logViewModel.getTheFullLogMessage()
}
}
}
and the ScrollView for the UI:
ScrollView{
VStack(alignment: .leading, content: {
Text("Logs")
.font(.footnote).fontWeight(.medium)
ForEach($fullLogMessage, id: \.self) { $logMessage in
Text(logMessage)
.font(.custom("FONT_NAME", size: 12))
.disabled(true)
}
})
.frame(width: 400, height: 50, alignment: .leading)
}
updateUIViewControllerunless you have checks so stuff doesn't get called unnecessarily. You can set it inmakeUIViewControllerand then call it on purpose with the methods when you need it. Your code isn't reproducible so I can't give you more than that. I can't see the whole picture.updatemethod in the second option.LogManagerthat everything and anything accesses theclasswhenever it wants. I have aNotificationthat gets posted when there is an error and aViewModelat the top level subscribes to it and posts andAlertfor me, sometimes I don't even know where the error comes from. You can easily maintain the log in the oneclassand make it visible. It is really seamless. Its is kind of an MVC setup because I use services to incorporate crashlytics and I have a system service the decides between Logger and osLog based on version