How are you getting around this? My spontaneous thought was to use a Task to dispatch to the MainActor, but that seems to introduce visual glitches that weren't there previously.
Post
Replies
Boosts
Views
Activity
[quote='795150022, AppleCare Staff, /thread/759244?answerId=795150022#795150022']
So just a few lines of code would be enough, I think it should fit in 20 lines or less. For data, you can inline an array of tuples as I described. No need for preview, extensions, structs etc. just a single Chart.
[/quote]
Apologies, I thought perhaps an example with a Preview would be beneficial for demonstration purposes. I'm happy to reduce the amount of code a bit. No more extra structs, previews or extensions!
However, I can't really get it down to 20 lines if I am to simulate my issue. I need a month of measurements and I'm not sure I can even create the array of ~30 items (with plenty of Calendar.current operations) in 20 lines of code. I hope about 40 lines of code is okay!
let cal: Calendar = .current
struct ChartWithNilValues2: View {
let start: Date = cal.date(from: cal.dateComponents([.year, .month], from: .now))!
let end: Date
init() {
end = cal.date(byAdding: .month, value: 1, to: start)!
}
var body: some View {
Chart(generateMeasurements(start: start, end: end), id: \.0) { measurement in
if let total = measurement.1 {
BarMark(
x: .value("", measurement.0, unit: .weekOfYear, calendar: cal),
y: .value("", total)
)
}
}
.chartXAxis {
AxisMarks(values: .stride(by: .weekOfYear)) { value in
AxisValueLabel("\(value.index)", centered: true)
}
}
.padding()
.chartXScale(domain: start...end)
}
func generateMeasurements(start: Date, end: Date) -> [(Date, Double?)] {
var measurements: [(Date, Double?)] = []
let dateComponents = cal.dateComponents([.day], from: start, to: end)
guard let count = dateComponents.value(for: .day) else { return [] }
for i in 0 ..< count {
if let time = cal.date(byAdding: .day, value: i, to: start), time <= end {
let production = i > count - 10 ? nil : Double.random(in: 0 ... 50)
let measurement = (time, production)
measurements.append(measurement)
}
}
return measurements
}
}
One thing that I observed trimming this down, is that if I move the definition of the end date from the init function to the struct definition, the problem seems to go away. 🤔 Hope that can be of some kind of assistance!
Final note: I'm in Stockholm, Sweden. So Calendar.current is Central European Time for me, if that makes a difference in your assessment.
[quote='794978022, AppleCare Staff, /thread/759244?answerId=794978022#794978022']
No need for an entire repo, just a minimal, but runnable example that exhibits the issue.
[/quote]
Alright! This should do:
import SwiftUI
import Charts
struct ChartWithoutNilValues: View {
let firstDayOfMonth: Date
let firstDayOfNextMonth: Date
let measurements: [PerformanceMeasurement]
init() {
firstDayOfMonth = Date.now.firstDayOfMonth()!
firstDayOfNextMonth = Calendar.current.date(byAdding: .month, value: 1, to: firstDayOfMonth)!
measurements = DataGenerator.generateMeasurements(
interval: DateInterval(start: firstDayOfMonth, end: firstDayOfNextMonth),
component: .day
)
}
var body: some View {
Chart(measurements, id: \.timestamp) { measurement in
BarMark(
x: .value(
"Timestamp",
measurement.timestamp,
unit: .weekOfYear,
calendar: .current
),
y: .value(
"Solar production",
measurement.production?.total ?? 0
)
)
}
.chartXAxis {
AxisMarks(values: .stride(by: .weekOfYear)) { value in
AxisValueLabel("\(value.index)", centered: true)
}
}
.padding()
}
}
struct ChartWithNilValues: View {
let firstDayOfMonth: Date
let firstDayOfNextMonth: Date
let measurements: [PerformanceMeasurement]
init() {
firstDayOfMonth = Date.now.firstDayOfMonth()!
firstDayOfNextMonth = Calendar.current.date(byAdding: .month, value: 1, to: firstDayOfMonth)!
measurements = DataGenerator.generateMeasurements(
interval: DateInterval(start: firstDayOfMonth, end: firstDayOfNextMonth),
component: .day
)
}
var body: some View {
Chart(measurements, id: \.timestamp) { measurement in
if let total = measurement.production?.total {
BarMark(
x: .value(
"Timestamp",
measurement.timestamp,
unit: .weekOfYear,
calendar: .current
),
y: .value(
"Solar production",
total
)
)
}
}
.chartXAxis {
AxisMarks(values: .stride(by: .weekOfYear)) { value in
AxisValueLabel("\(value.index)", centered: true)
}
}
.padding()
.chartXScale(domain: firstDayOfMonth...firstDayOfNextMonth)
}
}
#Preview {
VStack {
ChartWithoutNilValues()
ChartWithNilValues()
}
.padding()
}
struct DataGenerator {
let currentYear = Calendar.current.component(.year, from: .now)
let startOfDay = Calendar.current.startOfDay(for: .now)
static func generateMeasurements(interval: DateInterval, component: Calendar.Component) -> [PerformanceMeasurement] {
var measurements: [PerformanceMeasurement] = []
let calendar = Calendar.current
let dateComponents = calendar.dateComponents([component], from: interval.start, to: interval.end)
guard let count = dateComponents.value(for: component) else { return [] }
for i in 0 ..< count {
if let time = calendar.date(byAdding: component, value: i, to: interval.start), time <= interval.end {
let production = createProduction(from: i > count - 10 ? nil : randomNumber)
let measurement = PerformanceMeasurement(
timestamp: time,
production: production
)
measurements.append(measurement)
}
}
return measurements
}
static private var randomNumber: Double? {
Double.random(in: 0 ... 50)
}
static private func createProduction(from total: Double?) -> PerformanceMeasurement.Production {
guard let total else {
return PerformanceMeasurement.Production(total: nil, sold: nil, consumed: nil)
}
let sold = Double.random(in: 0 ... total)
let consumed = total - sold
return PerformanceMeasurement.Production(total: total, sold: sold, consumed: consumed)
}
}
struct PerformanceMeasurement: Codable, Equatable {
let timestamp: Date
let production: Production?
struct Production: Codable, Equatable {
let total: Double?
let sold: Double?
let consumed: Double?
}
}
extension Date {
func firstDayOfMonth(in calendar: Calendar = .current) -> Date? {
calendar.date(from: calendar.dateComponents([.year, .month], from: self))
}
}
[quote='794844022, AppleCare Staff, /thread/759244?answerId=794844022#794844022']
Got it, I see what you want. But I'm not sure I understand the full setup. The code on the top uses .weekOfYear and then the domain is not set to week boundaries but to month boundaries, and it's not clear to me how the two relate in your use. Have you a small, single self-contained example with data in place? You can even use tuples in an array like [(date0, value0), ...]
[/quote]
I apologize if things are not clear. It might simply be because I don't have a full understanding of how I'm supposed to use the modifier. Let me try and break it down.
My data set is an array of one measurement per day in a month (so I have about ~30 entries per array, naturally). This is the reason I try to set the domain of the chart from the first day in a month, to the last day in a month. I figured with that, the Chart would understand the timespan I'm trying to plot (no matter if I sort it all by .weekOfYear or not). Is that not the case? Should I set the domain in some different way when sorting the data by weekOfYear?
I'm not sure I get the last sentence? Tuples for the domain?
I do have an Xcode project with a minimum reproducible example, but I guess I can't share it here directly. Should I file a feedback with it, or share it in some other way?
[quote='794848022, AppleCare Staff, /thread/759244?answerId=794848022#794848022']
Currently, the bar placement is conditional (non-nil total). Can you make it unconditional, such that nils get mapped to zero values?
[/quote]
My intention is to differentiate between nil and 0. nil would mean there's data missing, and 0 would mean there technically is data present, the total value of the measurements is just 0.
So if I understand your question correctly, my answer would be: No, sadly not.
Done! FB13888781.
I have the same issue. Cannot even see the watch listed when I use xcrun devicectl list devices in the Terminal. Tried unpairing the Watch from my iPhone and pairing it again, cleaning out all sorts of caches, turning on and off developer mode. watchOS 10.4, Xcode 15.3 (15.2 doesn't work either).
Same issue. Xcode 15.3 (tried downloading 15.2 as well, no luck). watchOS 10.4.
Here's how I got into this state: Xcode would never be able to build to my watch, so I figured I'd try and unpair the Watch from the devices list in Xcode and then pair it again. Should probably never have done that, because even after unpairing the Watch from my iPhone and resetting it, turning on and off developer mode, cleaning out all sorts of caches... I've tried everything and it never shows up anywhere. It's not even listed when using xcrun devicectl list devices. It's now taken about 24 hours of dev time away from me, which is unacceptable for something this basic.
Since I'm still experiencing the same issue in Xcode 15.1, I've now also submitted feedback for this issue (FB13467714). It's quite ridiculous this hasn't been fixed yet.
I have the same issue using the latest Xcode 15.0.1 (15A507). Previews stop working if any dependency imported using the Swift Package Manager doesn't support watchOS, even if those frameworks (in my case Datadog and Lottie) are not directly linked or used in the watchOS target.
I'm wondering the same thing. It does seem like it's only run once though, as you said. :/
How are you initiating your ink annotations? More specifically, what bounds do you assign to them?
I had the same problem when I initially initiated each ink annotation with the bounds of the PDF page (which I at the time thought was the correct approach), only to later discover that initiating each annotation with the bounds of the bezier path that represents the drawing fixed the memory issue.