Post

Replies

Boosts

Views

Activity

Swift Charts: .hour annotations do not appear
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 } }
3
2
710
Jun ’24
Swift Charts: Changing chartXVisibleDomain changes chartScrollPosition
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 } }
3
2
850
Jun ’24
iOS 17 BUG IN CLIENT OF CLOUDKIT: Not entitled to listen to push notifications.
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?
1
0
814
Jul ’23
Keras with tensorflow-metal freezes during training with image augmentation
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?
3
0
1.9k
Feb ’23
CreateML image extraction ends in `Unexpected error`, opens `Create New Project` window
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?
1
0
1.2k
Jan ’23
How can I create a multi-input model using CreateML?
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?
1
0
1k
Jan ’23
UICloudSharingController sharing via Messages broken in iOS16
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
9
4
3.3k
Sep ’22
Build warning after migrating watchOS project to single target in Xcode 14
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?
1
0
1.9k
Sep ’22
App Store Connect: App was rejected, but in-app purchases remain “In Review” and cannot be edited
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?
1
0
998
May ’22