Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor -> iOS battery optimizations #208

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
Binary file added assets/blank_30.mp3
Binary file not shown.
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ SPEC CHECKSUMS:
alarm: 6c1f6a9688f94cd6bf8f104c67cc26e78c9d8d13
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_fgbg: 31c0d1140a131daea2d342121808f6aa0dcd879d
permission_handler_apple: 036b856153a2b1f61f21030ff725f3e6fece2b78
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78

PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011

Expand Down
18 changes: 18 additions & 0 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
38B35C95769EE88AAB27208F /* [CP] Embed Pods Frameworks */,
4A919127DC74F79AA2F73A63 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -253,6 +254,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
4A919127DC74F79AA2F73A63 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class _ExampleAlarmHomeScreenState extends State<ExampleAlarmHomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('alarm 3.1.4')),
appBar: AppBar(title: const Text('Battery Test #4')),
body: SafeArea(
child: alarms.isNotEmpty
? ListView.separated(
Expand Down
86 changes: 30 additions & 56 deletions ios/Classes/SwiftAlarmPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {

private var alarms: [Int: AlarmConfiguration] = [:]

private var silentAudioPlayer: AVAudioPlayer?
static private var silentAudioPlayer: AVAudioPlayer?
private let resourceAccessQueue = DispatchQueue(label: "com.gdelataillade.alarm.resourceAccessQueue")

private var notifOnKillEnabled: Bool!
Expand Down Expand Up @@ -187,15 +187,15 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
}

private func startSilentSound() {
let filename = registrar.lookupKey(forAsset: "assets/long_blank.mp3", fromPackage: "alarm")
let filename = registrar.lookupKey(forAsset: "assets/blank_30.mp3", fromPackage: "alarm")
if let audioPath = Bundle.main.path(forResource: filename, ofType: nil) {
let audioUrl = URL(fileURLWithPath: audioPath)
do {
self.silentAudioPlayer = try AVAudioPlayer(contentsOf: audioUrl)
self.silentAudioPlayer?.numberOfLoops = -1
self.silentAudioPlayer?.volume = 0.1
SwiftAlarmPlugin.silentAudioPlayer = try AVAudioPlayer(contentsOf: audioUrl)
SwiftAlarmPlugin.silentAudioPlayer?.numberOfLoops = -1
SwiftAlarmPlugin.silentAudioPlayer?.volume = 0.1
self.playSilent = true
self.silentAudioPlayer?.play()
SwiftAlarmPlugin.silentAudioPlayer?.play()
NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption), name: AVAudioSession.interruptionNotification, object: nil)
} catch {
NSLog("[SwiftAlarmPlugin] Error: Could not create and play silent audio player: \(error)")
Expand All @@ -206,28 +206,13 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
}

@objc func handleInterruption(notification: Notification) {
guard let info = notification.userInfo,
let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}

switch type {
case .began:
self.silentAudioPlayer?.play()
NSLog("SwiftAlarmPlugin: Interruption began")
case .ended:
self.silentAudioPlayer?.play()
NSLog("SwiftAlarmPlugin: Interruption ended")
default:
break
}
SwiftAlarmPlugin.silentAudioPlayer?.play()
}

private func loopSilentSound() {
self.silentAudioPlayer?.play()
SwiftAlarmPlugin.silentAudioPlayer?.play()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.silentAudioPlayer?.pause()
SwiftAlarmPlugin.silentAudioPlayer?.pause()
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
if self.playSilent {
self.loopSilentSound()
Expand Down Expand Up @@ -306,10 +291,10 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
safeModifyResources {
if self.alarms.isEmpty {
self.playSilent = false
SwiftAlarmPlugin.cancelBackgroundTasks()
DispatchQueue.main.async {
self.silentAudioPlayer?.stop()
SwiftAlarmPlugin.silentAudioPlayer?.stop()
NotificationCenter.default.removeObserver(self)
SwiftAlarmPlugin.cancelBackgroundTasks()
}
}
}
Expand Down Expand Up @@ -348,32 +333,23 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
}
}

private func backgroundFetch() {
@available(iOS 13.0, *)
private func backgroundFetch(task: BGTask) {
self.mixOtherAudios()

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

safeModifyResources {
let ids = Array(self.alarms.keys)

for id in ids {
NSLog("SwiftAlarmPlugin: Background check alarm with id \(id)")
if let audioPlayer = self.alarms[id]?.audioPlayer, let dateTime = self.alarms[id]?.triggerTime {
let currentTime = audioPlayer.deviceCurrentTime
let time = currentTime + dateTime.timeIntervalSinceNow
audioPlayer.play(atTime: time)
}
NSLog("SwiftAlarmPlugin: Background fetch triggered")

if let alarm = self.alarms[id], let delayInSeconds = alarm.triggerTime?.timeIntervalSinceNow {
DispatchQueue.main.async {
self.safeModifyResources {
alarm.timer = Timer.scheduledTimer(timeInterval: delayInSeconds, target: self, selector: #selector(self.executeTask(_:)), userInfo: id, repeats: false)
}
}
}
}
guard let silentAudioPlayer = SwiftAlarmPlugin.silentAudioPlayer else {
NSLog("SwiftAlarmPlugin: Failed to initialize silent audio player")
task.setTaskCompleted(success: false)
return
}

silentAudioPlayer.play()

task.setTaskCompleted(success: true)
NSLog("SwiftAlarmPlugin: Background fetch completed successfully.")
SwiftAlarmPlugin.scheduleAppRefresh()
}

private func stopNotificationOnKillService() {
Expand Down Expand Up @@ -456,28 +432,26 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
static public func registerBackgroundTasks() {
if #available(iOS 13.0, *) {
BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundTaskIdentifier, using: nil) { task in
self.scheduleAppRefresh()
sharedInstance.backgroundFetch()
task.setTaskCompleted(success: true)
sharedInstance.backgroundFetch(task: task)
}
} else {
NSLog("SwiftAlarmPlugin: BGTaskScheduler not available for your version of iOS lower than 13.0")
NSLog("SwiftAlarmPlugin: BGTaskScheduler not available for iOS versions lower than 13.0")
}
}

/// Enables background fetch
static func scheduleAppRefresh() {
if #available(iOS 13.0, *) {
let request = BGAppRefreshTaskRequest(identifier: backgroundTaskIdentifier)
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 minutes

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")
NSLog("SwiftAlarmPlugin: BGTaskScheduler not available for iOS versions lower than 13.0")
}
}

Expand All @@ -486,7 +460,7 @@ public class SwiftAlarmPlugin: NSObject, FlutterPlugin {
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")
NSLog("SwiftAlarmPlugin: BGTaskScheduler not available for iOS versions lower than 13.0")
}
}
}
}
Loading