头图

.NET MAUI was officially released at the Microsoft Build 2022 conference. For .NET developers, C# can be used to complete cross-platform front-end application development. Compared with Xamarin, the predecessor of MAUI, MAUI supports Blazor's hybrid development in addition to the traditional native development mode. This also allows developers in more directions to enter into cross-platform application development. Someone will propose that in the cloud native era, is front-end development still important? In fact, multi-terminal application compatibility is an indispensable aspect of cloud native. In the Internet era, there are many outstanding applications, and SDKs for third-party applications have been released. Developers can combine these SDKs to make related solutions. Can these SDKs be called through MAUI? I will introduce it to you through a series of articles.

Why Bind the Native SDK

We know that an application can be integrated into different scenarios. For example, a playing application needs a map. For example, a photo application needs to be socialized. For example, if you are a traditional IoT application, you need a A bluetooth communication protocol. Bringing doctrine is a way of saving, which can be combined with the SDK provided by the third party to complete the development of the application. It will be difficult for .NET developers, because DLLs are habitually called, but in iOS/Android native development, there are actually different library calling mechanisms. In the Xamarin era, many developers used C# to bind third-party libraries, such as Alipay, WeChat, AutoNavi, etc. in the Chinese market. What is the difference when it comes to MAUI? Roughly the same as the Xamarin bindings. But since MAUI is integrated into .NET 6, it is actually a change in the project file format. At this stage, you can quickly build iOS / Android binding projects through the command line.

▌Binding of MAUI iOS library

 dotnet new iosbinding -o iOS.AMapSDK.Binding

To do the binding of iOS / macOS, in addition to creating a binding project, you also need to install the Shapie tool ( https://aka.ms/objectivesharpie ) to do the corresponding conversion, you can use the command line to target the dynamic library of iOS and The static library does the corresponding conversion. This is to add that your Xcode environment must be installed. The following is a simple conversion statement. For more details, you can follow my series of iOS library file binding articles.

 sharpie bind -framework /your path/AMapFoundationKit.framework -sdk
iphoneos15.5

▌Binding of MAUI Android library

 dotnet new android-bindinglib -o Droid.AMapSDK.Binding

The binding of Android is different from that of iOS. You can directly compile the jar or aar package of the third-party library Android SDK into it.

If you want to know more, you can follow this series of articles on Android library bindings.

Control customization

In Xamarin.Forms, references to cross-platform respective controls via the renderer mechanism and rely on INotifyPropertyChanged. .NET MAUI did not cancel the renderer mechanism, but introduced a pattern called Handler. With Handlers it is more flexible and easier to extend or override when needed.

This is the new Handler mode of MAUI

We can build the MAUI control of AutoNavi map through the Handler mechanism

You can use and experience MAUI's AutoNavi Android / iOS controls at https://github.com/kinfey/AMapMAUIControls


After introducing some main knowledge of iOS / Android MAUI controls for AutoNavi map, I will focus on the knowledge of iOS native library binding, and tell you some skills in the process of binding native library. Give your little friends some inspiration.

Understand iOS dynamic library and static library

Before binding, we need to learn about iOS dynamic library and static library. The easiest way to understand is that in iOS static libraries end with .a suffix and dynamic libraries end with .dylib suffix. Both static libraries and dynamic libraries can be packaged into Framework.

▌The difference between static library and dynamic library

  1. The characteristic of the static library is that the library file will be copied directly to the target application when compiling, and this copy is resident in the target application, so after the compilation is completed, the static library file will be copied directly to the target application. item is useless. However, there is a disadvantage that the capacity of the generated application will be larger because of the need for copying.
  2. Dynamic libraries and static libraries are just the opposite. When compiling, they will not be copied to the target application, so the size of the generated application is small, and a dynamic library can be shared with multiple applications. program to use. However, the generated application depends on the dynamic library, which often leads to the situation that the dynamic library cannot be found.

Let's disassemble the SDK based on AutoNavi Map - AMapFoundationKit.framework

This includes the corresponding header file information, module information, and static library. You can clearly see the implementation of the Framrwork packaged into the Gaode map. This is also our understanding of the library concept. The compiled binary code exposes header files to third-party developers.

Generate an interface for C# calls through the Sharpie tool

Shapie is a very useful conversion tool that supports the conversion of Objective-C libraries under macOS. Through Sharpie, the header file given by the library file can be converted to complete the C# binding. The Shapie tool, the predecessor of MAUI, already exists, and I often use this tool for conversion.

Because I use 3D for the function of AutoNavi map this time, I will bind and convert the two frameworks of AutoNavi, AMapFoundationKit.Framework and MAMapKit.framework.

▌Convert AMapFoundationKit.Framework

 sharpie bind -framework AMapFoundationKit.framework -sdk iphoneos15.5

▌Convert MAMapKit.framework

 sharpie bind -framework MAMapKit.framework -sdk iphoneos15.5

Supplement: MAMapKit.framework depends on AMapFoundationKit.framework, so it should be placed in the same directory.
It should be noted here that you need to install Xcode. It is recommended to install it to the latest and correspond to the latest iOS SDK. Of course, you can also bind different versions of the iOS SDK according to your needs. You can view the environment with one command

sharpie xcode -sdks

The two files generated by command line binding are StructsAndEnums.cs and ApiDefinitions.cs. StructsAndEnums.cs corresponds to some constants and enumeration types, and ApiDefinitions.cs corresponds to some interfaces and methods.

Create an iOS binding project for MAUI

Attention should be paid to the creation here. Now the templates of Visual Studio 2022 are not completed. Now everyone uses the command line to create them, because we have two projects, and the projects that need to create two Bindings are the projects for AMapFoundationKit.Framework. Construct

 dotnet new iosbinding -o iOS.AMap.Foundation

Project build against MAMapKit.framework
`
dotnet new iosbinding -o iOS.AMap.3D
`
After generation, you need to put AMapFoundationKit.framework in the directory of iOS.AMap.Foundation, and put MAMapKit.framework in the directory of iOS.AMap.3D. And put the generated StructsAndEnums.cs and ApiDefinitions.cs into the corresponding directory.

Project setting adjustment

1. StructsAndEnum.cs in the directory generated by Sharpie, and ApiDefinition.cs in the built Binding directory, replace it. So make changes to the .csproj project

 <ItemGroup>
 <ObjcBindingApiDefinition Include="ApiDefinitions.cs" />
 <ObjcBindingCoreSource Include="StructsAndEnums.cs" />
</ItemGroup>

2. Compile iOS.AMap.Foundation

▌Add a reference to Framework in AMapFoundationKit.framework.csproj

 <ItemGroup>
 <NativeReference Include="AMapFoundationKit.framework">
 <Kind>Framework</Kind>
 <ForceLoad>True</ForceLoad>
 <SmartLink>False</SmartLink>
 </NativeReference>
 </ItemGroup>

Kind : The native binding type can be Framwork or StaticLibary

ForceLoad : strong loading, select True

SmartLink: Smart Link

The finished project.csproj is set to

 <Project Sdk="Microsoft.NET.Sdk">
 <PropertyGroup>
 <TargetFramework>net6.0-ios</TargetFramework>
 <Nullable>enable</Nullable>
 <ImplicitUsings>true</ImplicitUsings>
 <IsBindingProject>true</IsBindingProject>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoBindingEmbedding>false</NoBindingEmbedding>
 </PropertyGroup>
 <ItemGroup>
 <ObjcBindingApiDefinition Include="ApiDefinitions.cs" />
 <ObjcBindingCoreSource Include="StructsAndEnums.cs" />
 </ItemGroup>
 <ItemGroup>
 <NativeReference Include="AMapFoundationKit.framework">
 <Kind>Framework</Kind>
 <ForceLoad>True</ForceLoad>
 <SmartLink>False</SmartLink>
 </NativeReference>
 </ItemGroup>
</Project>

Compiling iOS.AMap.Foundation, you'll get a crash because of a lot of error messages. This is because when Shapie does the conversion, some conversions are not done well. At this time, you need to adjust one by one.

▌Categorize error messages

  • The type or namespace name 'VerifyAttribute' could not be found

For this type of information, because the attributes are not confirmed during the conversion, the VerifyAttribute field will be added. In general, this field can be commented out, such as

 static class CFunctions
{
// NSString * AMapEmptyStringIfNil (NSString *s);
[DllImport ("__Internal")]
// [Verify (PlatformInvoke)]
static extern NSString AMapEmptyStringIfNil (NSString s);
// extern CLLocationCoordinate2D AMapCoordinateConvert
(CLLocationCoordinate2D coordinate, AMapCoordinateType type);
[DllImport ("__Internal")]
// [Verify (PlatformInvoke)]
static extern CLLocationCoordinate2D AMapCoordinateConvert
(CLLocationCoordinate2D coordinate, AMapCoordinateType type);
// extern BOOL AMapDataAvailableForCoordinate (CLLocationCoordinate2D
coordinate);
[DllImport ("__Internal")]
// [Verify (PlatformInvoke)]
static extern bool AMapDataAvailableForCoordinate
(CLLocationCoordinate2D coordinate);
}
The type or namespace name 'AMapFoundationKit'

Namespace problem, you need to add named controls for StructsAndEnums.cs and ApiDefinitions.cs. You can use AMapFoundationKit directly, or you can modify the name you like. I use iOS.AMap.Foundation name and project here. correspond

  • Duplicate 'Static' attribute

This is because the Constants of ApiDefinitions.cs are repeatedly defined, and this needs to be rearranged and merged into one.

  • Unsupported type for Fields: bool for 'iOS.AMap.Foundation.Constants _amapLocationOverseas'.e

The type does not correspond to cause the compilation to fail. At this time, I modified it to

 [Field ("_amapLocationOverseas", "__Internal")]
IntPtr _amapLocationOverseas { get; }

This way you can compile via iOS.AMap.Foundation

3. Compile iOS.AMap.3D

▌Add a reference to iOS.AMap.Foundation

Because MAMapKit.framework depends on AMapFoundationKit.framework, so iOS.AMap.3D depends on iOS.AMap.Foundation

 <ItemGroup>
 <ProjectReference
Include="..\iOS.Amap.Foundation\iOS.Amap.Foundation.csproj" />
 </ItemGroup>

▌Introduce MAMapKit.framework

 <ItemGroup>
 <NativeReference Include="MAMapKit.framework">
 <Kind>Framework</Kind>
 <ForceLoad>True</ForceLoad>
 <SmartLink>True</SmartLink>
 <Frameworks>GLKit OpenGLES UIKit Foundation CoreGraphics QuartzCore
CoreLocation CoreTelephony SystemConfiguration Security AdSupport
JavaScriptCore</Frameworks>
 <LinkerFlags>-lz -lstdc++ -lc++</LinkerFlags>
 </NativeReference>
 </ItemGroup>

This is different from AMapFoundationKit.framework. You need to add the items that the Framework needs to depend on when compiling, as well as the compilation method used. This is related to the framework you bind to. I choose Gaode map here, so follow their text. File requirements have been set up.
The finished project.csproj is set to

 <Project Sdk="Microsoft.NET.Sdk">
 <PropertyGroup>
 <TargetFramework>net6.0-ios</TargetFramework>
 <RootNamespace>iOS.Amap._3D</RootNamespace>
 <Nullable>enable</Nullable>
 <ImplicitUsings>true</ImplicitUsings>
 <IsBindingProject>true</IsBindingProject>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoBindingEmbedding>false</NoBindingEmbedding>
 </PropertyGroup>
 <ItemGroup>
 <ObjcBindingApiDefinition Include="ApiDefinitions.cs" />
 <ObjcBindingCoreSource Include="StructsAndEnums.cs" />
 </ItemGroup>
 <ItemGroup>
 <NativeReference Include="MAMapKit.framework">
 <Kind>Framework</Kind>
 <ForceLoad>True</ForceLoad>
 <SmartLink>True</SmartLink>
 <Frameworks>GLKit OpenGLES UIKit Foundation CoreGraphics QuartzCore
CoreLocation CoreTelephony SystemConfiguration Security AdSupport
JavaScriptCore</Frameworks>
 <LinkerFlags>-lz -lstdc++ -lc++</LinkerFlags>
 </NativeReference>
 </ItemGroup>
 <ItemGroup>
 <ProjectReference
Include="..\iOS.Amap.Foundation\iOS.Amap.Foundation.csproj" />
 </ItemGroup>
</Project>

When compiling iOS.AMap.3D, you will be more crashed than before. At this time, you need to have enough patience. In addition to the error message similar to the previous one, there are some new situations. I will list them here.

  • Type 'MAMapViewDelegate' already defines a member called 'MapView' with the same parameter types

The reason for this is because the method has the same name, which is also the difference between the Objective-C declarative syntax and the traditional syntax, so you need to rename it for this

such as this

 // @optional -(void)mapView:(MAMapView *)mapView didAnnotationViewTapped:
(MAAnnotationView *)view;
[Export ("mapView:didAnnotationViewTapped:")]
void MapView (MAMapView mapView, MAAnnotationView view);

change into

 // @optional -(void)mapView:(MAMapView *)mapView didAnnotationViewTapped:
(MAAnnotationView *)view;
[Export ("mapView:didAnnotationViewTapped:")]
void MapViewDidAnnotationViewTapped (MAMapView mapView, MAAnnotationView
view);
  • The type or namespace name 'IMAOverlay' could not be found
    This is a naming error, in the ApiDefinitions.cs file you can find MAOverlay

     [Protocol]
    interface MAOverlay : IMAAnnotation
    {
    // @required -(CLLocationCoordinate2D)coordinate;
    [Abstract]
    [Export ("coordinate")]
    // [Verify (MethodToProperty)]
    CLLocationCoordinate2D Coordinate { get; }
    // @required -(MAMapRect)boundingMapRect;
    [Abstract]
    [Export ("boundingMapRect")]
    // [Verify (MethodToProperty)]
    MAMapRect BoundingMapRect { get; }
    }

    So replace all IMAOverlay with MAOverlay.

  • The type or namespace name 'AutoGeneratedName' could not be found

Cancel AutoGeneratedName

  • Constant value '-1' cannot be converted to a 'ulong'

Specified type error AllCorners = ~0x0 Changed to AllCorners = 0x0
Do not know how to make a signature for CoreLocation.CLLocationCoordinate2D in parameter`coordinates'
C# doesn't have pointers, there is an error in Sharpie conversion

  • 'MAMapView_UserLocation.HeadingFilter': cannot declare instance members in a static class
 // @property (nonatomic) CLLocationDegrees headingFilter;
[Export ("headingFilter")]
double HeadingFilter( { get; set; })

This definition should be replaced by

 // @property (nonatomic) CLLocationDegrees headingFilter;
[Export ("headingFilter")]
double HeadingFilter();
  • Cannot convert type 'Foundation.NSObject' to 'nint'
  • // @property (nonatomic, weak) id<MAOverlayRenderDelegate>
  • rendererDelegate;
  • [NullAllowed, Export ("rendererDelegate", ArgumentSemantic.Weak)]
  • NSObject WeakRendererDelegate { get; set; }
    change into
 // @property (nonatomic, weak) id<MAOverlayRenderDelegate>
rendererDelegate;
[NullAllowed, Export ("rendererDelegate", ArgumentSemantic.Weak)]
IntPtr WeakRendererDelegate { get; set; }

Or exclusion is a long process, but you will be very excited when the compilation is successful, so we have successfully bound AMapFoundationKit.framework and MAMapKit.framework.

Try to create a .NET for iOS project to verify

summary

Although the native library binding is more complicated, it is actually very healing. When you see the compilation pass, you will know the joy in it. Another point is that many people think that cross-platform mobile development does not require basic knowledge of the platform, but it is still needed. Especially in the binding of this kind of native library, you need to know both C# and Objective-C. I hope this example can inspire you. Please look forward to the next Android native library binding.

related resources


Long press to identify the QR code and follow Microsoft China MSDN

Click to learn about MAUI


微软技术栈
418 声望994 粉丝

微软技术生态官方平台。予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。