feat(premium): add store-identical Release run scheme and real-purchase priority
- 'Haru_Danim-Store' 공유 스킴 추가: Release 구성으로 실행해 DEBUG 코드 (프리미엄 테스트 토글·검증용 런치 인자)가 전부 빠진 '스토어에 올릴 그대로'의 앱을 실기기에서 확인. StoreKit 로컬 구성은 유지되어 실제 결제 시트가 뜨되 돈은 청구되지 않는다 - 실구매/복원 시 DEBUG 테스트 토글 강제 모드를 자동 해제(실권한 우선), 개발용 섹션에 '토글 강제 해제' 버튼과 동작 설명 footer 추가 - Localizable.xcstrings는 Xcode 직렬화 포맷 정규화 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
This commit is contained in:
parent
14fc5cb5bd
commit
040198beb4
@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
"스토어에 올릴 그대로" 확인용 스킴.
|
||||
- Release 구성으로 실행: #if DEBUG 코드(프리미엄 테스트 토글, 검증용 런치 인자)가 전부 빠진다
|
||||
- StoreKit 로컬 구성(HaruDanim.storekit)은 그대로 붙어서, 실기기에서 실제 결제 시트가 뜨되
|
||||
돈은 청구되지 않는다 (결제 시트에 [Environment: Xcode] 표시)
|
||||
-->
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2660"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
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 = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<StoreKitConfigurationFileReference
|
||||
identifier = "../../HaruDanim.storekit">
|
||||
</StoreKitConfigurationFileReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "725A9E832FFCC65000846FFB"
|
||||
BuildableName = "Haru_Danim.app"
|
||||
BlueprintName = "Haru_Danim"
|
||||
ReferencedContainer = "container:Haru_Danim.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Release">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -178,6 +178,10 @@ final class PremiumStore {
|
||||
|
||||
/// 구매·복원 직후: 권한 재계산 → 플랜 정보 갱신 → 위젯·워치에 즉시 반영
|
||||
private func didChangeEntitlement() async {
|
||||
#if DEBUG
|
||||
// 실구매/복원은 개발용 테스트 토글 강제 모드보다 우선한다 (DEBUG 빌드에서 구매 검증 시 혼동 방지)
|
||||
await PremiumManager.shared.clearMockOverride()
|
||||
#endif
|
||||
await PremiumManager.shared.refresh()
|
||||
await refreshEntitlement()
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -368,11 +368,24 @@ struct PremiumView: View {
|
||||
purchaseSection
|
||||
}
|
||||
#if DEBUG
|
||||
Section("개발용") {
|
||||
Section {
|
||||
Toggle("프리미엄 상태 (테스트)", isOn: Binding(
|
||||
get: { premium.isPremium },
|
||||
set: { premium.setMockPremium($0) }
|
||||
))
|
||||
Button {
|
||||
Task {
|
||||
await premium.clearMockOverride()
|
||||
await store.refreshEntitlement()
|
||||
}
|
||||
} label: {
|
||||
Text(verbatim: "토글 강제 해제 — 실제 구매 상태 따르기")
|
||||
.font(.callout)
|
||||
}
|
||||
} header: {
|
||||
Text(verbatim: "개발용")
|
||||
} footer: {
|
||||
Text(verbatim: "이 섹션은 개발(DEBUG) 빌드에서만 보여요. 출시(Release) 빌드와 'Haru_Danim-Store' 스킴 실행에는 나타나지 않아요. 토글을 한 번이라도 쓰면 실제 구매 상태를 무시하므로, 결제 검증 시에는 '토글 강제 해제'를 눌러 주세요.")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -176,6 +176,13 @@ final class PremiumManager {
|
||||
AppGroup.defaults.set(true, forKey: Self.mockActiveKey)
|
||||
apply(value)
|
||||
}
|
||||
|
||||
/// 테스트 토글 강제 모드 해제 (DEBUG 전용) — 이후 실제 StoreKit 구매 상태가 그대로 반영된다.
|
||||
/// 실구매·복원이 일어났을 때(PremiumStore)와 개발용 초기화 버튼이 호출한다.
|
||||
func clearMockOverride() async {
|
||||
AppGroup.defaults.removeObject(forKey: Self.mockActiveKey)
|
||||
await refresh()
|
||||
}
|
||||
#endif
|
||||
|
||||
/// 권한 제공자(StoreKit) 결과 반영. DEBUG 강제 프리미엄 중에는 무시
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user