Objective c binding not loading in xamarin forms ios - ios

I am building my first objective c library binding. I am following this documentation from Microsoft. I have successfully created a fat binary from IronSource.framework and have also generated apidefinition and structs using objective sharpie and added my binary to it.
After doing that I added binding library to my xamarin.ios project. when i try to create a new instance of a class from the exposed API I get an error.
The type or namespace name 'IronSource' could not be found (are you missing a using directive or an assembly reference?) Stock.iOS D:\Backup Version for Stock Adviser\Version 1.6\Version code 37\StockAdviserCode\Stock\Stock\Stock.iOS\AdControlViewRenderer.cs
'AdControlViewRenderer.BannerWrapper' does not implement inherited
abstract member
'ISBannerDelegate.DidClickBanner()' Stock.iOS D:\Backup Version for
Stock Adviser\Version 1.6\Version code
37\StockAdviserCode\Stock\Stock\Stock.iOS\AdControlViewRenderer.cs
I think my objective c library and xamarin.ios project are not linking properly
My Binding project name is IronSource and my xamarin forms ios project name is Stock.iOS
//APIDefinition
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace IronSource
{
// #interface ISBannerView : UIView
[BaseType(typeof(UIView))]
interface ISBannerView
{
}
// #protocol ISBannerDelegate <NSObject>
[BaseType(typeof(NSObject))]
[Model]
interface ISBannerDelegate
{
// #required -(void)bannerDidLoad:(ISBannerView *)bannerView;
[Abstract]
[Export("bannerDidLoad:")]
void BannerDidLoad(ISBannerView bannerView);
// #required -(void)bannerDidFailToLoadWithError:(NSError *)error;
[Abstract]
[Export("bannerDidFailToLoadWithError:")]
void BannerDidFailToLoadWithError(NSError error);
// #required -(void)didClickBanner;
[Abstract]
[Export("didClickBanner")]
void DidClickBanner();
// #required -(void)bannerWillPresentScreen;
[Abstract]
[Export("bannerWillPresentScreen")]
void BannerWillPresentScreen();
// #required -(void)bannerDidDismissScreen;
[Abstract]
[Export("bannerDidDismissScreen")]
void BannerDidDismissScreen();
// #required -(void)bannerWillLeaveApplication;
[Abstract]
[Export("bannerWillLeaveApplication")]
void BannerWillLeaveApplication();
}
}
//my xamarin.ios
using System;
using CoreGraphics;
using Foundation;
using Stock.iOS;
using Stock.Services;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using IronSource;
[assembly: ExportRenderer(typeof(AdControlView), typeof(AdControlViewRenderer))]
namespace Stock.iOS
{
public class AdControlViewRenderer : ViewRenderer
{
public AdControlViewRenderer()
{ }
protected AdControlView AdViewControl => (AdControlView)Element;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
return;
IronSource ironSource = new IronSource();
SetNativeControl();
}
private void SetNativeControl()
{
try
{
BannerWrapper bannerWrapper = new BannerWrapper(ViewController);
IronSource.SetBannerDelegate(bannerWrapper);
var bannerSize = new ISBannerSize("BANNER");
IronSource.LoadBannerWithViewController(ViewController, bannerSize);
var adview = bannerWrapper.BannerView();
SetNativeControl(adview);
}
catch (Exception ex)
{
}
}
}
public class BannerWrapper : ISBannerDelegate
{
readonly UIViewController parent;
ISBannerView bannerView = null;
public bool DestroyBanner()
{
if (bannerView != null)
{
IronSource.DestroyBanner(bannerView);
bannerView = null;
return true;
}
return false;
}
public BannerWrapper(UIViewController viewController)
{
this.parent = viewController;
}
public override void BannerDidClick()
{
}
public override void BannerDidDismissScreen()
{
}
public override void BannerDidFailToLoadWithError(NSError error)
{
}
public ISBannerView BannerView()
{
ISBannerView bannerView = new ISBannerView();
nfloat y = this.parent.View.Frame.Size.Height - (bannerView.Frame.Size.Height / 2);
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
y -= this.parent.View.SafeAreaInsets.Bottom;
}
bannerView.Center = new CGPoint(this.parent.View.Frame.Size.Width / 2, y);
return bannerView;
}
public override void BannerDidLoad(ISBannerView bnView)
{
InvokeOnMainThread(() =>
{
bannerView = bnView;
nfloat y = this.parent.View.Frame.Size.Height - (bannerView.Frame.Size.Height / 2);
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
y -= this.parent.View.SafeAreaInsets.Bottom;
}
bannerView.Center = new CGPoint(this.parent.View.Frame.Size.Width / 2, y);
this.parent.View.AddSubview(bannerView);
bannerView.AccessibilityLabel = "bannerContainer";
});
}
public override void BannerWillLeaveApplication()
{
}
public override void BannerWillPresentScreen()
{
}
}
}
Any help is welcome

So, I was able to make it work by adding fat binary to Xamarin.ios and changed the build type from "Do not copy" to "Copy Always" and then added a linker to that fat binary using
-cxx -gcc_flags "-L${ProjectDir} -lIronSource -force_load ${ProjectDir}/IronSource.a
I used this GitHub repo for guidance.
Overall to setup IronSoruce, I had to add dll in references, native framework to native reference, fat binary to projectdir and add linker to fat binary.

Related

Unity app crashes when built for iOS with camera enabled

I have an app which uses zxing to scan qr codes in the app. However when I build the app with these scripts in the scene the app crashes on startup. I thought it was something in the Awake() or Start() but I've wrapped those methods in a try catch, and even then I'm not getting any errors, and it doesn't crash on android and in the editor.
I don't have access to a Mac, and am using Unity Cloud Build to build it.
I also don't know how to enable permissions, I thought I did when creating the .p12 file, but I've also found that there's an info.plist file that I have to request permissions with.
Prior research I found this Unity Question about adding items to the Xcode project but not only did including the xcodeapi give me errors, but the using statements didn't work.
There are two scripts
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System;
public class WebCamController : MonoBehaviour {
public int desiredWidth = 1280;
public int desiredHeight = 720;
public int desiredFPS = 60;
public RawImage output;
[HideInInspector]
public WebCamTexture webcamTexture;
void Start ()
{
webcamTexture = new WebCamTexture(desiredWidth, desiredHeight, desiredFPS);
output.texture = webcamTexture;
Play();
}
public void Play()
{
webcamTexture.Play();
}
public void Pause()
{
webcamTexture.Stop();
}
}
and
using UnityEngine;
using System.Collections;
using ZXing;
using ZXing.QrCode;
using ZXing.Common;
using System;
public class CodeScanner : MonoBehaviour {
private static CodeScanner _instance;
public static CodeScanner Instance
{
get
{
if(null == _instance)
{
Debug.Log("Code Scanner Instance not found");
}
return _instance;
}
}
[Header("References")]
public WebCamController wcc;
[Header("Properties")]
private BarcodeReader codeScanner;
private string lastScanned = "";
public delegate void Found(string text, string type);
public event Found OnCodeScanned;
private bool active;
public void Awake()
{
_instance = this;
}
void Start () {
codeScanner = new BarcodeReader();
StartCoroutine(ReadCode());
wcc.Play();
}
IEnumerator ReadCode()
{
while (active)
{
try
{
var data = codeScanner.Decode(wcc.webcamTexture.GetPixels32(), wcc.webcamTexture.width, wcc.webcamTexture.height);
if (data != null)
{
//if (data.Text != lastScanned)
//{
OnCodeScanned(data.Text, data.BarcodeFormat.ToString());
//}
lastScanned = data.Text;
}
}
catch(Exception e)
{
}
yield return new WaitForSeconds(1.0f);
}
}
public void Activate()
{
wcc.Play();
active = true;
StartCoroutine(ReadCode());
}
public void Stop()
{
active = false;
wcc.Pause();
}
}
My device is added properly to the .p12 certificate I can compile and run the program without these scripts in the scene.

Xamarin.Android webview does not open the file, but file upload works fine (need C# code)

I have WebView which works fine for file upload but when I click on files to open or download, nothing happens. but in normal browser when I click on file it is opened successfully. the intention of code is to open the file when it is clicked. file choose chrome extension is fine. I think there is need to add some code in WebViewListner block.
The activity code is here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Net;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Webkit;
using Android.Widget;
namespace smartbookapp
{
[Activity(Label = "JobActivity")]
public class JobActivity : Activity
{
public WebView webview;
public IValueCallback mUploadMessage;
public static ProgressBar progressBar;
public static int FILECHOOSER_RESULTCODE = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Jobs);
webview = FindViewById<WebView>(Resource.Id.JobView);
// show progress bar
progressBar = FindViewById<ProgressBar>(Resource.Id.progressBar);
//
webview.Settings.JavaScriptEnabled = true;
webview.Settings.SetAppCacheEnabled(true);
webview.Settings.AllowFileAccess = true;
webview.Settings.BuiltInZoomControls = true;
webview.SetWebViewClient(new WebViewListener());
webview.SetWebChromeClient(new JobWebChromeClient(this));
webview.LoadUrl("https://smartbook.pk/Jobs/index");
//
}
//
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
if (requestCode == FILECHOOSER_RESULTCODE)
{
if (null == mUploadMessage) return;
Android.Net.Uri[] result = data == null || resultCode != Result.Ok ? null : new Android.Net.Uri[] { data.Data };
try
{
mUploadMessage.OnReceiveValue(result);
}
#pragma warning disable CS0168 // Variable is declared but never used
catch (Exception e)
#pragma warning restore CS0168 // Variable is declared but never used
{
}
mUploadMessage = null;
}
base.OnActivityResult(requestCode, resultCode, data);
}
// webview listener code here
public class WebViewListener : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, IWebResourceRequest request)
{
view.LoadUrl(request.Url.ToString());
return true;
}
public override void OnPageStarted(WebView view, string url, Android.Graphics.Bitmap favicon)
{
progressBar.Progress = view.Progress;
}
public override void OnLoadResource(WebView view, string url)
{
progressBar.Progress = view.Progress;
}
public override void OnPageFinished(WebView view, string url)
{
progressBar.Progress = 0;
}
}
public override bool OnKeyDown(Keycode keyCode, KeyEvent e)
{
if (keyCode == Keycode.Back && webview.CanGoBack())
{
webview.GoBack();
return true;
}
return base.OnKeyDown(keyCode, e);
}
}
// download files from webview
public class JobWebChromeClient : WebChromeClient
{
JobActivity WebViewActivity;
public JobWebChromeClient(JobActivity activity)
{
WebViewActivity = activity;
}
public override bool OnShowFileChooser(WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams)
{
WebViewActivity.mUploadMessage = filePathCallback;
Intent i = new Intent(Intent.ActionGetContent);
i.AddCategory(Intent.CategoryOpenable);
i.SetType("*/*");
WebViewActivity.StartActivityForResult(Intent.CreateChooser(i, "File Chooser"), JobActivity.FILECHOOSER_RESULTCODE);
return true;
}
}
}
First of all, make sure your WebView has enabled javascript and the WebViewClient is set correctly.
WebView mWebview = FindViewById<WebView>(Resource.Id.webView1);
mWebview.Download += MWebview_Download;
var client = new WebViewClient();
mWebview.Settings.JavaScriptEnabled = true;
mWebview.SetWebViewClient(client);
mWebview.LoadUrl("your url");
Then, we shouldd achieve WebView.Download event(use DownloadManager to download the file)
private void MWebview_Download(object sender, DownloadEventArgs e)
{
var url = e.Url;
DownloadManager.Request request = new
DownloadManager.Request(Uri.Parse(url));
request.AllowScanningByMediaScanner();
request.SetNotificationVisibility(DownloadManager.Request.VisibilityVisibleNotifyCompleted); //Notify client once download is completed!
request.SetDestinationInExternalPublicDir(Environment.DirectoryDownloads, "CPPPrimer");
DownloadManager dm = (DownloadManager)GetSystemService("download");
dm.Enqueue(request);
Toast.MakeText(ApplicationContext, "Downloading File",ToastLength.Long//To notify the Client that the file is being downloaded
).Show();
}

How to pass arguments from iOS (Objc/Swift) to Unity that are structs/classes? (without using UnitySendMessage)

I am trying to find a good way to pass parameters from my native unity plugin in objc/swift to C# unity.
It seems that I will need to use Marshall but I couldn't find a good example of this anywhere.
The only thing I did found is the UnitySendMessage, but that passes strings only as parameters, and even those are limited to 1024 bytes, which is not enough for a JSON representation of the objects, and handling multiple messages seems a bit of an overkill for this.
The idea is to be able to question the plugin for object detection from a MTLTexture and return the objects that are recognised.
Code samples:
SwiftBridge
import Foundation
import UIKit
import Vision
#objc public class SwiftBridge: NSObject {
var delegate: DelegateCallbackFunction?
#objc static let shared = SwiftBridge()
#objc func evaluate(texture: MTLTexture) -> Bool {
guard let delegate = self.delegate else {
return false
}
let rect = CGRect(x: 1, y: 2, width: 100, height: 200)
delegate(rect)
return true
}
#objc func setDelegate(callback: #escaping DelegateCallbackFunction) -> Bool {
self.delegate = callback
return true
}
}
Unity
using System;
using UnityEngine;
using System.Runtime.InteropServices;
using AOT;
[StructLayout(LayoutKind.Sequential)]
public struct CGPoint {
public float x;
public float y;
};
[StructLayout(LayoutKind.Sequential)]
public struct CGSize {
public float width;
public float height;
};
[StructLayout(LayoutKind.Sequential)]
public struct CGRect {
public CGPoint origin;
public CGSize size;
}
public class UnityBridge : MonoBehaviour {
#region Declare external C interface
// #if UNITY_IOS && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern int _vision_detectObjectsIn(IntPtr texture);
delegate bool ObjectDetectedCallback(ref CGRect rect);
[DllImport("__Internal")]
private static extern void _vision_setDelegate(ObjectDetectedCallback callback);
[MonoPInvokeCallback(typeof(ObjectDetectedCallback))]
private static bool delegateMessageReceived(ref CGRect rect) {
Debug.Log("Message received: " + rect.origin.x);
return true;
}
// #endif
#endregion
public void initializeDelegate() {
if (Application.platform == RuntimePlatform.IPhonePlayer) {
_vision_setDelegate(delegateMessageReceived);
}
}
#region Wrapped methods and properties
public void EvaluateTexture(IntPtr texture) {
initializeDelegate();
if (texture == IntPtr.Zero) {
Debug.LogError("[Texture] Pointer to buffer is null.");
return;
}
bool success;
#if UNITY_IOS && !UNITY_EDITOR
_vision_detectObjectsIn(texture);
#endif
}
#endregion
#region Singleton implementation
private static WeRDetectorUnity _instance;
public static WeRDetectorUnity Instance {
get {
if (_instance == null) {
var obj = new GameObject("WeRDetectorUnity");
_instance = obj.AddComponent<WeRDetectorUnity>();
}
return _instance;
}
}
void Awake() {
if (_instance != null) {
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
#endregion
}
The message receive print in Unity does not return a 1 as it should but rather a strange exponent small number.
Any idea??
Blockquote
You need to create struct for pass parameter its you can use parameters on multiple time and also access in main class.
struct Location {
let latitude:"latitude"
let longitude: "longitude"
}

NSItemProviderReading on Xamarin

To do files drag & drop for files in iOS 11 you need to implement NSItemProviderReading, there is the sample code for swift here: iOS 11 dropInteraction performDrop for files
However, how to do that in Xamarin, I guess the class definition should look like this, but how to implement the methods?
public class DocumentProvider : NSObject, INSItemProviderReading
{
}
You can implement the NSItemProviderReading like this:
class MyItemProvider : UIView, INSItemProviderReading {
[Export ("readableTypeIdentifiersForItemProvider")]
public static string [] ReadableTypeIdentifiersForItemProvider => new string [] { "public.image", "public.data" }
[Export ("objectWithItemProviderData:typeIdentifier:error:")]
public static MyItemProvider GetObject (NSData data, string typeIdentifier, out NSError outError)
{
outError = null;
switch (typeIdentifier) {
case "public.image": return new ...;
case "public.data": return new ...;
default:
outError = new NSError (...);
return null;
}
}
}
Reference:NSItemProviderReading requirements.

MvvmLight unable to create a controller for key

I am designing a cross platform application architecture using Xamarin iOS and Xamarin Android I decided to go with MvvmLight, it looks descent and is not hiding everything from the MVVM pattern, good and flexible.
While everything started to make sense trying to set it up and learn how to use it, I find myself difficult to understand why I get the following error.
Unable to create a controller for key ChartsPage
The setup.
In a PCL I have my ViewModels. I have a ViewModelLocator setup. I use the mvvmlightlibs Nuget Package.
public class ViewModelLocator
{
public static readonly string SchedulerPageKey = #"SchedulerPage";
public static readonly string ChartsPageKey = #"ChartsPage";
[SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public SchedulerViewModel Scheduler
{
get
{
return ServiceLocator.Current.GetInstance<SchedulerViewModel>();
}
}
public BizchartsViewModel Bizcharts
{
get
{
return ServiceLocator.Current.GetInstance<BizchartsViewModel>();
}
}
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
// Haven't declared something yet
}
else
{
// Haven't declared something yet
}
SimpleIoc.Default.Register<SchedulerViewModel>();
SimpleIoc.Default.Register<BizchartsViewModel>();
}
}
The I have a unified iOS application using universal storyboard with size classes which has an initial UINavigationViewController SchedulerViewController and in the ViewDidLoad method I test the navigation to BizchartsViewController with 3 seconds delay. After 3 seconds I get the exceptions.
In the AppDelegate.
private static ViewModelLocator _locator;
public static ViewModelLocator Locator
{
get
{
if (_locator == null)
{
SimpleIoc.Default.Register<IDialogService, DialogService>();
_locator = new ViewModelLocator();
}
return _locator;
}
}
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
var nav = new NavigationService();
nav.Initialize((UINavigationController)Window.RootViewController);
nav.Configure(ViewModelLocator.ChartsPageKey, typeof(BizchartsViewController));
SimpleIoc.Default.Register<INavigationService>(() => nav);
return true;
}
The SchedulerViewController.
partial class SchedulerViewController : UIViewController
{
public SchedulerViewModel Vm {
get;
private set;
}
public SchedulerViewController (IntPtr handle) : base (handle)
{
Vm = AppDelegate.Locator.Scheduler;
}
public async override void ViewDidLoad ()
{
base.ViewDidLoad ();
await Task.Delay (3000);
Vm.NavigateToCharts ();
}
}
The SchedulerViewModel.
public class SchedulerViewModel : ViewModelBase
{
public void NavigateToCharts()
{
var nav = ServiceLocator.Current.GetInstance<INavigationService>();
nav.NavigateTo(ViewModelLocator.ChartsPageKey);
}
}
I definitely miss a detail somewhere!!!
If you follow carefully the blog post here, it says that with Storyboard you should use the string overload and not the typeof() in nav.Configure(Key, ViewController) and always set the storyboardId and restorationId in the Storyboard ViewController.
Note that because we are using a Storyboard, you must make sure to use
the Configure(string, string) overload, and NOT the Configure(string,
Type) one.

Resources