feat: notification, purchase, and analytics services
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
7
plugins/CLAUDE.md
Normal file
7
plugins/CLAUDE.md
Normal file
@@ -0,0 +1,7 @@
|
||||
<claude-mem-context>
|
||||
# Recent Activity
|
||||
|
||||
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
||||
|
||||
*No recent activity*
|
||||
</claude-mem-context>
|
||||
148
plugins/withStoreKitConfig.js
Normal file
148
plugins/withStoreKitConfig.js
Normal 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
|
||||
Reference in New Issue
Block a user