Environment.System.Environment.SpecialFolder.MyDocuments Different in Xamarin.UITest than in app - xamarin

I have a Xamarin Forms Project That targets iOS. I am trying to write Xamarin.UITests that depend on existence of certain files inside the /Documents folder on the device
In trying to read/write the files to the correct Folder here is what I found.
string documentBasePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
Results From actual app :
"/Users/markwardell/Library/Developer/CoreSimulator/Devices/147FD387-FCAD-4E93-BFC7-4BC1572FF7D4/data/Containers/Data/Application/13E0B91B-8C70-4139-B570-7431DDF5B5CA/Documents"
Results From Xamarin.UITest :
"/Users/markwardell/”
Clearly I need a way of getting same result as 1..
How can I get the folder results same as app in the UITest?

The UITest project does not have access to the Xamarin.iOS SDK which is the SDK that is giving you the path from the actual app when deployed to a device or simulator. IOW, the System namespace in Xamarin.iOS's version of .NET/Mono implements some things differently depending on the platform, as is necessary in this case since the documents path is different on iOS than it is on Android, than it is on Windows, etc. So this is why the paths are different.
That said, you can get around this by using a backdoor method. See:
https://learn.microsoft.com/en-us/appcenter/test-cloud/uitest/working-with-backdoors
This allows you to call a method implemented in the iOS project itself, thereby using Xamarin.iOS SDK in that method.
You implement the backdoor method in your AppDelegate class in your iOS app project like so:
[Export("getMyDocumentsPath:")] // notice the colon at the end of the method name
public NSString GetMyDocumentsPath(NSString value)
{
// In through the backdoor - do some work.
return new NSString(System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments));
}
Then call it from your UI Test project:
var path = app.Invoke("getMyDocumentsPath:", "").ToString();
Worth noting from the linked doc (in case it ever goes away):
On iOS, IApp.Invoke can call a C# method on the project's AppDelegate according to the following rules:
The method must be public.
The method must be adorned with the ExportAttribute and the name of the exposed C# method identified. The exposed name must append a : (colon) to the name. IApp.Invoke must use the iOS form of the method name.
The method must take a parameter of NSString.
The method must return NSString or void.

Related

Reverse Dependency Service

I have a Xamarin Forms Project. I have used Dependency Service to call the Platform specific method to fire a local notification on Android and a Alert on iOS.
My problem is now, how to handle an action on both notification(android) and alert(iOS). Is it possible to call a method of the shared project from the android or iOS project?
Do I need another approach? Does someone know what I have to do?
Just for clarification, I know how the ordinary dependency services works i.e. Call a method on android or iOS from the shared project!
I believe I had a similar scenario on my app. If you want to push something from your platform methods to your PCL you would need to make use of call backs and event delegates.
In Interface PCL:
event OnMessageHandlerCallback OnMessageEvent;
event OnErrorHandlerCallBack OnErrorEvent;
In Platform specific Class inheriting interface:
private OnMessageHandlerCallback callback = null;
private OnErrorHandlerCallBack errorCallBack = null;
public event OnMessageHandlerCallback OnMessageEvent;
public event OnErrorHandlerCallBack OnErrorEvent;
Have you tried just calling the method from your native project...? No special patterns necessary. The native iOS and Android projects have a direct reference to your shared project, so it can call the method directly.

SwiftAutomation custom record compiler error

I'm using the SwiftAutomation framework to drive a scriptable app that searches for lyrics and returns a AS record. Everything was working correctly, until...
I mapped the AppleScript record to a custom Swift structure according to the SwiftAutomation documentation. The code in the xxxGlue.swift file looks correct, but the compiler complains about SwiftAutomation.SelfUnpacking, with several follow-on errors, when building the MacOSGlues framework.
public struct LFBLyricsInfoRecord: SwiftAutomation.SelfPacking, SwiftAutomation.SelfUnpacking { ... }
--> .../MacOSGlues/LyricsFBAGlue.swift:700:81: No type named 'SelfUnpacking' in module 'SwiftAutomation'
The SelfPacking public protocol is defined in SwiftAutomation, and SelfUnpacking protocol is defined right under it, but without the public keyword. Is that the cause of the compiler error, and if so, how do I fix it?
OK, I finally found a resolution. Seems you have to use different options for the aeglue utility when generating the glue file for the MacOSGlues framework and for the swift file where you actually use your automation, such as in the test project. In my case, where my scriptable app is named LyricsFBA.app, these were:
aeglue -S LyricsFBA.app
for MacOSGlues (generates a LyricsFBAGlue.swift that references SwiftAutomation, but does not include the custom record structure definition), and
aeglue -D -s 'LyricsInfo:lyricsInfo=score:Int+title:String+artist:String+composer:String+link:String+lyrics:String' LyricsFBA.app
for the test program (generatea a LyricsFBAGlue.swift that does not reference SwiftAutomation, but does include the custom record structure definition).

Exception while running ibtool: Cannot find value transformer with name

I just upgraded Xcode to 8.0 (8A218a) and am converting my project in Swift 2.3 to Swift 3.0. The only issue left now is this error:
"Exception while running ibtool: Cannot find value transformer with
name UTIToIconTransformer"
The UTIToIconTransformer is defined something like:
#objc(UTIToIconTransformer) class UTIToIconTransformer : ValueTransformer {
// ...
}
The code worked fine when it was in Swift 2.3. The binding using this value transformer is set like this:
If I remove this binding, the app runs, and the row titles are shown correctly.
I have tried calling NSValueTransformer.setValueTransformer() in the app delegate's +initialize(), in applicationDidFinishLaunching and in the value transformer's +initialize(), as suggested here, here at StackOverflow and here at NShipster (Though I don't think the statement of "Typically, the singleton instance would be registered in the +initialize method of the value transformer subclass, so it could be used without further setup." complies with the Apple's doc.), all without success.
In the Apple's doc, it says
Value transformers are typically registered by an application’s delegate
class, in response to receiving a initialize: class message. This allows
registration to occur early in the application startup process, providing
access to the value transformers as nib files load.
Availability in Interface Builder
Your NSValueTransformer subclasses are not automatically listed in the
Interface Builder bindings inspector. When inspecting a binding you can enter
the name that the value transformer is registered with, but the functionality
will not be present in Interface Builder’s test mode. When your application
is compiled and run the transformer will be used.
But registering in the AppDelegate's override class func initialize() didn't help. In Xcode 7 and Swift 2.3, it even worked without the registration.
Finally I solved the problem by removing the NSOutlineView from the storyboard and setting up a new one.
I have another project which also has an outlineview binded with an NSTreeController, and that project has no problem after the Xcode 8.0 upgrade. Then I tried creating a new ValueTransformer with a new name, with no luck.
I guess there may be something wrong with the storyboard, so I tried recreating the outline view. Then Xcode doesn't complain that it can't find the transformers!

Do I need to recreate bindings, if the native framework changes?

I made an implementation change to one of the methods in the native framework. Would I need to recreate bindings, in this case ?
Short answer: likely not
Long answer: Depends, you would need to rebuild the binding only if this change you mention is in the public API signature of the method / property. This is because the bindings matches 1 : 1 (most of the time) what the native API surfaces so for example if your method used to return a NSString and now it returns another class or the selector name changes or the type of any of the parameters changes then yes.
You also would need to rebuild the binding if the binding dll bundles the native library you are using. If you are manually linking the native library (using the additional touch args in your app project) you should be fine.

Calling Xamarin.Android Interop from Xamarin.UITest

I've created a method in the first Activity of my app which I have added the Java.Interop.Export tag to, as below.
[Export]
public string MyInvokeMethod(string myString)
{
_webview.LoadUrl("http://www.google.co.uk");
return "test";
}
And I'm trying to call this method in Xamarin.UITest with the below.
app.Invoke("MyInvokeMethod","test");
However, nothing seems to happen. I looked into the logs of the Android device and it seems as though it is trying to call the method but it gets below error.
"Method not found with correct argument types. Trying to type convert."
Is there a way to get this to work? I have tried with passing an argument, without passing an argument, adding a name for the method in the attribute but nothing has worked.
EDIT
After speaking to one of the Xamarin developers they believe it might be an issue with how MvvmCross handles the Android Activity.
I think you should add
[Export("myInvokeMethod:")]
on top of your method, instead of only add [Export]
Someone using xamarin uitests might find this useful too: https://forums.xamarin.com/discussion/57662/xamarin-forms-uitest

Resources