Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions KillingPart.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
12F7A0012F3F010000A00001 /* KakaoSDKCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 12F7A0112F3F010000A00001 /* KakaoSDKCommon */; };
12F7A0022F3F010000A00001 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 12F7A0122F3F010000A00001 /* KakaoSDKAuth */; };
12F7A0032F3F010000A00001 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 12F7A0132F3F010000A00001 /* KakaoSDKUser */; };
13A100032FF5A00000A1B001 /* AmplitudeUnified in Frameworks */ = {isa = PBXBuildFile; productRef = 13A100022FF5A00000A1B001 /* AmplitudeUnified */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -81,6 +82,7 @@
files = (
12F7A0012F3F010000A00001 /* KakaoSDKCommon in Frameworks */,
12F7A0022F3F010000A00001 /* KakaoSDKAuth in Frameworks */,
13A100032FF5A00000A1B001 /* AmplitudeUnified in Frameworks */,
1260BBC82FA88B900006BF01 /* GoogleSignInSwift in Frameworks */,
1224698D2FA765FE00A6EF76 /* FirebaseMessaging in Frameworks */,
1224698B2FA765FE00A6EF76 /* FirebaseCore in Frameworks */,
Expand Down Expand Up @@ -155,6 +157,7 @@
1224698C2FA765FE00A6EF76 /* FirebaseMessaging */,
1260BBC52FA88B900006BF01 /* GoogleSignIn */,
1260BBC72FA88B900006BF01 /* GoogleSignInSwift */,
13A100022FF5A00000A1B001 /* AmplitudeUnified */,
);
productName = KillingPart;
productReference = 1231F11D2F372E5B00CFA51D /* KillingPart.app */;
Expand Down Expand Up @@ -242,6 +245,7 @@
12F7A0212F3F010000A00001 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */,
122469892FA765FE00A6EF76 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
1260BBC42FA88B900006BF01 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */,
13A100012FF5A00000A1B001 /* XCRemoteSwiftPackageReference "AmplitudeUnified-Swift" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = 1231F11E2F372E5B00CFA51D /* Products */;
Expand Down Expand Up @@ -448,7 +452,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = KillingPart/KillingPart.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 38;
CURRENT_PROJECT_VERSION = 41;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = GQ89YG5G9R;
ENABLE_APP_SANDBOX = YES;
Expand All @@ -473,7 +477,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.2.3;
MARKETING_VERSION = 1.2.6;
PRODUCT_BUNDLE_IDENTIFIER = com.killingpoint.killingpart;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
Expand All @@ -493,7 +497,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = KillingPart/KillingPart.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 38;
CURRENT_PROJECT_VERSION = 41;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = GQ89YG5G9R;
ENABLE_APP_SANDBOX = YES;
Expand All @@ -518,7 +522,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.2.3;
MARKETING_VERSION = 1.2.6;
PRODUCT_BUNDLE_IDENTIFIER = com.killingpoint.killingpart;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
Expand Down Expand Up @@ -691,6 +695,14 @@
kind = branch;
};
};
13A100012FF5A00000A1B001 /* XCRemoteSwiftPackageReference "AmplitudeUnified-Swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/amplitude/AmplitudeUnified-Swift";
requirement = {
branch = main;
kind = branch;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand Down Expand Up @@ -729,6 +741,11 @@
package = 12F7A0212F3F010000A00001 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */;
productName = KakaoSDKUser;
};
13A100022FF5A00000A1B001 /* AmplitudeUnified */ = {
isa = XCSwiftPackageProductDependency;
package = 13A100012FF5A00000A1B001 /* XCRemoteSwiftPackageReference "AmplitudeUnified-Swift" */;
productName = AmplitudeUnified;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 1231F1152F372E5B00CFA51D /* Project object */;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions KillingPart/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<dict>
<key>APP_STORE_URL</key>
<string>$(APP_STORE_URL)</string>
<key>AMPLITUDE_API_KEY</key>
<string>$(AMPLITUDE_API_KEY)</string>
<key>BASE_URL</key>
<string>$(BASE_URL)</string>
<key>CFBundleURLTypes</key>
Expand Down
54 changes: 54 additions & 0 deletions KillingPart/Services/AmplitudeClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import AmplitudeUnified
import Foundation

final class AmplitudeClient {
static let shared = AmplitudeClient()

private var amplitude: Amplitude?
private var isConfigured = false

private init() {}

func configure(apiKey: String) {
guard !isConfigured else { return }
guard let normalizedKey = normalizedApiKey(from: apiKey) else { return }

amplitude = Amplitude(apiKey: normalizedKey)
isConfigured = true
}

func track(eventType: String, properties: [String: Any]? = nil) {
guard let amplitude else { return }

if let properties, !properties.isEmpty {
amplitude.track(eventType: eventType, eventProperties: properties)
return
}

amplitude.track(eventType: eventType)
}

func setUserId(_ userId: String?) {
guard
let amplitude,
let userId = userId?.trimmingCharacters(in: .whitespacesAndNewlines),
!userId.isEmpty
else {
return
}

amplitude.setUserId(userId: userId)
}

private func normalizedApiKey(from rawValue: String) -> String? {
let trimmed = rawValue.trimmingCharacters(in: .whitespacesAndNewlines)

guard !trimmed.isEmpty else { return nil }
guard !trimmed.hasPrefix("$(") else { return nil }
guard !trimmed.hasPrefix("YOUR_") else { return nil }
guard trimmed != "AMPLITUDE_API_KEY" else { return nil }
guard trimmed != "<TEST_API_KEY>" else { return nil }

return trimmed
}
}
4 changes: 4 additions & 0 deletions KillingPart/Services/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
if let remotePayload = launchOptions?[.remoteNotification] as? [AnyHashable: Any] {
FCMManager.shared.handleLaunchRemoteNotification(remotePayload)
}
let amplitudeApiKey = (Bundle.main.object(forInfoDictionaryKey: "AMPLITUDE_API_KEY") as? String ?? "")
.trimmingCharacters(in: .whitespacesAndNewlines)
AmplitudeClient.shared.configure(apiKey: amplitudeApiKey)
AmplitudeClient.shared.track(eventType: "app_opened")
print("[FCM][1] Firebase 초기화 완료")
return true
}
Expand Down
39 changes: 39 additions & 0 deletions KillingPart/ViewModels/InitialSetupFlowViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ final class InitialSetupFlowViewModel: ObservableObject {
private let diaryService: DiaryServicing
private let calendarService: CalendarServicing
private let shouldSkipNameSetupForAppleLogin: Bool
private var hasTrackedOnboardingTerminalEvent = false

var onComplete: (() -> Void)?

Expand Down Expand Up @@ -153,6 +154,7 @@ final class InitialSetupFlowViewModel: ObservableObject {
}

func skipAllTutorialAndFinish() {
trackOnboardingSkippedIfNeeded()
onComplete?()
}

Expand Down Expand Up @@ -229,6 +231,7 @@ final class InitialSetupFlowViewModel: ObservableObject {
}

func finishTutorial() {
trackOnboardingCompletedIfNeeded()
onComplete?()
}

Expand Down Expand Up @@ -321,6 +324,42 @@ final class InitialSetupFlowViewModel: ObservableObject {
}
}

private func trackOnboardingSkippedIfNeeded() {
guard !hasTrackedOnboardingTerminalEvent else { return }
hasTrackedOnboardingTerminalEvent = true

let properties: [String: Any] = [
"skip_step": skipStepName(for: step)
]
AmplitudeClient.shared.track(eventType: "onboard_skipped", properties: properties)
}

private func trackOnboardingCompletedIfNeeded() {
guard !hasTrackedOnboardingTerminalEvent else { return }
hasTrackedOnboardingTerminalEvent = true

AmplitudeClient.shared.track(eventType: "onboard_completed")
}

private func skipStepName(for step: Step) -> String {
switch step {
case .tutorialChoice:
return "tutorial_choice"
case .tutorialTrackSearch:
return "tutorial_track_search"
case .tutorialTrim:
return "tutorial_trim"
case .tutorialHome:
return "tutorial_home"
case .tutorialDiaryDetail:
return "tutorial_diary_detail"
case .tutorialNotification:
return "tutorial_notification"
case .policyAgreement, .nameSetup, .tagSetup, .tutorialFinal:
return "unknown"
}
}

private func resolveErrorMessage(from error: Error) -> String {
if let userError = error as? UserServiceError {
return userError.errorDescription ?? "요청 처리에 실패했어요."
Expand Down
Loading
Loading