3

I would like to use Optimizely to improve A/B testing in App. To do it i have implemented a Xamarin Binding that is equal to this repo:

https://github.com/JustGiving/XamarinBindings/tree/master/Optimizely.iOS

When i register my API Key on FinishedLaunching in that way:

static readonly string OptimizelyAPIToken = "mykey~projectid";

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
      // create a new window instance based on the screen size
      Window = new UIWindow(UIScreen.MainScreen.Bounds);
      root = new WelcomeController();
      nav = new UINavigationController(root);

      // If you have defined a root view controller, set it here:
      Window.RootViewController = nav;

      // make the window visible
      Window.MakeKeyAndVisible();

      OptimizelyiOS.Optimizely.SharedInstance().VerboseLogging = true;

      OptimizelyiOS.Optimizely.StartOptimizelyWithAPIToken(OptimizelyAPIToken, launchOptions ?? new NSDictionary());


      return true;
}

i have this issue

2015-09-01 11:57:23.588 MyAppOptm[3540:750731] [Optimizely Logging]: (ERROR) NSInvalidArgumentException: Stack Trace:
(
0 CoreFoundation 0x22787fa7 + 150
1 libobjc.A.dylib 0x310fec8b objc_exception_throw + 38
2 CoreFoundation 0x2278d3a9 + 0
3 CoreFoundation 0x2278b15f + 354
4 CoreFoundation 0x226baba8 CF_forwarding_prep_0 + 24
5 MyAppOptm 0x011e32f5 -[Optimizely registerGestureRecognizer] + 176
6 Foundation 0x234c136d __NSFireDelayedPerform + 468
7 CoreFoundation 0x2274dcbf + 14
8 CoreFoundation 0x2274d83b + 650
9 CoreFoundation 0x2274ba8b + 1418
10 CoreFoundation 0x22697f31 CFRunLoopRunSpecific + 476
11 CoreFoundation 0x22697d43 CFRunLoopRunInMode + 106
12 Foundation 0x2340713d + 264
13 MyAppOptm 0x011e24f9 -[Optimizely waitForNetworkWithTimeout:condition:] + 432
14 MyAppOptm 0x011e22af -[Optimizely waitForNetworkWithTimeout] + 274
15 MyAppOptm 0x011e181f -[Optimizely startOptimizelyWithToken:launchOptions:experimentsLoadedCallback:] + 1578
16 MyAppOptm 0x011dd8c1 +[Optimizely startOptimizelyWithAPIToken:launchOptions:experimentsLoadedCallback:] + 164
17 MyAppOptm 0x011dd811 +[Optimizely startOptimizelyWithAPIToken:launchOptions:] + 52
18 MyAppOptm 0x00e8006c wrapper_managed_to_native_ApiDefinition_Messaging_void_objc_msgSend_IntPtr_IntPtr_intptr_intptr_intptr_intptr + 236
19 MyAppOptm 0x00e76b44 OptimizelyiOS_Optimizely_StartOptimizelyWithAPIToken_string_Foundation_NSDictionary + 480
20 MyAppOptm 0x00091ad4 JR_UI_Touch_AppDelegate_FinishedLaunching_UIKit_UIApplication_Foundation_NSDictionary + 2448
21 MyAppOptm 0x00433fdc wrapper_runtime_invoke_object_runtime_invoke_dynamic_intptr_intptr_intptr_intptr + 256
22 MyAppOptm 0x012533f7 mono_jit_runtime_invoke + 1190
23 MyAppOptm 0x0129fd89 mono_runtime_invoke + 88
24 MyAppOptm 0x00ff556d native_to_managed_trampoline_4 + 420
25 MyAppOptm 0x00ffbac5 -[AppDelegate application:didFinishLaunchingWithOptions:] + 100
26 UIKit 0x25e750b3 + 374
27 UIKit 0x2606b929 + 2444
28 UIKit 0x2606dfe9 + 1412
29 UIKit 0x26078c69 + 36
30 UIKit 0x2606c78b + 130
31 FrontBoardServices 0x29328ec9 + 16
32 CoreFoundation 0x2274ddb5 + 12
33 CoreFoundation 0x2274d079 + 216
34 CoreFoundation 0x2274bbb3 + 1714
35 CoreFoundation 0x22697f31 CFRunLoopRunSpecific + 476
36 CoreFoundation 0x22697d43 CFRunLoopRunInMode + 106
37 UIKit 0x25e6ec87 + 558
38 UIKit 0x25e69879 UIApplicationMain + 1440
39 MyAppOptm 0x0022f1f4 wrapper_managed_to_native_UIKit_UIApplication_UIApplicationMain_int_string___intptr_intptr + 272
40 MyAppOptm 0x001aa9d4 UIKit_UIApplication_Main_string___intptr_intptr + 52
41 MyAppOptm 0x001aa994 UIKit_UIApplication_Main_string___string_string + 204
42 MyAppOptm 0x000909c0 JR_UI_Touch_Application_Main_string_ + 172
43 MyAppOptm 0x00433fdc wrapper_runtime_invoke_object_runtime_invoke_dynamic_intptr_intptr_intptr_intptr + 256
44 MyAppOptm 0x012533f7 mono_jit_runtime_invoke + 1190
45 MyAppOptm 0x0129fd89 mono_runtime_invoke + 88
46 MyAppOptm 0x012a34c3 mono_runtime_exec_main + 282
47 MyAppOptm 0x012a3305 mono_runtime_run_main + 476
48 MyAppOptm 0x0123d769 mono_jit_exec + 48
49 MyAppOptm 0x0130a0c8 xamarin_main + 2184
50 MyAppOptm 0x0100c269 main + 112
51 libdyld.dylib 0x316b0aaf + 2
)

My Info.plist node for Optimizely is:

        <dict>
            <key>CFBundleURLName</key>
            <string>com.optimizely</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>optlyprojectid</string>
            </array>
            <key>CFBundleURLTypes</key>
            <string>Editor</string>
            <key>CFBundleURLIconFile</key>
            <string>Images.xcassets/AppIcons.appiconset/Icon-Small@2x</string>
        </dict>

where optlyprojectid = optly + projectid (in my api key).

Luigi Saggese
  • 5,229
  • 3
  • 38
  • 89

1 Answers1

4

This may or may not help, but Optimizely does have a basic Xamarin binding - I would be interested to know if this helps or not:

Binding files can be found, here

  1. Place a copy of the Optimizely library file from your SDK into the same directory as the file Optimizely.linkwith.cs
  2. The project should now build in Xamarin and produce a .dll file
  3. Now in the target app project, either add a reference to the above .dll file, or include the binding project in the solution and add a project reference to it.
  4. Add the com.optimizely URL scheme to the plist file (as per xcode project)
  5. In AppDelegate.FinishedLaunching add code similar to this:

 


public override bool FinishedLaunching(UIApplication app, NSDictionary options) { // create a new window instance based on the screen size window = new UIWindow(UIScreen.MainScreen.Bounds);

ABTesting.Preregister();
Optimizely.Optimizely.SharedInstance.VerboseLogging = true;
Optimizely.Optimizely.StartWithAPIToken("***YOURTOKEN***", options);

}

  1. Add an exported property "Window": (not 100% sure if this is needed)

    
    
    
    

    [Export("window")] UIWindow window { get; set; }

  2. In AppDelegate.OpenUrl, this:

    
    public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
    {
        if (Optimizely.Optimizely.HandleOpenUrl(url))
            return true;
    }
    
  3. For ABTesting, C# doesn't support C-style macros as used by the Objective-C Optimizely headers, so I've set up a static class to encapsulate all our A/B variables. Currently we have just one integer variable "onboardingExperience".


public static class ABTesting
    {
        public static void Preregister()
        {
            Optimizely.Optimizely.Preregister(_onboardingExperienceKey);
        }

    public static int OnboardingExperience
    {
        get
        {
            return Optimizely.Optimizely.NumberForKey(_onboardingExperienceKey).IntValue;
        }
    }

    static OptimizelyVariableKey _onboardingExperienceKey = 
        OptimizelyVariableKey.Create("onboardingExperience", NSNumber.FromInt32(0));

}

  1. And then elsewhere we simply reference ABTesting.OnboardingExperience

Notes:

Drawing the Optimizly in-app gesture may crash the app in Debug mode builds due to thread checking performed by the Xamarin libraries that is violated by the Optimizely SDK. (This may only happen with sub-classed UIView classes - eg: for us it crashes in a subclass of UIButton).

Not all the Optimizely SDK is bound. There are features and API's that just need to have the appropriate signatures written into ApiDefinition.cs to support these.

It would be nice to simplify the variable implementation so it's not so verbose/repetitive - perhaps drive it with C# reflection and attributes.

Good luck!

Luigi Saggese
  • 5,229
  • 3
  • 38
  • 89
Joel Balmer
  • 1,171
  • 1
  • 14
  • 19
  • 1
    Hi @Joel Balmer, reported binding probably refers to an old version of Optimizely SDK (it must also be said that the interfaces have not be changed, which is why mine and yours are pretty much identical). What helped me to solve my crash (and that surprised me and it amazes even yourself) is the point 6!! Adding it, my application does not crash! Now I'm checking if Optimizely SDK is working correctly. But this solves problem posted, now I will verify the goodness of my binding. Really thanks! Luigi – Luigi Saggese Sep 04 '15 at 07:25
  • Hi @LuigiSaggese - Mark from JustGiving here. Thanks for helping us test out the Optimizely Binding - we're also working on getting it into our app, currently working through some Code Blocks issues. We will update our binding if we find that anything should change in the api definition etc! Let us know if you find any bugfixes yourself! – Mark Gibaud Sep 29 '15 at 10:10