Post

Replies

Boosts

Views

Activity

Charts: How do I position x axis value labels below the associated grid lines
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 }()
2
1
3.9k
Jan ’23
Charts module not available in Xcode 14.0.1 for macOS app
During the upgrade to Ventura it appears that Xcode was also upgraded to 14.0.1. In an attempt to use a new feature, Charts, I entered into a macOS app import Charts and I get an error message no such module Charts. If I use the same import Charts in an iOS app I do not get such an error. I am wondering if Charts in not available for macOS apps or did something go wrong during the Ventura upgrade. If it is the later, it makes sense to me to cleanly uninstall Xcode and reinstall. But it is unclear if just dragging Xcode from the application folder to the trash and emptying the trash completely uninstalls Xcode. As a side note, I do not ever remember Xcode being upgraded during an upgrade to macOS. Any input will be appreciated.
1
1
1.8k
Oct ’22
Create Spreadsheet Like Grid with SwiftUI
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 }()
2
0
2.6k
Oct ’22
How do I pass an instance of a class between models in MVVM
I have a working program utilizing the MVVM architecture. There are 3 view models and 3 models. In the first model called I do some testing and would like to pass the result of that testing, a boolean, to the other two models. I have tried to create a class with a boolean, create an instance in the content view and pass it into one of the view models. I would then pass it into the corresponding model. Unfortunately I have been unable to get it to work i.e. pass an instance of the class into the view model. If you see a fix to this approach or have a better approach please let me know. The failure message I get is related to attempting to pass csvData to vooVM. The message is => Cannot use instance member 'csvData' within property initializer; property initializers run before 'self' is available. My Class that contains the boolean that would be set in the first model and passed to the two other models via their view models: class CSVData: ObservableObject {     var updated: Bool = false } My Struct Content View initial section: struct ContentView: View { var csvData: CSVData // The following line gives the above mentioned error message @StateObject private var vooVM = VOOVM(csvData: csvData) @StateObject private var vfiaxVM = VFIAXVM() @StateObject private var prinVM = PrincipalVM() @State private var selectedItemID: Int? let bottomPadding: CGFloat = 2 init() { self.csvData = CSVData() } var body: some View { My View Model initial section: class VOOVM: ObservableObject { var ContainerValues1: [CommonAttributes] = [] var ContainerValues5: [CommonAttributes] = [] var MinAndMaxClose1: [String:Double] = [:] var MinAndMaxClose5: [String:Double] = [:] private var vooModel: VOOModel = VOOModel() var symbol: String var shares: Double var csvData: CSVData init(csvData: CSVData) { self.csvData = csvData
1
0
522
Aug ’22
Creating Instance of Class of type NSWindowController
I have a program that allows me to programmatically bounce a ball around a window. Everything works as desired. What I would like to be able to do is work with the window itself to do such things as remove the title, change the background color and make it non resizable. I think I do this by creating a class of type NSWindosController which I have done. I create an instance of it in the ContentView but it does not work. Is my class wrong or am I not creating an instance in the right place or is there some other problem. Thank you. import Cocoa class WindowController: NSWindowController { override func windowDidLoad() { super.windowDidLoad() window?.styleMask.remove(.titled) window?.styleMask.remove(.resizable) window?.backgroundColor = .red } } import SwiftUI struct ColorShading: Identifiable { let value: GraphicsContext.Shading let id = UUID() } struct ContentView: View { @State private var isPaused: Bool = true @State private var ballPosition = BallPosition() @State private var winController = WindowController() var body: some View { let ballColors = [ ColorShading(value: GraphicsContext.Shading.color(.orange)), ColorShading(value: GraphicsContext.Shading.color(.red)), ColorShading(value: GraphicsContext.Shading.color(.blue)), ColorShading(value: GraphicsContext.Shading.color(.green)), ColorShading(value: GraphicsContext.Shading.color(.gray)), ColorShading(value: GraphicsContext.Shading.color(.yellow)), ColorShading(value: GraphicsContext.Shading.color(.purple)) ] TimelineView(.animation(minimumInterval: 0.0001, paused: isPaused)) { timeline in Canvas { context, size in _ = timeline.date.timeIntervalSinceNow let circleDiameter: Double = (size.width * 0.2) let circleRadius: Double = circleDiameter / 2 if isPaused == true { ballPosition.updateBaseData(maxX: size.width, maxY: size.height, circleRadius: circleRadius) } let wheelOrigin = CGPoint(x: ballPosition.xPosition, y: ballPosition.yPostion) context.stroke( Path { path in path.addArc(center: wheelOrigin, radius: circleRadius, startAngle: .degrees(0), endAngle: .degrees(360), clockwise: false) }, with: ballColors[ballPosition.randomColor].value, lineWidth: 2 ) // end context stroke ballPosition.updatePosition() } // end canvas .frame(width: 800, height: 800) } // end time line view .onAppear() { Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { timer in isPaused = false timer.invalidate() } // end timer } // end on appear } }
1
0
652
Mar ’23
SwiftUI - Accessing StateObject's Object Without Being Installed on a View
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) ") } } }
2
0
2.0k
Apr ’23
Swift - Map two elements from an array to a new array
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") } }
2
0
1.9k
Apr ’23
Swift - access attribute in array of core data entities in a table column via a variable
The below code works. I pass in an array of core data entities and display attributes in a table. In the second table column I use a switch case statement to handle the 3 possible attributes I want to display. I could eliminate the switch case if I could figure out how to access a given core data attribute via a variable that contains the specific attribute name but have been unable to determine how to do so. Two attempts are commented out in the code. struct DataTable: View { private var closingValues: Array<TradingDayPrices> var heading: String var attribute: String init(closingValues: [TradingDayPrices], heading: String, attribute: String) { self.closingValues = closingValues self.heading = heading self.attribute = attribute } var body: some View { Text(heading) .foregroundColor(.black) .font(Font.custom("Arial", size: 18)) .bold() .padding(.top, 10) Table(self.closingValues) { TableColumn("Date") { value in HStack { Spacer() Text(dateToStringFormatter.string(from: value.timeStamp!)) Spacer() } } .width(100) TableColumn("Closing Value") { value in HStack { Spacer() // Text(String(format: "$ %.2f", value(forKey: attribute))) // Text(String(format: "$ %.2f", value(attribute))) switch attribute { case "s1Close": Text(String(format: "$ %.2f", value.s1Close)) case "s2Close": Text(String(format: "$ %.2f", value.s2Close)) default: Text(String(format: "$ %.2f", value.s3Close)) } Spacer() } } .width(100) } .foregroundColor(.black) .font(Font.custom("Arial", size: 16)) .frame(width: 250) Spacer() .frame(height: 30) } }
1
0
883
Apr ’23
How do I animate a Swift line mark chart
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()) } }
4
0
3k
Jun ’23
How do I initialize a view models variables in a view
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
2
0
992
Jun ’23
How do I disable a button while it has focus
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) } }
3
0
1.6k
Jun ’23
How do I extract HTML table data from a string with regex builder
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)") } }
2
0
812
Jul ’23
How do I control the formatting of values when encoding data as JSON
I have a program that reads in CSV data from a text file on disk. It converts it to an array. It then encodes this array into a JSON string. It all works. The problem I have is the during the encoding process a double like 402.71 is converted to 402.70999999. How do I control the encoding process so that this number is 402.71 in the JSON string. Below is a line of CSV data, a line of the resulting array element and the resulting JSON object, the encoding function and the structures used for creating the array from the CSV data. CSV data: 07/07/23, 403.03, 406.679, 402.71, 402.89, 3668080 Resulting array data: TradingDays(tradingday: [DownloadHTML.Tradingday(timeStamp: 2023-07-07 00:00:00 +0000, open: 403.03, high: 406.679, low: 402.71, close: 402.89, volume: 3668080), Resulting JSON object: { "tradingday" : [ { "timeStamp" : "2023-07-07T00:00:00Z", "low" : 402.70999999999998, "high" : 406.67899999999997, "volume" : 3668080, "open" : 403.02999999999997, "close" : 402.88999999999999 }, Function: func EncodeTradingDays (tradingDays: TradingDays) async -> String { var jsonData: Data? var jsonString: String let jsonEncoder = JSONEncoder() jsonEncoder.dateEncodingStrategy = .iso8601 jsonEncoder.outputFormatting = [.prettyPrinted] do { jsonData = try jsonEncoder.encode(tradingDays) } catch { print("error encoding json data") } jsonString = String(data: jsonData!, encoding: .utf8)! return jsonString } Data structures: struct TradingDays: Codable { var tradingday: [Tradingday] } struct Tradingday: Codable { var timeStamp: Date var open: Double var high: Double var low: Double var close: Double let volume: Int enum CodingKeys: String, CodingKey { case timeStamp case open, high, low, close, volume } }
1
0
578
Jul ’23
CoreData Failure During Entity Creation in the ViewContext
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") } } }
2
0
947
Jul ’23