diff --git a/ios/Platform/AGENTS.md b/ios/Platform/AGENTS.md new file mode 100644 index 0000000..1a9b266 --- /dev/null +++ b/ios/Platform/AGENTS.md @@ -0,0 +1,3 @@ +# AGENTS.md + +- If using XcodeBuildMCP, use the installed XcodeBuildMCP skill before calling XcodeBuildMCP tools. diff --git a/ios/Platform/Platform.xcodeproj/project.pbxproj b/ios/Platform/Platform.xcodeproj/project.pbxproj index f0aeede..4e943e2 100644 --- a/ios/Platform/Platform.xcodeproj/project.pbxproj +++ b/ios/Platform/Platform.xcodeproj/project.pbxproj @@ -52,8 +52,36 @@ A10047 /* FeedManagementSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10047 /* FeedManagementSheet.swift */; }; A10048 /* ScrollViewDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10048 /* ScrollViewDriver.swift */; }; F20549752F805F5800AE8DF5 /* ConfettiSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = F20549742F805F5800AE8DF5 /* ConfettiSwiftUI */; }; + W10001 /* PlatformWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = W10010 /* PlatformWidgetBundle.swift */; }; + W10002 /* PlatformWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = W10011 /* PlatformWidget.swift */; }; + W10003 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = W10012 /* Assets.xcassets */; }; + W10004 /* PlatformWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = W10020 /* PlatformWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + W10030 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = G10010 /* Project object */; + proxyType = 1; + remoteGlobalIDString = W10040; + remoteInfo = PlatformWidgetExtension; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + W10050 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + W10004 /* PlatformWidgetExtension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ B10001 /* PlatformApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformApp.swift; sourceTree = ""; }; B10002 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -101,6 +129,10 @@ B10048 /* ScrollViewDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewDriver.swift; sourceTree = ""; }; C10001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D10001 /* Platform.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Platform.app; sourceTree = BUILT_PRODUCTS_DIR; }; + W10010 /* PlatformWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformWidgetBundle.swift; sourceTree = ""; }; + W10011 /* PlatformWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformWidget.swift; sourceTree = ""; }; + W10012 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + W10020 /* PlatformWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PlatformWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -112,6 +144,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + W10060 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -119,6 +158,7 @@ isa = PBXGroup; children = ( F10002 /* Platform */, + W10070 /* PlatformWidget */, F10020 /* Products */, ); sourceTree = ""; @@ -283,10 +323,21 @@ isa = PBXGroup; children = ( D10001 /* Platform.app */, + W10020 /* PlatformWidgetExtension.appex */, ); name = Products; sourceTree = ""; }; + W10070 /* PlatformWidget */ = { + isa = PBXGroup; + children = ( + W10010 /* PlatformWidgetBundle.swift */, + W10011 /* PlatformWidget.swift */, + W10012 /* Assets.xcassets */, + ); + path = PlatformWidget; + sourceTree = ""; + }; F10021 /* Feedback */ = { isa = PBXGroup; children = ( @@ -353,18 +404,45 @@ G10002 /* Sources */, E10001 /* Frameworks */, G10003 /* Resources */, + W10050 /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( + W10031 /* PBXTargetDependency */, ); name = Platform; productName = Platform; productReference = D10001 /* Platform.app */; productType = "com.apple.product-type.application"; }; + W10040 /* PlatformWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = W10080 /* Build configuration list for PBXNativeTarget "PlatformWidgetExtension" */; + buildPhases = ( + W10041 /* Sources */, + W10060 /* Frameworks */, + W10042 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PlatformWidgetExtension; + productName = PlatformWidgetExtension; + productReference = W10020 /* PlatformWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ +/* Begin PBXTargetDependency section */ + W10031 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = W10040 /* PlatformWidgetExtension */; + targetProxy = W10030 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXProject section */ G10010 /* Project object */ = { isa = PBXProject; @@ -376,6 +454,9 @@ G10001 = { CreatedOnToolsVersion = 15.4; }; + W10040 = { + CreatedOnToolsVersion = 15.4; + }; }; }; buildConfigurationList = H10001 /* Build configuration list for PBXProject "Platform" */; @@ -395,6 +476,7 @@ projectRoot = ""; targets = ( G10001 /* Platform */, + W10040 /* PlatformWidgetExtension */, ); }; /* End PBXProject section */ @@ -408,6 +490,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + W10042 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + W10003 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -461,6 +551,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + W10041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + W10001 /* PlatformWidgetBundle.swift in Sources */, + W10002 /* PlatformWidget.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -641,6 +740,58 @@ }; name = Release; }; + W10081 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = CRN5A2VZ79; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = PlatformWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.quadjourney.Platform.PlatformWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + W10082 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = CRN5A2VZ79; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = PlatformWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.quadjourney.Platform.PlatformWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -662,6 +813,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + W10080 /* Build configuration list for PBXNativeTarget "PlatformWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + W10081 /* Debug */, + W10082 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/ios/Platform/Platform.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Platform/Platform.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Platform/Platform.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Platform/Platform.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Platform/Platform.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..b544b5f --- /dev/null +++ b/ios/Platform/Platform.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "0813d8074328ac339b3ad4d9e895dca3cbd4327633fce793259860c9993a0805", + "pins" : [ + { + "identity" : "confettiswiftui", + "kind" : "remoteSourceControl", + "location" : "https://github.com/simibac/ConfettiSwiftUI", + "state" : { + "revision" : "9ae5bc2ce2149980884d4612321c3a266d6bf82c", + "version" : "3.0.0" + } + } + ], + "version" : 3 +} diff --git a/ios/Platform/PlatformWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/Platform/PlatformWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/Platform/PlatformWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Platform/PlatformWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Platform/PlatformWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/ios/Platform/PlatformWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Platform/PlatformWidget/Assets.xcassets/Contents.json b/ios/Platform/PlatformWidget/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/Platform/PlatformWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Platform/PlatformWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/ios/Platform/PlatformWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/Platform/PlatformWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/Platform/PlatformWidget/PlatformWidget.swift b/ios/Platform/PlatformWidget/PlatformWidget.swift new file mode 100644 index 0000000..0fa25da --- /dev/null +++ b/ios/Platform/PlatformWidget/PlatformWidget.swift @@ -0,0 +1,62 @@ +import WidgetKit +import SwiftUI + +struct Provider: TimelineProvider { + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date()) + } + + func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) { + let entry = SimpleEntry(date: Date()) + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + var entries: [SimpleEntry] = [] + let currentDate = Date() + for hourOffset in 0 ..< 5 { + let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! + let entry = SimpleEntry(date: entryDate) + entries.append(entry) + } + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } +} + +struct SimpleEntry: TimelineEntry { + let date: Date +} + +struct PlatformWidgetEntryView: View { + var entry: Provider.Entry + + var body: some View { + VStack { + Text("Platform") + .font(.headline) + Text(entry.date, style: .time) + .font(.caption) + } + } +} + +struct PlatformWidget: Widget { + let kind: String = "PlatformWidget" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: Provider()) { entry in + PlatformWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + .configurationDisplayName("Platform") + .description("Platform widget.") + .supportedFamilies([.systemSmall, .systemMedium]) + } +} + +#Preview(as: .systemSmall) { + PlatformWidget() +} timeline: { + SimpleEntry(date: .now) +} diff --git a/ios/Platform/PlatformWidget/PlatformWidgetBundle.swift b/ios/Platform/PlatformWidget/PlatformWidgetBundle.swift new file mode 100644 index 0000000..d49cb44 --- /dev/null +++ b/ios/Platform/PlatformWidget/PlatformWidgetBundle.swift @@ -0,0 +1,9 @@ +import WidgetKit +import SwiftUI + +@main +struct PlatformWidgetBundle: WidgetBundle { + var body: some Widget { + PlatformWidget() + } +}