We needed a solution to connect to an external high accuracy GPS from an iOS application being developed in Xamarin Forms. As it was being done through Collector I knew there there had to be a solution out there. But I was having a hard time figuring out how the devices (non-BLE bluetooth device) connected to iOS. I finally started to find some information on Stack Overflow that pointed me towards using EAAccessoryManager.SharedAccessoryManager to get the connected devices. Some other searching got me to this post How to use external gps location data which went into detail on an iOS specific solution.
In order to do in Forms and then use from a .Net Standard library a little more plumbing is required than the straight iOS solution. First an interface is needed that will be developed in the main shared assembly. I kept this is simple, and also thought in terms of a pure iOS implementation and not a pluigin that could be used cross platform
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">interface</SPAN> <SPAN class="token class-name">IAccessorySessionController</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">event</SPAN> EventHandler<SPAN class="operator token"><</SPAN>NmeaSentenceEventArgs<SPAN class="operator token">></SPAN> OnNmeaSentenceReceived<SPAN class="punctuation token">;</SPAN>
IEnumerable<SPAN class="operator token"><</SPAN><SPAN class="keyword token">string</SPAN><SPAN class="operator token">></SPAN> AvailableAccessories <SPAN class="punctuation token">{</SPAN> <SPAN class="keyword token">get</SPAN><SPAN class="punctuation token">;</SPAN> <SPAN class="punctuation token">}</SPAN>
Task <SPAN class="token function">OpenSession</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">string</SPAN> protocal<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">bool</SPAN> <SPAN class="token function">CloseSession</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>This interface was then implemented in the iOS project.
The main piece is the OpenSession method. This is where the one connects to the device and starts getting data.
<SPAN class="keyword token">public</SPAN> Task <SPAN class="token function">OpenSession</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">string</SPAN> protocal<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">try</SPAN>
<SPAN class="punctuation token">{</SPAN>
EAAccessory connectedAccessory <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">null</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">/* Get list of ConnectedAccessories
//These must be defined as UISupportedExternalAccessoryProtocols in Info.plist */</SPAN>
<SPAN class="keyword token">foreach</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">var</SPAN> accessory <SPAN class="keyword token">in</SPAN> EAAccessoryManager<SPAN class="punctuation token">.</SPAN>SharedAccessoryManager<SPAN class="punctuation token">.</SPAN>ConnectedAccessories<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN>accessory<SPAN class="punctuation token">.</SPAN>ProtocolStrings<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Contains</SPAN><SPAN class="punctuation token">(</SPAN>protocal<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
connectedAccessory <SPAN class="operator token">=</SPAN> accessory<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN> connectedAccessory <SPAN class="operator token">==</SPAN> <SPAN class="keyword token">null</SPAN> <SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">return</SPAN> Task<SPAN class="punctuation token">.</SPAN><SPAN class="token function">FromResult</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">false</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
connectedAccessory<SPAN class="punctuation token">.</SPAN>Disconnected <SPAN class="operator token">+</SPAN><SPAN class="operator token">=</SPAN> ConnectedAccessoryOnDisconnected<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN> _session <SPAN class="operator token">!=</SPAN> <SPAN class="keyword token">null</SPAN> <SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">return</SPAN> Task<SPAN class="punctuation token">.</SPAN><SPAN class="token function">FromResult</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">true</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">try</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="comment token">//protacal here also must be UISupportedExternalAccessoryProtocols</SPAN>
_session <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">EASession</SPAN><SPAN class="punctuation token">(</SPAN>connectedAccessory<SPAN class="punctuation token">,</SPAN> protocal<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">catch</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="token class-name">Exception</SPAN> e<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN>e<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">return</SPAN> Task<SPAN class="punctuation token">.</SPAN><SPAN class="token function">FromResult</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">false</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="comment token">//Set delegate for callback</SPAN>
_session<SPAN class="punctuation token">.</SPAN>InputStream<SPAN class="punctuation token">.</SPAN>Delegate <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">;</SPAN>
_session<SPAN class="punctuation token">.</SPAN>InputStream<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Schedule</SPAN><SPAN class="punctuation token">(</SPAN>NSRunLoop<SPAN class="punctuation token">.</SPAN>Current<SPAN class="punctuation token">,</SPAN> NSRunLoopMode<SPAN class="punctuation token">.</SPAN>Default<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
_session<SPAN class="punctuation token">.</SPAN>InputStream<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Open</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN> <SPAN class="comment token">//start receiving data</SPAN>
<SPAN class="comment token">//not writing in this case but still best to open</SPAN>
_session<SPAN class="punctuation token">.</SPAN>OutputStream<SPAN class="punctuation token">.</SPAN>Delegate <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">;</SPAN>
_session<SPAN class="punctuation token">.</SPAN>OutputStream<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Schedule</SPAN><SPAN class="punctuation token">(</SPAN>NSRunLoop<SPAN class="punctuation token">.</SPAN>Current<SPAN class="punctuation token">,</SPAN> NSRunLoopMode<SPAN class="punctuation token">.</SPAN>Default<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
_session<SPAN class="punctuation token">.</SPAN>OutputStream<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Open</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">return</SPAN> Task<SPAN class="punctuation token">.</SPAN><SPAN class="token function">FromResult</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">true</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">catch</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="token class-name">Exception</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">return</SPAN> Task<SPAN class="punctuation token">.</SPAN><SPAN class="token function">FromResult</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">false</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>Not being an iOS developer I didn't really understand the part about needing the protocols in info.plist and spent a good amount of time banging my head on the table as to why I was not seeing the devices I knew were connected
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>key</SPAN><SPAN class="punctuation token">></SPAN></SPAN>UISupportedExternalAccessoryProtocols<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>key</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>array</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>string</SPAN><SPAN class="punctuation token">></SPAN></SPAN>com.comany.device<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>string</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>array</SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
Then it's just a matter of handling the callback and parsing the data...
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">override</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">HandleEvent</SPAN><SPAN class="punctuation token">(</SPAN>NSStream theStream<SPAN class="punctuation token">,</SPAN> NSStreamEvent streamEvent<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"Arrived HandleEvent"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">switch</SPAN> <SPAN class="punctuation token">(</SPAN>streamEvent<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">case</SPAN> NSStreamEvent<SPAN class="punctuation token">.</SPAN>None<SPAN class="punctuation token">:</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"StreamEventNone"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> NSStreamEvent<SPAN class="punctuation token">.</SPAN>HasBytesAvailable<SPAN class="punctuation token">:</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"StreamEventHasBytesAvailable"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="token function">ReadData</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> NSStreamEvent<SPAN class="punctuation token">.</SPAN>HasSpaceAvailable<SPAN class="punctuation token">:</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"StreamEventHasSpaceAvailable"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// Do write operations to the device here</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> NSStreamEvent<SPAN class="punctuation token">.</SPAN>OpenCompleted<SPAN class="punctuation token">:</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"StreamEventOpenCompleted"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> NSStreamEvent<SPAN class="punctuation token">.</SPAN>ErrorOccurred<SPAN class="punctuation token">:</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"StreamEventErroOccurred"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> NSStreamEvent<SPAN class="punctuation token">.</SPAN>EndEncountered<SPAN class="punctuation token">:</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"StreamEventEndEncountered"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">default</SPAN><SPAN class="punctuation token">:</SPAN>
Console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">WriteLine</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"Stream present but no event"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">private</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">ReadData</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">uint</SPAN> size <SPAN class="operator token">=</SPAN> <SPAN class="number token">128</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">byte</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="punctuation token">]</SPAN> bytesReceived <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">byte</SPAN><SPAN class="punctuation token">[</SPAN>size<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">;</SPAN>
_result <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">string</SPAN><SPAN class="punctuation token">.</SPAN>Empty<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">while</SPAN> <SPAN class="punctuation token">(</SPAN> _session<SPAN class="punctuation token">.</SPAN>InputStream<SPAN class="punctuation token">.</SPAN><SPAN class="token function">HasBytesAvailable</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">var</SPAN> bytes <SPAN class="operator token">=</SPAN> _session<SPAN class="punctuation token">.</SPAN>InputStream<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Read</SPAN><SPAN class="punctuation token">(</SPAN>bytesReceived<SPAN class="punctuation token">,</SPAN> size<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
_result <SPAN class="operator token">+</SPAN><SPAN class="operator token">=</SPAN> Encoding<SPAN class="punctuation token">.</SPAN>ASCII<SPAN class="punctuation token">.</SPAN><SPAN class="token function">GetString</SPAN><SPAN class="punctuation token">(</SPAN>bytesReceived<SPAN class="punctuation token">,</SPAN> <SPAN class="number token">0</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">int</SPAN><SPAN class="punctuation token">)</SPAN>bytes<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN> bytes <SPAN class="operator token"><</SPAN> <SPAN class="number token">10</SPAN> <SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">continue</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">string</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="punctuation token">]</SPAN> lines <SPAN class="operator token">=</SPAN> _result<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Split</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">new</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="punctuation token">]</SPAN> <SPAN class="punctuation token">{</SPAN> <SPAN class="string token">"\n"</SPAN> <SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">,</SPAN> StringSplitOptions<SPAN class="punctuation token">.</SPAN>RemoveEmptyEntries<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">for</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">var</SPAN> index <SPAN class="operator token">=</SPAN> <SPAN class="number token">0</SPAN><SPAN class="punctuation token">;</SPAN> index <SPAN class="operator token"><</SPAN> lines<SPAN class="punctuation token">.</SPAN>Length <SPAN class="operator token">-</SPAN> <SPAN class="number token">1</SPAN><SPAN class="punctuation token">;</SPAN> index<SPAN class="operator token">++</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">var</SPAN> line <SPAN class="operator token">=</SPAN> lines<SPAN class="punctuation token">[</SPAN>index<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="token function">OnNmeaSentenceReceived</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">NmeaSentenceEventArgs</SPAN><SPAN class="punctuation token">(</SPAN>line<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
_result <SPAN class="operator token">=</SPAN> lines<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Last</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>The NmeaSentenceReceived event is used to forward on the nmea sentence back to the custom LocationDataSource. Once all that was done it was a almost trivial matter to implement the LocationDataSource in the shared project.
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">class</SPAN> <SPAN class="token class-name">ExternalGpsLocationDataSource</SPAN> <SPAN class="punctuation token">:</SPAN> LocationDataSource
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">private</SPAN> MapPoint _mapPoint<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">private</SPAN> <SPAN class="keyword token">double</SPAN> _velocity<SPAN class="punctuation token">,</SPAN> _course<SPAN class="punctuation token">,</SPAN> _accuracy<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">private</SPAN> <SPAN class="keyword token">readonly</SPAN> IAccessorySessionController _accessorySessionController<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">public</SPAN> ExternalGpsLocationDataSource <SPAN class="punctuation token">(</SPAN>IAccessorySessionController accessorySessionController<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
_accessorySessionController <SPAN class="operator token">=</SPAN> accessorySessionController<SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">protected</SPAN> <SPAN class="keyword token">override</SPAN> Task <SPAN class="token function">OnStartAsync</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
_accessorySessionController<SPAN class="punctuation token">.</SPAN>NmeaSentenceReceived <SPAN class="operator token">+</SPAN><SPAN class="operator token">=</SPAN> OnNmeaSentenceReceived<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">return</SPAN> _accessorySessionController<SPAN class="punctuation token">.</SPAN><SPAN class="token function">OpenSession</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"com.company.protocal"</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">protected</SPAN> <SPAN class="keyword token">override</SPAN> Task <SPAN class="token function">OnStopAsync</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
_accessorySessionController<SPAN class="punctuation token">.</SPAN><SPAN class="token function">CloseSession</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">return</SPAN> Task<SPAN class="punctuation token">.</SPAN><SPAN class="token function">FromResult</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">true</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">private</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">OnNmeaSentenceReceived</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">object</SPAN> sender<SPAN class="punctuation token">,</SPAN> NmeaSentenceEventArgs e<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">var</SPAN> line <SPAN class="operator token">=</SPAN> e<SPAN class="punctuation token">.</SPAN>NmeaSentence<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">try</SPAN>
<SPAN class="punctuation token">{</SPAN>
line <SPAN class="operator token">=</SPAN> line<SPAN class="punctuation token">.</SPAN><SPAN class="token function">TrimEnd</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">'\n'</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">'\r'</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">var</SPAN> message <SPAN class="operator token">=</SPAN> NmeaMessage<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Parse</SPAN><SPAN class="punctuation token">(</SPAN>line<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="token function">ReadLocationAttributesFromMessage</SPAN><SPAN class="punctuation token">(</SPAN>message<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">catch</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="token class-name">Exception</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="comment token">//</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN>_mapPoint <SPAN class="operator token">==</SPAN> <SPAN class="keyword token">null</SPAN> <SPAN class="operator token">||</SPAN> <SPAN class="keyword token">double</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">IsNaN</SPAN><SPAN class="punctuation token">(</SPAN>_mapPoint<SPAN class="punctuation token">.</SPAN>X<SPAN class="punctuation token">)</SPAN> <SPAN class="operator token">||</SPAN> <SPAN class="keyword token">double</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">IsNaN</SPAN><SPAN class="punctuation token">(</SPAN>_mapPoint<SPAN class="punctuation token">.</SPAN>Y<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="token function">UpdateLastKnownLocation</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">return</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="token function">UpdateLocation</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">Esri<SPAN class="punctuation token">.</SPAN>ArcGISRuntime<SPAN class="punctuation token">.</SPAN>Location<SPAN class="punctuation token">.</SPAN>Location</SPAN><SPAN class="punctuation token">(</SPAN>_mapPoint<SPAN class="punctuation token">,</SPAN> _accuracy<SPAN class="punctuation token">,</SPAN> _velocity<SPAN class="punctuation token">,</SPAN> _course<SPAN class="punctuation token">,</SPAN> <SPAN class="keyword token">false</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">private</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">ReadLocationAttributesFromMessage</SPAN><SPAN class="punctuation token">(</SPAN>NmeaMessage message<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">switch</SPAN> <SPAN class="punctuation token">(</SPAN>message<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">case</SPAN> Gpgga gpgga<SPAN class="punctuation token">:</SPAN>
_mapPoint <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">MapPoint</SPAN><SPAN class="punctuation token">(</SPAN>gpgga<SPAN class="punctuation token">.</SPAN>Longitude<SPAN class="punctuation token">,</SPAN> gpgga<SPAN class="punctuation token">.</SPAN>Latitude<SPAN class="punctuation token">,</SPAN> SpatialReference<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Create</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="number token">4326</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> Gpgsa gpgsa<SPAN class="punctuation token">:</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> Gprmc gprmc<SPAN class="punctuation token">:</SPAN>
_velocity <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">double</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">IsNaN</SPAN><SPAN class="punctuation token">(</SPAN>gprmc<SPAN class="punctuation token">.</SPAN>Speed<SPAN class="punctuation token">)</SPAN> <SPAN class="operator token">?</SPAN> <SPAN class="number token">0</SPAN> <SPAN class="punctuation token">:</SPAN> gprmc<SPAN class="punctuation token">.</SPAN>Speed <SPAN class="operator token">*</SPAN> <SPAN class="number token">0.5144</SPAN><SPAN class="punctuation token">;</SPAN> <SPAN class="comment token">//knots to m\s</SPAN>
_course <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">double</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">IsNaN</SPAN><SPAN class="punctuation token">(</SPAN>gprmc<SPAN class="punctuation token">.</SPAN>Course<SPAN class="punctuation token">)</SPAN> <SPAN class="operator token">?</SPAN> <SPAN class="number token">0</SPAN> <SPAN class="punctuation token">:</SPAN> gprmc<SPAN class="punctuation token">.</SPAN>Course<SPAN class="punctuation token">;</SPAN>
_mapPoint <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">MapPoint</SPAN><SPAN class="punctuation token">(</SPAN>gprmc<SPAN class="punctuation token">.</SPAN>Longitude<SPAN class="punctuation token">,</SPAN> gprmc<SPAN class="punctuation token">.</SPAN>Latitude<SPAN class="punctuation token">,</SPAN> SpatialReference<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Create</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="number token">4326</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> Gpgsv gpgsv<SPAN class="punctuation token">:</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">case</SPAN> Gpgst gpgst<SPAN class="punctuation token">:</SPAN>
<SPAN class="keyword token">double</SPAN> latError <SPAN class="operator token">=</SPAN> gpgst<SPAN class="punctuation token">.</SPAN>SigmaLatitudeError<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">double</SPAN> lonError <SPAN class="operator token">=</SPAN> gpgst<SPAN class="punctuation token">.</SPAN>SigmaLongitudeError<SPAN class="punctuation token">;</SPAN>
_accuracy <SPAN class="operator token">=</SPAN> Math<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Sqrt</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="number token">0.5</SPAN> <SPAN class="operator token">*</SPAN> <SPAN class="punctuation token">(</SPAN>Math<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Pow</SPAN><SPAN class="punctuation token">(</SPAN>latError<SPAN class="punctuation token">,</SPAN> <SPAN class="number token">2</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="operator token">+</SPAN> Math<SPAN class="punctuation token">.</SPAN><SPAN class="token function">Pow</SPAN><SPAN class="punctuation token">(</SPAN>lonError<SPAN class="punctuation token">,</SPAN> <SPAN class="number token">2</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">break</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">private</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">UpdateLastKnownLocation</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
<SPAN class="comment token">//If latest point not valid send previous point as LastKnown</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN>_mapPoint <SPAN class="operator token">==</SPAN> <SPAN class="keyword token">null</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">{</SPAN>
_mapPoint <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">MapPoint</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">double</SPAN><SPAN class="punctuation token">.</SPAN>NaN<SPAN class="punctuation token">,</SPAN> <SPAN class="keyword token">double</SPAN><SPAN class="punctuation token">.</SPAN>NaN<SPAN class="punctuation token">,</SPAN> SpatialReferences<SPAN class="punctuation token">.</SPAN>Wgs84<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="token function">UpdateLocation</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">Esri<SPAN class="punctuation token">.</SPAN>ArcGISRuntime<SPAN class="punctuation token">.</SPAN>Location<SPAN class="punctuation token">.</SPAN>Location</SPAN><SPAN class="punctuation token">(</SPAN>_mapPoint<SPAN class="punctuation token">,</SPAN> _accuracy<SPAN class="punctuation token">,</SPAN> _velocity<SPAN class="punctuation token">,</SPAN> _course<SPAN class="punctuation token">,</SPAN> <SPAN class="keyword token">true</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>I use a NMEA parser that Morten Nielsen wrote so no need to spend any time on the parsing GitHub - dotMorten/NmeaParser: Library for handling NMEA message in Windows Desktop, Store, Phone, Universal, and Xamari…
Beyond that it is just replacing the DataSource property on the MapView:LocationDisplay to the custom LocationDataSource and setting Enabled = true;
Cheers!