친구 초대 미션
친구 초대 미션은 [Client Admin]에서 프로모션 등록 시 설정할 수 있는 게임 미션입니다. 친구 초대 코드를 공유하여 신규 유저를 초대하면 초대한 유저와 초대받은 유저가 모두 리워드를 받을 수 있습니다. 언어별 설정 방법이 다르므로 개발 언어를 먼저 확인해 주세요!
Android
런처 Activity 내 선언된 Javascript Interface
에 sharedInviteLink
케이스를 추가합니다.
해당 코드 실행 시 OS 공유 시트를 노출 시키게 됩니다.
sharedInviteLink()
친구 초대 링크 공유 시 호출되는 함수
parameters
inviteCode
- 발급된 초대코드infoMessage
- 안내 메세지link
- 초대 링크
// 블리피 Launcher Web <-> App 인터페이스 규격
class ExampleWebAppInterface(private val mContext: Context) {
...
@JavascriptInterface
fun sharedInviteLink(inviteCode: String, infoMessage: String, link: String) {
// 초대 미션 공유
val msg = "초대코드: $inviteCode\n$infoMessage\n$link"
// Android Sharesheet를 통해 텍스트 콘텐츠를 공유합니다.
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, msg)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
mContext.startActivity(shareIntent)
}
...
}
iOS
1. 링크 공유 시트 노출을 위한 구조체 생성
친구 초대 데이터를 전달 받고 iOS 앱 안에서 공유 시트를 노출 시키는 구조체를 생성합니다.
공유 시트 노출을 위해
UIViewControllerRepresentable
사용
// ShareSheet.swift
import SwiftUI
import UIKit
// postMessage로 넘어오는 문자열을 JSON 형태로 파싱하기 위해 선언
// inviteCode - 발급된 초대코드
// infoMessage - Client Admin에 입력된 안내 메세지
// link - Client Admin에 입력된 초대 링크
struct InviteLink: Codable {
var inviteCode: String
var infoMessage: String
var link: String
}
// 공유하기 시트
struct ShareSheet: UIViewControllerRepresentable {
var items: [Any] // 공유할 항목
func makeUIViewController(context: Context) -> UIActivityViewController {
let controller = UIActivityViewController(activityItems: items, applicationActivities: nil)
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
// 업데이트가 필요할 경우 여기에 로직 추가
}
}
2. WebView 구조체에서 Script Message 핸들링 추가
WKWebView
의userContentController
에서 친구 초대 링크 공유 관련 이벤트 처리 로직을 추가합니다.런처는
postMessage
문자열 데이터 형태로 전달하기 때문에 JSON 형태로 디코딩 과정이 필요합니다.디코딩된 데이터를 부모 contentView의
onSharedInviteLink
콜백 함수에 전달합니다.
// LauncherWebView.swift
import WebKit
import SwiftUI
struct LauncherWebView: UIViewRepresentable {
...
// 초대 링크 처리할 콜백 함수
let onSharedInviteLink: (String, String, String) -> Void
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
...
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// 런처 친구초대 링크 공유 이벤트 수신 시 처리
if message.name == "sharedInviteLink" {
// 메시지의 body는 JSON.stringify 문자열이라 디코딩 필요
if let jsonString = message.body as? String,
let jsonData = jsonString.data(using: .utf8) {
do {
// JSON 문자열을 ShareSheet 클래스 내 InviteLink 구조체로 디코딩
let inviteData = try JSONDecoder().decode(InviteLink.self, from: jsonData)
// 부모의 onSharedInviteLink 메서드 호출
parent.onSharedInviteLink(inviteData.inviteCode, inviteData.infoMessage, inviteData.link)
} catch {
print("Failed to decode JSON: (error.localizedDescription)")
}
} else {
print("Failed to convert message body to String or Data")
}
}
}
...
}
...
func makeUIView(context: Context) -> WKWebView {
...
// javascript에서 전달하는 이벤트 등록
contentController.add(context.coordinator, name: "sharedInviteLink")
...
}
...
static func dismantleUIView(_ uiView: WKWebView, coordinator: Self.Coordinator) {
uiView.configuration.userContentController.removeScriptMessageHandler(forName: "sharedInviteLink")
}
}
3. WebView가 포함된 ContentView 내에서 ShareSheet 클래스 호출
WebView
에 실행된 런처에서 친구 초대 링크 공유에 대한 이벤트가 발생하면 ContentView
에 선언된 onSharedInviteLink
콜백 함수가 호출 됩니다.
shareItems
state 값에 공유할 내용을 설정합니다.isShowingShareSheet
state 값 변경을 통해ShareSheet
노출 여부를 결정합니다.
// LauncherContentView.swift
import SwiftUI
import WebKit
struct LauncherContentView: View {
...
// ShareSheet를 표시할 때 사용할 상태 변수
@State private var isShowingShareSheet = false
@State private var shareItems: [Any] = []
var body: some View {
LauncherWebView(
url: URL(string: "런처 URL"),
// 친구 초대 링크 공유 이벤트 핸들링 처리
onSharedInviteLink: { inviteCode, infoMessage, link in
// 공유할 항목 설정
shareItems = ["\(infoMessage)\n\n초대코드 : \(inviteCode)\n\n\(link)"]
// ShareSheet를 표시하도록 상태를 변경
isShowingShareSheet = true
},
)
.navigationBarBackButtonHidden(true)
// ShareSheet를 표시하는 시트 추가
.sheet(isPresented: $isShowingShareSheet) {
ShareSheet(items: shareItems)
}
}
...
}
Flutter
🤝 OS 공유 시트 사용 시
1. 링크 공유 패키지 설치
아래 패키지를 설치하면 링크 공유 기능을 수행할 수 있습니다.
사용 패키지:
share_plus
해당 패키지는 플랫폼의 공유 대화상자를 통해 콘텐츠 공유를 가능하게 합니다.
flutter pub add share_plus
2. javascript handler 추가
런처를 사용하는 Screen 내 Javascript Handler
에 sharedInviteLink
타입을 추가합니다. handlerName
값은 BlpLauncher
로 설정해야 합니다.
sharedInviteLink
친구 초대 링크 공유 시 호출
inviteCode
- 발급된 초대코드infoMessage
- 안내 메세지link
- 초대 링크
// lib/screens/bleepy_screen.dart
InAppWebView(
key: webViewKey,
initialUrlRequest:
URLRequest(url: WebUri(widget.launcherUrl)),
initialSettings: options,
onWebViewCreated: (controller) {
webViewController = controller;
// 자바스크립트 핸들러 추가
controller.addJavaScriptHandler(handlerName: 'BlpLauncher', callback: (message) {
final data = jsonDecode(message[0]);
final key = data['type'];
switch (key) {
...
// 추가
case "sharedInviteLink":
sharedInviteLink(data["inviteCode"], data["infoMessage"], data["link"]);
break;
default:
break;
}
});
}
)
3. sharedInviteLink(inviteCode, infoMessage, link)
share_plus
패키지를 통해서 친구 초대 링크를 공유하는 기능을 수행합니다.
// lib/screens/bleepy_screen.dart
import 'package:share_plus/share_plus.dart';
// 친구초대 링크 공유하기
void sharedInviteLink(String infoMessage, String inviteCode, String link) async {
try {
// 메세지 예시
const msg = "초대코드: $inviteCode\n$infoMessage\n$link"
await Share.share(msg);
} catch (error: any) {
// Share API error
}
}
🤝 카카오톡 공유 기능 사용 시
Flutter에서 카카오톡 공유 기능을 위해 추가적인 조치가 필요합니다. 카카오톡 실행 URL 오픈 시 필요한 아래 패키지를 설치해주세요.
flutter pub add url_launcher
flutter_inappwebview
내 shouldOverrideUrlLoading
에 아래 로직을 추가합니다.
기존 앱 존재 시 : 앱 실행
미설치 시 : 스토어 이동
...
import 'dart:io';
import 'package:url_launcher/url_launcher.dart';
InAppWebView(
...
shouldOverrideUrlLoading: (controller, navigationAction) async {
var uri = navigationAction.request.url!;
if(Platform.isIOS) {
// iOS - kakao
if (uri.toString().startsWith("kakaolink://")) {
if (await canLaunchUrl(uri)) {
// KakaoTalk is installed, launch it
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
// KakaoTalk is not installed, redirect to App Store
const appStoreUrl = "https://apps.apple.com/app/id362057947";
await launchUrl(Uri.parse(appStoreUrl), mode: LaunchMode.externalApplication);
}
return NavigationActionPolicy.CANCEL;
}
}
if(Platform.isAndroid) {
// AOS - kakao
if (uri.toString().startsWith("intent:")) {
final fallbackUrl = Uri.tryParse(uri.queryParameters['browser_fallback_url'] ?? '');
if (fallbackUrl != null && await canLaunchUrl(fallbackUrl)) {
await launchUrl(fallbackUrl, mode: LaunchMode.externalApplication);
} else {
final kakaolinkUrl = uri.toString().replaceFirst("intent:", "");
try {
await launchUrl(Uri.parse(kakaolinkUrl), mode: LaunchMode.externalApplication);
} catch (e) {
print("Failed to launch KakaoLink URL: $e");
// KakaoTalk is not installed, redirect to Google Play Store
const appStoreUrl = "https://play.google.com/store/apps/details?id=com.kakao.talk";
await launchUrl(Uri.parse(appStoreUrl), mode: LaunchMode.externalApplication);
}
}
return NavigationActionPolicy.CANCEL;
}
}
return NavigationActionPolicy.ALLOW;
},
...
)
iOS 환경의 경우 Info.plist
파일 내 kakaolink
값을 추가해주세요.
// ios/Runner/Info.plist
<key>LSApplicationQueriesSchemes</key>
<array>
<string>kakaolink</string>
</array>
React Native
🤝 OS 공유 시트 사용 시
기본 제공되는 Share API
를 통해 친구 초대 링크를 공유하는 기능을 수행합니다.
WebView
의 onMessage
함수 내에 해당 케이스를 추가합니다.
sharedInviteLink
친구 초대 링크 공유 시 호출되는 함수
inviteCode
- 발급된 초대코드infoMessage
- 안내 메세지link
- 초대 링크
// src/screens/launcher/Launcher.tsx
import {WebView, WebViewMessageEvent} from 'react-native-webview';
export default function Launcher() {
// WebView에서 보내온 메세지 처리
const onMessage = (e: WebViewMessageEvent) => {
const {type, inviteCode, infoMessage, link}
= JSON.parse(e.nativeEvent.data);
switch (type) {
...
// 추가
case 'sharedInviteLink':
sharedInviteLink(inviteCode, infoMessage, link);
break;
}
};
return (
<View>
<WebView
...
onMessage={onMessage}
...
/>
</View>
);
}
}
1. sharedInviteLink(inviteCode, infoMessage, link)
Share API
를 통해서 친구 초대 링크를 공유하는 기능을 수행합니다.
// src/screens/launcher/Launcher.tsx
import { Share } from 'react-native';
// 친구초대 링크 공유하기
const sharedInviteLink = async (inviteCode: string, infoMessage: string, link: string) => {
try {
// 메세지 예시
const msg = '{infoMessage}\n초대코드 : {inviteCode}\n\n{link}'
await Share.share({
message: msg,
});
} catch (error: any) {
// Share API error
}
};
🤝 카카오톡 공유 기능 사용 시
React Native 앱으로 개발된 경우, 카카오톡 공유 기능을 위해 추가적인 조치가 필요합니다. 올바른 URL 스키마 처리를 위해 아래 패키지를 설치해주세요.
npm install react-native-url-polyfill
패키지 설치가 완료되면 WebView
관련 설정을 추가합니다.
originWhiteList
domStorageEnabled
onShouldStartLoadWithRequest
handleShouldStartLoadWithRequest
메서드 내에서 AOS, iOS 플랫폼 별 처리를 진행합니다.
앱이 설치된 경우는 앱을 실행하고, 앱 미설치 시 스토어로 이동하게 됩니다.
import 'react-native-url-polyfill/auto';
import {Alert, Linking, Platform} from 'react-native';
export default function Launcher() {
...
const handleShouldStartLoadWithRequest = (event: any): boolean => {
const url = event.url;
// 1. Android intent scheme process
if (Platform.OS === 'android' && url.startsWith('intent:')) {
const handleAndroidIntent = async () => {
try {
const kakaoUrl = url.replace('intent:', '');
const fallbackUrl = new URLSearchParams(kakaoUrl).get(
'browser_fallback_url',
);
const canOpen = await Linking.canOpenURL(kakaoUrl);
if (canOpen) {
// run KakaoTalk
await Linking.openURL(kakaoUrl);
} else if (fallbackUrl) {
// move to fallback URL
await Linking.openURL(fallbackUrl);
} else {
// move to Google Play Store
const playStoreUrl =
'https://play.google.com/store/apps/details?id=com.kakao.talk';
await Linking.openURL(playStoreUrl);
}
} catch (error) {
console.error('Error handling intent URL:', error);
}
};
handleAndroidIntent();
return false; // WebView load stop
}
// 2. IOS kakaolink process
if (Platform.OS === 'ios' && url.startsWith('kakaolink://')) {
const handleIosKakaoLink = async () => {
try {
const canOpen = await Linking.canOpenURL(url);
if (canOpen) {
await Linking.openURL(url);
} else {
const appStoreUrl = 'https://apps.apple.com/app/id362057947';
await Linking.openURL(appStoreUrl);
}
} catch (error) {
console.error('Error handling Kakao URL on iOS:', error);
}
};
handleIosKakaoLink();
return false; // WebView load stop
}
return true; // WebView load allow
};
...
return (
<View>
<WebView
ref={webviewRef}
source={{
uri: route.params.url,
}}
originWhitelist={['kakaolink', 'intent', 'http', 'https']}
javaScriptEnabled={true}
domStorageEnabled={true}
cacheEnabled={false}
// 카카오톡 공유하기 처리
onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest}
/>
</View>
);
}
Android 추가 처리
AndroidManifest.xml
파일 내 수정이 필요합니다.
<queries></queries>
선언을 통해kakaolink
외부intents
를 명시합니다.<application>
및<activity>
태그 내android:exported
속성 값을true
로 설정합니다.
// android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
<!-- Declare queries for external intents -->
<queries>
<!-- KakaoTalk-specific intent -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="kakaolink" />
</intent>
</queries>
<application
...
android:exported="true">
<activity
...
android:exported="true">
...
</activity>
</application>
</manifest>
```
iOS
Info.plist
파일 내 kakaolink
URL 스키마 추가가 필요합니다.
// ios/{projectName}/Info.plist
...
<!-- KakaoTalk URL Scheme Support -->
<key>LSApplicationQueriesSchemes</key>
<array>
<string>kakaolink</string>
</array>
...
Web
Web 도메인의 서비스인 경우 블리피 런처에서 기본적으로 Web Share API사용하게 됩니다.
친구 초대 미션 사용 시 임베디드 코드 내 유저의 회원가입 시점 추가 전달이 필요합니다.
signUpAt
신규 유저 판단을 위한 회원가입 일시를 전달해야 합니다.
timestamp 형식 (10자리)
ex) 1717143687
<!-- {signUpAt} 영역에 로그인된 회원의 회원가입 일시 전달 (timestamp) -->
<div style="height: 100%; max-width: 960px">
<iframe
src="https://web-launcher.bleepy.io?userKey={userKey}&secretKey={secretKey}&signUpAt={signUpAt}"
style="overflow: hidden; width: 100%; height: 100%"
allowfullscreen
sandbox="allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox allow-top-navigation"
id="bleepy-iframe"
allow="web-share;clipboard-read; clipboard-write"
/>
</div>
Last updated