Couldn't find the method 'PerformFetch' when implementing iOS 7 Background Fetch

QonstruktQonstrukt NLMember ✭✭
edited November 2013 in Xamarin.iOS

I've implemented Background Fetch in a MonoTouch application, but iOS can't seem to find the PerformFetch method I've added to the AppDelegate:

[...]

namespace MyApp.iOS
{
    [Register ("AppDelegate")]
    public partial class AppDelegate : UIApplicationDelegate
    {
        public override bool FinishedLaunching (UIApplication application, NSDictionary options)
        {
            [ ...Window initialization... ]

            UIApplication.SharedApplication.SetMinimumBackgroundFetchInterval (UIApplication.BackgroundFetchIntervalMinimum);

            return true;
        }

        public override async void PerformFetch (UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
        {
            Console.WriteLine ("AppDelegate PerformFetch");

            bool success = await PerformAsyncTask ();
            UIBackgroundFetchResult result = success ? UIBackgroundFetchResult.NewData : UIBackgroundFetchResult.Failed;
            completionHandler (result);
        }
    }
}

When executing this application, everything works perfectly as expected until iOS calls PerformFetch. The following message appears in the Application Output:

Couldn't find the method 'PerformFetch' in the type MyApp.iOS.AppDelegate

When calling PerformFetch myself from FinishedLaunching for example, everything executes without problems, no exceptions what soever. Do I need to register anything beside putting fetch in the Required background modes in the Info.plist? It seems that works as expected as my app is showing up in the corresponding section in the Settings app, and the fact that iOS tries to call the PerformFetch method. Why can't the system find the method?

I've also tried removing the async keyword and making the method synchronous, but that didn't help either.

Posts

  • QonstruktQonstrukt NLMember ✭✭
    edited December 2013

    Nobody that ran into the same issue yet?

    I'm using the latest stable releases of Xamarin Studio and MonoTouch on top of iOS SDK 7.0.x.

    Xamarin Studio
    Version 4.2.1 (build 1)
    Installation UUID: be7f10df-39e4-47f7-bdc1-54fbdc568d45
    Runtime:
        Mono 3.2.5 ((no/964e8f0)
        GTK+ 2.24.20 theme: Raleigh
        GTK# (2.12.0.0)
        Package version: 302050000
    
    Apple Developer Tools
    Xcode 5.0.2 (3335.32)
    Build 5A3005
    
    Xamarin.iOS
    Version: 7.0.4.209 (Indie Edition)
    Hash: 23a0827
    Branch: 
    Build date: 2013-11-11 16:04:00-0500
    
    [...]
    
    Build Information
    Release ID: 402010001
    Git revision: 844a84fe0aa0cb5f986d4e3c4807a51487d07845
    Build date: 2013-11-13 22:12:16+0000
    Xamarin addins: 97e44e4863da6c479427794457637e75b3d22600
    
    Operating System
    Mac OS X 10.9.0
    Darwin Stefans-MacBook-Pro.local 13.0.0 Darwin Kernel Version 13.0.0
        Thu Sep 19 22:22:27 PDT 2013
        root:xnu-2422.1.72~6/RELEASE_X86_64 x86_64
    
  • RolfBjarneKvingeRolfBjarneKvinge USXamarin Team Xamurai

    Try making it non-async.

  • QonstruktQonstrukt NLMember ✭✭
    edited December 2013

    As you can see at the end of my first post I've already tried that. Same error unfortunately!

    I've also downloaded the BackgroundFetch.Sample project from GitHub to try and see what that does, but I wasn't able to have it trigger a Background Fetch yet...
    (http://redth.info/ios7-recipe-background-fetching/ which also uses async.)

    I sure hope this related issue gets solved soon: https://bugzilla.xamarin.com/show_bug.cgi?id=14671, because at the moment this potentially great feature has been impossible to use for me yet with no known reason because I can't debug it.

    [edit]
    Is it possible my mtouch arguments are spoiling the fun?

    iPhoneSimulator:

    -gcc_flags "-L${ProjectDir} -framework ExternalAccessory" --registrar:dynamic

    iPhone

    -gcc_flags "-L${ProjectDir} -framework ExternalAccessory" --registrar:static

    I need the --registrar parameter to use outlets in base classes.

    Guess that's something I can fiddle with, hope someone else might still have some suggestions.

  • RolfBjarneKvingeRolfBjarneKvinge USXamarin Team Xamurai

    Does this happen on device or in the simulator (or both)?

  • QonstruktQonstrukt NLMember ✭✭

    On both, the type of registrar doesn't seem to matter. I'm trying to retrieve more debugging information through Xcode, the Console of the device is unfortunately not very helpful, and the crashlog is pure gibberish to me I'm afraid.

  • QonstruktQonstrukt NLMember ✭✭

    This is in the console when the call fails and the app crashes:

    <Warning>: Couldn't find the method 'PerformFetch' in the type MyApp.iOS.AppDelegate
    <Error>: Stacktrace:
    <Error>:   at <unknown> <0xffffffff>
    <Error>:   at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <0xffffffff>
    <Error>:   at MonoTouch.UIKit.UIApplication.Main (string[],string,string) <0x000db>
    <Error>:   at MyApp.iOS.Application.Main (string[]) <0x0001f>
    <Error>:   at (wrapper runtime-invoke) object.runtime_invoke_dynamic (intptr,intptr,intptr,intptr) <0xffffffff>
    
  • LarryOBrienLarryOBrien USXamarin Team Xamurai

    MAYBE the problem is that you only call the completionHandler within your awaited scope and, since you haven't blocked, the PerformFetch method may complete without having called completionHandler? :

    public override async void PerformFetch (UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
        {
            Console.WriteLine ("AppDelegate PerformFetch");
    
            bool success = await PerformAsyncTask ();
            UIBackgroundFetchResult result = success ? UIBackgroundFetchResult.NewData : UIBackgroundFetchResult.Failed;
            completionHandler (result);
        }
    
  • QonstruktQonstrukt NLMember ✭✭

    Well, I've already tried it synchronous, that should rule it out. Also, it's a callback so I can't imagine it being the problem.

    I'm also running the method as a test from FinishedLaunching, and it runs to completion in the sense that it always calls the completionHandler.

  • QonstruktQonstrukt NLMember ✭✭

    Nobody else encountered this error so far?
    I'm still searching in the dark I'm afraid.

  • DimitrisTavlikosDimitrisTavlikos GRInsider, University ✭✭

    Can you post your synchronous attempt? Asynchronous is out of the question for this method.

  • QonstruktQonstrukt NLMember ✭✭
    edited March 2014

    Sure, this is how it's currently implemented. Still the same error:

    public override void PerformFetch (UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
    {
        PerformAsyncTask ().ContinueWith ((Task<bool> task) => {
            UIBackgroundFetchResult result = task.Result ? UIBackgroundFetchResult.NewData : UIBackgroundFetchResult.Failed;
            completionHandler (result);
        });
    }
    

    I've also tried explicitly Exporting the method:

    [Export ("application:performFetchWithCompletionHandler:")]
    public override void PerformFetch (UIApplication application, Action<UIBackgroundFetchResult> completionHandler)
    [...]
    

    Additional mtouch arguments:

    -gcc_flags "-L${ProjectDir} -framework ExternalAccessory"
    

    Type of registrar doesn't seem to matter. Also I'm not using LLVM.

  • DimitrisTavlikosDimitrisTavlikos GRInsider, University ✭✭
    edited March 2014

    This is weird. I just created a similar example and it works fine. Maybe something is wrong with other project settings.

    I have a project attached. Minimum interval set to 2 secs. It also includes a version of the method which does not use Tasks at all (commented). Do you mind having a look if this works? If it does, then it's probably something to do with your project settings. If this does not work, consider doing a clean up/re-install of Xamarin.

  • QonstruktQonstrukt NLMember ✭✭

    Thanks, I'll be sure to give this a try!

  • QonstruktQonstrukt NLMember ✭✭

    Your sample seems to work correctly. Now to investigate what the differences are...

  • DimitrisTavlikosDimitrisTavlikos GRInsider, University ✭✭

    Asynchronous is out of the question for this method.

    Just to correct my earlier statement. There is absolutely no reason why asynchronous would not work. I was under the impression that you had to complete everything and call the callback before the method returns. It seems that this is not the case. The only limit is the 30 second timeframe.

  • QonstruktQonstrukt NLMember ✭✭

    After a lot of trial and error I've found the problem, and it seems to be the new static registrar. I'm not sure why it hasn't come up earlier (I thought I ruled that out). But after adding that to the mtouch arguments PerformFetch stopped working.

    The good news is that --registrar=dynamic seems to work correctly as my code depends on the new registrars.

  • RolfBjarneKvingeRolfBjarneKvinge USXamarin Team Xamurai

    I can reproduce this now, I'll see what I can find out.

  • RolfBjarneKvingeRolfBjarneKvinge USXamarin Team Xamurai

    I've fixed this issue now, and it should get into Xamarin.iOS 7.2.1.

    Here's a bug report if you're curious: https://bugzilla.xamarin.com/show_bug.cgi?id=18443

  • QonstruktQonstrukt NLMember ✭✭

    Great, thanks for picking this up!

Sign In or Register to comment.