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

Unhandled method registerOneOffTask in iOS #89

Closed
winsonet opened this issue Oct 9, 2019 · 34 comments
Closed

Unhandled method registerOneOffTask in iOS #89

winsonet opened this issue Oct 9, 2019 · 34 comments
Assignees
Labels
bug Something isn't working iOS iOS specific RTM A bad case of 'issues' where the problem resides with not having (fully) read the documentation

Comments

@winsonet
Copy link

winsonet commented Oct 9, 2019

it's works fine in my android but for iOS I always get below errors:

Unhandled Exception: PlatformException(unhandledMethod("registerOneOffTask") error, Unhandled method registerOneOffTask, null)
#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:569:7)
#1      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
<asynchronous suspension>
#2      Workmanager.registerOneOffTask (package:workmanager/src/workmanager.dart:123:32)
<asynchronous suspension>
#3      main.<anonymous closure>.<anonymous closure> (package:my_app/main.dart:83:19)
#4      _rootRun (dart:async/zone.dart:1120:38)
#5      _CustomZone.run (dart:async/zone.dart:1021:19)
#6      _CustomZone.runGuarded (dart:async/zone.dart:923:7)
#7      _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:963:23)
#8      _rootRun (dart:async/zone.dart:1124:13)
#9      _CustomZone.run (dart:async/zone.dart:1021:19)
#10     _CustomZone.bindCallback.<anonymous closure> 

in my AppDelegate.swift file

import workmanager

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    UNUserNotificationCenter.current().delegate = self
    //UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*15))

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

extension AppDelegate: UNUserNotificationCenterDelegate {

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler(.alert) // shows banner even if app is in foreground
    }
}

any ideas?

thanks!

@winsonet
Copy link
Author

winsonet commented Oct 9, 2019

oh~~maybe I misunderstand, it seems all of the methods are just for android, right? so for ios, just need to set below in the AppDelegate:

UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*15))

for let the task run on schedule?

so when I execute the registerOneOffTask just show me the above error, right?

@timrijckaert
Copy link
Contributor

timrijckaert commented Oct 9, 2019

Refer to the documentation please.
It clearly states Android only.

iOS automatically schedules 1 task for you. There is no way to specify custom constraints on iOS for now. We will however support this in the future for iOS 13 and up.
Refer to #17

Workmanager.executeTask((task, inputData) async {
    switch (task) {
      case Workmanager.iOSBackgroundTask:
        developer.log("The iOS background fetch was triggered");
        break;
    }
    return Future.value(true);
  });

Refer to the sample which shows a full sample how to deal with iOS.

@timrijckaert timrijckaert self-assigned this Oct 9, 2019
@timrijckaert timrijckaert added the RTM A bad case of 'issues' where the problem resides with not having (fully) read the documentation label Oct 9, 2019
@winsonet
Copy link
Author

winsonet commented Oct 9, 2019

but I tried in xcode and trigger it by manual, I just got below error:

Could not cast value of type 'FlutterError' (0x109be5ad0) to 'NSNumber' (0x7fff87bb7538).

@timrijckaert
Copy link
Contributor

Can you share the code that is failing?

@timrijckaert timrijckaert reopened this Oct 9, 2019
@winsonet
Copy link
Author

winsonet commented Oct 9, 2019

I just tried with your example codes and didn't change anything :(

@winsonet
Copy link
Author

winsonet commented Oct 9, 2019

I tried again just with your example code(the latest version), please find the below error screen capture for your reference:

image

@timrijckaert
Copy link
Contributor

timrijckaert commented Oct 9, 2019

@winsonet If I understand correctly, you are saying the sample app does not run?
Which XCode version, Swift version?
If not, can you show us the Dart side?
Do you return a bool ?

@Kymer or @jeremie-movify Does the sample app still run on iOS?

@timrijckaert timrijckaert added bug Something isn't working iOS iOS specific labels Oct 9, 2019
@winsonet
Copy link
Author

winsonet commented Oct 9, 2019

yes, my xcode version is : 11.1(11A1027), Swift version is: 4.2
and I try to run it on iPhone 11 Pro Max (iOS 13.1) simulator
and I just use your main.dart as below

void callbackDispatcher() {
  Workmanager.executeTask((task, inputData) async {
    switch (task) {
      case simpleTaskKey:
        developer.log("$simpleTaskKey was executed. inputData = $inputData");
        Directory tempDir = await getTemporaryDirectory();
        String tempPath = tempDir.path;
        developer
            .log("You can access other plugins in the background: $tempPath");
        break;
      case simpleDelayedTask:
        developer.log("$simpleDelayedTask was executed");
        break;
      case simplePeriodicTask:
        developer.log("$simplePeriodicTask was executed");
        break;
      case simplePeriodic1HourTask:
        developer.log("$simplePeriodic1HourTask was executed");
        break;
      case Workmanager.iOSBackgroundTask:
        developer.log("The iOS background fetch was triggered");
        break;
    }

    return Future.value(true);
  });
}

@timrijckaert
Copy link
Contributor

Does the sample app run?

@raphaelakpan
Copy link

raphaelakpan commented Oct 10, 2019

I am observing the exact same things as @winsonet, including error while simulating background fetch from Xcode. The Sample app runs but breaks when I simulate background fetch.

My setup is exactly the same.

@Kymer
Copy link

Kymer commented Oct 10, 2019

Thanks for letting us know. Odd that flutterResult is of type FlutterError in that case, not sure yet how Xcode 11 would affect this... I'll look into it 👍

@timrijckaert timrijckaert assigned Kymer and unassigned timrijckaert Oct 10, 2019
@raphaelakpan
Copy link

I switched back to version 0.1.1 and was able to successfully simulate background fetch in the example app with the same Xcode 11 setup so will probably use this version.

I have one question I would appreciate clarity. For the iOS app, I know there's no guarantee but how frequent can I realistically expect background fetch to be triggered by iOS in my app? Does it happen only once? Once in a couple days? Once daily?

I ask because I had the example app running for hours (after initializing workmanager) hoping to see a trigger from iOS to run the background fetch but it didn't happen (it only triggered through the simulated background fetch).

@winsonet
Copy link
Author

I switched back to version 0.1.1 and was able to successfully simulate background fetch in the example app with the same Xcode 11 setup so will probably use this version.

I have one question I would appreciate clarity. For the iOS app, I know there's no guarantee but how frequent can I realistically expect background fetch to be triggered by iOS in my app? Does it happen only once? Once in a couple days? Once daily?

I ask because I had the example app running for hours (after initializing workmanager) hoping to see a trigger from iOS to run the background fetch but it didn't happen (it only triggered through the simulated background fetch).

I think you need to set the frequent in swift code as below:

class AppDelegate:UIResponder,UIApplicationDelegate{
    func application(_ application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplicationLaunchOptionsKey:Any]?)->Bool{
        // Other intialization code…
        UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*15))

        return true
    }
}

but I really hope it can fix the issue in latest version!

@raphaelakpan
Copy link

Yeah, I did. I copied that same line over to my AppDelegate.swift

@winsonet
Copy link
Author

but I tried to back to 0.1.1 it's also not working, the same error for me :(

@raphaelakpan
Copy link

Did you close Xcode and reopen? Firstly though, I think I did flutter clean to clear previous builds before building again.

My Experience

When I downgraded to 0.1.1, I tried building it from VS Code first and got some errors relating to changed Swift APIs. I tried to google the errors.

I think I made adjustments to two files before it built and ran:

  • ios/.symlinks/plugins/workmanager/ios/Classes/ThumbnailGenerator.swift
    UIImagePNGRepresentation(self) to self.pngData()

  • ios/.symlinks/plugins/workmanager/example/ios/Runner/AppDelegate.swift
    UIApplicationLaunchOptionsKey to UIApplication.LaunchOptionsKey

After that I stopped the app and jumped on XCode to run the app and then was able to simulate the background fetch.

@winsonet
Copy link
Author

hum...sorry, I can't find any codes what you mentioned in that's files:(

there is no UIImagePNGRepresentation(self) in

ios/.symlinks/plugins/workmanager/ios/Classes/ThumbnailGenerator.swift

and also no UIApplicationLaunchOptionsKey in

ios/.symlinks/plugins/workmanager/example/ios/Runner/AppDelegate.swift

and I have clean and rebuild the flutter sample project , after that just open Xcode to run again...

@raphaelakpan
Copy link

Okay, can't get those swift errors again.

I deleted the repo and cloned again, then opened the example folder and changed the version to 0.1.1 and, cleaned and built from VS Code, no errors, successfully built. Opened in Xcode and built, simulated background fetch and it worked.

@winsonet
Copy link
Author

I just find an interesting thing that I can't back to 0.1.1 version, even I set workmanager: ^0.1.1 in pubsepc.yaml file, but it always get the 0.1.2 version for me, I think that's why I can't sucess anymore :(

@raphaelakpan
Copy link

I dunno, maybe removing the ^ might help. This is how I have mine 👇 in pubspec.yml

workmanager: 0.1.1

@winsonet
Copy link
Author

thanks for your reply, when I remove the ^ and it can back to 0.1.1, but unfortunately, there is still same error in my Xcode :(

@alp1396
Copy link

alp1396 commented Oct 13, 2019

About the error. I've got that:

Could not cast value of type 'NSObject' (0x110a63ec8) to 'NSNumber' (0x1100bed60).
2019-10-13 16:57:14.398120+0500 Runner[40366:215112] Could not cast value of type 'NSObject' (0x110a63ec8) to 'NSNumber' (0x1100bed60).
Printing description of flutterResult:
▿ Optional<Any>
  - some : <NSObject: 0x600000818000>

There is 2 cases I found. First, the FlutterResult is not an scalar or string anymore - it is an Object... And second, even if I hardcode the result as UIBackgroundFetchResult.newData, the error is gone. But the dart code inside of callbackDispatcher is not executing.

Xcode: 10.3, Swift 4.2

@timrijckaert
Copy link
Contributor

timrijckaert commented Oct 13, 2019

Thank you for the investigation we will pick this up on monday 😀

@alp1396
Copy link

alp1396 commented Oct 13, 2019

Woah... the problem in my case was in my dart code inside of callbackDispatcher... it throws an error inside. Wrapping code in try...catch shows that SharedPreferences are failed. On Android platform all works fine, but it is another story...

So, try... catch is really helps find out what exactly is going on. Maybe It is good to note about in the docs, I suppose.
Cheers!

@timrijckaert
Copy link
Contributor

What do you mean?
You can't interact with the SharedPreferences on iOS?

There is nothing magic about the callbackDispatcher placing a try catch has the same effect as if you would've placed it elsewhere.
Why would this be relevant for the documentation?

@alp1396
Copy link

alp1396 commented Oct 13, 2019

Yep, SharedPreferences are not working in IOS.
My code:

void callbackDispatcher() {
  print('dispatched');
  Workmanager.executeTask((backgroundTask) async {
    print('inside task');
    print("Called bg task: $backgroundTask at ${DateTime.now()}");
    try {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      print(prefs.get('foo.bar'));
    } catch(e) {
      print(e);
    }
    return Future.value(true);
  });
}

result:

2019-10-13 19:00:19.070142+0500 Runner[62124:323133] flutter: 
MissingPluginException(No implementation found for method getAll 
on channel plugins.flutter.io/shared_preferences)

Flutter: 1.7.8+hotfix4
Xcode: 10.3
Swift: 4.2
Android works well with the same code.

About the magic)) I've coded my app, it was tested and worked like a charm) but on Android. When I switched to IOS, and tried to test 'Simulate Background Fetch' option to run my code, I've got a XCODE that stops on line 115 of SwiftWorkmanagerPlugin.swift with the message that I've posted earlier.

Could not cast value of type 'NSObject' (0x110a63ec8) to 'NSNumber' (0x1100bed60).
2019-10-13 16:57:14.398120+0500 Runner[40366:215112] Could not cast value of type 'NSObject' (0x110a63ec8) to 'NSNumber' (0x1100bed60).
Printing description of flutterResult:
▿ Optional<Any>
  - some : <NSObject: 0x600000818000>

And no more. Nothing about Shared or Preferences or even any of Exceptions. It is hard to figure out what is going on. And where the problem is (your code or plugin code, or else). Especially if on the other platform (Android) all is working well. Maybe that would save someone couple of hours of issues checking...)))

@timrijckaert
Copy link
Contributor

It seems we have an extra issues then on iOS if plugins are not registered in the plugins.
Can you try the sample on iOS does that work?
We have a plugin in the callbackDispatcher there too.

@alp1396
Copy link

alp1396 commented Oct 13, 2019

Sample works well, just because IOS notify event in callback actually doesn't use any other plugins!
And its failed if I do make any plugin call:

case Workmanager.iOSBackgroundTask:
        developer.log("The iOS background fetch was triggered");
        getTemporaryDirectory().then((dir) {
          print(dir.uri);
        });
        break;

error:

2019-10-14 00:04:55.339765+0500 Runner[24262:144456] [VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: MissingPluginException(No implementation found for method getTemporaryDirectory on channel plugins.flutter.io/path_provider)
#0      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:314:7)
<asynchronous suspension>
#1      getTemporaryDirectory (package:path_provider/path_provider.dart:26:22)
<asynchronous suspension>
#2      callbackDispatcher.<anonymous closure> (package:workmanager_example/main.dart:44:9)
#3      Workmanager.executeTask.<anonymous closure> (package:workmanager/src/workmanager.dart:77:61)
<asynchronous suspension>
#4      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:397:55)
<asynchronous suspension>
#5      MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:365:54)
#6      _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binary_messenger.dart:110:33)
<asynchronous suspension>
#7      _invoke3.<anonymous closure> (dart:ui/hooks.dart:293:15)
#8      _rootRun (dart:async/zone.dart:1124:13)
#9      _CustomZone.run (dart:async/zone.dart:1021:19)
#10     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
#11     _invoke3 (dart:ui/hooks.dart:292:10)
#12     _dispatchPlatformMessage (dart:ui/hooks.dart:154:5)
2019-10-14 00:04:55.347829+0500 Runner[24262:144228] [<workmanager.SwiftWorkmanagerPlugin: 0x600003b82130>] application(_:performFetchWithCompletionHandler:) -> UIBackgroundFetchResult.newData (finished in 0.25 seconds)

@Kymer
Copy link

Kymer commented Oct 15, 2019

@winsonet @raphaelakpan I have pushed a fix (#94) for the failing FlutterResult cast that fails. We published a new version containing those fixes: 0.1.3. Can you try to update the plugin and report back? 👍

@alp1396 If you want to use other plugins in iOS' background fetch you need to make sure you set the WorkmanagerPlugin.setPluginRegistrantCallback as described in our iOS readme:

    override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        GeneratedPluginRegistrant.register(with: self)
        UNUserNotificationCenter.current().delegate = self
        
        WorkmanagerPlugin.setPluginRegistrantCallback { registry in
            // registry in this case is the FlutterEngine that is created in Workmanager's performFetchWithCompletionHandler
            // This will make other plugins available during a background fetch
            GeneratedPluginRegistrant.register(with: registry)
        }
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        
    }

@Kymer Kymer added the answered Used to auto-close 'answered' issues after a certain time of inactivity label Oct 15, 2019
@winsonet
Copy link
Author

that's grate!!! it's working, thanks a lot!!

@no-response no-response bot removed the answered Used to auto-close 'answered' issues after a certain time of inactivity label Oct 15, 2019
@subhadippramanik
Copy link

I am also facing similar issue on iOS.

I followed the setup guide. My plugin version is 0.4.0.

My code looks like

void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    switch (task) {
      case Workmanager.iOSBackgroundTask:
        stderr.writeln("The iOS background fetch was triggered");
        break;
    }
    return Future.value(true);
  });
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Workmanager().initialize(
    callbackDispatcher,
    isInDebugMode: true,
  );

  runApp(MyApp());
}

when I want to invoke it is

ElevatedButton(
              onPressed: () => {
                print('button pressed'),
                Workmanager().registerOneOffTask("2", "simpleOneOffTask")
              },
              child: Text('Notify'),
            )

The AppDeligate.swift is

import UIKit
import Flutter
import workmanager

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    
    override func userNotificationCenter(_ center: UNUserNotificationCenter,
                                             willPresent notification: UNNotification,
                                             withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
             completionHandler(.alert) // shows banner even if app is in foreground
         }

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    UNUserNotificationCenter.current().delegate = self
    UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*15))

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

The error is

[VERBOSE-2:ui_dart_state.cc(186)] Unhandled Exception: PlatformException(unhandledMethod("registerOneOffTask") error, Unhandled method registerOneOffTask, null, null)
#0      StandardMethodCodec.decodeEnvelope
package:flutter/…/services/message_codecs.dart:581
#1      MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:158
<asynchronous suspension>
#2      Workmanager.registerOneOffTask
package:workmanager/src/workmanager.dart:139
<asynchronous suspension>

I tried with Workmanager().registerPeriodicTask and the error is same for that, Unhandled method
Am I missing something?

Any help appreciated.
Thanks in Advance

@CaoGiaHieu-dev
Copy link

I am also facing similar issue on iOS.

I followed the setup guide. My plugin version is 0.4.0.

My code looks like

void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    switch (task) {
      case Workmanager.iOSBackgroundTask:
        stderr.writeln("The iOS background fetch was triggered");
        break;
    }
    return Future.value(true);
  });
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Workmanager().initialize(
    callbackDispatcher,
    isInDebugMode: true,
  );

  runApp(MyApp());
}

when I want to invoke it is

ElevatedButton(
              onPressed: () => {
                print('button pressed'),
                Workmanager().registerOneOffTask("2", "simpleOneOffTask")
              },
              child: Text('Notify'),
            )

The AppDeligate.swift is

import UIKit
import Flutter
import workmanager

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    
    override func userNotificationCenter(_ center: UNUserNotificationCenter,
                                             willPresent notification: UNNotification,
                                             withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
             completionHandler(.alert) // shows banner even if app is in foreground
         }

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    UNUserNotificationCenter.current().delegate = self
    UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*15))

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

The error is

[VERBOSE-2:ui_dart_state.cc(186)] Unhandled Exception: PlatformException(unhandledMethod("registerOneOffTask") error, Unhandled method registerOneOffTask, null, null)
#0      StandardMethodCodec.decodeEnvelope
package:flutter/…/services/message_codecs.dart:581
#1      MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:158
<asynchronous suspension>
#2      Workmanager.registerOneOffTask
package:workmanager/src/workmanager.dart:139
<asynchronous suspension>

I tried with Workmanager().registerPeriodicTask and the error is same for that, Unhandled method
Am I missing something?

Any help appreciated.
Thanks in Advance

same issue :< I still dont know how to resolve

@Praveen-Ramalingam
Copy link

Please give any update on this issue i am also facing this same issue

@ened
Copy link
Collaborator

ened commented Jun 22, 2021

registerOneOffTask was previously unsupported on iOS, but now has a limited implementation in version 0.5.0-dev.4. Please give it a go and report bugs.

@fluttercommunity fluttercommunity locked as resolved and limited conversation to collaborators Jun 22, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working iOS iOS specific RTM A bad case of 'issues' where the problem resides with not having (fully) read the documentation
Projects
None yet
Development

No branches or pull requests

9 participants