Skip to content

Commit

Permalink
Move background fetch to native code
Browse files Browse the repository at this point in the history
  • Loading branch information
gdelataillade committed Sep 19, 2023
1 parent e06cf9d commit 1fc6b6f
Show file tree
Hide file tree
Showing 13 changed files with 64 additions and 140 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This Flutter plugin provides a simple and easy-to-use interface for setting and

[See screenshot 1](https://github.com/gdelataillade/alarm/assets/32983806/2ad67186-1f22-462c-b953-fe55a35fea09)

2) Still in Xcode, open your Info.plist and add the key `Permitted background task scheduler identifiers`, with the item `com.transistorsoft.fetch` inside.
2) Still in Xcode, open your Info.plist and add the key `Permitted background task scheduler identifiers`, with the item `com.gdelataillade.fetch` inside.

[See screenshot 2](https://github.com/gdelataillade/alarm/assets/32983806/a7ab90be-4518-48a7-a21d-d2844b4b036c)

Expand Down
6 changes: 0 additions & 6 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ PODS:
- Flutter
- audio_session (0.0.1):
- Flutter
- background_fetch (1.2.1):
- Flutter
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
Expand All @@ -28,7 +26,6 @@ PODS:
DEPENDENCIES:
- alarm (from `.symlinks/plugins/alarm/ios`)
- audio_session (from `.symlinks/plugins/audio_session/ios`)
- background_fetch (from `.symlinks/plugins/background_fetch/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- flutter_fgbg (from `.symlinks/plugins/flutter_fgbg/ios`)
Expand All @@ -44,8 +41,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/alarm/ios"
audio_session:
:path: ".symlinks/plugins/audio_session/ios"
background_fetch:
:path: ".symlinks/plugins/background_fetch/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter:
Expand All @@ -68,7 +63,6 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
alarm: 6c1f6a9688f94cd6bf8f104c67cc26e78c9d8d13
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
background_fetch: 896944864b038d2837fc750d470e9841e1e6a363
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_fgbg: 31c0d1140a131daea2d342121808f6aa0dcd879d
Expand Down
2 changes: 2 additions & 0 deletions example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import UIKit
import Flutter
import UserNotifications
import alarm

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
Expand All @@ -11,6 +12,7 @@ import UserNotifications
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
SwiftAlarmPlugin.registerBackgroundTasks()

GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.transistorsoft.fetch</string>
<string>com.gdelataillade.fetch</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
Expand Down
20 changes: 1 addition & 19 deletions example/lib/screens/home.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:async';

import 'package:alarm/alarm.dart';
import 'package:alarm/service/background_fetch.dart';
import 'package:alarm_example/screens/edit_alarm.dart';
import 'package:alarm_example/screens/ring.dart';
import 'package:alarm_example/screens/shortcut_button.dart';
Expand Down Expand Up @@ -72,24 +71,7 @@ class _ExampleAlarmHomeScreenState extends State<ExampleAlarmHomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('alarm 2.0.0-dev.4'),
actions: [
IconButton(
onPressed: () {
final checksDt = AlarmBackgroundFetch.fetches.map((e) {
return '${e.year}-${e.month}-${e.day} ${e.hour}h${e.minute}';
});
final content =
"Background App Checks done this session:\n$checksDt";
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(content)),
);
},
icon: const Icon(Icons.info),
),
],
),
appBar: AppBar(title: const Text('alarm 2.0.0-dev.4')),
body: SafeArea(
child: alarms.isNotEmpty
? ListView.separated(
Expand Down
8 changes: 0 additions & 8 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.16"
background_fetch:
dependency: transitive
description:
name: background_fetch
sha256: f70b28a0f7a3156195e9742229696f004ea3bf10f74039b7bf4c78a74fbda8a4
url: "https://pub.dev"
source: hosted
version: "1.2.1"
boolean_selector:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions help/INSTALL-ANDROID.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Android Setup
1 change: 1 addition & 0 deletions help/INSTALL-IOS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# iOS Setup
67 changes: 57 additions & 10 deletions ios/Classes/SwiftAlarmPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import UIKit
import AVFoundation
import AudioToolbox
import MediaPlayer
import BackgroundTasks

public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
#if targetEnvironment(simulator)
Expand All @@ -12,6 +13,8 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
#endif

private var registrar: FlutterPluginRegistrar!
static let sharedInstance = SwiftAlarmPlugin()
static let backgroundTaskIdentifier: String = "com.gdelataillade.fetch"

public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "com.gdelataillade/alarm", binaryMessenger: registrar.messenger())
Expand Down Expand Up @@ -50,10 +53,6 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
let args = call.arguments as! Dictionary<String, Any>
let id = args["id"] as! Int
self.audioCurrentTime(id: id, result: result)
} else if call.method == "backgroundCheck" {
let args = call.arguments as! Dictionary<String, Any>
let ids = args["ids"] as! [Int]
self.backgroundCheck(ids: ids, result: result)
} else {
DispatchQueue.main.sync {
result(FlutterMethodNotImplemented)
Expand Down Expand Up @@ -149,6 +148,7 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {

DispatchQueue.main.async {
self.timers[id] = Timer.scheduledTimer(timeInterval: delayInSeconds, target: self, selector: #selector(self.executeTask(_:)), userInfo: id, repeats: false)
SwiftAlarmPlugin.scheduleAppRefresh()
}

result(true)
Expand Down Expand Up @@ -280,6 +280,7 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
self.playSilent = false
self.silentAudioPlayer?.stop()
NotificationCenter.default.removeObserver(self)
SwiftAlarmPlugin.cancelBackgroundTasks()
}
}

Expand Down Expand Up @@ -316,14 +317,17 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
}
}

private func backgroundCheck(ids: [Int], result: FlutterResult) {
self.mixOtherAudios()
// Runs when a background fetch event is triggered
private func backgroundFetch() {
NSLog("SwiftAlarmPlugin: -> Background fetch !")

let isPlaying = self.silentAudioPlayer?.isPlaying ?? false
self.mixOtherAudios()

self.silentAudioPlayer?.pause()
self.silentAudioPlayer?.play()

let ids = Array(self.audioPlayers.keys)

for id in ids {
NSLog("SwiftAlarmPlugin: Background check alarm with id \(id)")
if let audioPlayer = self.audioPlayers[id] {
Expand All @@ -339,8 +343,6 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
self.timers[id] = Timer.scheduledTimer(timeInterval: delayInSeconds, target: self, selector: #selector(self.executeTask(_:)), userInfo: id, repeats: false)
}
}

result(isPlaying)
}

private func stopNotificationOnKillService() {
Expand All @@ -349,6 +351,7 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
observerAdded = false
}
}

@objc func applicationWillTerminate(_ notification: Notification) {
let content = UNMutableNotificationContent()
content.title = notificationTitleOnKill
Expand Down Expand Up @@ -382,4 +385,48 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
NSLog("SwiftAlarmPlugin: Error setting up audio session with option duckOthers: \(error.localizedDescription)")
}
}
}

// Runs from AppDelegate when the app is launched
static public func registerBackgroundTasks() {
NSLog("SwiftAlarmPlugin: -> registerBackgroundTasks !")

if #available(iOS 13.0, *) {
BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundTaskIdentifier, using: nil) { task in
self.scheduleAppRefresh()
sharedInstance.backgroundFetch()
task.setTaskCompleted(success: true)
}
} else {
NSLog("SwiftAlarmPlugin: BGTaskScheduler not available for your version of iOS lower than 13.0")
}
}

// Enables background fetch
static func scheduleAppRefresh() {
NSLog("SwiftAlarmPlugin: -> scheduleAppRefresh !")

if #available(iOS 13.0, *) {
let request = BGAppRefreshTaskRequest(identifier: backgroundTaskIdentifier)

request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
NSLog("SwiftAlarmPlugin: Could not schedule app refresh: \(error)")
}
} else {
NSLog("SwiftAlarmPlugin: BGTaskScheduler not available for your version of iOS lower than 13.0")
}
}

// Disable background fetch
static func cancelBackgroundTasks() {
NSLog("SwiftAlarmPlugin: -> cancelBackgroundTasks !")

if #available(iOS 13.0, *) {
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: backgroundTaskIdentifier)
} else {
NSLog("SwiftAlarmPlugin: BGTaskScheduler not available for your version of iOS lower than 13.0")
}
}
}
5 changes: 0 additions & 5 deletions lib/alarm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export 'package:alarm/model/alarm_settings.dart';
import 'dart:async';

import 'package:alarm/model/alarm_settings.dart';
import 'package:alarm/service/background_fetch.dart';
import 'package:alarm/src/ios_alarm.dart';
import 'package:alarm/src/android_alarm.dart';
import 'package:alarm/service/notification.dart';
Expand Down Expand Up @@ -103,8 +102,6 @@ class Alarm {
}

if (iOS) {
AlarmBackgroundFetch.set();

return IOSAlarm.setAlarm(
alarmSettings.id,
alarmSettings.dateTime,
Expand Down Expand Up @@ -153,8 +150,6 @@ class Alarm {

AlarmNotification.instance.cancel(id);

if (iOS && !hasAlarm()) AlarmBackgroundFetch.stop();

return iOS ? await IOSAlarm.stopAlarm(id) : await AndroidAlarm.stop(id);
}

Expand Down
75 changes: 0 additions & 75 deletions lib/service/background_fetch.dart

This file was deleted.

14 changes: 0 additions & 14 deletions lib/src/ios_alarm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,20 +114,6 @@ class IOSAlarm {
return pos2 > pos1;
}

/// Checks if alarm audio players are still active during the background check.
static Future<bool> backgroundCheck() async {
final alarms = Alarm.getAlarms();
if (alarms.isEmpty) return true;

final alarmIds = alarms.map((e) => e.id).toList();

return await methodChannel.invokeMethod<bool?>(
'backgroundCheck',
{'ids': alarmIds},
) ??
false;
}

/// Listens when app goes foreground so we can check if alarm is ringing.
/// When app goes background, periodical timer will be disposed.
static void listenAppStateChange({
Expand Down
1 change: 0 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ environment:

dependencies:
android_alarm_manager_plus: ^3.0.1
background_fetch: ^1.2.1
flutter:
sdk: flutter
flutter_fgbg: ^0.3.0
Expand Down

0 comments on commit 1fc6b6f

Please sign in to comment.