Section 16/171 menit
16. Real Use Cases
16. Real Use Cases
Use Case 1: Setup CI/CD Lengkap untuk Startup iOS
Sebuah startup dengan tim 4 iOS developer ingin menyiapkan CI/CD yang zero maintenance. Mereka menggunakan GitHub, TestFlight untuk beta testing, dan langsung release ke App Store.
Workflow Setup:
swift
Workflow 1: "Feature PR"
├── Start: PR ke develop (file restriction: Sources/, Tests/)
├── Actions: Build (Debug) + Test (Unit Tests only)
├── Post: Notify Slack (on failure)
└── Target: cepat feedback ke developer, target <10 menit
Workflow 2: "Develop Integration"
├── Start: Push ke develop
├── Actions: Build (Debug) + Test (All Tests) + Archive (Debug)
├── Post: TestFlight Internal Testing + Notify Slack (always)
└── Target: QA bisa testing build terbaru setiap hari
Workflow 3: "Release"
├── Start: Tag yang match "v*.*.*"
├── Actions: Build (Release) + Test (All Tests) + Archive (Release)
├── Post: TestFlight External Testing + Notify Slack
└── Target: kirim ke beta external tester sebelum App Store release
ci_post_clone.sh untuk setup:
swift
#!/bin/zsh
# ci_scripts/ci_post_clone.sh
set -e
echo "Setting up build environment..."
echo "Branch: $CI_BRANCH | Build: $CI_BUILD_NUMBER"
# Inject environment-specific configuration
case "$CI_BRANCH" in
main|v*.*.*)
ENV_NAME="production"
echo "$PROD_CONFIG_BASE64" | base64 --decode \
> "$CI_PRIMARY_REPOSITORY_PATH/MyApp/Config.plist"
echo "$PROD_GOOGLE_SERVICES_BASE64" | base64 --decode \
> "$CI_PRIMARY_REPOSITORY_PATH/MyApp/GoogleService-Info.plist"
;;
develop)
ENV_NAME="staging"
echo "$STAGING_CONFIG_BASE64" | base64 --decode \
> "$CI_PRIMARY_REPOSITORY_PATH/MyApp/Config.plist"
echo "$STAGING_GOOGLE_SERVICES_BASE64" | base64 --decode \
> "$CI_PRIMARY_REPOSITORY_PATH/MyApp/GoogleService-Info.plist"
;;
*)
ENV_NAME="development"
echo "$DEV_CONFIG_BASE64" | base64 --decode \
> "$CI_PRIMARY_REPOSITORY_PATH/MyApp/Config.plist"
echo "$DEV_GOOGLE_SERVICES_BASE64" | base64 --decode \
> "$CI_PRIMARY_REPOSITORY_PATH/MyApp/GoogleService-Info.plist"
;;
esac
echo "Environment configured: $ENV_NAME"
ci_pre_xcodebuild.sh untuk build number:
swift
#!/bin/zsh
# ci_scripts/ci_pre_xcodebuild.sh
set -e
if [[ "$CI_XCODEBUILD_ACTION" == "archive" ]]; then
echo "Setting build number to $CI_BUILD_NUMBER..."
# Update xcconfig
XCCONFIG="$CI_PRIMARY_REPOSITORY_PATH/Configuration/App.xcconfig"
if [ -f "$XCCONFIG" ]; then
sed -i '' "s/^CURRENT_PROJECT_VERSION = .*/CURRENT_PROJECT_VERSION = $CI_BUILD_NUMBER/" "$XCCONFIG"
fi
# Generate BuildInfo.swift
cat > "$CI_PRIMARY_REPOSITORY_PATH/MyApp/Generated/BuildInfo.swift" << EOF
// Auto-generated — do not edit
enum BuildInfo {
static let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
static let build = "$CI_BUILD_NUMBER"
static let commit = "${CI_COMMIT:0:8}"
static let environment = "${ENV_NAME:-unknown}"
}
EOF
fi
ci_post_xcodebuild.sh untuk Sentry dSYM upload:
swift
#!/bin/zsh
# ci_scripts/ci_post_xcodebuild.sh
set -e
if [[ "$CI_XCODEBUILD_ACTION" == "archive" ]]; then
echo "Uploading dSYMs to Sentry..."
# Install Sentry CLI
curl -sL https://sentry.io/get-cli/ | bash
DSYM_PATH="${CI_ARCHIVE_PATH}/dSYMs"
if [[ -d "$DSYM_PATH" && -n "$SENTRY_AUTH_TOKEN" ]]; then
sentry-cli \
--auth-token "$SENTRY_AUTH_TOKEN" \
upload-dif \
--org "${SENTRY_ORG}" \
--project "${SENTRY_PROJECT}" \
--include-sources \
"$DSYM_PATH"
echo "dSYMs uploaded successfully"
else
echo "Skipping Sentry upload (no dSYMs or auth token)"
fi
fi
Use Case 2: Enterprise App dengan Multi-Target dan Testing Ketat
Sebuah perusahaan fintech memiliki app iOS dengan beberapa target (main app, Share Extension, Widget), ribuan unit test, dan regulasi yang mengharuskan code coverage minimum 80%.
Struktur project:
swift
FinanceApp/
├── FinanceApp/ ← main target
├── ShareExtension/ ← share extension target
├── WidgetExtension/ ← widget target
├── FinanceAppTests/ ← unit tests
├── FinanceAppUITests/ ← UI tests (slow)
└── ci_scripts/
Test Plans:
swift
// UnitTests.xctestplan — digunakan di PR workflow
{
"configurations": [
{
"id": "unit",
"name": "Unit Tests Only",
"options": {
"codeCoverageEnabled": true,
"codeCoverageTargets": [
{"name": "FinanceApp"},
{"name": "ShareExtension"},
{"name": "WidgetExtension"}
]
}
}
],
"testTargets": [
{
"target": {"name": "FinanceAppTests"},
"enabled": true
}
],
"version": 1
}
swift
// AllTests.xctestplan — digunakan di nightly workflow
{
"configurations": [
{
"id": "all",
"name": "All Tests",
"options": {
"codeCoverageEnabled": true,
"maximumTestExecutionTimeAllowance": 120,
"testTimeoutsEnabled": true
}
}
],
"testTargets": [
{"target": {"name": "FinanceAppTests"}, "enabled": true},
{"target": {"name": "FinanceAppUITests"}, "enabled": true}
],
"version": 1
}
Coverage enforcement:
swift
#!/bin/zsh
# ci_scripts/ci_post_xcodebuild.sh
set -e
MINIMUM_COVERAGE_PERCENT=80
if [[ "$CI_XCODEBUILD_ACTION" == "test" && -n "$CI_RESULT_BUNDLE_PATH" ]]; then
echo "Checking code coverage..."
# Export coverage menggunakan xccov
xcrun xccov view \
--report \
--json \
"$CI_RESULT_BUNDLE_PATH" > /tmp/coverage.json 2>/dev/null || {
echo "Warning: Could not generate coverage report"
exit 0
}
# Parse overall coverage
COVERAGE=$(python3 -c "
import json, sys
with open('/tmp/coverage.json') as f:
data = json.load(f)
targets = [t for t in data.get('targets', [])
if t['name'] in ['FinanceApp.app', 'ShareExtension.appex', 'WidgetExtension.appex']]
if targets:
total_lines = sum(t.get('executableLines', 0) for t in targets)
covered_lines = sum(t.get('coveredLines', 0) for t in targets)
pct = (covered_lines / total_lines * 100) if total_lines > 0 else 0
print(f'{pct:.1f}')
else:
print('0')
" 2>/dev/null || echo "0")
echo "Code coverage: ${COVERAGE}%"
COVERAGE_INT=$(echo "$COVERAGE" | cut -d. -f1)
if (( COVERAGE_INT < MINIMUM_COVERAGE_PERCENT )); then
echo "❌ Coverage ${COVERAGE}% di bawah minimum ${MINIMUM_COVERAGE_PERCENT}%"
# Kirim alert ke Slack
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"⚠️ Code coverage ${COVERAGE}% di bawah minimum ${MINIMUM_COVERAGE_PERCENT}% — Build #${CI_BUILD_NUMBER} — Branch: ${CI_BRANCH}\"}" \
"$SLACK_WEBHOOK_URL"
fi
exit 1
fi
echo "✅ Coverage ${COVERAGE}% memenuhi minimum ${MINIMUM_COVERAGE_PERCENT}%"
fi
Workflow fintech:
swift
Workflow 1: "PR Security Check"
├── Start: PR ke develop/main
├── Actions: Build (Debug) + Analyze + Test (UnitTests plan)
├── Post: Notify Slack (on failure, mandatory)
└── Note: Analyze mendeteksi potential security issues via static analysis
Workflow 2: "Integration Build"
├── Start: Push ke develop
├── Actions: Build + Test (AllTests plan) + Archive
├── Post: TestFlight Internal (QA team) + Notify Slack
└── Note: Wajib coverage ≥80%
Workflow 3: "Release Candidate"
├── Start: Tag "rc-v*.*.*"
├── Actions: Build (Release) + Test (AllTests) + Archive
├── Post: TestFlight External + Notify email ke compliance team
└── Note: Butuh approval manual sebelum App Store submit
Workflow 4: "Nightly Regression"
├── Start: Schedule — 01:00 UTC setiap hari
├── Actions: Build + Analyze + Test (AllTests plan, multiple devices)
├── Post: Notify Slack hanya jika gagal
└── Note: Jalankan di iPhone SE (kecil) + iPad Pro + iPhone 15 Pro