79188528

Date: 2024-11-14 11:25:10
Score: 1.5
Natty:
Report link

Solution

1. Configure UNNotificationSound in AppDelegate.swift

import UIKit
import Flutter
import UserNotifications
import AVFoundation

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Configure notification settings
        if #available(iOS 10.0, *) {
            UNUserNotificationCenter.current().delegate = self
            
            let options: UNAuthorizationOptions = [
                .alert,
                .badge,
                .sound,
                .criticalAlert  // Add critical alert permission
            ]
            
            UNUserNotificationCenter.current().requestAuthorization(
                options: options,
                completionHandler: { _, _ in }
            )
        }
        
        // Configure audio session
        do {
            try AVAudioSession.sharedInstance().setCategory(
                .playback,
                mode: .default,
                options: [.mixWithOthers]
            )
            try AVAudioSession.sharedInstance().setActive(true)
        } catch {
            print("Failed to set audio session category: \(error)")
        }
        
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

// Add UNNotificationSoundName extension
extension UNNotificationSoundName {
    static let customSound = UNNotificationSoundName("notification_sound.wav")
}

2. Update Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- Other existing entries -->
    
    <!-- Add background modes -->
    <key>UIBackgroundModes</key>
    <array>
        <string>audio</string>
        <string>fetch</string>
        <string>remote-notification</string>
    </array>
    
    <!-- Add critical alerts entitlement -->
    <key>com.apple.developer.usernotifications.critical-alerts</key>
    <true/>
    
    <!-- Add notification service extension -->
    <key>com.apple.developer.usernotifications.time-sensitive</key>
    <true/>
    
    <!-- Configure audio session -->
    <key>UIBackgroundModes</key>
    <array>
        <string>audio</string>
    </array>
</dict>
</plist>

3. Update Flutter Notification Configuration

import 'package:awesome_notifications/awesome_notifications.dart';

Future<void> initializeNotifications() async {
  await AwesomeNotifications().initialize(
    null,
    [
      NotificationChannel(
        channelKey: "alarm_Category",
        channelName: "Alarm Category",
        channelDescription: 'Long duration alarm notifications',
        defaultColor: Colors.blue,
        ledColor: Colors.white,
        playSound: true,
        soundSource: 'resource://raw/notification_sound',
        importance: NotificationImportance.Max,
        criticalAlerts: true,
        enableVibration: true,
        enableLights: true,
        locked: true,
        // Add these settings for iOS
        defaultRingtoneType: DefaultRingtoneType.Alarm,
        channelShowBadge: true,
      ),
    ],
    debug: true,
  );
}

Future<void> scheduleAlarmNotification({
  required int subId,
  required DateTime scheduledTime,
  required String memoText,
}) async {
  await AwesomeNotifications().createNotification(
    content: NotificationContent(
      id: subId,
      channelKey: "alarm_Category",
      title: DateFormat('yyyy.MM.dd HH:mm').format(scheduledTime),
      body: 'Alarm Memo: $memoText',
      category: NotificationCategory.Alarm,
      notificationLayout: NotificationLayout.Default,
      criticalAlert: true,
      fullScreenIntent: true,
      wakeUpScreen: true,
      autoDismissible: false,
      locked: true,
      displayOnForeground: true,
      displayOnBackground: true,
      customSound: 'resource://raw/notification_sound',
      // Add iOS specific settings
      timeoutAfter: Duration(seconds: 30), // Extend timeout
      payload: {
        'sound_duration': '29',  // Add sound duration info
        'critical': 'true',      // Mark as critical
      },
    ),
    schedule: NotificationCalendar(
      repeats: true,
      preciseAlarm: true,
      allowWhileIdle: true,
      year: scheduledTime.year,
      month: scheduledTime.month,
      day: scheduledTime.day,
      hour: scheduledTime.hour,
      minute: scheduledTime.minute,
      second: scheduledTime.second,
    ),
  );
}

4. Add Audio Processing Extension

Create a new Notification Service Extension target in Xcode:

// NotificationService.swift
import UserNotifications
import AVFoundation

class NotificationService: UNNotificationServiceExtension {
    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?
    var audioPlayer: AVAudioPlayer?
    
    override func didReceive(
        _ request: UNNotificationRequest,
        withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        
        if let bestAttemptContent = bestAttemptContent {
            // Configure sound
            if let soundDuration = request.content.userInfo["sound_duration"] as? String,
               let soundPath = Bundle.main.path(forResource: "notification_sound", ofType: "wav") {
                let soundUrl = URL(fileURLWithPath: soundPath)
                bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "notification_sound.wav"))
                
                // Setup audio session
                do {
                    try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
                    try AVAudioSession.sharedInstance().setActive(true)
                    
                    audioPlayer = try AVAudioPlayer(contentsOf: soundUrl)
                    audioPlayer?.prepareToPlay()
                    audioPlayer?.play()
                } catch {
                    print("Error setting up audio: \(error)")
                }
            }
            
            contentHandler(bestAttemptContent)
        }
    }
    
    override func serviceExtensionTimeWillExpire() {
        if let contentHandler = contentHandler,
           let bestAttemptContent = bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}

5. Sound File Requirements

  1. Convert your sound file to WAV format:
ffmpeg -i input.mp3 -acodec pcm_s16le -ac 2 -ar 44100 notification_sound.wav
  1. Add the sound file to both:

6. Entitlements Configuration

  1. Add entitlements file to your project
  2. Enable these capabilities:
    • Background Modes
    • Critical Alerts
    • Push Notifications
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.usernotifications.critical-alerts</key>
    <true/>
    <key>aps-environment</key>
    <string>development</string>
</dict>
</plist>

Best Practices

  1. Sound File Optimization
// Check sound file properties
void validateSoundFile() {
  final file = File('notification_sound.wav');
  if (file.lengthSync() > 5 * 1024 * 1024) {
    print('Warning: Sound file too large');
  }
}
  1. Error Handling
Future<bool> checkNotificationPermissions() async {
  final status = await AwesomeNotifications().isNotificationAllowed();
  if (!status) {
    // Request permissions
    return await AwesomeNotifications().requestPermissionToSendNotifications();
  }
  return status;
}
  1. Testing
void testNotification() async {
  final now = DateTime.now();
  await scheduleAlarmNotification(
    subId: 1,
    scheduledTime: now.add(Duration(seconds: 5)),
    memoText: 'Test notification',
  );
}

Remember to:

Would you like me to explain any part in more detail?

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Ends in question mark (2):
  • Low reputation (1):
Posted by: Darshan Parmar