Search code examples
c#xamarinxamarin.formsxamarin.iosapple-watch

How can I debug a Xamarin watchOS Linker Error


I am developing an iOS App using Xamarin Forms for which I have created a core model around which all app functionality works.

I would like to include a simple watchOS app which allows the user to operate on a single instance of this model at any one time. I have implemented some code to update the model in the watchOS App using WCSession (via this WCSessionManager Class). I have also reused some code for implementing a timer from my Xamarin Forms project.

However I am encountering a Linker error when building my solution. I think it may be because I have referenced my Xamarin Forms project from my watchOS project, which may not be allowed. Here is the error:

/Users/luketimothy/Projects/TodoQ/TodoQ.Watch/TodoQ.Watch.WatchOSExtension/MTOUCH: Error MT2001: Could not link assemblies. Reason: Error while processing references of 'TodoQWatchWatchOSExtension, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' (MT2001) (TodoQ.Watch.WatchOSExtension)

The file the error references is MTOUCH. I am not sure what this is exactly, but the only place in my watchOS app I have referenced my Xamarin Forms code is this object:

    using System;
    using System.Collections.Generic;
    using TodoQ.Models;
    using TodoQ.Utilities;
    using WatchConnectivity;
    using WatchKit;

    namespace TodoQ.Watch.WatchOSExtension
    {
        internal class TodoState
        {
            private TodoItem current;
            private ISessionTimer timer;

            public TodoItem Current { get => current; set { current = value; TaskUpdated(this, value); } }

            public event TaskUpdatedEventHandler TaskUpdated;
            public delegate void TaskUpdatedEventHandler(object sender, TodoItem current);

            public event TimerElapsedEventHandler TimerElapsed;
            public delegate void TimerElapsedEventHandler(object sender, TimerElapsedEventArgs current);

            public TodoState()
            {
                WCSessionManager.SharedManager.ApplicationContextUpdated += DidReceiveApplicationContext;

                timer = new PomodoroTimer();

                timer.ProgressUpdate += (object sender, ProgressUpdateEventArgs e) => 
                {
                    TimerElapsed(this, new TimerElapsedEventArgs() { Elapsed = e.Elapsed, EndTime = e.EndTime });
                };

                timer.MilestoneUpdate += (object sender, PomodoroStateID e) =>
                {
                    var audio_file = WKAudioFilePlayerItem.Create(WKAudioFileAsset.Create(new Foundation.NSUrl("ShortBreak.wav")));
                    var audio_player = WKAudioFilePlayer.Create(audio_file);
                    audio_player.Play();
                    WKInterfaceDevice.CurrentDevice.PlayHaptic(WKHapticType.Notification);
                };
            }

            public void DidReceiveApplicationContext(WCSession session, Dictionary<string, object> applicationContext)
            {
                var message = (TodoItem)applicationContext["FocusedItem"];
                if (message != null)
                {
                    Console.WriteLine($"Application context update received : {message.Heading}");
                    Current = message;
                }
            }

            public void StartTimer()
            {
                timer.StartSession();
            }
        }

        public class TimerElapsedEventArgs
        {
            public TimeSpan Elapsed;
            public TimeSpan EndTime;
        }
    }

So, my question is. If this ought to be allowed, and the error is something else, could I get some help tracking down what this MTOUCH is and why it's throwing this error? If it is not allowed, what is the recommended solution for sharing this kind of code between my Phone App and my Watch App? Could I put it in a PCL? Should I copy the code between projects?


Solution

  • You should not reference your WatchOS project to the Forms project. It should be added in iOS project directly.

    And if you want to define some common code for reuse. You could create a shared library: enter image description here

    Add some public classes there:

    namespace UtiLibrary
    {
        public static class UtiClass
        {
            public static List<Model> datas { get => new List<Model> { new Model { Name = "name" } }; }
        }
    
        public class Model
        {
            public string Name { set; get; }
        }
    }
    

    Then you could utilize it on each platform which has referenced this library.