diff --git a/app/ios/ExportOptions.plist b/app/ios/ExportOptions.plist
index 6f7de16..73f108b 100644
--- a/app/ios/ExportOptions.plist
+++ b/app/ios/ExportOptions.plist
@@ -6,13 +6,14 @@
`xcodebuild -exportArchive` 가 이 파일을 읽는다. 없으면 대화형으로 물어보므로
자동 배포가 불가능하다 — 계정이 열린 뒤에 만들면 그날 하루를 여기서 쓴다.
+ teamID 는 발주사 Fancake Inc. 개발자 팀이다. 앱(kr.co.fanuzu.app)이 그 팀 소속이라
+ 다른 팀으로 서명하면 업로드가 엉뚱한 App Store Connect 로 간다.
-->
method
app-store-connect
-
teamID
V28G543VK5
diff --git a/app/ios/Flutter/Debug.xcconfig b/app/ios/Flutter/Debug.xcconfig
index 093bdc1..c858fd2 100644
--- a/app/ios/Flutter/Debug.xcconfig
+++ b/app/ios/Flutter/Debug.xcconfig
@@ -1,6 +1,7 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
-// Google 로그인 콜백 스킴. tool/ios/inject_url_scheme.py 가 만든다.
-// `#include?` 는 파일이 없어도 빌드를 멈추지 않는다 — 그때는 Google 로그인만 동작하지 않는다.
+// Google 로그인 콜백 스킴. tools/ios/inject_url_scheme.py 가 만든다.
+// `#include?` 는 파일이 없어도 빌드를 멈추지 않는다. 설정 파일 투입 전에도
+// 빌드가 되어야 하기 때문이다 — 그때는 Google 로그인만 동작하지 않는다.
#include? "../config/Google.xcconfig"
diff --git a/app/ios/Flutter/Release.xcconfig b/app/ios/Flutter/Release.xcconfig
index 02fef1a..f1c1184 100644
--- a/app/ios/Flutter/Release.xcconfig
+++ b/app/ios/Flutter/Release.xcconfig
@@ -1,6 +1,7 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
-// Google 로그인 콜백 스킴. tool/ios/inject_url_scheme.py 가 만든다.
-// `#include?` 는 파일이 없어도 빌드를 멈추지 않는다 — 그때는 Google 로그인만 동작하지 않는다.
+// Google 로그인 콜백 스킴. tools/ios/inject_url_scheme.py 가 만든다.
+// `#include?` 는 파일이 없어도 빌드를 멈추지 않는다. 설정 파일 투입 전에도
+// 빌드가 되어야 하기 때문이다 — 그때는 Google 로그인만 동작하지 않는다.
#include? "../config/Google.xcconfig"
diff --git a/app/ios/Runner.xcodeproj/project.pbxproj b/app/ios/Runner.xcodeproj/project.pbxproj
index a848bd9..6db6495 100644
--- a/app/ios/Runner.xcodeproj/project.pbxproj
+++ b/app/ios/Runner.xcodeproj/project.pbxproj
@@ -65,8 +65,8 @@
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
- 4F2B0E1A2C3D4E5F60718293 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 4F2B0E1A2C3D4E5F60718293 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
A68C3B80D45981965B89CA40 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
AB2C33122C7A95220B242430 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CBE157220A7C382F845775F0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
diff --git a/app/ios/Runner/Info.plist b/app/ios/Runner/Info.plist
index 8798140..09b578d 100644
--- a/app/ios/Runner/Info.plist
+++ b/app/ios/Runner/Info.plist
@@ -26,6 +26,269 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+
+
+
+ ITSAppUsesNonExemptEncryption
+
+
+
+ NSPhotoLibraryUsageDescription
+ 프로필 사진을 고르기 위해 사진에 접근합니다.
+ NSCameraUsageDescription
+ 프로필 사진을 찍고 방문 인증 사진을 남기기 위해 카메라를 사용합니다.
+ NSLocationWhenInUseUsageDescription
+ 지정한 장소에 방문하셨는지 확인해 퀘스트 보상을 드리기 위해 위치를 확인합니다.
+ NSMotionUsageDescription
+ 걸음 수를 세어 만보기 퀘스트를 확인하기 위해 동작 데이터를 읽습니다.
+ NSHealthShareUsageDescription
+ 걸음 수를 읽어 만보기 퀘스트 달성을 확인합니다. 다른 건강 정보는 읽지 않습니다.
+
+ GADApplicationIdentifier
+ ca-app-pub-3940256099942544~1458002511
+
+ SKAdNetworkItems
+
+
+ SKAdNetworkIdentifier
+ cstr6suwn9.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 4fzdc2evr5.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 2fnua5tdw4.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ ydx93a7ass.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ p78axxw29g.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ v72qych5uu.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ ludvb6z3bs.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ cp8zw746q7.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 3sh42y64q3.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ c6k4g5qg8m.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ s39g8k73mm.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ wg4vff78zm.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 3qy4746246.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ f38h382jlk.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ hs6bdukanm.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ mlmmfzh3r3.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ v4nxqhlyqp.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ wzmmz9fp6w.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ su67r6k2v3.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ yclnxrl5pm.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ t38b2kh725.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 7ug5zh24hu.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ gta9lk7p23.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ vutu7akeur.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ y5ghdn5j9k.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ v9wttpbfk9.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ n38lu8286q.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 47vhws6wlr.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ kbd757ywx3.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 9t245vhmpl.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ a2p9lx4jpn.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 22mmun2rn5.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 44jx6755aq.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ k674qkevps.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 4468km3ulz.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 2u9pt9hc89.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 8s468mfl3y.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ klf5c3l5u5.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ ppxm28t8ap.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ kbmxgpxpgc.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ uw77j35x4d.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 578prtvx9j.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 4dzt52r2t5.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ tl55sbb4fm.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ c3frkrj4fj.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ e5fvkxwrpn.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 8c4e2ghe7u.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 3rd42ekr43.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 97r2b46745.skadnetwork
+
+
+ SKAdNetworkIdentifier
+ 3qcr597p9d.skadnetwork
+
+
+ NSUserTrackingUsageDescription
+ 관심에 맞는 광고를 보여 드리기 위해 사용합니다. 허용하지 않으셔도 앱을 그대로 쓰실 수 있습니다.
+
+
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ google-sign-in
+ CFBundleURLSchemes
+
+ $(GOOGLE_REVERSED_CLIENT_ID)
+
+
+
+
UIApplicationSceneManifest
UIApplicationSupportsMultipleScenes
@@ -66,29 +329,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
-
- ITSAppUsesNonExemptEncryption
-
-
-
- CFBundleURLTypes
-
-
- CFBundleTypeRole
- Editor
- CFBundleURLName
- google-sign-in
- CFBundleURLSchemes
-
- $(GOOGLE_REVERSED_CLIENT_ID)
-
-
-
diff --git a/app/ios/Runner/Runner.entitlements b/app/ios/Runner/Runner.entitlements
index 53eb7ec..647988c 100644
--- a/app/ios/Runner/Runner.entitlements
+++ b/app/ios/Runner/Runner.entitlements
@@ -3,8 +3,12 @@
com.apple.developer.applesignin
@@ -12,8 +16,8 @@
aps-environment
development
diff --git a/app/tool/ios/inject_url_scheme.py b/app/tools/ios/inject_url_scheme.py
similarity index 90%
rename from app/tool/ios/inject_url_scheme.py
rename to app/tools/ios/inject_url_scheme.py
index 5d56d67..8f94419 100755
--- a/app/tool/ios/inject_url_scheme.py
+++ b/app/tools/ios/inject_url_scheme.py
@@ -8,7 +8,7 @@ Info.plist 에 손으로 옮기면 오타 한 글자에 로그인이 **조용히
설정 파일이 투입될 때 한 번 돌리면 `ios/config/Google.xcconfig` 가 만들어지고,
Info.plist 의 `$(GOOGLE_REVERSED_CLIENT_ID)` 가 그 값으로 치환된다.
-실행: python3 tool/ios/inject_url_scheme.py
+실행: python3 tools/ios/inject_url_scheme.py
"""
from __future__ import annotations
@@ -25,7 +25,7 @@ def main() -> int:
if not SOURCE.exists():
print(
f"설정 파일이 아직 없습니다: {SOURCE.relative_to(ROOT)}\n"
- "Firebase 콘솔에서 iOS 앱 설정 파일을 받아 그 자리에 두십시오.",
+ "Firebase 프로젝트를 연 뒤 firebase/setup-project.sh 를 돌리십시오.",
file=sys.stderr,
)
return 1
@@ -45,7 +45,7 @@ def main() -> int:
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(
- "// 자동 생성 — tool/ios/inject_url_scheme.py\n"
+ "// 자동 생성 — tools/ios/inject_url_scheme.py\n"
"// GoogleService-Info.plist 가 바뀌면 다시 돌리십시오.\n"
f"GOOGLE_REVERSED_CLIENT_ID = {reversed_id}\n",
encoding="utf-8",
diff --git a/app/tools/ios/upload_testflight.sh b/app/tools/ios/upload_testflight.sh
new file mode 100755
index 0000000..f75fb66
--- /dev/null
+++ b/app/tools/ios/upload_testflight.sh
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+# iOS 아카이브 → TestFlight 업로드.
+#
+# **계정이 열린 날 이 스크립트 하나로 끝나게 하는 것이 목적**이다. 별첨 2 가
+# 3주차부터 매주 TestFlight 제출을 요구했고 그것을 지키지 못했으므로(미이행 F-2),
+# 계정이 열리면 지체 없이 재개되어야 한다.
+#
+# 사전 조건
+# 1. ios/Runner/GoogleService-Info.plist 배치 · tools/ios/inject_url_scheme.py 실행
+# 2. Xcode 정식판을 `xcode-select` 로 지정 (베타 SDK 빌드는 업로드가 거부된다)
+# 3. App Store Connect API 키 (환경변수 3종)
+#
+# 실행:
+# export ASC_KEY_ID=... ASC_ISSUER_ID=... ASC_KEY_PATH=~/private_keys/AuthKey_XXX.p8
+# bash tools/ios/upload_testflight.sh
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
+APP="$ROOT"
+ARCHIVE="$APP/build/ios/Runner.xcarchive"
+EXPORT_DIR="$APP/build/ios/ipa"
+OPTIONS="$APP/ios/ExportOptions.plist"
+
+if grep -q REPLACE_WITH_TEAM_ID "$OPTIONS"; then
+ echo "ExportOptions.plist 의 teamID 를 먼저 채우십시오." >&2
+ exit 1
+fi
+for var in ASC_KEY_ID ASC_ISSUER_ID ASC_KEY_PATH; do
+ if [ -z "${!var:-}" ]; then
+ echo "환경변수 $var 이 없습니다." >&2
+ exit 1
+ fi
+done
+
+echo "▸ 아카이브"
+cd "$APP"
+flutter build ipa --release --export-options-plist="$OPTIONS"
+
+# flutter build ipa 가 아카이브와 내보내기를 함께 한다. 실패했을 때만 손으로 나눈다.
+IPA="$(find "$EXPORT_DIR" -name '*.ipa' -maxdepth 1 | head -1)"
+if [ -z "$IPA" ]; then
+ echo "IPA 를 찾지 못했습니다. 아카이브만 따로 시도합니다." >&2
+ xcodebuild -workspace ios/Runner.xcworkspace -scheme Runner \
+ -configuration Release -archivePath "$ARCHIVE" archive
+ xcodebuild -exportArchive -archivePath "$ARCHIVE" \
+ -exportOptionsPlist "$OPTIONS" -exportPath "$EXPORT_DIR"
+ IPA="$(find "$EXPORT_DIR" -name '*.ipa' -maxdepth 1 | head -1)"
+fi
+
+echo "▸ 검증"
+xcrun altool --validate-app -f "$IPA" -t ios \
+ --apiKey "$ASC_KEY_ID" --apiIssuer "$ASC_ISSUER_ID"
+
+echo "▸ 업로드"
+xcrun altool --upload-app -f "$IPA" -t ios \
+ --apiKey "$ASC_KEY_ID" --apiIssuer "$ASC_ISSUER_ID"
+
+cat <<'DONE'
+
+업로드했습니다. 남은 것 둘.
+
+ 1. App Store Connect 에서 처리가 끝나면(보통 10~30분) TestFlight 탭에 뜹니다.
+ 2. 발주사 담당자 이메일을 외부 테스터로 초대하십시오.
+
+수출 규정 응답은 Info.plist 의 ITSAppUsesNonExemptEncryption 이 대신합니다.
+DONE