From fd0a37c220a5f5b791aabea29f5462fc422bdcd8 Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Fri, 10 Jul 2026 19:52:59 -0700 Subject: [PATCH] Live Activities for Event Schedules --- RubyEvents.xcodeproj/project.pbxproj | 212 ++++++++++- .../UserInterfaceState.xcuserstate | Bin 15413 -> 194902 bytes .../xcschemes/xcschememanagement.plist | 10 + RubyEvents/App.swift | 23 ++ RubyEvents/AppDelegate.swift | 3 +- RubyEvents/Info.plist | 21 +- RubyEvents/RubyEvents.entitlements | 10 + RubyEvents/SceneDelegate.swift | 22 ++ RubyEvents/bridge/LiveActivityComponent.swift | 57 +++ .../liveactivity/LiveActivityManager.swift | 335 +++++++++++++++++ .../liveactivity/LiveActivityShared.swift | 18 + .../ScheduleActivityAttributes.swift | 37 ++ RubyEventsWidgets/AppIntent.swift | 15 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../Assets.xcassets/Contents.json | 6 + .../WidgetBackground.colorset/Contents.json | 11 + RubyEventsWidgets/Info.plist | 13 + RubyEventsWidgets/RubyEventsWidgets.swift | 88 +++++ .../RubyEventsWidgetsBundle.swift | 18 + .../RubyEventsWidgetsControl.swift | 77 ++++ .../RubyEventsWidgetsLiveActivity.swift | 344 ++++++++++++++++++ RubyEventsWidgetsExtension.entitlements | 10 + 23 files changed, 1370 insertions(+), 6 deletions(-) create mode 100644 RubyEvents/RubyEvents.entitlements create mode 100644 RubyEvents/bridge/LiveActivityComponent.swift create mode 100644 RubyEvents/liveactivity/LiveActivityManager.swift create mode 100644 RubyEvents/liveactivity/LiveActivityShared.swift create mode 100644 RubyEvents/liveactivity/ScheduleActivityAttributes.swift create mode 100644 RubyEventsWidgets/AppIntent.swift create mode 100644 RubyEventsWidgets/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 RubyEventsWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 RubyEventsWidgets/Assets.xcassets/Contents.json create mode 100644 RubyEventsWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 RubyEventsWidgets/Info.plist create mode 100644 RubyEventsWidgets/RubyEventsWidgets.swift create mode 100644 RubyEventsWidgets/RubyEventsWidgetsBundle.swift create mode 100644 RubyEventsWidgets/RubyEventsWidgetsControl.swift create mode 100644 RubyEventsWidgets/RubyEventsWidgetsLiveActivity.swift create mode 100644 RubyEventsWidgetsExtension.entitlements diff --git a/RubyEvents.xcodeproj/project.pbxproj b/RubyEvents.xcodeproj/project.pbxproj index 17c031b..a943fa1 100644 --- a/RubyEvents.xcodeproj/project.pbxproj +++ b/RubyEvents.xcodeproj/project.pbxproj @@ -10,10 +10,41 @@ 973DD3652D95F8BE008E0AED /* CachedAsyncImage in Frameworks */ = {isa = PBXBuildFile; productRef = 973DD3642D95F8BE008E0AED /* CachedAsyncImage */; }; 97613B4C2D0635C600220031 /* HotwireNative in Frameworks */ = {isa = PBXBuildFile; productRef = 97613B4B2D0635C600220031 /* HotwireNative */; }; 97613B572D06512900220031 /* YouTubePlayerKit in Frameworks */ = {isa = PBXBuildFile; productRef = 97613B562D06512900220031 /* YouTubePlayerKit */; }; + 97AAF32F2FFF2733009DB8C2 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97AAF32E2FFF2733009DB8C2 /* WidgetKit.framework */; }; + 97AAF3312FFF2733009DB8C2 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97AAF3302FFF2733009DB8C2 /* SwiftUI.framework */; }; + 97AAF3422FFF2734009DB8C2 /* RubyEventsWidgetsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 97AAF32C2FFF2733009DB8C2 /* RubyEventsWidgetsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 97AAF3402FFF2734009DB8C2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97613B0E2D0634F500220031 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97AAF32B2FFF2733009DB8C2; + remoteInfo = RubyEventsWidgetsExtension; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 97AAF3432FFF2734009DB8C2 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 97AAF3422FFF2734009DB8C2 /* RubyEventsWidgetsExtension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 97613B162D0634F500220031 /* RubyEvents.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RubyEvents.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97AAF32C2FFF2733009DB8C2 /* RubyEventsWidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = RubyEventsWidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 97AAF32E2FFF2733009DB8C2 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 97AAF3302FFF2733009DB8C2 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 97AAF3552FFF37FE009DB8C2 /* RubyEventsWidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RubyEventsWidgetsExtension.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -24,6 +55,21 @@ ); target = 97613B152D0634F500220031 /* RubyEvents */; }; + 97AAF3472FFF2734009DB8C2 /* Exceptions for "RubyEventsWidgets" folder in "RubyEventsWidgetsExtension" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 97AAF32B2FFF2733009DB8C2 /* RubyEventsWidgetsExtension */; + }; + 97AAF3502FFF2D25009DB8C2 /* Exceptions for "RubyEvents" folder in "RubyEventsWidgetsExtension" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + liveactivity/LiveActivityShared.swift, + liveactivity/ScheduleActivityAttributes.swift, + ); + target = 97AAF32B2FFF2733009DB8C2 /* RubyEventsWidgetsExtension */; + }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -31,10 +77,19 @@ isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( 97613B3E2D0634FA00220031 /* Exceptions for "RubyEvents" folder in "RubyEvents" target */, + 97AAF3502FFF2D25009DB8C2 /* Exceptions for "RubyEvents" folder in "RubyEventsWidgetsExtension" target */, ); path = RubyEvents; sourceTree = ""; }; + 97AAF3322FFF2733009DB8C2 /* RubyEventsWidgets */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 97AAF3472FFF2734009DB8C2 /* Exceptions for "RubyEventsWidgets" folder in "RubyEventsWidgetsExtension" target */, + ); + path = RubyEventsWidgets; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -48,13 +103,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 97AAF3292FFF2733009DB8C2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 97AAF3312FFF2733009DB8C2 /* SwiftUI.framework in Frameworks */, + 97AAF32F2FFF2733009DB8C2 /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 97613B0D2D0634F500220031 = { isa = PBXGroup; children = ( + 97AAF3552FFF37FE009DB8C2 /* RubyEventsWidgetsExtension.entitlements */, 97613B182D0634F500220031 /* RubyEvents */, + 97AAF3322FFF2733009DB8C2 /* RubyEventsWidgets */, + 97AAF32D2FFF2733009DB8C2 /* Frameworks */, 97613B172D0634F500220031 /* Products */, ); sourceTree = ""; @@ -63,10 +130,20 @@ isa = PBXGroup; children = ( 97613B162D0634F500220031 /* RubyEvents.app */, + 97AAF32C2FFF2733009DB8C2 /* RubyEventsWidgetsExtension.appex */, ); name = Products; sourceTree = ""; }; + 97AAF32D2FFF2733009DB8C2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 97AAF32E2FFF2733009DB8C2 /* WidgetKit.framework */, + 97AAF3302FFF2733009DB8C2 /* SwiftUI.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -77,10 +154,12 @@ 97613B122D0634F500220031 /* Sources */, 97613B132D0634F500220031 /* Frameworks */, 97613B142D0634F500220031 /* Resources */, + 97AAF3432FFF2734009DB8C2 /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( + 97AAF3412FFF2734009DB8C2 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( 97613B182D0634F500220031 /* RubyEvents */, @@ -95,6 +174,28 @@ productReference = 97613B162D0634F500220031 /* RubyEvents.app */; productType = "com.apple.product-type.application"; }; + 97AAF32B2FFF2733009DB8C2 /* RubyEventsWidgetsExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97AAF3462FFF2734009DB8C2 /* Build configuration list for PBXNativeTarget "RubyEventsWidgetsExtension" */; + buildPhases = ( + 97AAF3282FFF2733009DB8C2 /* Sources */, + 97AAF3292FFF2733009DB8C2 /* Frameworks */, + 97AAF32A2FFF2733009DB8C2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 97AAF3322FFF2733009DB8C2 /* RubyEventsWidgets */, + ); + name = RubyEventsWidgetsExtension; + packageProductDependencies = ( + ); + productName = RubyEventsWidgetsExtension; + productReference = 97AAF32C2FFF2733009DB8C2 /* RubyEventsWidgetsExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -102,12 +203,15 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1610; + LastSwiftUpdateCheck = 2620; LastUpgradeCheck = 1610; TargetAttributes = { 97613B152D0634F500220031 = { CreatedOnToolsVersion = 16.1; }; + 97AAF32B2FFF2733009DB8C2 = { + CreatedOnToolsVersion = 26.2; + }; }; }; buildConfigurationList = 97613B112D0634F500220031 /* Build configuration list for PBXProject "RubyEvents" */; @@ -130,6 +234,7 @@ projectRoot = ""; targets = ( 97613B152D0634F500220031 /* RubyEvents */, + 97AAF32B2FFF2733009DB8C2 /* RubyEventsWidgetsExtension */, ); }; /* End PBXProject section */ @@ -142,6 +247,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 97AAF32A2FFF2733009DB8C2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -152,8 +264,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 97AAF3282FFF2733009DB8C2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 97AAF3412FFF2734009DB8C2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97AAF32B2FFF2733009DB8C2 /* RubyEventsWidgetsExtension */; + targetProxy = 97AAF3402FFF2734009DB8C2 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 97613B402D0634FA00220031 /* Debug */ = { isa = XCBuildConfiguration; @@ -161,6 +288,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CODE_SIGN_ENTITLEMENTS = RubyEvents/RubyEvents.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5UY2R75SM3; @@ -168,12 +296,14 @@ INFOPLIST_FILE = RubyEvents/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = RubyEvents; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; + INFOPLIST_KEY_NSSupportsLiveActivities = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + INFOPLIST_KEY_UIUserInterfaceStyle = Light; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -193,6 +323,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CODE_SIGN_ENTITLEMENTS = RubyEvents/RubyEvents.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5UY2R75SM3; @@ -200,12 +331,14 @@ INFOPLIST_FILE = RubyEvents/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = RubyEvents; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; + INFOPLIST_KEY_NSSupportsLiveActivities = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + INFOPLIST_KEY_UIUserInterfaceStyle = Light; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -338,6 +471,70 @@ }; name = Release; }; + 97AAF3442FFF2734009DB8C2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = RubyEventsWidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5UY2R75SM3; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = RubyEventsWidgets/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = RubyEventsWidgets; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 26.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.rubyevents.RubyEvents.RubyEventsWidgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97AAF3452FFF2734009DB8C2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = RubyEventsWidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5UY2R75SM3; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = RubyEventsWidgets/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = RubyEventsWidgets; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 26.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.rubyevents.RubyEvents.RubyEventsWidgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -359,6 +556,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 97AAF3462FFF2734009DB8C2 /* Build configuration list for PBXNativeTarget "RubyEventsWidgetsExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97AAF3442FFF2734009DB8C2 /* Debug */, + 97AAF3452FFF2734009DB8C2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/RubyEvents.xcodeproj/project.xcworkspace/xcuserdata/marcoroth.xcuserdatad/UserInterfaceState.xcuserstate b/RubyEvents.xcodeproj/project.xcworkspace/xcuserdata/marcoroth.xcuserdatad/UserInterfaceState.xcuserstate index b82f64c13680c65ed1ca02b68a54e49859bce5e7..db59f34e0cc44b4dfb8430239ee97b7633f92212 100644 GIT binary patch literal 194902 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

0Z*k@eV|w6 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 URQSLDt(^Ve|Nj5K!{b)|4+iBbHvj+t delta 8300 zcmZu#33yXQ*Pb(T+icC+&^Bq3F4>wiThr2(y+8{T3Iz(47AzDRN}(HFSVYKNL1Ym` zszzmzE+C+yh=_Qd)BY6NVd;f zF+l-ikOW1cSQLjes2l43WOclMC@MmuP$?=y1v!ul zgJBqqfUz(R9)vQe03TFAH8emY%!E0>!z1u0tb*0B2G+t0unt;3nLH+i(Z&!k_RL28?kG zj>U0UgS9vw>#!aha02d%GjSHq#@%pt+ynQ-y>M?l3>V_zcmy7ai|{Bs0hi;6coMF_ zQ*afo!}YicH{*x!Li`wBgcsu_cqx7ouk_<*Z~(8z8}N&GBi@8Je zAveh_a+};CcgdgRFY-5sxJXXGMR74)EEmUVI4x)5%$$w0bDk)!E7yn1;renzxPlf= z-;1N|w5Vl(evsdWvbUgkq(gdSKnW-jC811%76S(RQ zjaS5t^iA^Bwj>)Cwj4H`?>Pt+w4;197!9Fn8cQ`a{wNxTYLQ`?@b;I5sYj3eft^Z{jDd4X0z4As;F^ilj)!Se=5ZQFc43LQ~N+s;35;(2ixjD!mB=<0yi{{Z}YNV!iL=lf3p=Ro#X+aq} z0@J^P=s@as5D)jpcfW=Bz}|1cntvq3)FM>SiWXA~jX#DSN6XN1#^@7h1$q*#ENS#r zGZ9qP)s{3>)%Y5l%4_P!R@6;uuJP42F-cJyO`%rm4j#SS*!P1&C+EMo=0qoZ&!Fc} zb{h(yXQ`d0wxQ?ID(aw4>MG8wsA{TfC>~YbQ0Z$LuTYJdR9E3k58sB@N=G(NoSip= zZPOSQX&Y*9*^?3%--bF+$+%%fxm6*)${S{f*EgUS13#RAL_s^+h&G{;;7#V3|4F-( zzn9S~D0|(y`|^AZy&e?wU>e(sw$b$a3vZ&gf(tunMl0G$yD~jpcvJ63d(gh%bUszL zqIYT5{pa6DpQG%}=m7cveTY6n2hqpq6LbiDiaw*+v>WYCd(fV=7wt{^&>Y%#Gdhg2 zm~nW}G4v%f6c74_8Am^61OwT35G`Q_GLVi98-=#0uDM~7uYX;|wxPMXFbw?^RNv3E zKaCHo^E@i(K)<33!5__~d7)ptf>Ss|!p@UlLpM=O8@i5e(0n?$4c$Vw=@2@K8GI`p zn2i1gf@0bM0RW~2bSNFx4je>4BrT*P=*Zw>l|?OE``MV+jzV&ff{baz)!bZFF>!X2 zudz^}xU;|OF3rxI>Mb8)dHtPnU(n$pHGu+@#mvY?=jXP80+kqZD%PRk=Lo17p)UVT zXwdBFlEmrxq8FZ6e{A9tyRG*Kp$>>^Lw<&ZduIYI=-HVV%yo1$Ep7t?B+v)w7+O@; z3D^Q(ZDrGxvDH-z;CF65b674a}%u9gR2cy%b^wDNrX>J2-<>Q^7&U(FfZg z0St`h1GH%L>LDWZG;RlDn(C~wk!_*34c-gR`q0LR@ z6RUk8H`5M<|8g`i5{eB$6&ArL7)__pDmt|t9)K}WLZ{I>+Q@kP?0Z=3hw)I(HiQYZ zx)mnU8tVGD;lX5fgi4qabge=A4_WxbgAXLr+AtqeVH!JCZh4ch4W`~#MGe#iiPqBk zR;Z`b|C1;q*#ylnql6I^Ha0a>)mGAmK;M&)8uraF3ucd3$p1NBQRtC&HvLe@D~Inp z1@I2#3tH%NE@&9`M zTlfx6&}V3XK1*H3KKJZt5=t~sp+1hL!{`dk=BXW^#@|Is%?Q+Yd_ zg`ZJL7+d9?CqD<5Q1%AK->+~14#GwFopHH}uBI#L8oG7^T!t%f75-q?EA$1rj<(V^ zc4huOFyU8N>o?{I?^!p5;qiAE9;bR~?`F~3vQc#v<+BsZn-bHycK2r8+Y}QVg|atd z4oBcfEWko6!eT6;9dte2KwqRA=_b0FzO*suz_FbDL|7FZhn)Az3@MxFE1|J#$Gi)m z1HU0HGb6E^CowI<<6-}v#NanA%bT5;=E>@o*tu;YwxjG#I0+|XBQ{|(wqPqx!8W>u zzDi%CTj}d`8-0Urr*ATlVh46&7j|P0PGfiJY{MP&H+qp?rGL=t?Cw@jcZ&SnyrO{j zBCIHUkPX9ewr_*4Q@*$l9>C;_b8uhW5BJBpIFG(Xcha}%F8U7Ly#WuzgK$1u9D)nz z9{Mpo#uk61XPA7SxDLiZ-|cgvny;!;?`Ai_k@Z!Pi*Q;=Tp~6`qQ# znY+c)=m)L1hJF}cG(_U*xB)i?YOjG_Ll4rA3YD;6VtsX0W0S{o3^3s$BDD#D_1B=M z|ARxP9(>z%ZG%9nP{+g_5sJi7qw33>rj%3#!@{7d+NL&%RbO7) z$g-iLCg$?egZU19;N44%fHJyWB2g7pS4~tzuxGnOg%;=AihHod*r-+so799SDe)Vx zx5l%1ouPxhq%X^>t*EbKo4OijR!weF;I>3#TT-$y3|54U`%K|so5^g}4lu6PEX8Q60b?2&jDp3O3?i&x=kJD!7SP`3}` zxp*G-(@*Fj`YHV^sM|;I{E%)xr-wUrJ3gSk2FWV?I9?vqh!_Dvv(mu@{b%A4GQpAA!A@C#-7D%QTBTL9DW|J!mIHb z`X&8}eoeoj$JgT*f>{=B!|n82W|{-(|IzP*mLU&XMkv$uRRk_yf?|0Yn<=hp47zky zgfs=dXQ1|X?(h{U&q_RDdu=Z%$zwM zN1`*$GuM$g$D8GKBxZZM&Yj!&zFVf5O3frxQ z!$n>3)ljY$CUN~9iF;`1Byk(xWmy5fL$9@lLbfY#p2KD>0m3oT1QSAU(3@>RG=y;A z3ZzEhzJ4Mhu_*f`5=Ep$M&v|6lte|MiJEjFG4wXQL+{c*>0k739uN<#IqQPYfi1B$6bO%tHh%P{=5pD&!r!q2{lrT_}V)6hP!$UL=Y96|@lTzl{Ts*|E z5~!3RAR__yF_VeGyxGZG1>us1Ob&8g$wM5Se1uFzax#rn2b3u|p0S|eA^sStC3U2p z)wRQeU(yud6B1V0Jj@o@C@v7=#?f)aPblFT8;_9rWC3}UEF_PSMaUJ1mf)!hu=9|>gOdj) zDFZDHTui`OMyc7IkUBtEKHEV)ARm&C$Uz>wJf!oG!9&*$@(DQvSIB2P zWb)91uHzw>p=-1N_i~Xh$+yARz9L_fZ^&^TvUtemp&Jj~JD9_8;oq1`8OYFhnI%@P zz&-(Vc&4k z-|2(~hVsNWEDd zTO)Qy?2ULgVt-^zWJ07pGCi_eWbep#Ca z2=)sO2tE`X6dV>D5gZeICHO|D5)Ks36fO`x<`*s&J}z7?Tp?T|>=14cZWL}7ZWV45 zZWrzl?iBtaiWTLE@NXAHNBz2PMl153hWTs@cWR7I6#4q6`&q$t? zJTF--Su0s5X_ItFHb^!~HcMWX9F+VL6&2-*8W}Y!YIW4EsBfcgNRgCCBcv?NmujT( zQoS@mnj|$!%~Gp0MQW2~`lUUjy`+7lxzYjBLDIp}8tD>gK)P1CPTD5zkiH~+Mf$3A zt8|<6tSnOImSxNGWMgEdvPrV3vO3u;882HdYn8RjUY2c@?UC)59guw}J0$x|c35^q zc2f46?2_!3?6&N#JVGvztK}Mbyj(9&mYd`jd5YX6?2pDDgpe6KjIxTLtMxTd(N zxUIOW6etybrBO0H6>qE%f~u_}!!UZqzhsFGAhl}nYUDpO5YEmEyfy{Ou&`bKp! zIwo2dZHvx`?iJlTIwyK)baC{U=+fwM(Phz<(f+FFY0)*&b!F0)LOMpZBu*HUUh~#Q{7A5N8MN5U!A9}Rj*cmr2ayERQ;p+XZ1Ptuj=2_ zf2gmkZ>n#r?{~pe6JO1z1kkyUfKcLLhT6c1Z|CWo|bAK(Js&~)GpF4(LSzSu6;q< zs%_V<*S@ITq-wAe+xoluzYGF{$RIID z4RV9hkYea*7;UIG%r(q2_zk>awPBOtWy2Q3R>L;KcEe%Aal>`L;ilns;vVO zm3Ss8KdCrrd{TMRq$FQbWm0`oLsC=HjHFpf4<$XBv_5H1(($A}k|CK(j!YIMOOmC@ zDamQc!;*(5k2DI5YGZ;i$!Ij1jaH-0m|@H`W*fU3dm4KibBrU64;wp-+l(I@zczkr zJYoF7c+z;N!qQ@S z+7hsAx4dWh&~ni7iRG~6h~=2&E6YWz!m77sSTn8J*6!Ax*51|}Yk%tiYreI>T4@}qo2)af4_W70sdc_}p>>hZ*#eX#A zr<8LkS5p465u4nmwdrhWw(hoGwj5hOTcK^7t;|+#tFTqtrrK(3583A0sBOM&p>45k zsco69)waWS!1jghvhAuJ*#&m7U20d@W9?eI&Tg{IQH_8Ios z_Br+i_807J_VxBR?H}7u`0Y3Cx9xZBf2BgII5jENlG-bEKx$d)q|`a7%ThO_?o9nC z^~cn!4!J|+=;DZT#5)X*M8`!Bs`b(T+om-qcoS!<6IDc_o zcHVXV?ZU1+*DzP9tK3!N^1Bwfwz=MNopxPv-E!S^{q4r?2)E3wbgSJlZjC$MZF0NZ z-P|MGW$sG%RCkTL-reYa*gelp-H*5zxEH!R-0!;2xc~HsJ@FocC&^>-SUq-+)8p}^ zdvg7r0iHpg!JY!oFwbz$NY5xwv1ftj4bRCmMOu%vX=!WH4y7GWJCXK7+Nrd&X+Njk z@p4{~H_9vbD!m4;*_+}`^}4)Wz1iOG-d^55-l5)7Z;f|`cfNOtcbRvEx5fLsca8T2 zZ=1Kn`-=B%?=J5<-aX!Z-cP-Uy+^%YdB-02e&;>oJ?FjP{oQ-n`&W8Ix-eamE=%u{ z9+$37*QY0>+tR)1eOR|RKfNHmFugdvBz SchemeUserState + AnyCodable (Playground).xcscheme + + orderHint + 3 + RubyEvents.xcscheme_^#shared#^_ orderHint 0 + RubyEventsWidgetsExtension.xcscheme_^#shared#^_ + + orderHint + 3 + diff --git a/RubyEvents/App.swift b/RubyEvents/App.swift index 0731079..28313d9 100644 --- a/RubyEvents/App.swift +++ b/RubyEvents/App.swift @@ -78,6 +78,29 @@ class App { func navigatorFor(title: String) -> Navigator? { tabBarController.navigatorFor(title: title) } + + func route(_ url: URL) { + let target = webURL(from: url) + + guard isTabbed else { + navigator.route(target) + return + } + + if let index = tabBarController.configuration?.items.firstIndex(where: { $0.title == "Events" }) { + tabBarController.selectedIndex = index + } + + (navigatorFor(title: "Events") ?? tabBarController.currentNavigator)?.route(target) + } + + private func webURL(from url: URL) -> URL { + guard url.scheme == "rubyevents" else { return url } + + let path = "/\(url.host ?? "")\(url.path)" + + return Router.instance.root_url().appendingPathComponent(path) + } } extension App: NavigatorDelegate { diff --git a/RubyEvents/AppDelegate.swift b/RubyEvents/AppDelegate.swift index da3323d..668e704 100644 --- a/RubyEvents/AppDelegate.swift +++ b/RubyEvents/AppDelegate.swift @@ -19,7 +19,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Hotwire.config.applicationUserAgentPrefix = "Hotwire Native iOS; app_version: \(versionNumber); unique_device_id: \(uniqueDeviceId);" Hotwire.registerBridgeComponents([ - ButtonComponent.self + ButtonComponent.self, + LiveActivityComponent.self ]) Hotwire.config.showDoneButtonOnModals = true diff --git a/RubyEvents/Info.plist b/RubyEvents/Info.plist index e0f24c0..e9d4abf 100644 --- a/RubyEvents/Info.plist +++ b/RubyEvents/Info.plist @@ -2,8 +2,21 @@ - UIUserInterfaceStyle - Light + UIUserInterfaceStyle + Light + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + org.rubyevents.RubyEvents + CFBundleURLSchemes + + rubyevents + + + UIApplicationSceneManifest UIApplicationSupportsMultipleScenes @@ -23,5 +36,9 @@ + UIBackgroundModes + + audio + diff --git a/RubyEvents/RubyEvents.entitlements b/RubyEvents/RubyEvents.entitlements new file mode 100644 index 0000000..0d57a92 --- /dev/null +++ b/RubyEvents/RubyEvents.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.org.rubyevents.RubyEvents + + + diff --git a/RubyEvents/SceneDelegate.swift b/RubyEvents/SceneDelegate.swift index f2d24ff..15a6138 100644 --- a/RubyEvents/SceneDelegate.swift +++ b/RubyEvents/SceneDelegate.swift @@ -5,5 +5,27 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { App.instance.start(sceneDelegate: self) + + if let url = connectionOptions.urlContexts.first?.url ?? connectionOptions.userActivities.first?.webpageURL { + routeAfterLaunch(url) + } + } + + func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { + if let url = URLContexts.first?.url { + App.instance.route(url) + } + } + + func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + if let url = userActivity.webpageURL { + App.instance.route(url) + } + } + + private func routeAfterLaunch(_ url: URL) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + App.instance.route(url) + } } } diff --git a/RubyEvents/bridge/LiveActivityComponent.swift b/RubyEvents/bridge/LiveActivityComponent.swift new file mode 100644 index 0000000..754c534 --- /dev/null +++ b/RubyEvents/bridge/LiveActivityComponent.swift @@ -0,0 +1,57 @@ +// +// LiveActivityComponent.swift +// RubyEvents +// + +import Foundation +import HotwireNative +import UIKit + +final class LiveActivityComponent: BridgeComponent { + override class var name: String { "live-activity" } + + override func onReceive(message: Message) { + switch message.event { + case "status": + replyStatus(to: message) + + case "start": + guard let data: StartData = message.data() else { return } + + Task { + await LiveActivityManager.shared.start(eventSlug: data.slug, eventName: data.name ?? "") + + if LiveActivityManager.shared.isRunning { + UINotificationFeedbackGenerator().notificationOccurred(.success) + } + + replyStatus(to: message) + } + + case "stop": + Task { + await LiveActivityManager.shared.stop() + replyStatus(to: message) + } + + default: + break + } + } + + private func replyStatus(to message: Message) { + let data: StartData? = message.data() + let active = data.map { LiveActivityManager.shared.isActive(eventSlug: $0.slug) } ?? false + + reply(to: message.event, with: StatusReply(active: active)) + } + + private struct StartData: Decodable { + let slug: String + let name: String? + } + + private struct StatusReply: Encodable { + let active: Bool + } +} diff --git a/RubyEvents/liveactivity/LiveActivityManager.swift b/RubyEvents/liveactivity/LiveActivityManager.swift new file mode 100644 index 0000000..ee138fa --- /dev/null +++ b/RubyEvents/liveactivity/LiveActivityManager.swift @@ -0,0 +1,335 @@ +// +// LiveActivityManager.swift +// RubyEvents +// + +import ActivityKit +import Foundation +import UIKit + +struct LiveScheduleResponse: Decodable { + struct EventInfo: Decodable { + let name: String + let slug: String + let avatar_url: String? + let featured_background: String? + let featured_color: String? + } + + let event: EventInfo + let time_zone: String? + let sessions: [LiveSession] +} + +struct LiveTalk: Decodable { + let title: String? + let speakers: [String]? + let speaker_avatars: [String]? + let track: String? + let track_color: String? +} + +struct LiveSession: Decodable { + let talks: [LiveTalk]? + let start_at: String? + let end_at: String? + let date: String? + + var startDate: Date? { start_at.flatMap { ISO8601DateFormatter().date(from: $0) } } + var endDate: Date? { end_at.flatMap { ISO8601DateFormatter().date(from: $0) } } +} + +@MainActor +final class LiveActivityManager { + static let shared = LiveActivityManager() + static let didEndNotification = Notification.Name("LiveActivityManager.didEnd") + + private var activity: Activity? + private var sessions: [LiveSession] = [] + private var eventSlug: String? + private var advanceTimer: Timer? + + private init() { + NotificationCenter.default.addObserver( + forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main + ) { [weak self] _ in + Task { await self?.refreshIfRunning() } + } + } + + private var systemActivities: [Activity] { + Activity.activities + } + + var isRunning: Bool { !systemActivities.isEmpty } + var activeEventSlug: String? { systemActivities.first?.attributes.eventSlug } + + func isActive(eventSlug: String) -> Bool { + systemActivities.contains { $0.attributes.eventSlug == eventSlug } + } + + func start(eventSlug: String, eventName: String) async { + guard ActivityAuthorizationInfo().areActivitiesEnabled else { return } + guard let response = await fetchSchedule(eventSlug: eventSlug) else { return } + + sessions = sortedSessions(response.sessions) + + self.eventSlug = eventSlug + + if let existing = systemActivities.first(where: { $0.attributes.eventSlug == eventSlug }) { + activity = existing + observeActivityState() + + await advance() + + return + } + + await endActivities(exceptSlug: eventSlug) + + guard let state = await buildState() else { return } + + let attributes = ScheduleActivityAttributes( + eventName: response.event.name, + eventSlug: eventSlug, + featuredBackground: response.event.featured_background ?? "#000000", + featuredColor: response.event.featured_color ?? "#FFFFFF", + eventAvatarFile: await cacheImage(from: response.event.avatar_url, as: "event-avatar"), + scheduleURL: scheduleDeepLink(eventSlug: eventSlug) + ) + + do { + activity = try Activity.request( + attributes: attributes, + content: .init(state: state, staleDate: nextBoundary().addingTimeInterval(60)), + pushType: nil + ) + + scheduleAdvance() + observeActivityState() + } catch { + print("Live Activity start failed:", error) + } + } + + func stop() async { + advanceTimer?.invalidate() + advanceTimer = nil + + for activity in systemActivities { + await activity.end(nil, dismissalPolicy: .immediate) + } + + activity = nil + eventSlug = nil + sessions = [] + + NotificationCenter.default.post(name: Self.didEndNotification, object: nil) + } + + private func endActivities(exceptSlug: String?) async { + for activity in systemActivities where activity.attributes.eventSlug != exceptSlug { + await activity.end(nil, dismissalPolicy: .immediate) + } + } + + private func observeActivityState() { + guard let activity else { return } + + Task { [weak self] in + for await state in activity.activityStateUpdates { + if state == .ended || state == .dismissed { + self?.clearActiveState() + NotificationCenter.default.post(name: Self.didEndNotification, object: nil) + + return + } + } + } + } + + private func clearActiveState() { + advanceTimer?.invalidate() + advanceTimer = nil + activity = nil + eventSlug = nil + sessions = [] + } + + private func refreshIfRunning() async { + guard let eventSlug = activeEventSlug else { return } + + if activity == nil { + activity = systemActivities.first { $0.attributes.eventSlug == eventSlug } + self.eventSlug = eventSlug + observeActivityState() + } + + if let response = await fetchSchedule(eventSlug: eventSlug) { + sessions = sortedSessions(response.sessions) + } + + await advance() + } + + private func currentSession(_ now: Date = Date()) -> LiveSession? { + sessions.first { session in + guard let start = session.startDate, let end = session.endDate else { return false } + return start <= now && now < end + } + } + + private func nextSession(_ now: Date = Date()) -> LiveSession? { + sessions + .filter { ($0.startDate ?? .distantPast) > now } + .min { ($0.startDate ?? .distantFuture) < ($1.startDate ?? .distantFuture) } + } + + private func isTalkSession(_ session: LiveSession) -> Bool { + (session.talks ?? []).contains { !($0.speakers ?? []).isEmpty } + } + + private func nextTalkSession(_ now: Date = Date()) -> LiveSession? { + sessions + .filter { ($0.startDate ?? .distantPast) > now && isTalkSession($0) } + .min { ($0.startDate ?? .distantFuture) < ($1.startDate ?? .distantFuture) } + } + + private func buildState(_ now: Date = Date()) async -> ScheduleActivityAttributes.ContentState? { + let next = nextTalkSession(now) + let nextTalk = next?.talks?.first { !($0.speakers ?? []).isEmpty } ?? next?.talks?.first + + if let current = currentSession(now), let start = current.startDate, let end = current.endDate { + return .init( + talks: await sessionTalks(current), + sessionStart: start, + sessionEnd: end, + isLive: true, + nextTitle: nextTalk?.title, + nextSpeakers: nextTalk?.speakers?.joined(separator: ", "), + nextTrack: nextTalk?.track, + nextTrackColor: nextTalk?.track_color, + nextSpeakerAvatarFile: await cacheImage(from: nextTalk?.speaker_avatars?.first, as: "next-speaker"), + nextStart: next?.startDate + ) + } + + if let next, let start = next.startDate, let end = next.endDate { + return .init( + talks: await sessionTalks(next), + sessionStart: start, + sessionEnd: end, + isLive: false, + nextTitle: nil, + nextSpeakers: nil, + nextTrack: nil, + nextTrackColor: nil, + nextSpeakerAvatarFile: nil, + nextStart: nil + ) + } + + return nil + } + + private func sessionTalks(_ session: LiveSession) async -> [ScheduleActivityAttributes.SessionTalk] { + var result: [ScheduleActivityAttributes.SessionTalk] = [] + + for (index, talk) in (session.talks ?? []).enumerated() { + result.append(.init( + title: talk.title ?? "Session", + speakers: talk.speakers?.joined(separator: ", ") ?? "", + track: talk.track, + trackColor: talk.track_color, + avatarFile: await cacheImage(from: talk.speaker_avatars?.first, as: "talk-\(index)") + )) + } + + return result + } + + private func scheduleDeepLink(eventSlug: String) -> String { + let day = (currentSession() ?? nextSession() ?? sessions.first)?.date + + if let day { + return "rubyevents://events/\(eventSlug)/schedule/day/\(day)" + } + + return "rubyevents://events/\(eventSlug)/schedule" + } + + private func nextBoundary(_ now: Date = Date()) -> Date { + if let current = currentSession(now), let end = current.endDate { return end } + if let next = nextSession(now), let start = next.startDate { return start } + + return now.addingTimeInterval(3600) + } + + private func scheduleAdvance() { + advanceTimer?.invalidate() + + let interval = max(nextBoundary().timeIntervalSinceNow + 1, 1) + + advanceTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: false) { [weak self] _ in + Task { await self?.advance() } + } + } + + private func advance() async { + guard let activity else { return } + + guard let state = await buildState() else { + await stop() + return + } + + await activity.update(.init(state: state, staleDate: nextBoundary().addingTimeInterval(60))) + + scheduleAdvance() + } + + private func sortedSessions(_ sessions: [LiveSession]) -> [LiveSession] { + sessions.sorted { ($0.startDate ?? .distantFuture) < ($1.startDate ?? .distantFuture) } + } + + private func fetchSchedule(eventSlug: String) async -> LiveScheduleResponse? { + let endpoint = Router.instance.root_url() + .appendingPathComponent("/hotwire/native/v1/events/\(eventSlug)/live_schedule.json") + .absoluteString + + return try? await APIService.shared.fetchData(from: endpoint) + } + + private func cacheImage(from urlString: String?, as name: String) async -> String? { + guard let urlString, !urlString.isEmpty, + let destination = LiveActivityShared.imageURL(for: "\(name).png") else { return nil } + + let absolute = urlString.hasPrefix("http") + ? urlString + : Router.instance.root_url().appendingPathComponent(urlString).absoluteString + + guard let url = URL(string: absolute) else { return nil } + + do { + let (data, _) = try await URLSession.shared.data(from: url) + guard let png = Self.downscaledPNG(data, maxDimension: 120) else { return nil } + + try png.write(to: destination, options: .atomic) + + return "\(name).png" + } catch { + return nil + } + } + + private static func downscaledPNG(_ data: Data, maxDimension: CGFloat) -> Data? { + guard let image = UIImage(data: data) else { return nil } + + let longest = max(image.size.width, image.size.height, 1) + let scale = min(1, maxDimension / longest) + let size = CGSize(width: image.size.width * scale, height: image.size.height * scale) + let renderer = UIGraphicsImageRenderer(size: size) + + return renderer.image { _ in image.draw(in: CGRect(origin: .zero, size: size)) }.pngData() + } +} diff --git a/RubyEvents/liveactivity/LiveActivityShared.swift b/RubyEvents/liveactivity/LiveActivityShared.swift new file mode 100644 index 0000000..2e25cc7 --- /dev/null +++ b/RubyEvents/liveactivity/LiveActivityShared.swift @@ -0,0 +1,18 @@ +// +// LiveActivityShared.swift +// RubyEvents +// + +import Foundation + +enum LiveActivityShared { + static let appGroupID = "group.org.rubyevents.RubyEvents" + + static var containerURL: URL? { + FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID) + } + + static func imageURL(for filename: String) -> URL? { + containerURL?.appendingPathComponent(filename) + } +} diff --git a/RubyEvents/liveactivity/ScheduleActivityAttributes.swift b/RubyEvents/liveactivity/ScheduleActivityAttributes.swift new file mode 100644 index 0000000..1941f72 --- /dev/null +++ b/RubyEvents/liveactivity/ScheduleActivityAttributes.swift @@ -0,0 +1,37 @@ +// +// ScheduleActivityAttributes.swift +// RubyEvents +// + +import ActivityKit +import Foundation + +struct ScheduleActivityAttributes: ActivityAttributes { + public struct SessionTalk: Codable, Hashable { + var title: String + var speakers: String + var track: String? + var trackColor: String? // hex + var avatarFile: String? + } + + public struct ContentState: Codable, Hashable { + var talks: [SessionTalk] + var sessionStart: Date + var sessionEnd: Date + var isLive: Bool + var nextTitle: String? + var nextSpeakers: String? + var nextTrack: String? + var nextTrackColor: String? + var nextSpeakerAvatarFile: String? + var nextStart: Date? + } + + var eventName: String + var eventSlug: String + var featuredBackground: String // hex + var featuredColor: String // hex + var eventAvatarFile: String? + var scheduleURL: String +} diff --git a/RubyEventsWidgets/AppIntent.swift b/RubyEventsWidgets/AppIntent.swift new file mode 100644 index 0000000..5e6752b --- /dev/null +++ b/RubyEventsWidgets/AppIntent.swift @@ -0,0 +1,15 @@ +// +// AppIntent.swift +// RubyEventsWidgets +// + +import WidgetKit +import AppIntents + +struct ConfigurationAppIntent: WidgetConfigurationIntent { + static var title: LocalizedStringResource { "Configuration" } + static var description: IntentDescription { "This is an example widget." } + + @Parameter(title: "Favorite Emoji", default: "πŸ˜ƒ") + var favoriteEmoji: String +} diff --git a/RubyEventsWidgets/Assets.xcassets/AccentColor.colorset/Contents.json b/RubyEventsWidgets/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/RubyEventsWidgets/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/RubyEventsWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json b/RubyEventsWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2305880 --- /dev/null +++ b/RubyEventsWidgets/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/RubyEventsWidgets/Assets.xcassets/Contents.json b/RubyEventsWidgets/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/RubyEventsWidgets/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/RubyEventsWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json b/RubyEventsWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/RubyEventsWidgets/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/RubyEventsWidgets/Info.plist b/RubyEventsWidgets/Info.plist new file mode 100644 index 0000000..464a4f8 --- /dev/null +++ b/RubyEventsWidgets/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + NSSupportsLiveActivities + + + diff --git a/RubyEventsWidgets/RubyEventsWidgets.swift b/RubyEventsWidgets/RubyEventsWidgets.swift new file mode 100644 index 0000000..56e4a82 --- /dev/null +++ b/RubyEventsWidgets/RubyEventsWidgets.swift @@ -0,0 +1,88 @@ +// +// RubyEventsWidgets.swift +// RubyEventsWidgets +// +// Created by Marco Roth on 09.07.2026. +// + +import WidgetKit +import SwiftUI + +struct Provider: AppIntentTimelineProvider { + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date(), configuration: ConfigurationAppIntent()) + } + + func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> SimpleEntry { + SimpleEntry(date: Date(), configuration: configuration) + } + + func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline { + var entries: [SimpleEntry] = [] + + // Generate a timeline consisting of five entries an hour apart, starting from the current date. + let currentDate = Date() + for hourOffset in 0 ..< 5 { + let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! + let entry = SimpleEntry(date: entryDate, configuration: configuration) + entries.append(entry) + } + + return Timeline(entries: entries, policy: .atEnd) + } + +// func relevances() async -> WidgetRelevances { +// // Generate a list containing the contexts this widget is relevant in. +// } +} + +struct SimpleEntry: TimelineEntry { + let date: Date + let configuration: ConfigurationAppIntent +} + +struct RubyEventsWidgetsEntryView: View { + var entry: Provider.Entry + + var body: some View { + VStack { + Text("Time:") + Text(entry.date, style: .time) + + Text("Favorite Emoji:") + Text(entry.configuration.favoriteEmoji) + } + } +} + +struct RubyEventsWidgets: Widget { + let kind: String = "RubyEventsWidgets" + + var body: some WidgetConfiguration { + AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: Provider()) { entry in + RubyEventsWidgetsEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + } +} + +extension ConfigurationAppIntent { + fileprivate static var smiley: ConfigurationAppIntent { + let intent = ConfigurationAppIntent() + intent.favoriteEmoji = "πŸ˜€" + return intent + } + + fileprivate static var starEyes: ConfigurationAppIntent { + let intent = ConfigurationAppIntent() + intent.favoriteEmoji = "🀩" + return intent + } +} + +#Preview(as: .systemSmall) { + RubyEventsWidgets() +} timeline: { + SimpleEntry(date: .now, configuration: .smiley) + SimpleEntry(date: .now, configuration: .starEyes) +} diff --git a/RubyEventsWidgets/RubyEventsWidgetsBundle.swift b/RubyEventsWidgets/RubyEventsWidgetsBundle.swift new file mode 100644 index 0000000..1aa540e --- /dev/null +++ b/RubyEventsWidgets/RubyEventsWidgetsBundle.swift @@ -0,0 +1,18 @@ +// +// RubyEventsWidgetsBundle.swift +// RubyEventsWidgets +// +// Created by Marco Roth on 09.07.2026. +// + +import WidgetKit +import SwiftUI + +@main +struct RubyEventsWidgetsBundle: WidgetBundle { + var body: some Widget { + RubyEventsWidgets() + RubyEventsWidgetsControl() + RubyEventsWidgetsLiveActivity() + } +} diff --git a/RubyEventsWidgets/RubyEventsWidgetsControl.swift b/RubyEventsWidgets/RubyEventsWidgetsControl.swift new file mode 100644 index 0000000..f0af355 --- /dev/null +++ b/RubyEventsWidgets/RubyEventsWidgetsControl.swift @@ -0,0 +1,77 @@ +// +// RubyEventsWidgetsControl.swift +// RubyEventsWidgets +// +// Created by Marco Roth on 09.07.2026. +// + +import AppIntents +import SwiftUI +import WidgetKit + +struct RubyEventsWidgetsControl: ControlWidget { + static let kind: String = "org.rubyevents.RubyEvents.RubyEventsWidgets" + + var body: some ControlWidgetConfiguration { + AppIntentControlConfiguration( + kind: Self.kind, + provider: Provider() + ) { value in + ControlWidgetToggle( + "Start Timer", + isOn: value.isRunning, + action: StartTimerIntent(value.name) + ) { isRunning in + Label(isRunning ? "On" : "Off", systemImage: "timer") + } + } + .displayName("Timer") + .description("A an example control that runs a timer.") + } +} + +extension RubyEventsWidgetsControl { + struct Value { + var isRunning: Bool + var name: String + } + + struct Provider: AppIntentControlValueProvider { + func previewValue(configuration: TimerConfiguration) -> Value { + RubyEventsWidgetsControl.Value(isRunning: false, name: configuration.timerName) + } + + func currentValue(configuration: TimerConfiguration) async throws -> Value { + let isRunning = true // Check if the timer is running + return RubyEventsWidgetsControl.Value(isRunning: isRunning, name: configuration.timerName) + } + } +} + +struct TimerConfiguration: ControlConfigurationIntent { + static let title: LocalizedStringResource = "Timer Name Configuration" + + @Parameter(title: "Timer Name", default: "Timer") + var timerName: String +} + +struct StartTimerIntent: SetValueIntent { + static let title: LocalizedStringResource = "Start a timer" + + @Parameter(title: "Timer Name") + var name: String + + @Parameter(title: "Timer is running") + var value: Bool + + init() {} + + init(_ name: String) { + self.name = name + } + + func perform() async throws -> some IntentResult { + // Start the timer… + return .result() + } +} diff --git a/RubyEventsWidgets/RubyEventsWidgetsLiveActivity.swift b/RubyEventsWidgets/RubyEventsWidgetsLiveActivity.swift new file mode 100644 index 0000000..ce6f1c3 --- /dev/null +++ b/RubyEventsWidgets/RubyEventsWidgetsLiveActivity.swift @@ -0,0 +1,344 @@ +// +// RubyEventsWidgetsLiveActivity.swift +// RubyEventsWidgets +// + +import ActivityKit +import SwiftUI +import UIKit +import WidgetKit + +private typealias Talk = ScheduleActivityAttributes.SessionTalk + +private func sharedImage(_ filename: String?) -> Image? { + guard let filename, + let url = LiveActivityShared.imageURL(for: filename), + let uiImage = UIImage(contentsOfFile: url.path) else { return nil } + + return Image(uiImage: uiImage) +} + +private func hexColor(_ hex: String?) -> Color { + guard let hex else { return .gray } + + let string = hex.hasPrefix("#") ? String(hex.dropFirst()) : hex + + guard string.count == 6, let value = UInt64(string, radix: 16) else { return .gray } + + return Color( + red: Double((value >> 16) & 0xFF) / 255, + green: Double((value >> 8) & 0xFF) / 255, + blue: Double(value & 0xFF) / 255 + ) +} + +private func darkerColor(_ hex: String, by factor: Double = 0.2) -> Color { + let string = hex.hasPrefix("#") ? String(hex.dropFirst()) : hex + guard string.count == 6, let value = UInt64(string, radix: 16) else { return .black } + let scale = 1 - factor + + return Color( + red: Double((value >> 16) & 0xFF) / 255 * scale, + green: Double((value >> 8) & 0xFF) / 255 * scale, + blue: Double(value & 0xFF) / 255 * scale + ) +} + +private func timeRange(_ start: Date, _ end: Date) -> String { + "\(start.formatted(date: .omitted, time: .shortened)) – \(end.formatted(date: .omitted, time: .shortened))" +} + +@ViewBuilder +private func trackBadge(_ track: String?, _ colorHex: String?) -> some View { + if let track, !track.isEmpty { + Text(track) + .font(.caption2.weight(.bold)) + .padding(.horizontal, 6) + .padding(.vertical, 1) + .background(hexColor(colorHex)) + .foregroundStyle(.white) + .clipShape(Capsule()) + } +} + +@ViewBuilder +private func avatarCircle(_ filename: String?, size: CGFloat, border: Color) -> some View { + if let avatar = sharedImage(filename) { + avatar.resizable().scaledToFill() + .frame(width: size, height: size) + .clipShape(Circle()) + .overlay(Circle().strokeBorder(border, lineWidth: 0.5)) + } +} + +struct RubyEventsWidgetsLiveActivity: Widget { + var body: some WidgetConfiguration { + ActivityConfiguration(for: ScheduleActivityAttributes.self) { context in + LiveActivityLockScreen(context: context) + .activityBackgroundTint(hexColor(context.attributes.featuredBackground)) + .activitySystemActionForegroundColor(hexColor(context.attributes.featuredColor)) + } dynamicIsland: { context in + let accent = hexColor(context.attributes.featuredColor) + let talks = context.state.talks + + return DynamicIsland { + DynamicIslandExpandedRegion(.leading) { + if let avatar = sharedImage(context.attributes.eventAvatarFile) { + avatar.resizable().scaledToFill() + .frame(width: 38, height: 38) + .clipShape(RoundedRectangle(cornerRadius: 8)) + } else { + Label("LIVE", systemImage: "record.circle.fill") + .font(.caption2.weight(.semibold)).foregroundStyle(.red) + } + } + DynamicIslandExpandedRegion(.trailing) { + if context.state.isLive { + Text(timerInterval: context.state.sessionStart...context.state.sessionEnd, countsDown: true) + .font(.caption.monospacedDigit()).foregroundStyle(.secondary).frame(width: 56) + } + } + DynamicIslandExpandedRegion(.center) { + VStack(alignment: .leading, spacing: 2) { + Text(talks.first?.title ?? "Session") + .font(.subheadline.weight(.semibold)).lineLimit(2) + Text(timeRange(context.state.sessionStart, context.state.sessionEnd)) + .font(.caption2).foregroundStyle(.secondary) + } + } + DynamicIslandExpandedRegion(.bottom) { + VStack(alignment: .leading, spacing: 4) { + ForEach(0.. 1 ? talks[i].title : talks[i].speakers) + .font(.caption).foregroundStyle(.secondary).lineLimit(1) + } + } + if talks.count > 2 { + Text("+\(talks.count - 2) more").font(.caption2).foregroundStyle(.secondary) + } + if let next = context.state.nextTitle { + Text("Up next: \(next)").font(.caption2).foregroundStyle(.secondary).lineLimit(1) + } + } + } + } compactLeading: { + Image(systemName: "record.circle.fill").foregroundStyle(.red) + } compactTrailing: { + if context.state.isLive { + Text(timerInterval: context.state.sessionStart...context.state.sessionEnd, countsDown: true) + .font(.caption2.monospacedDigit()).frame(width: 40) + } + } minimal: { + Image(systemName: "record.circle.fill").foregroundStyle(.red) + } + .keylineTint(accent) + } + } +} + +struct LiveActivityLockScreen: View { + let context: ActivityViewContext + + private var background: Color { hexColor(context.attributes.featuredBackground) } + private var foreground: Color { hexColor(context.attributes.featuredColor) } + + private var visibleTalks: [Talk] { Array(context.state.talks.prefix(2)) } + private var extraTalks: Int { max(0, context.state.talks.count - 2) } + + var body: some View { + VStack(spacing: 0) { + HStack(alignment: context.state.talks.count > 1 ? .center : .top, spacing: 12) { + VStack(alignment: .leading, spacing: 6) { + eventAvatar + + VStack(alignment: .leading, spacing: 1) { + Text("\(context.state.sessionStart.formatted(date: .omitted, time: .shortened)) –") + .font(.caption2) + .foregroundStyle(foreground.opacity(0.8)) + .lineLimit(1) + Text(context.state.sessionEnd.formatted(date: .omitted, time: .shortened)) + .font(.caption2) + .foregroundStyle(foreground.opacity(0.55)) + .lineLimit(1) + + if context.state.isLive { + Text(timerInterval: context.state.sessionStart...context.state.sessionEnd, countsDown: true) + .font(.caption.monospacedDigit().weight(.semibold)) + .foregroundStyle(foreground) + .lineLimit(1) + .padding(.top, 8) + } + } + } + .frame(width: 62, alignment: .leading) + + VStack(alignment: .leading, spacing: 10) { + ForEach(visibleTalks.indices, id: \.self) { i in + if i > 0 { + Rectangle() + .fill(foreground.opacity(0.18)) + .frame(height: 0.5) + } + talkBlock(visibleTalks[i]) + } + if extraTalks > 0 { + Text("+\(extraTalks) more") + .font(.caption2.weight(.medium)) + .foregroundStyle(foreground.opacity(0.6)) + } + } + + Spacer(minLength: 0) + } + .padding(16) + .frame(maxWidth: .infinity, alignment: .leading) + .background(background) + + if let next = context.state.nextTitle { + Group { + if context.state.talks.count <= 1 { + upNextFull(next) + } else { + upNextCompact(next) + } + } + .padding(.horizontal, 16) + .padding(.vertical, context.state.talks.count <= 1 ? 12 : 10) + .frame(maxWidth: .infinity, alignment: .leading) + .background(darkerColor(context.attributes.featuredBackground)) + } + } + .widgetURL(URL(string: context.attributes.scheduleURL)) + } + + private func talkBlock(_ talk: Talk) -> some View { + VStack(alignment: .leading, spacing: 3) { + Text(talk.title) + .font(.subheadline.weight(.semibold)) + .foregroundStyle(foreground) + .lineLimit(context.state.talks.count > 1 ? 1 : 2) + + if !talk.speakers.isEmpty || !(talk.track ?? "").isEmpty { + HStack(spacing: 6) { + if !talk.speakers.isEmpty { + avatarCircle(talk.avatarFile, size: 18, border: foreground.opacity(0.25)) + + Text(talk.speakers).font(.caption).foregroundStyle(foreground.opacity(0.85)).lineLimit(1) + } + + Spacer(minLength: 4) + trackBadge(talk.track, talk.trackColor) + } + } + } + } + + private func upNextCompact(_ next: String) -> some View { + HStack(alignment: .center, spacing: 8) { + Text("UP NEXT") + .font(.caption2.weight(.bold)) + .foregroundStyle(foreground.opacity(0.55)) + + avatarCircle(context.state.nextSpeakerAvatarFile, size: 16, border: foreground.opacity(0.25)) + + Text(next).font(.caption2).foregroundStyle(foreground.opacity(0.85)).lineLimit(1) + + Spacer(minLength: 4) + + trackBadge(context.state.nextTrack, context.state.nextTrackColor) + + if let start = context.state.nextStart { + Text(start.formatted(date: .omitted, time: .shortened)) + .font(.caption2.monospacedDigit()).foregroundStyle(foreground.opacity(0.85)) + } + } + .frame(height: 20) + } + + private func upNextFull(_ next: String) -> some View { + HStack(alignment: .center, spacing: 12) { + if sharedImage(context.state.nextSpeakerAvatarFile) != nil { + avatarCircle(context.state.nextSpeakerAvatarFile, size: 40, border: foreground.opacity(0.25)) + } + + VStack(alignment: .leading, spacing: 3) { + HStack(spacing: 6) { + Text("UP NEXT") + .font(.caption2.weight(.bold)) + .foregroundStyle(foreground.opacity(0.55)) + trackBadge(context.state.nextTrack, context.state.nextTrackColor) + + Spacer(minLength: 4) + + if let start = context.state.nextStart { + Text(start.formatted(date: .omitted, time: .shortened)) + .font(.caption.monospacedDigit()).foregroundStyle(foreground.opacity(0.85)) + } + } + + Text(next) + .font(.subheadline.weight(.semibold)).foregroundStyle(foreground).lineLimit(1) + + if let speakers = context.state.nextSpeakers, !speakers.isEmpty { + Text(speakers) + .font(.caption).foregroundStyle(foreground.opacity(0.8)).lineLimit(1) + } + } + } + } + + @ViewBuilder + private var eventAvatar: some View { + if let avatar = sharedImage(context.attributes.eventAvatarFile) { + avatar.resizable().scaledToFill() + .frame(width: 46, height: 46) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .overlay(RoundedRectangle(cornerRadius: 10).strokeBorder(foreground.opacity(0.25), lineWidth: 1)) + } else { + Image(systemName: "record.circle.fill") + .font(.title2).foregroundStyle(.red).frame(width: 46, height: 46) + } + } +} + +extension ScheduleActivityAttributes { + fileprivate static var preview: ScheduleActivityAttributes { + ScheduleActivityAttributes( + eventName: "Rails World 2025", + eventSlug: "rails-world-2025", + featuredBackground: "#3B1D62", + featuredColor: "#FFFFFF", + eventAvatarFile: nil, + scheduleURL: "rubyevents://events/rails-world-2025/schedule/day/2025-09-04" + ) + } +} + +extension ScheduleActivityAttributes.ContentState { + fileprivate static var sample: ScheduleActivityAttributes.ContentState { + .init( + talks: [ + .init(title: "Multi-Tenant Rails", speakers: "Mike Dalessio", track: "Track 1", trackColor: "#DC2626", avatarFile: nil), + .init(title: "Startup Speed, Enterprise Scale", speakers: "Austin Story", track: "Track 2", trackColor: "#2563EB", avatarFile: nil) + ], + sessionStart: .now, + sessionEnd: .now.addingTimeInterval(45 * 60), + isLive: true, + nextTitle: "SQLite Replication with Beamer", + nextSpeakers: "Kevin McConnell", + nextTrack: "Track 1", + nextTrackColor: "#DC2626", + nextSpeakerAvatarFile: nil, + nextStart: .now.addingTimeInterval(45 * 60) + ) + } +} + +#Preview("Live Activity", as: .content, using: ScheduleActivityAttributes.preview) { + RubyEventsWidgetsLiveActivity() +} contentStates: { + ScheduleActivityAttributes.ContentState.sample +} diff --git a/RubyEventsWidgetsExtension.entitlements b/RubyEventsWidgetsExtension.entitlements new file mode 100644 index 0000000..0d57a92 --- /dev/null +++ b/RubyEventsWidgetsExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.org.rubyevents.RubyEvents + + +