Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
354 changes: 322 additions & 32 deletions Flinky.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

109 changes: 109 additions & 0 deletions Flinky.xcodeproj/xcshareddata/xcschemes/ShareExtension.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1640"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D509DE2E4621120067A402"
BuildableName = "ShareExtension.appex"
BlueprintName = "ShareExtension"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D359D82E1BDF540006226D"
BuildableName = "Flinky.app"
BlueprintName = "Flinky"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D50A032E46286D0067A402"
BuildableName = "ShareExtensionUITests.xctest"
BlueprintName = "ShareExtensionUITests"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D359D82E1BDF540006226D"
BuildableName = "Flinky.app"
BlueprintName = "Flinky"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D359D82E1BDF540006226D"
BuildableName = "Flinky.app"
BlueprintName = "Flinky"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1640"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
reference = "container:ShareExtensionUITests.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D50A032E46286D0067A402"
BuildableName = "ShareExtensionUITests.xctest"
BlueprintName = "ShareExtensionUITests"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4A5DC4D2E46133A00027944"
BuildableName = "FlinkySharedTests.xctest"
BlueprintName = "FlinkySharedTests"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4E789112E461A83005DA720"
BuildableName = "FlinkyCoreTests.xctest"
BlueprintName = "FlinkyCoreTests"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D50A032E46286D0067A402"
BuildableName = "ShareExtensionUITests.xctest"
BlueprintName = "ShareExtensionUITests"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D359D82E1BDF540006226D"
BuildableName = "Flinky.app"
BlueprintName = "Flinky"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D4D359D82E1BDF540006226D"
BuildableName = "Flinky.app"
BlueprintName = "Flinky"
ReferencedContainer = "container:Flinky.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
8 changes: 8 additions & 0 deletions Scripts/generate-localization.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ cd ..
# Ensure output directories exist
mkdir -p Targets/App/Sources/Generated
mkdir -p Targets/FlinkyCore/Sources/Generated
mkdir -p Targets/ShareExtension/Sources/Generated

# Function to generate localization for a target
generate_localization() {
Expand Down Expand Up @@ -89,9 +90,16 @@ generate_localization "FlinkyCore" \
"Targets/FlinkyCore/Sources/Generated/L10n.swift" \
"true"

# Generate localization for ShareExtension target (internal access)
generate_localization "ShareExtension" \
"Targets/ShareExtension/Sources/Resources/Localizable.xcstrings" \
"Targets/ShareExtension/Sources/Generated/L10n.swift" \
"false"

echo "✅ Localization generation complete!"
echo "📁 Generated: Targets/App/Sources/Generated/L10n.swift"
echo "📁 Generated: Targets/FlinkyCore/Sources/Generated/L10n.swift"
echo "📁 Generated: Targets/ShareExtension/Sources/Generated/L10n.swift"

# -- End Script --

Expand Down
59 changes: 26 additions & 33 deletions Targets/App/Sources/Main/FlinkyApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,36 @@ struct FlinkyApp: App {
static let logger = Logger.forType(Self.self)

@StateObject private var toastManager = ToastManager()
private let sharedModelContainer: ModelContainer

init() {
SentrySDK.start { options in
Self.configureSentry(options: options)
}

// Build shared model container (App Group) for app runtime, in-memory for tests
if ProcessInfo.processInfo.environment["TESTING"] == "1" {
// Keep existing testing behavior using in-memory store
let schema = Schema([
LinkListModel.self,
LinkModel.self,
DatabaseMetadata.self
])
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
do {
sharedModelContainer = try ModelContainer(for: schema, configurations: [config])
} catch {
fatalError("Failed to create in-memory ModelContainer: \(error)")
}
} else {
do {
sharedModelContainer = try SharedModelContainerFactory.make()
// Seed if needed on first app launch
DataSeedingService.seedDataIfNeeded(modelContext: sharedModelContainer.mainContext)
} catch {
fatalError("Failed to create shared ModelContainer: \(error)")
}
}
}

/// Configures the Sentry SDK options.
Expand Down Expand Up @@ -241,38 +266,6 @@ struct FlinkyApp: App {
options.publicKey = "30d2f7cc2fa469eaf8e4bdf958ad9d66bce491a7da1fb08ff0a7156a8e15a47d"
}
}
.modelContainer(
for: [
LinkListModel.self,
LinkModel.self,
DatabaseMetadata.self
],
// For testing we use an in-memory store to avoid persistent storage.
inMemory: ProcessInfo.processInfo.environment["TESTING"] == "1",
isAutosaveEnabled: false,
isUndoEnabled: false,
onSetup: { result in
switch result {
case .failure(let error):
Self.logger.error("Failed to setup ModelContainer: \(error)")
SentrySDK.capture(error: error) { scope in
scope.setTag(value: "model_container_setup", key: "operation")
scope.setContext(
value: [
"action": "setting_up_model_container"
], key: "error_details")
}

case .success(let container):
let breadcrumb = Breadcrumb()
breadcrumb.message = "ModelContainer setup successful, starting data seeding check"
breadcrumb.category = "database.setup"
breadcrumb.level = .info
SentrySDK.addBreadcrumb(breadcrumb)

DataSeedingService.seedDataIfNeeded(modelContext: container.mainContext)
}
}
)
.modelContainer(sharedModelContainer)
}
}
4 changes: 4 additions & 0 deletions Targets/App/Sources/Resources/App.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@
<array>
<string>TAG</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.techprimate.Flinky</string>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation
import SwiftData

public enum SharedModelContainerFactory {
/// Creates a SwiftData ModelContainer stored in the shared App Group so the app and extensions use the same store.
/// - Parameter appGroupId: The App Group identifier. Keep this in sync in both targets' entitlements.
/// - Returns: Configured ModelContainer
public static func make(appGroupId: String = "group.com.techprimate.Flinky") throws -> ModelContainer {
let schema = Schema([
LinkListModel.self,
LinkModel.self,
DatabaseMetadata.self
])

guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupId) else {

Check warning on line 15 in Targets/FlinkyCore/Sources/Utils/SharedModelContainerFactory.swift

View workflow job for this annotation

GitHub Actions / Build

value 'containerURL' was defined but never used; consider replacing with boolean test

Check warning on line 15 in Targets/FlinkyCore/Sources/Utils/SharedModelContainerFactory.swift

View workflow job for this annotation

GitHub Actions / Test

value 'containerURL' was defined but never used; consider replacing with boolean test
throw NSError(domain: "Flinky", code: 1, userInfo: [NSLocalizedDescriptionKey: "App Group container URL not found for \(appGroupId)"])
}

// iOS 18+: use groupContainer to co-locate the store in the App Group container
let configuration = ModelConfiguration(
schema: schema,
isStoredInMemoryOnly: false,
allowsSave: true,
groupContainer: .identifier(appGroupId),
cloudKitDatabase: .none
)

return try ModelContainer(for: schema, configurations: [configuration])
}
}
29 changes: 29 additions & 0 deletions Targets/ShareExtension/Config/ShareExtensionUITests.xctestplan
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"configurations" : [
{
"id" : "300A4DBC-F520-4BDC-B9CD-FC0E8AE78326",
"name" : "Test Scheme Action",
"options" : {

}
}
],
"defaultOptions" : {
"targetForVariableExpansion" : {
"containerPath" : "container:Flinky.xcodeproj",
"identifier" : "D4D359D82E1BDF540006226D",
"name" : "Flinky"
}
},
"testTargets" : [
{
"parallelizable" : true,
"target" : {
"containerPath" : "container:Flinky.xcodeproj",
"identifier" : "D4D50A032E46286D0067A402",
"name" : "ShareExtensionUITests"
}
}
],
"version" : 1
}
Loading
Loading