I'm building a Swift Chart that displays locations in a horizontal timeline. The chart is scrollable.
When the chart is zoomed in, I want to show an annotation for every 6 hours.
Unfortunately, when axisMarks are set to .stride(by: .hour, count: 6), the annotations do not appear for the first several months in the timeline. I tried setting .stride(by: .minute, count: 360), but the result is the same.
Is this a Swift Charts bug, or am I doing something wrong?
A reproducible example is below. To reproduce:
Run the code below
See that annotations are missing at the leading edge of the chart. They only show up from a certain point on the chart's domain.
Tested on various iPhone and iPad simulators and physical devices, the issue appears everywhere.
P.S. I am aware that the example code below is not performant and that the annotations overlap when the chart is zoomed out. I have workarounds for that, but it's beyond the scope of my question and the minimum reproducible example.
struct ChartAnnotationsBug: View {
/// Sample data
let data = SampleData.samples
let startDate = SampleData.samples.first?.startDate ?? Date()
let endDate = Date()
/// Scroll position of the chart, expressed as Date along the x-axis.
@State var chartPosition: Date = SampleData.samples.first?.startDate ?? Date()
/// Sets the granularity of the shown view.
@State var visibleDomain: VisibleDomain = .month
var body: some View {
Chart(data, id: \.id) { element in
BarMark(xStart: .value("Start", element.startDate),
xEnd: .value("End", element.endDate),
yStart: 0,
yEnd: 50)
.foregroundStyle(by: .value("Type", element.type.rawValue))
.clipShape(.rect(cornerRadius: 8, style: .continuous))
}
.chartScrollableAxes(.horizontal) // enable scroll
.chartScrollPosition(x: $chartPosition) // track scroll offset
.chartXVisibleDomain(length: visibleDomain.seconds)
.chartXScale(domain: startDate...endDate)
.chartXAxis {
AxisMarks(values: .stride(by: .hour, count: 6)) { value in
if let date = value.as(Date.self) {
let hour = Calendar.current.component(.hour, from: date)
if hour == 0 { // midnight
AxisValueLabel(collisionResolution: .truncate) {
VStack(alignment: .leading) {
Text(date, format: .dateTime.hour().minute())
Text(date, format: .dateTime.weekday().month().day())
.bold()
}
}
AxisTick(stroke: .init(lineWidth: 1))
} else if [6, 12, 18].contains(hour) { // period
AxisValueLabel(collisionResolution: .truncate) {
Text(date, format: .dateTime.hour().minute())
}
AxisTick(length: .label)
}
}
}
}
.frame(height: 100)
.padding(.bottom, 40) // for overlay picker
.overlay {
Picker("", selection: $visibleDomain.animation()) {
ForEach(VisibleDomain.allCases) { variant in
Text(variant.label)
.tag(variant)
}
}
.pickerStyle(.segmented)
.frame(width: 240)
.padding(.trailing)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing)
} //: overlay
} //: body
} //: struct
// MARK: - Preview
#Preview {
ChartAnnotationsBug()
}
// MARK: - Data
enum SampleDataType: String, CaseIterable {
case city, wood, field
var label: String {
switch self {
case .city:
"City"
case .wood:
"Wood"
case .field:
"Field"
}
}
}
enum VisibleDomain: Identifiable, CaseIterable {
case day
case week
case month
var id: Int {
self.seconds
}
var seconds: Int {
switch self {
case .day:
3600 * 24 * 2
case .week:
3600 * 24 * 10
case .month:
3600 * 24 * 40
}
}
var label: String {
switch self {
case .day:
"Days"
case .week:
"Weeks"
case .month:
"Months"
}
}
}
struct SampleData: Identifiable {
let startDate: Date
let endDate: Date
let name: String
let type: SampleDataType
var id: String { name }
static let samples: [SampleData] = [
.init(startDate: Date.from(year: 2024, month: 3, day: 1, hour: 23, minute: 59),
endDate: Date.from(year: 2024, month: 3, day: 10),
name: "New York",
type: .city),
.init(startDate: Date.from(year: 2024, month: 3, day: 10, hour: 6),
endDate: Date.from(year: 2024, month: 3, day: 20),
name: "London",
type: .city),
.init(startDate: Date.from(year: 2024, month: 3, day: 20),
endDate: Date.from(year: 2024, month: 4, day: 10),
name: "Backcountry ABC",
type: .field),
.init(startDate: Date.from(year: 2024, month: 4, day: 10),
endDate: Date.from(year: 2024, month: 4, day: 20),
name: "Field DEF",
type: .field),
.init(startDate: Date.from(year: 2024, month: 4, day: 20),
endDate: Date.from(year: 2024, month: 5, day: 10),
name: "Wood 123",
type: .wood),
.init(startDate: Date.from(year: 2024, month: 5, day: 10),
endDate: Date.from(year: 2024, month: 5, day: 20),
name: "Paris",
type: .city),
.init(startDate: Date.from(year: 2024, month: 5, day: 20),
endDate: Date.from(year: 2024, month: 6, day: 5),
name: "Field GHI",
type: .field),
.init(startDate: Date.from(year: 2024, month: 6, day: 5),
endDate: Date.from(year: 2024, month: 6, day: 10),
name: "Wood 456",
type: .wood),
.init(startDate: Date.from(year: 2024, month: 6, day: 10),
endDate: Date(),
name: "Field JKL",
type: .field)
]
}
extension Date {
/**
Constructs a Date from a given year (Int). Use like `Date.from(year: 2020)`.
*/
static func from(year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil) -> Date {
let components = DateComponents(year: year, month: month, day: day, hour: hour, minute: minute)
guard let date = Calendar.current.date(from: components) else {
print(#function, "Failed to construct date. Returning current date.")
return Date()
}
return date
}
}
Post
Replies
Boosts
Views
Activity
I'm building a Swift Chart that displays locations in a horizontal timeline. The chart is scrollable.
Unfortunately, when chartScrollPosition is offset by some amount (i.e. the user has scrolled the chart), changing chartXVisibleDomain results in chartScrollPosition jumping backwards by some amount.
This results in bad user experience.
A minimum reproducible example is below. To reproduce:
Run the code below
Using the picker, change chartXVisibleDomain. ThechartScrollPosition remains the same, as expected.
Scroll the chart on the horizontal axis.
Using the picker, change chartXVisibleDomain. ThechartScrollPosition changes unexpectedly.
You can verify this by watching the labels at the bottom of the chart. The chart simply ends up showing a different area of the domain. Tested on various iPhone and iPad simulators and physical devices, the issue appears everywhere.
Is this a SwiftUI bug, or am I doing something wrong?
struct ScrollableChartBug: View {
/// Sample data
let data = SampleData.samples
let startDate = SampleData.samples.first?.startDate ?? Date()
let endDate = Date()
/// Scroll position of the chart, expressed as Date along the x-axis.
@State var chartPosition: Date = SampleData.samples.first?.startDate ?? Date()
/// Sets the granularity of the shown view.
@State var visibleDomain: VisibleDomain = .year
var body: some View {
Chart(data, id: \.id) { element in
BarMark(xStart: .value("Start", element.startDate),
xEnd: .value("End", element.endDate),
yStart: 0,
yEnd: 50)
.foregroundStyle(by: .value("Type", element.type.rawValue))
.clipShape(.rect(cornerRadius: 8, style: .continuous))
}
.chartScrollableAxes(.horizontal) // enable scroll
.chartScrollPosition(x: $chartPosition) // track scroll offset
.chartXVisibleDomain(length: visibleDomain.seconds)
.chartXScale(domain: startDate...endDate)
.chartForegroundStyleScale { typeName in
// custom colors for bars and for legend
SampleDataType(rawValue: typeName)?.color ?? .clear
}
.chartXAxis {
AxisMarks(values: .stride(by: .month, count: 1)) { value in
if let date = value.as(Date.self) {
AxisValueLabel {
Text(date, format: .dateTime.year().month().day())
.bold()
}
AxisTick(length: .label)
}
}
}
.frame(height: 90)
.padding(.bottom, 40) // for overlay picker
.overlay {
Picker("", selection: $visibleDomain.animation()) {
ForEach(VisibleDomain.allCases) { variant in
Text(variant.label)
.tag(variant)
}
}
.pickerStyle(.segmented)
.frame(width: 240)
.padding(.trailing)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing)
} //: overlay
} //: body
} //: struct
// MARK: - Preview
#Preview {
ScrollableChartBug()
}
// MARK: - Data
enum SampleDataType: String, CaseIterable {
case city, wood, field
var color: Color {
switch self {
case .city:
.gray
case .wood:
.green
case .field:
.brown
}
}
var label: String {
switch self {
case .city:
"City"
case .wood:
"Wood"
case .field:
"Field"
}
}
}
enum VisibleDomain: Identifiable, CaseIterable {
case day
case week
case month
case year
var id: Int {
self.seconds
}
var seconds: Int {
switch self {
case .day:
3600 * 24 * 2
case .week:
3600 * 24 * 10
case .month:
3600 * 24 * 40
case .year:
3600 * 24 * 400
}
}
var label: String {
switch self {
case .day:
"Days"
case .week:
"Weeks"
case .month:
"Months"
case .year:
"Years"
}
}
}
struct SampleData: Identifiable {
let startDate: Date
let endDate: Date
let name: String
let type: SampleDataType
var id: String { name }
static let samples: [SampleData] = [
.init(startDate: Date.from(year: 2022, month: 3, day: 1),
endDate: Date.from(year: 2022, month: 3, day: 10),
name: "New York",
type: .city),
.init(startDate: Date.from(year: 2022, month: 3, day: 20, hour: 6),
endDate: Date.from(year: 2022, month: 5, day: 1),
name: "London",
type: .city),
.init(startDate: Date.from(year: 2022, month: 5, day: 4),
endDate: Date.from(year: 2022, month: 7, day: 5),
name: "Backcountry ABC",
type: .field),
.init(startDate: Date.from(year: 2022, month: 7, day: 5),
endDate: Date.from(year: 2022, month: 10, day: 10),
name: "Field DEF",
type: .field),
.init(startDate: Date.from(year: 2022, month: 10, day: 10),
endDate: Date.from(year: 2023, month: 2, day: 10),
name: "Wood 123",
type: .wood),
.init(startDate: Date.from(year: 2023, month: 2, day: 10),
endDate: Date.from(year: 2023, month: 3, day: 20),
name: "Paris",
type: .city),
.init(startDate: Date.from(year: 2023, month: 3, day: 21),
endDate: Date.from(year: 2023, month: 10, day: 5),
name: "Field GHI",
type: .field),
.init(startDate: Date.from(year: 2023, month: 10, day: 5),
endDate: Date.from(year: 2024, month: 3, day: 5),
name: "Wood 456",
type: .wood),
.init(startDate: Date.from(year: 2024, month: 3, day: 6),
endDate: Date(),
name: "Field JKL",
type: .field)
]
}
extension Date {
/**
Constructs a Date from a given year (Int). Use like `Date.from(year: 2020)`.
*/
static func from(year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil) -> Date {
let components = DateComponents(year: year, month: month, day: day, hour: hour, minute: minute)
guard let date = Calendar.current.date(from: components) else {
print(#function, "Failed to construct date. Returning current date.")
return Date()
}
return date
}
}
I have a SwiftUI app that uses Core Data and CloudKit.
Using the Xcode 15 beta and iOS17 beta simulator, some of the runs cause the app to crash on startup.
The console prints the following:
BUG IN CLIENT OF CLOUDKIT: Not entitled to listen to push notifications. Please add the 'aps-connection-initiate' entitlement.
Push notifications are added correctly and everything works on iOS16 and earlier.
This crash also only occurs in about 20% runs on iOS17 beta.
Any tips on how to resolve this?
I am trying to train an image classification network in Keras with tensorflow-metal.
The training freezes after the first 2-3 epochs if image augmentation layers are used (RandomFlip, RandomContrast, RandomBrightness)
The system appears to use both GPU as well as CPU (as indicated by Activity Monitor). Also, warnings appear both in Jupyter and Terminal (see below).
When the image augmentation layers are removed (i.e. we only rebuild the head and feed images from disk), CPU appears to be idle, no warnings appear, and training completes successfully.
Versions: python 3.8, tensorflow-macos 2.11.0, tensorflow-metal 0.7.1
Sample code:
img_augmentation = Sequential(
[
layers.RandomFlip(),
layers.RandomBrightness(factor=0.2),
layers.RandomContrast(factor=0.2)
],
name="img_augmentation",
)
inputs = layers.Input(shape=(384, 384, 3))
x = img_augmentation(inputs)
model = tf.keras.applications.EfficientNetV2S(include_top=False, input_tensor=x, weights='imagenet')
model.trainable = False
x = tf.keras.layers.GlobalAveragePooling2D(name="avg_pool")(model.output)
x = tf.keras.layers.BatchNormalization()(x)
top_dropout_rate = 0.2
x = tf.keras.layers.Dropout(top_dropout_rate, name="top_dropout")(x)
outputs = tf.keras.layers.Dense(179, activation="softmax", name="pred")(x)
newModel = Model(inputs=model.input, outputs=outputs, name="EfficientNet_DF20M_species")
reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_accuracy', factor=0.9, patience=2, verbose=1, min_lr=0.000001)
optimizer = tf.keras.optimizers.legacy.SGD(learning_rate=0.01, momentum=0.9)
newModel.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])
history = newModel.fit(x=train_ds, validation_data=val_ds, epochs=30, verbose=2, callbacks=[reduce_lr])
During training with image augmentation, Jupyter prints the following warnings while training the first epoch:
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformFullIntV2 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting StatelessRandomGetKeyCounter cause there is no registered converter for this op.
...
During training with image augmentation, Terminal keeps spamming the following warning:
2023-02-21 23:13:38.958633: I metal_plugin/src/kernels/stateless_random_op.cc:282] Note the GPU implementation does not produce the same series as CPU implementation.
2023-02-21 23:13:38.958920: I metal_plugin/src/kernels/stateless_random_op.cc:282] Note the GPU implementation does not produce the same series as CPU implementation.
2023-02-21 23:13:38.959071: I metal_plugin/src/kernels/stateless_random_op.cc:282] Note the GPU implementation does not produce the same series as CPU implementation.
2023-02-21 23:13:38.959115: I metal_plugin/src/kernels/stateless_random_op.cc:282] Note the GPU implementation does not produce the same series as CPU implementation.
2023-02-21 23:13:38.959359: I metal_plugin/src/kernels/stateless_random_op.cc:282] Note the GPU implementation does not produce the same series as CPU implementation.
...
Any suggestions?
I'm trying to train an image classification model in CreateML application using the DF20 dataset from https://sites.google.com/view/danish-fungi-dataset?pli=1
When I tap on Train, the application begins to extract the necessary data.
This process ends after the first cca 30 000 images. The footer shows Unexpected error and (strangely) a new window opens to Create New Project. No further info about the error is provided anywhere.
The training succeeds when using a smaller portion of the dataset (<20 000 images). Anything over 40 000 images results in this error.
Any ideas?
Let's say I would like to build a classification model to recognise plants. Two plants may look identical but differ by region or month of growth, so I would like to build a multi-input model that accepts not only an image, but also additional metadata, such as the month and location where the photo was taken.
Inputs:
imageOfPlant: Image
month: Double
latitude: Double
longitude: Double
Output:
speciesName: [String] // sorted by probability, like default image classification
Can this be done in CreateML, or using Apple frameworks in general?
In the summer, Apple published an informative sample app on sharing objects between iCloud users using Core Data, CloudKit and UICloudSharingController in SwiftUI: https://developer.apple.com/documentation/coredata/sharing_core_data_objects_between_icloud_users
However, Apple also announced the new Collaboration features for Messages in iOS16.
Now, it seems Collaboration is incompatible with UICloudSharingController and the sharing sheet it invokes, on iOS16.
MRE: See the Apple sample app linked above.
Reproduction:
Create new share,
Manage share with UICloudSharingController,
Share With More People,
Share using Messages (may succeed first time but fails on subsequent attempts on iOS16. Works great on iOS15).
Expectation: We can use UICloudSharingController to add new participants, using Messages, Mail or other platforms. The link will display correctly on all devices.
Reality: On iOS16+, attempting to share via Messages engages the "Collaboration" framework and leads to an alert: "An Error Occurred. Unable to start collaboration" (see Image 1). Triggering this error breaks ANY further shares - the link now cannot be created for Mail and other platforms either (see Image 2). Furthermore, if the first attempt succeeds, the link does not appear correctly on the receiving device (see Image 3). The link appears correctly when sent from a device running iOS15.
Question: How can this be resolved, so that we can share CloudKit records between users using Messages in iOS16+?
Images: https://imgur.com/a/o1fdABT
I have an iOS app with an independent watchOS app.
With Xcode 14, I migrated the project to only use a single target for watchOS (i.e. remove the "extension" target).
Everything builds, but I get the following build warning:
appName.app is a Foundation extension and must be embedded in the parent app bundle's PlugIns directory, but is embedded in the parent app bundle's Watch directory.
where "appName" is the product name of the watchOS target.
I tried the following, to no avail:
restart Xcode
restart computer
clean build folder
delete derived data
Any ideas?
My submission was rejected because I submitted a duplicate or identical promotional images for different promoted in-app purchase products.
I was directed by Apple to edit the in-app purchases.
However, editing the in-app purchases is disabled with the following message:
“At least one of your in-app purchases is associated with a version of an app that's not yet approved. Until this version is approved, you can't submit any changes for review.”
I tried the following to enable editing of the in-app purchases:
Cancel review submission
Submit a new build
Submit a new version
Reply to the Apple rejection. They referred me to App Store Connect Help documentation, which does not contain information about this issue.
None of the above approaches worked, and I remain unable to edit in-app purchasees in App Store Connect. But I need to be able to edit in-app purchases to fix the underlying issue.
Please advise?