locked
iOS Native App Extensions in Xamarin.iOS project RRS feed

  • Question

  • User153709 posted

    Hello,

    I've ben wondering if it is possible to link fully native application extension in xamarin.ios project? I mean app extension written in swift compiled using XCode and included as native reference in xamarin project.

    I'am aware that xamarin allows to code app extension in c# but honestly it's quite hard without proper tools (debugger for example. Available right know in alpha 6.2 xamarin studio, but quite hard buggy).

    Also this could allow to write lightweight external native extensions for our apps.

    What i tried so far: Created app in xamarin and in xcode both with same names and identifiers. Created app extension in xcode using swift (tried obj-c also) Build extension and moved it to xamarin.ios project Added extension as native reference (tried many different settings) <ItemGroup> <NativeReference Include="ExtShare.appex"> <Kind>Static</Kind> // Tried Framework also <SmartLink>False</SmartLink> // Tried SmartLink True also </NativeReference> </ItemGroup>

    Of course they have proper bundle id, app group etc. At the end I'm ending with this result :

    Error MT5209: Native linking error: framework not found ExtShare (MT5209) (ExtShare) ExtShare/MTOUCH: Error MT5201: Native linking failed. Please review the build log and the user flags provided to gcc: -lExtShare.appex -Xlinker -sectcreate -Xlinker _TEXT -Xlinker _entitlements -Xlinker ExtShare/obj/iPhoneSimulator/Debug/Entitlements.xcent (MT5201) (ExtShare) ExtShare/MTOUCH: Error MT5202: Native linking failed. Please review the build log. (MT5202) (ExtShare)

    I'm aware that this could not be even possible to achieve, still would be nice to have such capabilites.

    Thursday, December 1, 2016 2:52 PM

All replies

  • User35208 posted

    @PatrykRomanczuk - have you figured this out? I want to do the same thing as I haven't been able to make a FileProvider with Xamarin that can stay under the 15 MB limit.

    Thursday, October 26, 2017 1:44 PM
  • User153709 posted

    @TedRogers Actually at first we paused this feature because of IDE limitations. But as soon as Xamarin Studio 6.2 stable has been released we implemented it using "classic Xamarin C# way". I have never found solution to add fully native extension into Xamarin app.

    Thursday, October 26, 2017 2:17 PM
  • User39 posted

    Yes, it's possible to use iOS extensions written in Swift in Xamarin.iOS apps, but it's somewhat complicated.

    Here's a basic description: https://bugzilla.xamarin.com/show_bug.cgi?id=43985#c12 (you should probably read the entire bug history, there are many problems and the corresponding solutions there).

    This can help too: https://stackoverflow.com/a/43647608/183422

    Wednesday, June 27, 2018 7:16 AM
  • User35208 posted

    I have successfully done this, so it is definitely possible. @RolfBjarneKvinge is right it is a bit complicated and requires adding some scripting into the Xamarin build process so that it puts the XCode built components into the right place at the right time. Message me if you need any help.

    Wednesday, June 27, 2018 11:38 AM
  • User372736 posted

    @TedRogers I need help. Please can you explain how to do it in detail?

    Sunday, September 23, 2018 2:32 PM
  • User35208 posted

    @ashwindmk

    I have only been able to make this work building the XCode and Xamarin projects on a Mac.

    The basic idea is this. Build your FileProviderHostApp and FileProvider in XCode and have XCode copy those files to a location that is easy for your Xamarin iOS project to reference. From your Xamarin iOS project add "BeforeCodeSign" and "AfterCodeSign" targets. "BeforeCodeSign" copies your native files over the files created in your shell/dummy FileProvider that is built in Xamarin. "AfterCodeSign" copies you native symbols to the appropriate place. Code signing copies symbol files around so you have to overwrite those with your native versions after the code signing. Make sure the bundle ids match in your XCode File Provider and your dummy FileProvider built in Xamarin.

    It has been quite a while since I wrote/copied these scripts. Please read the bug referenced above as that is how I figured it out. This stack overflow article outlines the solution: https://stackoverflow.com/questions/41428243/xamarin-notification-service-extension-issue

    1. Create an XCode Swift project with a test host app and your file provider. I call mine something like FileProviderHostApp. Your going to need a test host app as it makes it much easier to debug. Use the same bundle id for your test host app as your real app.

    2. Add a script in XCode to the host app target to copy the components to where it is easy for your Xamarin project to find them. See script below. I copy the files to a bin folder at the same level as my other project folders (one level up).

    Input file: $BUILDDIR/$CONFIGURATION$EFFECTIVEPLATFORMNAME/$FULLPRODUCT_NAME

        DEST_DIR=$PROJECT_DIR/../Bin
        mkdir -p $DEST_DIR
        env > $DEST_DIR/env-app.txt
        echo $BUILD_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/$FULL_PRODUCT_NAME > $DEST_DIR/junk-app.txt
    
        if [ -e $DEST_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/Frameworks ]
            then rm -r $DEST_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/Frameworks
        fi
        mkdir -p $DEST_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/Frameworks
        cp -r "$BUILD_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/$FULL_PRODUCT_NAME/Frameworks/"*.* $DEST_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/Frameworks
    
        if [ -e $DEST_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/FileProvider.appex ]
            then rm -r $DEST_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/FileProvider.appex
        fi
        cp -r $BUILD_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/FileProvider.appex $DEST_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME
        if [ -e $BUILD_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/FileProvider.appex.dSYM ]
            then cp -r $BUILD_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME/FileProvider.appex.dSYM $DEST_DIR/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME
        fi
    
    1. Folder structure for a debug iphone build should look something like this:

    2. Add two targets to the bottom of your Xamarin iOS csproj file. You need to tweak the names in the below script to match the names of your items.

        <Target Name="BeforeCodeSign">
          <Message Text="BeforeCodeSign: Configuration = $(Configuration) Platform = $(Platform)" />
          <!-- Debug|iPhone -->
          <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
            <FrameworksDirectory Include="..\..\Bin\Debug-iphoneos\Frameworks\**/*.*" />
            <NativeExtensionDirectory Include="..\..\Bin\Debug-iphoneos\FileProvider.appex\**\*.*" />
          </ItemGroup>
          <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
            <FrameworksDestination>bin\iPhone\Debug\TheRealApp\Frameworks</FrameworksDestination>
            <ExtensionDestination>bin\iPhone\Debug\TheRealApp\Plugins\FileProvider.appex</ExtensionDestination>
          </PropertyGroup>
          <!-- Release|iPhone -->
          <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
            <FrameworksDirectory Include="..\..\Bin\Release-iphoneos\Frameworks\**/*.*" />
            <NativeExtensionDirectory Include="..\..\Bin\Release-iphoneos\FileProvider.appex\**\*.*" />
          </ItemGroup>
          <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
            <FrameworksDestination>bin\iPhone\Release\TheRealApp\Frameworks</FrameworksDestination>
            <ExtensionDestination>bin\iPhone\Release\TheRealApp\Plugins\FileProvider.appex</ExtensionDestination>
          </PropertyGroup>
          <!-- Debug|iPhoneSimulator -->
          <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
            <FrameworksDirectory Include="..\..\Bin\Debug-iphonesimulator\Frameworks\**/*.*" />
            <NativeExtensionDirectory Include="..\..\Bin\Debug-iphonesimulator\FileProvider.appex\**\*.*" />
          </ItemGroup>
          <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
            <FrameworksDestination>bin\iPhoneSimulator\Debug\TheRealApp\Frameworks</FrameworksDestination>
            <ExtensionDestination>bin\iPhoneSimulator\Debug\TheRealApp\Plugins\FileProvider.appex</ExtensionDestination>
          </PropertyGroup>
          <!-- Release|iPhoneSimulator -->
          <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
            <FrameworksDirectory Include="..\..\Bin\Release-iphonesimulator\Frameworks\**/*.*" />
            <NativeExtensionDirectory Include="..\..\Bin\Release-iphonesimulator\FileProvider.appex\**\*.*" />
          </ItemGroup>
          <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
            <FrameworksDestination>bin\iPhoneSimulator\Release\TheRealApp\Frameworks</FrameworksDestination>
            <ExtensionDestination>bin\iPhoneSimulator\Release\TheRealApp\Plugins\FileProvider.appex</ExtensionDestination>
          </PropertyGroup>
          <Message Text="TheRealApp.csproj BeforeCodeSign: copying Frameworks = @(FrameworksDirectory)" />
          <Message Text="FrameworksDestination = $(FrameworksDestination)" />
          <!-- copy the Frameworks for the native extension -->
          <Copy SessionId="$(BuildSessionId)" SourceFiles="@(FrameworksDirectory)" DestinationFiles="@(FrameworksDirectory-&gt;'$(FrameworksDestination)\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" Retries="3" RetryDelayMilliseconds="300" />
          <Message Text="TheRealApp.csproj BeforeCodeSign: copying native FileProvider extension = @(NativeExtensionDirectory)" />
          <!-- cleanup the application extension built with Xamarin (too heavy in memory)-->
          <RemoveDir SessionId="$(BuildSessionId)" Directories="$(ExtensionDestination)" />
          <!-- copy the native one, built in XCode -->
          <Copy SessionId="$(BuildSessionId)" SourceFiles="@(NativeExtensionDirectory)" DestinationFolder="$(ExtensionDestination)" SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" Retries="3" RetryDelayMilliseconds="300" />
        </Target>
        <Target Name="AfterCodeSign">
          <Message Text="AfterCodeSign: Configuration = $(Configuration) Platform = $(Platform)" />
          <!-- Debug|iPhone -->
          <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
            <NativeExtensionSymbolsDirectory Include="..\..\Bin\Debug-iphoneos\FileProvider.appex.dSYM\**\*.*" />
          </ItemGroup>
          <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
            <ExtensionSymbolDestination>bin\iPhone\Debug\FileProvider.appex.dSYM</ExtensionSymbolDestination>
          </PropertyGroup>
          <!-- Release|iPhone -->
          <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
            <NativeExtensionSymbolsDirectory Include="..\..\Bin\Release-iphoneos\FileProvider.appex.dSYM\**\*.*" />
          </ItemGroup>
          <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
            <ExtensionSymbolDestination>bin\iPhone\Release\FileProvider.appex.dSYM</ExtensionSymbolDestination>
          </PropertyGroup>
          <!-- Debug|iPhoneSimulator -->
          <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
            <NativeExtensionSymbolsDirectory Include="..\..\Bin\Debug-iphonesimulator\FileProvider.appex.dSYM\**\*.*" />
          </ItemGroup>
          <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
            <ExtensionSymbolDestination>bin\iPhoneSimulator\Debug\FileProvider.appex.dSYM</ExtensionSymbolDestination>
          </PropertyGroup>
          <!-- Release|iPhoneSimulator -->
          <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
            <NativeExtensionSymbolsDirectory Include="..\..\Bin\Release-iphonesimulator\FileProvider.appex.dSYM\**\*.*" />
          </ItemGroup>
          <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
            <ExtensionSymbolDestination>bin\iPhoneSimulator\Release\FileProvider.appex.dSYM</ExtensionSymbolDestination>
          </PropertyGroup>
          <Message Text="TheRealApp.csproj AfterCodeSign: copying native FileProvider symbols = @(NativeExtensionSymbolsDirectory)" />
          <!-- cleanup the application extension built with Xamarin (too heavy in memory)-->
          <RemoveDir SessionId="$(BuildSessionId)" Directories="$(ExtensionSymbolDestination)" />
          <Copy SessionId="$(BuildSessionId)" SourceFiles="@(NativeExtensionSymbolsDirectory)" DestinationFolder="$(ExtensionSymbolDestination)" SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" Retries="3" RetryDelayMilliseconds="300" />
        </Target>
      
    Monday, September 24, 2018 1:22 PM
  • User372736 posted

    @TedRogers Thank you so much.

    However I did not had to do any of these. I had a cocoapod project which is a native Notification Content Extension library.

    I created a Xamarin.iOS binding project using 'sharpie pod' commands as mentioned in the docs and used its reference in the extension project added in my Xamarin.iOS app.

    Tuesday, September 25, 2018 1:34 AM