ios widget showing what is available at chucks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add support back to ios 16

+75 -16
+6 -6
ios/wasup-chucks.xcodeproj/project.pbxproj
··· 310 310 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 311 311 GCC_WARN_UNUSED_FUNCTION = YES; 312 312 GCC_WARN_UNUSED_VARIABLE = YES; 313 - IPHONEOS_DEPLOYMENT_TARGET = 26.2; 313 + IPHONEOS_DEPLOYMENT_TARGET = 16.0; 314 314 LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 315 315 MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 316 316 MTL_FAST_MATH = YES; ··· 368 368 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 369 369 GCC_WARN_UNUSED_FUNCTION = YES; 370 370 GCC_WARN_UNUSED_VARIABLE = YES; 371 - IPHONEOS_DEPLOYMENT_TARGET = 26.2; 371 + IPHONEOS_DEPLOYMENT_TARGET = 16.0; 372 372 LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 373 373 MTL_ENABLE_DEBUG_INFO = NO; 374 374 MTL_FAST_MATH = YES; ··· 386 386 ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; 387 387 CODE_SIGN_ENTITLEMENTS = "wasup-chucks/wasup-chucks.entitlements"; 388 388 CODE_SIGN_STYLE = Automatic; 389 - CURRENT_PROJECT_VERSION = 3; 389 + CURRENT_PROJECT_VERSION = 4; 390 390 DEVELOPMENT_TEAM = M67B42LX8D; 391 391 ENABLE_PREVIEWS = YES; 392 392 GENERATE_INFOPLIST_FILE = YES; ··· 422 422 ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; 423 423 CODE_SIGN_ENTITLEMENTS = "wasup-chucks/wasup-chucks.entitlements"; 424 424 CODE_SIGN_STYLE = Automatic; 425 - CURRENT_PROJECT_VERSION = 3; 425 + CURRENT_PROJECT_VERSION = 4; 426 426 DEVELOPMENT_TEAM = M67B42LX8D; 427 427 ENABLE_PREVIEWS = YES; 428 428 GENERATE_INFOPLIST_FILE = YES; ··· 457 457 ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; 458 458 CODE_SIGN_ENTITLEMENTS = widgetExtension.entitlements; 459 459 CODE_SIGN_STYLE = Automatic; 460 - CURRENT_PROJECT_VERSION = 3; 460 + CURRENT_PROJECT_VERSION = 4; 461 461 DEVELOPMENT_TEAM = M67B42LX8D; 462 462 GENERATE_INFOPLIST_FILE = YES; 463 463 INFOPLIST_FILE = widget/Info.plist; ··· 488 488 ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; 489 489 CODE_SIGN_ENTITLEMENTS = widgetExtension.entitlements; 490 490 CODE_SIGN_STYLE = Automatic; 491 - CURRENT_PROJECT_VERSION = 3; 491 + CURRENT_PROJECT_VERSION = 4; 492 492 DEVELOPMENT_TEAM = M67B42LX8D; 493 493 GENERATE_INFOPLIST_FILE = YES; 494 494 INFOPLIST_FILE = widget/Info.plist;
+55 -8
ios/wasup-chucks/ContentView.swift
··· 149 149 Text(remaining.expandedCountdown) 150 150 .font(.system(size: 64, weight: .bold, design: .rounded)) 151 151 .monospacedDigit() 152 - .contentTransition(.numericText()) 152 + .modifier(NumericContentTransition()) 153 153 154 154 Text(status.isOpen ? "until \(status.currentPhase.shortName) ends" : "until \(status.nextPhase?.shortName ?? "open")") 155 155 .font(.subheadline) ··· 274 274 } 275 275 .buttonStyle(.plain) 276 276 .foregroundStyle(isCurrent ? .green : .primary) 277 - .sensoryFeedback(.selection, trigger: meal.phase) 277 + .selectionHaptic(trigger: meal.phase) 278 278 .accessibilityLabel("\(meal.phase.shortName), \(formatTime(meal.startHour, meal.startMinute)) to \(formatTime(meal.endHour, meal.endMinute))\(isCurrent ? ", current meal" : "")") 279 279 .accessibilityHint("Double tap to view menu") 280 280 } ··· 305 305 NavigationStack { 306 306 Group { 307 307 if venues.isEmpty { 308 - ContentUnavailableView( 309 - "No Menu Available", 310 - systemImage: "fork.knife.circle", 311 - description: Text("No specific menu items for \(meal.phase.rawValue) today.") 312 - ) 308 + if #available(iOS 17.0, *) { 309 + ContentUnavailableView( 310 + "No Menu Available", 311 + systemImage: "fork.knife.circle", 312 + description: Text("No specific menu items for \(meal.phase.rawValue) today.") 313 + ) 314 + } else { 315 + VStack(spacing: 16) { 316 + Image(systemName: "fork.knife.circle") 317 + .font(.system(size: 48)) 318 + .foregroundStyle(.secondary) 319 + Text("No Menu Available") 320 + .font(.headline) 321 + Text("No specific menu items for \(meal.phase.rawValue) today.") 322 + .font(.subheadline) 323 + .foregroundStyle(.secondary) 324 + .multilineTextAlignment(.center) 325 + } 326 + .frame(maxWidth: .infinity, maxHeight: .infinity) 327 + } 313 328 } else { 314 329 List { 315 330 ForEach(venues) { venue in ··· 487 502 .font(.subheadline.weight(.semibold)) 488 503 } 489 504 .tint(.orange) 490 - .sensoryFeedback(.selection, trigger: isExpanded) 505 + .selectionHaptic(trigger: isExpanded) 491 506 .padding(16) 492 507 .frame(maxWidth: .infinity, alignment: .leading) 493 508 .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) ··· 563 578 .padding(.vertical, 3) 564 579 .background(color.opacity(0.15), in: RoundedRectangle(cornerRadius: 4)) 565 580 .accessibilityHidden(true) 581 + } 582 + } 583 + 584 + // MARK: - iOS 16 Compatibility 585 + 586 + struct NumericContentTransition: ViewModifier { 587 + func body(content: Content) -> some View { 588 + if #available(iOS 17.0, *) { 589 + content.contentTransition(.numericText()) 590 + } else { 591 + content 592 + } 593 + } 594 + } 595 + 596 + struct SelectionHaptic: ViewModifier { 597 + let trigger: AnyHashable 598 + 599 + func body(content: Content) -> some View { 600 + if #available(iOS 17.0, *) { 601 + content.sensoryFeedback(.selection, trigger: trigger) 602 + } else { 603 + content.onChange(of: trigger) { _ in 604 + UISelectionFeedbackGenerator().selectionChanged() 605 + } 606 + } 607 + } 608 + } 609 + 610 + extension View { 611 + func selectionHaptic<T: Hashable>(trigger: T) -> some View { 612 + modifier(SelectionHaptic(trigger: AnyHashable(trigger))) 566 613 } 567 614 } 568 615
+14 -2
ios/widget/widget.swift
··· 363 363 364 364 var body: some WidgetConfiguration { 365 365 StaticConfiguration(kind: kind, provider: ChucksProvider()) { entry in 366 - WidgetView(entry: entry) 367 - .containerBackground(.fill.tertiary, for: .widget) 366 + if #available(iOS 17.0, *) { 367 + WidgetView(entry: entry) 368 + .containerBackground(.fill.tertiary, for: .widget) 369 + } else { 370 + WidgetView(entry: entry) 371 + .padding() 372 + .background() 373 + } 368 374 } 369 375 .configurationDisplayName("Chuck's Status") 370 376 .description("See current meal times and specials at Chuck's.") ··· 397 403 } 398 404 399 405 // MARK: - Previews 406 + @available(iOS 17.0, *) 400 407 #Preview("Small", as: .systemSmall) { 401 408 ChucksWidget() 402 409 } timeline: { 403 410 ChucksEntry(date: Date(), status: ChucksStatus.calculate(), specials: [], venueName: "Home Cooking") 404 411 } 405 412 413 + @available(iOS 17.0, *) 406 414 #Preview("Medium", as: .systemMedium) { 407 415 ChucksWidget() 408 416 } timeline: { ··· 414 422 ], venueName: "Home Cooking") 415 423 } 416 424 425 + @available(iOS 17.0, *) 417 426 #Preview("Large", as: .systemLarge) { 418 427 ChucksWidget() 419 428 } timeline: { ··· 427 436 ], venueName: "Home Cooking") 428 437 } 429 438 439 + @available(iOS 17.0, *) 430 440 #Preview("Lock Screen - Inline", as: .accessoryInline) { 431 441 ChucksWidget() 432 442 } timeline: { 433 443 ChucksEntry(date: Date(), status: ChucksStatus.calculate(), specials: [], venueName: "Home Cooking") 434 444 } 435 445 446 + @available(iOS 17.0, *) 436 447 #Preview("Lock Screen - Circular", as: .accessoryCircular) { 437 448 ChucksWidget() 438 449 } timeline: { 439 450 ChucksEntry(date: Date(), status: ChucksStatus.calculate(), specials: [], venueName: "Home Cooking") 440 451 } 441 452 453 + @available(iOS 17.0, *) 442 454 #Preview("Lock Screen - Rectangular", as: .accessoryRectangular) { 443 455 ChucksWidget() 444 456 } timeline: {