Flutter 웹앱을 InAppWebView package 사용 기준으로 작성했습니다.
[배경]
iOS는 앱이 종료된 상태에서 백그라운드 작업을 실행시킬 수 없습니다.
따라서 알림을 받았을때, 플러터로 알림으로 전달된 데이터를 저장할 수 있는 방법을 찾기 어렵습니다.
그렇다면 어떻게 앱이 종료된 상태에서 FCM Push notification으로 전달받은 url 데이터를 앱에 저장할 수 있는가?
AppDelegate.swift 를 통해 가능합니다!
[원리]
AppDelegate.swift에서는 앱이 종료/백그라운드 상태에서 알림을 받았을때 호출되는 함수를 오버라이드 할 수 있습니다.
그 때 !!
iOS단에서 UserDefaults에 받아온 데이터를 저장하고 앱이 켜졌을때 Flutter와 채널을 통해 데이터를 전달할 수 있습니다.
[구현 방법]
1. xcode에서 AppDelegate.swift 파일을 열어서 다음과 같이 코드를 작성해줍니다.
FCM관련 함수가 있으므로 FCM을 안 사용하신다면 코드가 상이할 수 있어요~
import Flutter
import UIKit
import Firebase
import UserNotifications
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Flutter와 연결 채널
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let notificationChannel = FlutterMethodChannel(name: "com.my.test.app/notification",
binaryMessenger: controller.binaryMessenger)
notificationChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getNotificationURL" {
self.getNotificationURL(result: result)
} else {
result(FlutterMethodNotImplemented)
}
})
// 푸시 알림 권한 요청
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
if granted {
os_log("iOS: Notification permission granted.", type: .info)
} else if let error = error {
os_log("iOS: Notification permission error: %@", type: .error, error.localizedDescription)
}
}
}
application.registerForRemoteNotifications()
FirebaseApp.configure()
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func getNotificationURL(result: @escaping FlutterResult) {
if let url = UserDefaults.standard.string(forKey: "notificationURL") {
result(url)
// Optionally, clear the URL after retrieving it
UserDefaults.standard.removeObject(forKey: "notificationURL")
} else {
result(nil)
}
}
override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let urlString = userInfo["contents"] as? String {
UserDefaults.standard.set(urlString, forKey: "notificationURL")
}
completionHandler(.newData)
}
}
@available(iOS 10, *)
extension AppDelegate {
override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
if let urlString = userInfo["contents"] as? String {
UserDefaults.standard.set(urlString, forKey: "notificationURL")
}
completionHandler()
}
}
2. Flutter
import 'package:flutter/services.dart';
const MethodChannel notiChannel = MethodChannel('com.my.test.app/notification');
class NotificationURLChecker {
static Future<String?> getNotificationURL() async {
try {
final String? url = await notiChannel.invokeMethod('getNotificationURL');
return url;
} on PlatformException catch (e) {
print("${e.message}");
return null;
}
}
}
class _InAppWebViewScreenState extends State<InAppWebViewScreen> {
final webViewKey = GlobalKey();
late String initialUrl;
@override
void initState() {
super.initState();
initialUrl = _loadInitialUrl();
}
String _loadInitialUrl() async {
String? notificationUrl = await NotificationURLChecker.getNotificationURL();
return notificationUrl ?? "기본url";
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: InAppWebView(
key: webViewKey,
initialUrlRequest: URLRequest(url: WebUri(initialUrl)),
onWebViewCreated: (controller) {
WebControllerSingleton().webController = controller;
},
// ... (rest of the InAppWebView configuration)
),
3. 백엔드
FCM 으로 요청을 보낼때 POST로 보낼 Json Data에 '// 코드 추가' 라고 적은 라인을 추가합니다.
// google api request json data
{
"message": {
"topic": "news",
"notification": {
"title": "Breaking News",
"body": "New news story available."
},
"data": {
"contents": "url" // 코드 추가 (원하는 url 작성)
"click_action": "FLUTTER_NOTIFICATION_CLICK", // 코드 추가
},
"android": {
"notification": {
"click_action": "TOP_STORY_ACTIVITY",
"body": "Check out the Top Story"
}
},
"apns": {
"payload": {
"aps": {
"alert": {
"title": "Notificaion Title",
"body": "알림 내용"
},
"content-available": 1, // 코드 추가
"mutable-content": 1, // 코드 추가
"interruption-level": "active" // 코드 추가
}
}
}
}
}
'Flutter' 카테고리의 다른 글
[Flutter] android/AOS Terminated/killed Heads-up notification 배너 알림 띄우기 (0) | 2024.10.08 |
---|---|
[Flutter] InAppWebView mailto: open 메일 앱으로 연동 / 열기 (0) | 2024.09.11 |
[Flutter] Android InAppWebView download / 다운로드 구현 (1) | 2024.09.10 |
[Flutter] AndroidID 확인방법 / android SSAID 확인 방법 (2) | 2024.09.10 |
[Flutter] iOS app badge count handle / iOS 배지 숫자 제어 (2) | 2024.08.30 |