Hello,
I am trying to learn Swift. I have the code below.
With a button click I can move to a new view. What I would like to do is once the new view is displayed have the navigation link destination changed to "CurrentView" and the button text to "Previous View".
Any insight on how to resolve this issue would be appreciated.
Regards,
Chris
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: NextContentView()){
Text("Next View")
}
}
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I have an SQLite table that holds a fund name, as a string, a time stamp, as an iso8601 string, and a close, as a double. SQLite appears to call strings text and a double a real. In the function below, I query this table with a select statement to return the last year of data based on the latest date in the table. To get the latest date I have a separate function that finds the latest date, converts if from a string to a date, subtracts 1 year from the date then converts it back to an iso8601 date string. It is this date string that is being passed into the function below as the variable startingDate. The function below works just fine and is relatively fast. What I am trying to determine is there a way to modify the select statement in the function below such that it does everything and I will no longer have to separately calculate the starting date and pass it in?
func QuerySQLiteData(db: OpaquePointer?, fundName: String, startingDate: String) -> [TradingDay] {
var queryResults: [TradingDay] = []
let timeStampFormatter = ISO8601DateFormatter()
let queryTradingDaysStatement = """
Select
FundName,
TimeStamp,
Close
FROM
TradingDays
WHERE
FundName = '\(fundName)'
AND
TimeStamp >= '\(startingDate)'
ORDER By
TimeStamp ASC
;
"""
var queryTradingDaysCPtr: OpaquePointer?
var tradingDay: TradingDay = TradingDay(fundName: "", timeStamp: Date(), close: 0.0)
if sqlite3_prepare_v2(db, queryTradingDaysStatement, -1, &queryTradingDaysCPtr, nil) == SQLITE_OK {
while (sqlite3_step(queryTradingDaysCPtr) == SQLITE_ROW) {
let fundName = sqlite3_column_text(queryTradingDaysCPtr, 0)
let timeStamp = sqlite3_column_text(queryTradingDaysCPtr, 1)
let close = sqlite3_column_double(queryTradingDaysCPtr, 2)
let fundNameAsString = String(cString: fundName!)
let timeStampAsString = String(cString: timeStamp!)
let timeStampAsDate = timeStampFormatter.date(from: timeStampAsString)!
tradingDay.fundName = fundNameAsString
tradingDay.timeStamp = timeStampAsDate
tradingDay.close = close
queryResults.append(tradingDay)
} // end while loop
} else {
let errorMessage = String(cString: sqlite3_errmsg(db))
print("\nQuery is not prepared \(errorMessage)")
}
sqlite3_finalize(queryTradingDaysCPtr)
return queryResults
}
I am creating my own NSTableView for macOS. I can display formatted data and provide scrolling by embedding it in a ScrollView. When the table is displayed there is a horizontal gap between adjacent cells. The same gap occurs between the ScrollView header cells. I do not know how to. solve this problem. There is one other issue, I do not know how to set the vertical height of the ScrollView header cells. I would like to make them taller. Below is my code along with the test data.
struct ClosingValue: Identifiable {
var id: UUID
var name: String
var date: String
var close: Float
static var closingValues = [
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 01, 2009", close: 1.10),
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 02, 2009", close: 2.10),
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 03, 2009", close: 3.10),
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 04, 2009", close: 4.10),
ClosingValue(id: UUID(), name: "Stock1", date: "Nov 05, 2009", close: 5.10)
]
}
struct BetterTableView: NSViewRepresentable {
class Coordinator: NSObject, NSTableViewDelegate, NSTableViewDataSource {
@State var closingValues: [ClosingValue] = ClosingValue.closingValues
func numberOfRows(in tableView: NSTableView) -> Int {
closingValues.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var nsView = NSView()
let tableColumnWidth: CGFloat = 135
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
let attributes1: [NSAttributedString.Key: Any] = [
.foregroundColor: NSColor.blue,
.paragraphStyle: paragraphStyle,
.font: NSFont(name: "Arial", size: 14.0) as Any
]
switch tableColumn {
case tableView.tableColumns[0]:
tableColumn?.width = tableColumnWidth
let attributedString = NSAttributedString(string: closingValues[row].name, attributes: attributes1)
let tempNSView = NSTextField()
tempNSView.backgroundColor = NSColor.white
tempNSView.isBordered = true
tempNSView.isEditable = false
tempNSView.attributedStringValue = attributedString
nsView = tempNSView
case tableView.tableColumns[1]:
tableColumn?.width = tableColumnWidth
let attributedString = NSAttributedString(string: closingValues[row].date, attributes: attributes1)
let tempNSView = NSTextField()
tempNSView.backgroundColor = NSColor.white
tempNSView.isBordered = true
tempNSView.isEditable = false
tempNSView.attributedStringValue = attributedString
nsView = tempNSView
case tableView.tableColumns[2]:
tableColumn?.width = tableColumnWidth
let closeAsString = String(format: "$%.2f", closingValues[row].close)
let attributedString = NSAttributedString(string: closeAsString, attributes: attributes1)
let tempNSView = NSTextField()
tempNSView.backgroundColor = NSColor.white
tempNSView.isBordered = true
tempNSView.isEditable = false
tempNSView.attributedStringValue = attributedString
nsView = tempNSView
default:
print("problem in table view switch statement")
}
return nsView
}
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeNSView(context: Context) -> NSScrollView {
let tableView = NSTableView()
tableView.delegate = context.coordinator
tableView.dataSource = context.coordinator
tableView.addTableColumn(NSTableColumn())
tableView.addTableColumn(NSTableColumn())
tableView.addTableColumn(NSTableColumn())
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: NSColor.black,
.paragraphStyle: paragraphStyle,
.font: NSFont.systemFont(ofSize: 14)
]
let column0AttributedString = NSAttributedString(string: "Stock", attributes: attributes)
let column0Header = tableView.tableColumns[0]
column0Header.headerCell.drawsBackground = true
column0Header.headerCell.backgroundColor = NSColor.systemMint
column0Header.headerCell.alignment = .center
column0Header.headerCell.attributedStringValue = column0AttributedString
let column1AttributedString = NSAttributedString(string: "Date", attributes: attributes)
let column1Header = tableView.tableColumns[1]
column1Header.headerCell.drawsBackground = true
column1Header.headerCell.backgroundColor = NSColor.systemMint
column1Header.headerCell.alignment = .center
column1Header.headerCell.attributedStringValue = column1AttributedString
let column2AttributedString = NSAttributedString(string: "Closing Value", attributes: attributes)
let column2Header = tableView.tableColumns[2]
column2Header.headerCell.drawsBackground = true
column2Header.headerCell.backgroundColor = NSColor.systemMint
column2Header.headerCell.alignment = .center
column2Header.headerCell.attributedStringValue = column2AttributedString
let scrollView = NSScrollView()
scrollView.documentView = tableView
return scrollView
}
func updateNSView(_ nsView: NSScrollView, context: Context) {
let tableView = (nsView.documentView as! NSTableView)
// work on this section
}
}
I have a program that utilizes a Navigation Stack. I want each view to be centered on the screen and be a specific size. I accomplished this with some position logic which sets the views frame size and origin. There is unique position logic for each of the 3 view sizes, the Content View and 2 navigation destination views. When the Content View is first displayed the associated position logic code runs and the view is the correct size and centered. The same is true every time one of the 2 navigation destination views is displayed. Unfortunately, when I return to the Content View from a navigation destination view the position logic does not run again and the Content View is now the same size and position as the previous navigation destination view. How do I resolve this problem? Below is the position logic code associated with the Content View and how it is called.
@main
struct TableViewTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.hostingWindowPositionHome(
screen: .main
)
}
}
}
extension View {
func hostingWindowPositionHome(
screen: NSScreen? = nil
) -> some View {
modifier(
WindowPositionModifierHome(
screen: screen
)
)
}
}
private struct WindowPositionModifierHome: ViewModifier {
let screen: NSScreen?
func body(content: Content) -> some View {
content.background(
HostingWindowFinderHome {
$0?.setPositionHome(in: screen)
}
)
}
}
private struct HostingWindowFinderHome: NSViewRepresentable {
var callback: (NSWindow?) -> ()
func makeNSView(context: Self.Context) -> NSView {
let view = NSView()
DispatchQueue.main.async { self.callback(view.window) }
return view
}
func updateNSView(_ nsView: NSView, context: Context) {
DispatchQueue.main.async { self.callback(nsView.window) }
}
}
extension NSWindow {
func setPositionHome(in screen: NSScreen?) {
let nsRectangle: NSRect = NSRect(x: 1055.0, y: 370.0, width: 450, height: 700)
setFrame(nsRectangle, display: true)
}
}
I have a view that displays a chart based on an array. Each element in the array is all of the attributes associated with a core data entity. The data is properly displayed. I would like to animate the rendering of the charts data but cannot seem to figure out how to do it. If someone could point me in the right direction it would be appreciated. Below is the code.
struct ClosingValuesChart: View {
@State private var selectedDate: Date? = nil
@State private var selectedClose: Float? = nil
@State private var xAxisLabels: [Date] = []
var closingValues: [TradingDayClose] = []
var heading: String = ""
init(fundName: String, numYears: Int, closingValues: [TradingDayClose]) {
self.heading = fundName + String(" - \(numYears) Year")
self.closingValues = closingValues
}
var body: some View {
GroupBox (heading) {
let xMin = closingValues.first?.timeStamp
let xMax = closingValues.last?.timeStamp
let yMin = closingValues.map { $0.close }.min()!
let yMax = closingValues.map { $0.close }.max()!
let xAxisLabels: [Date] = GetXAxisLabels(xMin: xMin!, xMax: xMax!)
var yAxisLabels: [Float] {
stride(from: yMin, to: yMax + ((yMax - yMin)/7), by: (yMax - yMin) / 7).map { $0 }
}
Chart {
ForEach(closingValues) { value in
LineMark(
x: .value("Time", value.timeStamp!),
y: .value("Closing Value", value.close)
)
.foregroundStyle(Color.blue)
.lineStyle(StrokeStyle(lineWidth: 1.25))
}
}
.chartXScale(domain: xMin!...xMax!)
.chartXAxisLabel(position: .bottom, alignment: .center, spacing: 25) {
Text("Date")
.textFormatting(fontSize: 14)
}
.chartXAxis {
AxisMarks(position: .bottom, values: xAxisLabels) { value in
AxisGridLine(centered: true, stroke: StrokeStyle(lineWidth: 1))
AxisValueLabel(anchor: .top) {
if value.as(Date.self) != nil {
Text("")
}
}
}
}
.chartYScale(domain: yMin...yMax)
.chartYAxisLabel(position: .leading, alignment: .center, spacing: 25) {
Text("Closing Value")
.font(Font.custom("Arial", size: 14))
.foregroundColor(.black)
}
.chartYAxis {
AxisMarks(position: .leading, values: yAxisLabels) { value in
AxisGridLine(centered: true, stroke: StrokeStyle(lineWidth: 1))
AxisValueLabel() {
if let labelValue = value.as(Double.self) {
Text(String(format: "$ %.2f", labelValue))
.textFormatting(fontSize: 12)
}
}
}
}
.chartOverlay { proxy in
GeometryReader { geometry in
ChartOverlayRectangle(selectedDate: $selectedDate, selectedClose: $selectedClose, proxy: proxy, geometry: geometry, xMin: xMin!, xMax: xMax!, closingValues: closingValues)
}
}
.overlay {
XAxisDates(dateLabels: xAxisLabels)
}
.overlay {
DateAndClosingValue(selectedDate: selectedDate ?? Date(), selectedClose: selectedClose ?? 0.0)
}
}
.groupBoxStyle(ChartGroupBoxStyle())
}
}
Hello,
I have a swift program that allows me to transition from a primary view controller to a secondary view controller. To complete this task I create a segue between a button on the primary view controller and the secondary view controller by dragging from the button to the secondary view controller then assigning an identifier to this seque. The balance is handled programatically.
What I would like to do is eliminate the creation of the segue via dragging and handle transitioning to the secondary view controller 100% in code. I have given the secondary view controller a storyboard id so it seems logical to me that I should be able to locate the secondary view controller via this identifier and use this to transition to it.
Is there a way to do this?
Regards,
Chris
Hello,
I have the code below which creates a view. Unfortunately it puts a small gap between the two LazyVGrids that I would like to eliminate. Any suggestions would be appreciated.
Below is my code.
Chris
import SwiftUI
struct SecondView: View {
var columns = [
GridItem(.fixed(100), spacing: 0.1),
GridItem(.fixed(100), spacing: 0.1),
GridItem(.fixed(100), spacing: 0.1),
GridItem(.fixed(100), spacing: 0.1),
GridItem(.fixed(100), spacing: 0.1)
]
let principalData: convertCSVtoArray
var body: some View {
let numArrays = principalData.cvsData.count
let numElementsPerArray = principalData.cvsData[0].count
VStack{
Text("")
Text("Historical Data")
.font(.title)
.padding(5)
Divider()
LazyVGrid(
columns: columns,
alignment: .center,
spacing: 0)
{
ForEach(0..<1) {row in
ForEach(0..<numElementsPerArray) {col in
Rectangle()
.foregroundColor(.white)
.overlay (Text(principalData.cvsData[row][col]).bold())
.frame(height: 30)
.border(Color.gray, width: 2)
}
}
}
ScrollView{
LazyVGrid(
columns: columns,
alignment: .center,
spacing: 0)
{
ForEach(1..<numArrays) {row in
ForEach(0..<numElementsPerArray) {col in
Rectangle()
.foregroundColor(.white)
.overlay (Text(principalData.cvsData[row][col]))
.frame(height: 30)
.border(Color.gray, width: 0.5)
}
}
}
ForEach(0..<30){index in
Text("")
}
}// end scrollview
}
}
}
I have a view that shows a table and 4 buttons. Each button allows the user to step forward and backwards through the data. Buttons are enabled and disabled based on where you are in the data. If you are less than 200 values to the end of the data for example, the "Page Down - 200" button is disabled. Everything works fine if the mouse is used to run the code associated with each button. But in this case I think the buttons never get focus. Focus I think remains with the sidebar content item that brought up the table view (am using a navigation split view). If I tab over to the page down 200 button and use the space bar to run its associated code I get the error "AttributeGraph: cycle detected through attribute 864480". I think the problem lies with attempting to disable the button while it has focus but am not 100% sure. I have tried to change the focus prior to disabling the button but I get the same error. I think there is some fundamental that I am missing. Below is my table view along with the page down 200 button view.
struct DataTable: View {
@FocusState var buttonWithFocus: Field?
@ObservedObject var vm: ButtonsViewModel = ButtonsViewModel.shared
@State private var hasAppeared = false
var closingValues: [TradingDayClose]
var heading: String = ""
init(fundName: String, closingValues: [TradingDayClose]) {
self.heading = fundName
self.closingValues = closingValues
}
var body: some View {
HStack {
Spacer()
.frame(width: 150)
GroupBox(heading) {
if hasAppeared {
Table (of: TradingDayClose.self) {
TableColumn("") { closingValue in
Text(dateToStringFormatter.string(from: closingValue.timeStamp!))
.id(closingValue.timeStamp!)
.textFormatting(fontSize: 14)
.frame(width: 100, alignment: .center)
} // end table column
TableColumn("") { closingValue in
Text(String(format: "$ %.2f", closingValue.close))
.textFormatting(fontSize: 14)
.frame(width: 100, alignment: .center)
} // end table column
} rows: {
ForEach((closingValues.indices), id: \.self) { index in
if vm.showData[index] == true {
TableRow(closingValues[index])
}
}
}
.focusable(false)
.frame(width: 250)
.overlay {
let tempValue1: String = "Date"
let tempValue2: String = "Closing Value"
Text(tempValue1).position(x: 63, y: 15)
.textFormatting(fontSize: 16)
Text(tempValue2).position(x: 180, y: 15)
.textFormatting(fontSize: 16)
}
} // end has appeared
} // end group box
.groupBoxStyle(Table2GroupBoxStyle())
Spacer()
.frame(width: 50)
VStack {
Spacer()
.frame(height: 100)
Form {
Section {
ButtonUp25(closingValuesCount: closingValues.count)
.focused($buttonWithFocus, equals: .btnUp25)
ButtonUp200(closingValuesCount: closingValues.count)
.focused($buttonWithFocus, equals: .btnUp200)
} header: {
Text("Page Up")
}
Spacer()
.frame(height: 20)
Section {
ButtonDown25(closingValuesCount: closingValues.count)
.focused($buttonWithFocus, equals: .btnDn25)
ButtonDown200(buttonWithFocus: $buttonWithFocus, closingValuesCount: closingValues.count)
.focused($buttonWithFocus, equals: .btnDn200)
} header: {
Text("Page Down")
}
} // end form
.navigationTitle("Data Table")
Spacer()
.frame(height: 100)
} // end v stack
Spacer()
.frame(width: 50)
} // end h stack
.onAppear {
vm.InitializeShowData(closingValueCount: closingValues.count)
vm.InitializeIndexes()
vm.InitializeButtons()
for i in vm.startIndex...vm.endIndex {
DispatchQueue.main.asyncAfter(deadline: .now() + vm.renderRate * Double(i)) {
vm.showData[i] = true
} // end dispatch queue main async
}
hasAppeared = true
}
} // end body
} // end struct
struct ButtonDown200: View {
var buttonWithFocus: FocusState<Field?>.Binding
@ObservedObject var vm: ButtonsViewModel = ButtonsViewModel.shared
var closingValuesCount: Int
var body: some View {
Button("Page Down - 200") {
for i in vm.startIndex...vm.endIndex {
vm.showData[i] = false
}
vm.startIndex = vm.startIndex + 200
vm.endIndex = vm.startIndex + 24
var j: Int = 0
for i in vm.startIndex...vm.endIndex {
DispatchQueue.main.asyncAfter(deadline: .now() + vm.renderRate * Double(j)) {
vm.showData[i] = true
}
j = j + 1
}
if (closingValuesCount - 1) - (vm.startIndex + 200) < 25 {
// buttonWithFocus.wrappedValue = .btnDn25
vm.pageDownDisabled200 = true
}
if vm.startIndex > 24 {
vm.pageUpDisabled25 = false
}
if vm.startIndex - 200 >= 0 {
vm.pageUpDisabled200 = false
}
}
.controlSize(.large)
.buttonStyle(.borderedProminent)
.disabled(vm.pageDownDisabled200)
}
}
I have some test code in which I am attempting to create a spreadsheet style grid with data. The code creates a grid but I am unable to get the bottom border of one row to overlap the top border of the row below it but I can get it to work left to right with padding using a negative trailing border. Any assistance would be appreciated. Below is the code:
import SwiftUI
struct DateAndClose {
let date: Date
let close: Double
}
func BuildArray() -> [DateAndClose] {
var dataArray: [DateAndClose] = []
dataArray.append(DateAndClose(date: Date(), close: 123.4))
dataArray.append(DateAndClose(date: Date() + 10, close: 245.8))
dataArray.append(DateAndClose(date: Date() + 30, close: 329.3))
dataArray.append(DateAndClose(date: Date() + 45, close: 438.9))
return dataArray
}
struct Test: View {
let heading: String
var body: some View {
let moArray: [DateAndClose] = BuildArray()
Text("")
Text(heading)
.font(.system(size: 18, weight: .bold, design: .default))
.foregroundColor(.blue)
.padding(.trailing, 24)
Text("")
List(moArray, id: \.date) { element in
ListRow1(element: element)
}
.frame(width: 250)
.background(Color.gray)
Spacer()
}
}
struct ListRow1: View {
let element: DateAndClose
var body: some View {
let theDate = dateToStringFormatter.string(from: element.date)
let theClose = String(format: "$%.2f", element.close )
let frameWidth: CGFloat = 100
let frameHeight: CGFloat = 25
let trailingPadding: CGFloat = -8.55
let bottomPadding: CGFloat = -4.8
HStack(){
Spacer()
Text(theDate)
.font(.system(size: 14, weight: .regular, design: .default))
.background(.white)
.frame(width: frameWidth, height: frameHeight, alignment: .center)
.border(.black, width: 1)
.padding(.trailing, trailingPadding)
.padding(.bottom, bottomPadding)
Text(theClose)
.font(.system(size: 14, weight: .regular, design: .default))
.background(.white)
.frame(width: frameWidth, height: frameHeight, alignment: .center)
.border(.black, width: 1)
.padding(.bottom, bottomPadding)
Spacer()
}
}
}
let dateToStringFormatter: DateFormatter = {
let result = DateFormatter()
result.dateFormat = "MM/dd/yy"
result.timeZone = TimeZone(secondsFromGMT: 0)
return result
}()
I am working with the new Charts Framework. The axis value labels for the x axis are dates (mm/dd/yy). I have 5 vertical grid lines and am trying to get the dates to be centered below the associated grid line with no success. One additional problem is that the values are in an array that contains 5 dates but only the first 4 get displayed. Any solutions will be appreciated. Below is the code.
struct LineChart: View {
private var localArray: [TradingDayPrices]
init(passedInArray: [TradingDayPrices]) {
self.localArray = passedInArray
}
var body: some View {
let minCloseValue: Double = localArray.map { $0.close }.min()!
let maxCloseValue: Double = localArray.map { $0.close }.max()!
let minDate: Date = localArray[0].timeStamp!
let itemCount: Int = localArray.count - 1
let maxDate: Date = localArray[itemCount].timeStamp!
Spacer()
GroupBox (label: Text("VOO Closing Values For The Past Year")
.font(.system(size: 20))
.fontWeight(.bold)
.frame(width: 700, height: 50, alignment: .center)) {
Chart {
ForEach(localArray) { item in
LineMark (
x: .value("TimeStamp", item.timeStamp!),
y: .value("Close", item.close)
)
.foregroundStyle(Color.blue)
.lineStyle(StrokeStyle(lineWidth: 1.25))
} // end for each
} // end chart
.padding(50)
.chartBackground { item in
Color.white
}
.chartXAxisLabel(position: .bottom, alignment: .center) {
Text("Date")
.font(.system(size: 14))
.foregroundColor(Color.black)
.frame(width: 50, height: 35, alignment: .bottom)
}
.chartYAxisLabel(position: .leading, alignment: .center, spacing: 0) {
Text("Closing Values")
.font(.system(size: 14))
.foregroundColor(Color.black)
}
.chartXAxis {
AxisMarks (values: GetXAxisLabels(min: minDate, max: maxDate)) { value in
AxisGridLine(stroke: StrokeStyle(lineWidth: 0.5))
.foregroundStyle(Color.gray)
AxisValueLabel() {
if let localDate = value.as(Date.self) {
let formattedDate = dateToStringFormatter.string(from: localDate)
Text(formattedDate)
.font(.system(size: 12))
.foregroundColor(Color.black)
}
} // end axis value label
} // end axis marks
} // end chart x axis
.chartYAxis {
AxisMarks (position: .leading, values: GetYAxisLabels(min: minCloseValue, max: maxCloseValue)) { value in
AxisGridLine(stroke: StrokeStyle(lineWidth: 0.5))
.foregroundStyle(Color.gray)
AxisValueLabel() {
if let localValue = value.as(Double.self) {
let formattedValue = String(format: "$%.2f", localValue)
Text(formattedValue)
.font(.system(size: 12))
.foregroundColor(Color.black)
}
}
} // end axis marks
} // end chart y axis
.chartXScale(domain: minDate...maxDate)
.chartYScale(domain: minCloseValue...maxCloseValue)
.frame(width: 700, height: 700, alignment: .center)
} // end group box
} // end of body
} // end of structure
func GetXAxisLabels(min: Date, max: Date) -> [Date] {
var xLabels: [Date] = []
let increment: Int = 90
for i in 0...3 {
let value = Calendar.current.date(byAdding: DateComponents(day: (i * increment)), to: min)!
xLabels.append(value)
}
xLabels.append(max)
print("\(xLabels)")
return xLabels
}
func GetYAxisLabels(min: Double, max: Double) -> [Double] {
var yLabels: [Double] = []
let increment: Double = (max - min) / 4
for i in 0...3 {
let value = min + (Double(i) * increment)
yLabels.append(value)
}
yLabels.append(max)
return yLabels
}
let dateToStringFormatter: DateFormatter = {
let result = DateFormatter()
result.dateFormat = "MM/dd/yy"
return result
}()
I have a core data program. I run my fetch request in a class i.e. a view model and assign it to a StateObject in the ContentView. When I attempt to use the fetch request results from the view model in a selected view, the DataTable, I get this message: Accessing StateObject's object without being installed on a View. This will create a new instance each time. Not sure what to do. Below is my code:
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@State var selection: DataDisplayed? = nil
@StateObject var dataViewModel: FetchCoreData = FetchCoreData()
var body: some View {
NavigationSplitView{
sidebarContent
.navigationSplitViewColumnWidth(min: 200, ideal: 200, max: 200)
} detail: {
detailContent
}
.navigationSplitViewStyle(.balanced)
.navigationTitle("Testing")
.frame(width: 1000, height: 800)
}
}
extension ContentView {
@ViewBuilder
var detailContent: some View {
if let selection = selection {
detailContent(for: selection)
.buttonStyle(.bordered)
} else {
Text("Make A Selection")
}
}
@ViewBuilder
func detailContent(for screen: DataDisplayed) -> some View {
switch screen {
case .VOODT: DataTable(dataModel: dataViewModel.fetchRequest)
case .NWDT: Text("Start")
case .VOOG1: Text("VOO Graph 1 Year")
default: Text("Not yet implemented")
}
}
}
class FetchCoreData: ObservableObject {
@FetchRequest var fetchRequest: FetchedResults<TradingDayPrices>
init() {
_fetchRequest = FetchRequest<TradingDayPrices>(sortDescriptors: [NSSortDescriptor(key: "timeStamp", ascending: true)], predicate: NSPredicate(format: "net > %d", 0.0))
}
}
struct DataTable: View {
var dataModel: FetchedResults<TradingDayPrices>
init(dataModel: FetchedResults<TradingDayPrices>) {
self.dataModel = dataModel
}
var body: some View {. // fails on this line ########################
List(dataModel.fetchRequest, id: \.self) { item in
Text("\(dateToStringFormatter.string(from: item.timeStamp!)) - ") +
Text("\(item.vooClose) ")
}
}
}
I have an array of core data entities, named TradingDayPrices, with 1 date and 4 double attributes. I would like to be able to copy the date value and 1 specific double into a new array. I have not been able to determine the correct syntax. With the code below I get the error "cannot assign value of type [[Any]] to type [Date:Double]"
var allClosingValues: [TradingDayPrices]
var closingValues: [Date:Double]
var selection: DataDisplayed
init(selection: DataDisplayed, allClosingValues: [TradingDayPrices]) {
self.selection = selection
self.allClosingValues = allClosingValues
switch selection {
case .VOODT:
closingValues = allClosingValues.map {
[$0.timeStamp! , $0.vooClose]
}
default:
let _ = print("Error in Data Table Structure")
}
}
I have a view that displays core data values (passed in as closingValues) in a data table. To make the view generic, so I can pass in arrays of different lengths (all are from the same entity and thus attributes) and account for the view having already been called with a different set of values, I need to initialize the showData array contained in the associated view model. I attempt to do so in the init of the view but I get an error message "publishing changes from within a view updates is not allowed. This will cause undefined behavior". It should be noted that in the view model showData is Published and initialized = []. I attempted to do the initialization in the views onappear but the tables rows: ForEach runs before onappear and I get an index out of range on the first call to the view. I tried adding @MainActor to the view models class, which also failed to solve the problem. I am clearly not understanding a concept. Below is the code for the view.
struct DataTable: View {
@ObservedObject var vm: ButtonsViewModel = ButtonsViewModel.shared
var closingValues: [TradingDayClose]
var heading: String = ""
init(fundName: String, closingValues: [TradingDayClose]) {
self.heading = fundName
self.closingValues = closingValues
vm.showData = []
vm.showData = Array(repeating: false, count: closingValues.count)
// Publishing changes from within view updates is not allowed, this will cause undefined behavior.
}
var body: some View {
HStack {
Spacer()
.frame(width: 150)
GroupBox(heading) {
Table (of: TradingDayClose.self) {
TableColumn("") { closingValue in
Text(dateToStringFormatter.string(from: closingValue.timeStamp!))
.id(closingValue.timeStamp!)
.textFormatting(fontSize: 14)
.frame(width: 100, alignment: .center)
} // end table column
TableColumn("") { closingValue in
Text(String(format: "$ %.2f", closingValue.close))
.textFormatting(fontSize: 14)
.frame(width: 100, alignment: .center)
} // end table column
} rows: {
ForEach((closingValues.indices), id: \.self) { index in
if vm.showData[index] == true {
TableRow(closingValues[index])
}
}
}
.onAppear {
vm.InitializeIndexes()
vm.InitializeButtons()
for i in vm.startIndex...vm.endIndex {
DispatchQueue.main.asyncAfter(deadline: .now() + vm.renderRate * Double(i)) {
vm.showData[i] = true
} // end dispatch queue main async
}
}
.frame(width: 250)
.overlay {
let tempValue1: String = "Date"
let tempValue2: String = "Closing Value"
Text(tempValue1).position(x: 63, y: 15)
.textFormatting(fontSize: 16)
Text(tempValue2).position(x: 180, y: 15)
.textFormatting(fontSize: 16)
}
} // end group box
.groupBoxStyle(Table2GroupBoxStyle())
Spacer()
.frame(width: 50)
VStack {
Spacer()
.frame(height: 100)
ButtonUp25(closingValuesCount: closingValues.count)
ButtonUp200(closingValuesCount: closingValues.count)
Spacer()
.frame(height: 20)
ButtonDown25(closingValuesCount: closingValues.count)
ButtonDown200(closingValuesCount: closingValues.count)
Spacer()
.frame(height: 100)
} // end v stack
Spacer()
.frame(width: 50)
} // end h stack
} // end body
} // end struct
I am attempting to learn regex builder. I scape data from a web site, pull out just the table rows and table data and place them into a string. I have attempted to extract table data with regex builder with no success. For testing I placed 3 scrapped table rows into a multi line string and apply my regex pattern in a for loop. It does not appear to find any matches. I am clearly overlooking something. Below is my code:
func GetHTMLTableData() {
let stringData = """
<tr class=BdT Bdc($seperatorColor) Ta(end) Fz(s) Whs(nw)><td class=Py(10px) Ta(start) Pend(10px)><span>Jun 30, 2023</span></td><td class=Py(10px) Pstart(10px)><span>405.40</span></td><td class=Py(10px) Pstart(10px)><span>408.22</span></td><td class=Py(10px) Pstart(10px)><span>405.29</span></td><td class=Py(10px) Pstart(10px)><span>407.28</span></td><td class=Py(10px) Pstart(10px)><span>407.28</span></td><td class=Py(10px) Pstart(10px)><span>5,160,100</span></td></tr>
<tr class=BdT Bdc($seperatorColor) Ta(end) Fz(s) Whs(nw)><td class=Py(10px) Ta(start) Pend(10px)><span>Jun 29, 2023</span></td><td class=Py(10px) Pstart(10px)><span>400.60</span></td><td class=Py(10px) Pstart(10px)><span>402.67</span></td><td class=Py(10px) Pstart(10px)><span>400.19</span></td><td class=Py(10px) Pstart(10px)><span>402.51</span></td><td class=Py(10px) Pstart(10px)><span>402.51</span></td><td class=Py(10px) Pstart(10px)><span>3,914,800</span></td></tr>
<tr class=BdT Bdc($seperatorColor) Ta(end) Fz(s) Whs(nw)><td class=Py(10px) Ta(start) Pend(10px)><span>Jun 28, 2023</span></td><td class=Py(10px) Pstart(10px)><span>401.35</span></td><td class=Py(10px) Pstart(10px)><span>403.49</span></td><td class=Py(10px) Pstart(10px)><span>400.71</span></td><td class=Py(10px) Pstart(10px)><span>402.55</span></td><td class=Py(10px) Pstart(10px)><span>400.97</span></td><td class=Py(10px) Pstart(10px)><span>4,320,700</span></td></tr>
"""
let tradingDayPattern = Regex {
"<tr class=BdT Bdc($seperatorColor) Ta(end) Fz(s) Whs(nw)>"
"<td class=Py(10px) Ta(start) Pend(10px)><span>"
Capture(.date(format: "\(month: .abbreviated) \(day: .twoDigits), \(year: .extended(minimumLength: 4))", locale: Locale(identifier: "en_US_POSIX") , timeZone: .gmt))
"</span></td><td class=Py(10px) Pstart(10px)><span>"
TryCapture {
OneOrMore(.digit)
"."
Repeat(.digit, count: 2)
} transform: {
Double($0)
}
"</span></td><td class=Py(10px) Pstart(10px)><span>"
TryCapture {
OneOrMore(.digit)
"."
Repeat(.digit, count: 2)
} transform: {
Double($0)
}
"</span></td><td class=Py(10px) Pstart(10px)><span>"
TryCapture {
OneOrMore(.digit)
"."
Repeat(.digit, count: 2)
} transform: {
Double($0)
}
"</span></td><td class=Py(10px) Pstart(10px)><span>"
TryCapture {
OneOrMore(.digit)
"."
Repeat(.digit, count: 2)
} transform: {
Double($0)
}
"</span></td><td class=Py(10px) Pstart(10px)><span>"
TryCapture {
OneOrMore(.digit)
"."
Repeat(.digit, count: 2)
} transform: {
Double($0)
}
"</span></td><td class=Py(10px) Pstart(10px)><span>"
TryCapture {
OneOrMore(.digit)
","
Repeat(.digit, count: 3)
","
Repeat(.digit, count: 3)
} transform: {
Int($0)
}
"</span></td></tr>"
}
for match in stringData.matches(of: tradingDayPattern) {
let (line, date, open, high, low, close, adjClose, volume ) = match.output
print("\(date) - \(close)")
}
}
I have a program that reads in 3 json files and updates CoreData if one or more of the JSON files contains new data. It runs successfully 9 times out of 10. The failure occurs during the creation of an entity in the managed object context. The failure occurs in the function AddRecordsToCoreData. I have placed the error messages below the offending line of code. Further, it appears to only fail during the third call to UpdateCoreDataRecoreds which in turn calls AddRecordsToCoreData. Setting up the calls to run in series I thought I could eliminate any problems but clearly not the case. What error am I making in the code? Or is there some other approach I should utilize? Below is the code in question.
class UpdateCoreData: ObservableObject {
let persistentContainer = CoreDataStack.shared.persistentContainer
@Published var loadingData: Bool = true
@Published var updateStatus: String = ""
init() {
Task {
async let fund1Complete: Bool = UpdateCoreDataRecords(fundName: "Fund1", moc: persistentContainer.viewContext)
let _ = await (fund1Complete)
async let fund2Complete: Bool = UpdateCoreDataRecords(fundName: "Fund2", moc: persistentContainer.viewContext)
let _ = await (fund2Complete)
async let fund3Complete: Bool = UpdateCoreDataRecords(fundName: "Fund3", moc: persistentContainer.viewContext)
let _ = await (fund3Complete)
persistentContainer.viewContext.vacuum()
let persistentStore = persistentContainer.persistentStoreCoordinator.persistentStores.first
do {
try persistentContainer.persistentStoreCoordinator.remove(persistentStore!)
} catch {
print("Unable to remove store -> \(error)")
}
DispatchQueue.main.async {
self.loadingData = false
self.updateStatus = "Core Date is Updated"
}
}
}
}
func UpdateCoreDataRecords(fundName: String, moc: NSManagedObjectContext) async -> Bool {
var latestDate: Date = Date()
var decodedJSON: TradingDays = TradingDays.init(tradingday: [])
latestDate = await LatestCoreDataDate(fundName: fundName, moc: moc)
decodedJSON = await DecodeJSONFile(fileName: fundName)
await AddRecordsToCoreData(jsonData: decodedJSON, fundName: fundName, latestDate: latestDate, moc: moc)
return true
}
func LatestCoreDataDate(fundName: String, moc: NSManagedObjectContext) async -> Date {
var coreDataValues: [TradingDayClose] = []
var latestDate: Date = Date()
let fetchRequest: NSFetchRequest<TradingDayClose> = TradingDayClose.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timeStamp", ascending: false)]
fetchRequest.predicate = NSPredicate(format: "fundName = %@", fundName)
fetchRequest.fetchLimit = 1
do {
coreDataValues = try moc.fetch(fetchRequest)
} catch let error {
print("Error fetching max date. \(error.localizedDescription)")
}
if coreDataValues.isEmpty {
latestDate = Calendar.current.date(byAdding: DateComponents(year: -6), to: latestDate)!
} else {
latestDate = coreDataValues[0].timeStamp!
}
return latestDate
}
func AddRecordsToCoreData(jsonData: TradingDays, fundName: String, latestDate: Date, moc: NSManagedObjectContext) async {
print("\(fundName)")
for item in jsonData.tradingday {
if item.timeStamp > latestDate {
let newRecord = TradingDayClose(context: moc)
// Thread 4: EXC_BAD_ACCESS (code=1, address=0x8)
// thread 3 signal Sigbrt
// Thread 2: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
newRecord.fundName = fundName
newRecord.id = UUID()
newRecord.timeStamp = item.timeStamp
newRecord.close = (item.close) as NSDecimalNumber
} else {
break
}
}
if moc.hasChanges {
do {
print("Saving moc")
try moc.save()
} catch {
print("Errors attempting to save moc")
}
}
}