DEV Community

Jay Malli
Jay Malli

Posted on

Develop & Publish .NET MAUI Application for Mac using Visual Studio Code

Hello Developers,

=> So today we are developing a basic app using .NET maui framework & we'll publish it for the Mac platform.

=> .NET MAUI, which stands for .NET Multi-platform App UI, is an open-source, cross-platform framework for building native mobile and desktop applications. It is an evolution of the Xamarin.Forms framework and is part of the broader .NET ecosystem.

Before we start our work , let's install required tools.

Step - 1 : Installation task

-> Add required Extensions in vs code

  • C# (by Microsoft)
  • .NET Runtime Install Tool (by Microsoft)

-> after that open settings & search Omnisharp , make sure that
"Use Omnisharp" checkbox is enabled.

omnisharp

-> check above tools are installed successfully or not by following commands from terminal.

dotnet --list-sdks
dotnet --list-runtimes

workload

-> Check whether maui workload is exist not.

dotnet workload list

workload

-> List all available workloads.

dotnet workload search

workload

-> Install maui workload

dotnet workload install maui

-> Check installed workload

dotnet workload list

workload

Step - 2 : Create Maui Project

-> Create Maui Template.

dotnet new maui

maui template

-> open the [project_name].csproj file & perform following acions

remove net7.0-android
comment windows platform tag

csproj file

-> perform restore & build.

dotnet restore (add nuget packages,mentioned in .csproj file)

dotnet build

dotnet build

-> You may encounter the following error. You can either upgrade the Xcode version or add the following tags in the .csproj file.

Error

<PropertyGroup>
     <ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
        <MtouchLink>SdkOnly</MtouchLink>
        <GenerateRuntimeConfiguratonFiles>true</GenerateRuntimeConfiguratonFiles>
    </PropertyGroup>
Enter fullscreen mode Exit fullscreen mode

dotnet build

build

Step - 3 : Let's do some code

=> Add fonts :

-> Add OpenSans-Regular.ttf & OpenSans-Semibold.ttf fonts file in the Resources/Fonts folder. (check out my GitHub repo for required resources : Repo)

fonts

=> AppShell.xaml :

// remove title from header
<ShellContent
    ContentTemplate="{DataTemplate local:MainPage}"
    Route="MainPage"
    Shell.NavBarIsVisible="False" 
/>
Enter fullscreen mode Exit fullscreen mode

=> MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Groot_App_Maui.MainPage">

    <ScrollView>
        <VerticalStackLayout
            Spacing="25"
            Padding="30,0"
            VerticalOptions="Center">

            <Image
                Source="groot_img.jpg" // Add this image in resources folder
                SemanticProperties.Description="Cute groot waving hi to you!"
                HeightRequest="400"
                HorizontalOptions="Center" />

            <Label
                Text="I am Groot!"
                SemanticProperties.HeadingLevel="Level1"
                FontSize="32"
                HorizontalOptions="Center" />

              <Button
                FontAttributes="Bold"  
                Text="Click Me"  
                FontSize="{OnPlatform WinUI=16, MacCatalyst=18}"
                HorizontalOptions="Center"
                Clicked="OnClickBtn"  
             /> 

        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

Enter fullscreen mode Exit fullscreen mode

=> Add below image in Resources/Images folder.

resources

=> MainPage.xaml.cs :

// remove unnecessary code

namespace Groot_App_Maui;

public partial class MainPage : ContentPage
{

    public MainPage()
    {
        InitializeComponent();
    }

        public async void OnClickBtn(object sender, EventArgs e)
    {
        await 
 Application.Current.MainPage.DisplayAlert(null, "Again I am Groot :)", "OK");
    } 
}

Enter fullscreen mode Exit fullscreen mode

-> Add icon image in Resources/AppIcon folder

icon

=> [project_name].csproj :

// other code
<ItemGroup>
        <!-- App Icon -->
        <MauiIcon Include="Resources\AppIcon\app_icon.png"/>
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

icon

-> modify icon name in /Platforms/MacCataslyst/Info.plist file

    <key>XSAppIconAssets</key>
    <string>Assets.xcassets/groot_app_icon.appiconset</string>
Enter fullscreen mode Exit fullscreen mode

-> modify icon name in /Platforms/iOS/Info.plist file

<key>XSAppIconAssets</key>
<string>Assets.xcassets/groot_app_icon.appiconset</string>
Enter fullscreen mode Exit fullscreen mode

-> Modify Properties/launchSettings.json file

{
  "profiles": {
    "Windows (Debug)": {
      "commandName": "Project",
      "commandLineArgs": "--device windows --theme Light",
      "workingDirectory": "$(ProjectDir)",
      "launchBrowser": true,
      "launchUrl": "",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "Windows (Release)": {
      "commandName": "Project",
      "commandLineArgs": "--device windows --theme Light",
      "workingDirectory": "$(ProjectDir)",
      "launchBrowser": true,
      "launchUrl": "",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    },
    "Mac (Debug)": {
      "commandName": "Project",
      "commandLineArgs": "--device macos --theme Light",
      "workingDirectory": "$(ProjectDir)",
      "launchBrowser": true,
      "launchUrl": "",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "Mac (Release)": {
      "commandName": "Project",
      "commandLineArgs": "--device macos --theme Light",
      "workingDirectory": "$(ProjectDir)",
      "launchBrowser": true,
      "launchUrl": "",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Step - 4 : lets run our app

dotnet build
dotnet run -f net7.0-maccatalyst (from .csproj file)

-> yaahh!!!

o/p image

Step - 5 : Its time to publish our app

-> We'll create a pkg for our app.

-> Modify bundleId in .csproj file
<ApplicationId>com.companyname.groot_app_maui</ApplicationId>

-> Modify Info.plist file for mac & ios

<key>CFBundleIdentifier</key>
    <string>com.companyname.groot_app_maui</string>
<key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>NSHumanReadableCopyright</key>
    <string>Groot © 2023</string>
    <key>ITSAppUsesNonExemptEncryption</key>
    <false/>
    <key>NSDownloadsUsageDescription</key>
    <string>We need access to your Downloads folder to do XYZ.</string>

Enter fullscreen mode Exit fullscreen mode

-> Create Entitlements.plist file Platforms/mac & add below code

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.network.client</key>
    <true/>
    <key>com.apple.security.cs.allow-jit</key>
    <true/><!-- Specify permissions for accessing user's location -->
    <key>com.apple.security.personal-information.location</key>
    <true/><!-- Specify permissions for accessing user's contacts -->
    <key>com.apple.security.personal-information.contacts</key>
    <true/><!-- Specify permissions for accessing user's photos -->
    <key>com.apple.security.assets.photos.read-write</key>
    <true/>
  </dict>
</plist>

Enter fullscreen mode Exit fullscreen mode

=> Publish app without signed certificate

dotnet publish -f net7.0-maccatalyst -c Release -r maccatalyst-x64

-> If you encounter this error in the production app,

Attempting to JIT compile method '(wrapper delegate-invoke) Google.Apis.Drive.v3.Data.User :invoke_callvirt_User_About (Google.Apis.Drive.v3.Data.About)' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.

then add UseInterpreter tag in the csproj file.

<PropertyGroup>
     <ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
        <MtouchLink>SdkOnly</MtouchLink>
        <UseInterpreter>true</UseInterpreter><GenerateRuntimeConfiguratonFiles>true</GenerateRuntimeConfiguratonFiles>
    </PropertyGroup>
Enter fullscreen mode Exit fullscreen mode

pkg path

-> Open the terminal and execute the following command to monitor the logs of the app while the installation task is currently executing.

tail -f /private/var/log/install.log

-> Additionally, open the Console and click on the start button to monitor logs of our app and view crash reports if the app crashes.

-> If you encounter this error in the console after installing the app from the app package and your published app is not working, then ignore it. There is no reason to believe that the app is not working because of this error. I also got this error in my console, & still app is working. :)

Non-fatal error enumerating at , continuing: Error Domain=NSCocoaErrorDomain Code=260 "The file “PlugIns” couldn’t be opened because there is no such file." UserInfo={NSURL=PlugIns/ -- app_path/Contents/, NSFilePath=/Users/mufaddalbharmal/Desktop/app_name.app/Contents/PlugIns, NSUnderlyingError=0x7f7d61708ad0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}

app installer

-> You can see in the yellow-highlighted part that there is no sign of a lock, which means our app is not verified.

=> Create Signed Certificate :

-> First, we need to create a signed certificate. Follow the steps outlined in the provided documentation on creating signed certificates for your app: Docs for certificate

-> Now, publish our app with the signature created by the certificate.

dotnet publish -f net7.0-maccatalyst -c Release -p:MtouchLink=SdkOnly -p:CreatePackage=true -p:EnableCodeSigning=true -p:EnablePackageSigning=true -p:CodesignKey="your_certificate_codesignkey" -p:CodesignProvision="DSR_DevID" -p:CodesignEntitlements="Platforms\MacCatalyst\Entitlements.plist" -p:PackageSigningKey="your_certificate_codesignkey" -p:UseHardenedRuntime=true

where ,

CodesignKey : The name of the code signing key. Set to the name of your distribution certificate, as displayed in Keychain Access.
e.g.CodesignKey="Developer ID Application: *** (***)"

PackageSigningKey : same as CodesignKey

CodesignProvision :The provisioning profile to use when signing the app bundle.

-> Now you can see there is a lock at the top right corner, indicating that our app is verified and trusted.

app installer with lock

-> Yaah!!! our production app ready!

app icon in menu bar

app demo

=> The example above demonstrates how to develop & publish .NET maui app for Mac platform , you can find the necessary code in the repository mentioned below.

=> GithHub Repository

Thank you for joining me on this journey of discovery and learning. If you found this blog post valuable and would like to connect further, I'd love to connect with you on LinkedIn. You can find me at LinkedIn

If you have thoughts, questions, or experiences related to this topic, please drop a comment below.

Top comments (0)