feat: notification, purchase, and analytics services

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Millian Lamiaux
2026-02-21 00:04:47 +01:00
parent d6bc7f5a4c
commit 540bb015c7
10 changed files with 759 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
/**
* Expo Config Plugin: StoreKit Configuration for Sandbox Testing
*
* Adds TabataFit.storekit to the Xcode project and configures the scheme
* to use it for StoreKit testing. This enables purchase testing in the
* iOS simulator without real Apple Pay charges.
*/
const {
withXcodeProject,
withDangerousMod,
} = require('@expo/config-plugins')
const fs = require('fs')
const path = require('path')
const STOREKIT_FILENAME = 'TabataFit.storekit'
/**
* Step 1: Copy the .storekit file and add it to the Xcode project (project.pbxproj)
*/
function withStoreKitXcodeProject(config) {
return withXcodeProject(config, (config) => {
const project = config.modResults
const projectName = config.modRequest.projectName
const platformRoot = config.modRequest.platformProjectRoot
// Copy .storekit file into the iOS app directory
const sourceFile = path.resolve(__dirname, '..', 'storekit', STOREKIT_FILENAME)
const destDir = path.join(platformRoot, projectName)
const destFile = path.join(destDir, STOREKIT_FILENAME)
fs.copyFileSync(sourceFile, destFile)
// Add file to the Xcode project manually via pbxproj APIs
// Find the app's PBXGroup
const mainGroupKey = project.getFirstProject().firstProject.mainGroup
const mainGroup = project.getPBXGroupByKey(mainGroupKey)
// Find the app target group (e.g., "tabatago")
let appGroupKey = null
if (mainGroup && mainGroup.children) {
for (const child of mainGroup.children) {
if (child.comment === projectName) {
appGroupKey = child.value
break
}
}
}
if (!appGroupKey) {
console.warn('[withStoreKitConfig] Could not find app group in Xcode project')
return config
}
const appGroup = project.getPBXGroupByKey(appGroupKey)
// Check if already added
const alreadyExists = appGroup.children?.some(
(child) => child.comment === STOREKIT_FILENAME
)
if (!alreadyExists) {
// Generate a unique UUID for the file reference
const fileRefUuid = project.generateUuid()
// Add PBXFileReference — NOT added to any build phase
// .storekit files are testing configs, not app resources
const pbxFileRef = project.hash.project.objects['PBXFileReference']
pbxFileRef[fileRefUuid] = {
isa: 'PBXFileReference',
lastKnownFileType: 'text.json',
path: STOREKIT_FILENAME,
sourceTree: '"<group>"',
}
pbxFileRef[`${fileRefUuid}_comment`] = STOREKIT_FILENAME
// Add to the app group's children (visible in Xcode navigator)
appGroup.children.push({
value: fileRefUuid,
comment: STOREKIT_FILENAME,
})
console.log('[withStoreKitConfig] Added', STOREKIT_FILENAME, 'to Xcode project')
}
return config
})
}
/**
* Step 2: Configure the Xcode scheme to use the StoreKit configuration
*/
function withStoreKitScheme(config) {
return withDangerousMod(config, [
'ios',
(config) => {
const projectName = config.modRequest.projectName
const schemePath = path.join(
config.modRequest.platformProjectRoot,
`${projectName}.xcodeproj`,
'xcshareddata',
'xcschemes',
`${projectName}.xcscheme`
)
if (!fs.existsSync(schemePath)) {
console.warn('[withStoreKitConfig] Scheme not found:', schemePath)
return config
}
let scheme = fs.readFileSync(schemePath, 'utf8')
// Skip if already configured
if (scheme.includes('storeKitConfigurationFileReference')) {
console.log('[withStoreKitConfig] StoreKit config already in scheme')
return config
}
// Insert StoreKitConfigurationFileReference as a child element of LaunchAction
// The identifier path is relative to the workspace root (ios/ directory)
const storeKitRef = `
<StoreKitConfigurationFileReference
identifier = "${projectName}/${STOREKIT_FILENAME}">
</StoreKitConfigurationFileReference>`
// Insert before the closing </LaunchAction> tag
scheme = scheme.replace(
'</LaunchAction>',
`${storeKitRef}\n </LaunchAction>`
)
fs.writeFileSync(schemePath, scheme, 'utf8')
console.log('[withStoreKitConfig] Added StoreKit config to scheme')
return config
},
])
}
/**
* Main plugin
*/
function withStoreKitConfig(config) {
config = withStoreKitXcodeProject(config)
config = withStoreKitScheme(config)
return config
}
module.exports = withStoreKitConfig