Skip to content

Commit

Permalink
Merge branch 'main' into xcode15.3-bump
Browse files Browse the repository at this point in the history
  • Loading branch information
dalexsoto authored May 28, 2024
2 parents ff5523b + 3c50f16 commit 84638c6
Show file tree
Hide file tree
Showing 50 changed files with 796 additions and 310 deletions.
131 changes: 131 additions & 0 deletions docs/objective-c-protocols.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Objective-C protocols

This document describes how we bind Objective-C protocols in C#, and in
particular improvements we've done in .NET 9.

## Can Objective-C protocols be modeled as C# interfaces?

Objective-C protocols are quite similar to C# interfaces, except when they're
not, and that makes binding them somewhat complicated.

### Optional/required members

Objective-C protocols can have both optional and required members. It's always
been possible to represent required members in a C# interface (any interface
member would be required), but optional members were not possible until C#
added support for default interface members in C# 8.

We represent optional members in two ways:

* As an extension method on the interface (useful when calling the optional member).
* As an IDE feature that would show any optional members from an interface by
typing 'override ...' in the text editor (useful when implementing an optional member).

This has a few drawbacks:

* There are no extension properties, so optional properties would have to be
bound as a pair of GetProperty/SetProperty methods.

* The IDE feature was obscure, few people knew about it, it broke on pretty
much every major release of Visual Studio for Mac, and it was never
implemented for Visual Studio on Windows. This made it quite hard to
implement optional members in a managed class extending an Objective-C
protocol, since developers would have to figure out the correct Export
attribute as well as the signature (which is quite complicated for more
complex signatures, especially if blocks are involved).

### Changing requiredness

It's entirely possible to change a member from being required to being optional
in Objective-C. Technically it's also a breaking change to do the opposite (make
an optional member required), but Apple does it all the time.

We've handled this by just not updating the binding until we're able to do
breaking changes (which happens very rarely).

### Static members

Objective-C protocols can have static members. C# didn't allow for static
members in interfaces until C# 11, so until recently there hasn't been any
good way to bind static protocol members on a protocol.

Our workaround is to manually inline every static member in all classes that
implemented a given protocol.

### Initializers

Objective-C protocols can have initializers (constructors). C# still doesn't
allow for constructors in interfaces.

In the past we haven't bound any protocol initializer at all, we've completely
ignored them.

## Binding in C#

### Initializers

Given the following API definition:

```cs
[Protocol]
public interface Protocol {
[Abstract]
[Export ("init")]
IntPtr Constructor ();

[Export ("initWithValue:")]
IntPtr Constructor (IntPtr value);

[Bind ("Create")]
[Export ("initWithPlanet:")]
IntPtr Constructor ();
}
```

we're binding it like this:

```cs
[Protocol ("Protocol")]
public interface IProtocol : INativeObject {
[Export ("init")]
public static T CreateInstance<T> () where T: NSObject, IProtocol { /* default implementation */ }

[Export ("initWithValue:")]
public static T CreateInstance<T> () where T: NSObject, IProtocol { /* default implementation */ }

[Export ("initWithPlanet:")]
public static T Create<T> () where T: NSObject, IProtocol { /* default implementation */ }
}
```

In other words: we bind initializers as a static C# factory method that takes
a generic type argument specifying the type to instantiate.

Notes:

1. Constructors are currently not inlined in any implementing classes, like
other members are. This is something we could look into if there's enough
interest.
2. If a managed class implements a protocol with a constructor, the class has
to implement the constructor manually using the `[Export]` attribute in
order to conform to the protocol:

```cs
[Protocol]
interface IMyProtocol {
[Export ("initWithValue:")]
IntPtr Constructor (string value);
}
```

```cs
class MyClass : NSObject, IMyProtocol {
public string Value { get; private set; }

[Export ("initWithValue:")]
public MyClass (string value)
{
this.Value = value;
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
<Reference Include="Microsoft.Build.Utilities.Core">
<HintPath>$(MonoMSBuildBinPath)/Microsoft.Build.Utilities.Core.dll</HintPath>
</Reference>
<Compile Include="..\..\Versions.g.cs">
<Link>Versions.g.cs</Link>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
10 changes: 10 additions & 0 deletions msbuild/Xamarin.Localization.MSBuild/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: AssemblyCompanyAttribute ("Microsoft Corp.")]
[assembly: AssemblyFileVersionAttribute (VersionConstants.XamarinIOSVersion)]
[assembly: AssemblyInformationalVersionAttribute (VersionConstants.XamarinIOSVersion + "." + VersionConstants.NuGetPrereleaseIdentifier + "+" + VersionConstants.NuGetBuildMetadata)]
[assembly: AssemblyProductAttribute ("Xamarin.Localization.MSBuild")]
[assembly: AssemblyTitleAttribute ("Xamarin.Localization.MSBuild")]
[assembly: AssemblyVersionAttribute (VersionConstants.XamarinIOSVersion)]
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
Expand All @@ -27,6 +28,9 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Versions.g.cs">
<Link>Versions.g.cs</Link>
</Compile>
<EmbeddedResource Include="TranslatedAssemblies/MSBStrings.cs.resx">
<Link>MSBStrings.cs.resx</Link>
<ManifestResourceName>Xamarin.Localization.MSBuild.MSBStrings.cs</ManifestResourceName>
Expand Down
3 changes: 0 additions & 3 deletions msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@
<Compile Include="..\..\tools\common\NullableAttributes.cs">
<Link>external\NullableAttributes.cs</Link>
</Compile>
<Compile Include="..\Versions.g.cs">
<Link>Versions.g.cs</Link>
</Compile>
<Compile Remove="Errors.designer.cs" /> <!-- The 'CoreResGen' target will add it again from the EmbeddedResource item, this avoids a warning about the file being compiled twice -->
<Compile Include="..\..\tools\common\SdkVersions.cs">
<Link>external\SdkVersions.cs</Link>
Expand Down
4 changes: 2 additions & 2 deletions msbuild/Xamarin.Shared/Xamarin.Mac.Common.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!--
<!--
***********************************************************************************************
Xamarin.Mac.Common.props
Expand Down Expand Up @@ -56,7 +56,7 @@ Copyright (C) 2013-2014 Xamarin. All rights reserved.
</PropertyGroup>

<!-- Do not resolve from the GAC in Modern or Full unless allow-unsafe-gac-resolution is passed in -->
<PropertyGroup Condition="'$(TargetFrameworkName)' != 'System' And !$(_BundlerArguments.Contains('--allow-unsafe-gac-resolution'))" >
<PropertyGroup Condition="'$(TargetFrameworkName)' != 'System' And !$(AppBundleExtraOptions.Contains('--allow-unsafe-gac-resolution'))" >
<AssemblySearchPaths>$([System.String]::Copy('$(AssemblySearchPaths)').Replace('{GAC}',''))</AssemblySearchPaths>
<AssemblySearchPaths Condition="'$(MSBuildRuntimeVersion)' != ''">$(AssemblySearchPaths.Split(';'))</AssemblySearchPaths>
</PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion msbuild/Xamarin.Shared/Xamarin.Mac.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Copyright (C) 2014 Xamarin. All rights reserved.
HttpClientHandler="$(HttpClientHandler)"
I18n="$(I18n)"
Profiling="$(Profiling)"
ExtraArgs="$(_BundlerArguments)"
ExtraArgs="$(AppBundleExtraOptions)"
NativeReferences="@(_FrameworkNativeReference);@(_FileNativeReference)"
References="@(ReferencePath);@(_BundlerReferencePath)"
Registrar="$(Registrar)"
Expand Down
4 changes: 2 additions & 2 deletions msbuild/Xamarin.Shared/Xamarin.Shared.props
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ Copyright (C) 2020 Microsoft. All rights reserved.
<_BundlerDebug Condition="'$(_BundlerDebug)' == ''">false</_BundlerDebug>

<!-- Extra arguments -->
<_BundlerArguments Condition="'$(_PlatformName)' == 'macOS'">$(MonoBundlingExtraArgs)</_BundlerArguments>
<_BundlerArguments Condition="'$(_PlatformName)' != 'macOS'">$(MtouchExtraArgs)</_BundlerArguments>
<AppBundleExtraOptions Condition="'$(_PlatformName)' == 'macOS'">$(AppBundleExtraOptions) $(MonoBundlingExtraArgs)</AppBundleExtraOptions>
<AppBundleExtraOptions Condition="'$(_PlatformName)' != 'macOS'">$(AppBundleExtraOptions) $(MtouchExtraArgs)</AppBundleExtraOptions>

<!-- NoSymbolStrip -->
<!-- Xamarin.Mac never had an equivalent for MtouchNoSymbolStrip and was never stripped -> true -->
Expand Down
2 changes: 1 addition & 1 deletion msbuild/Xamarin.Shared/Xamarin.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2015,7 +2015,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
<Target Name="_ParseBundlerArguments">
<ParseBundlerArguments
Aot="@(_AotArguments)"
ExtraArgs="$(_BundlerArguments)"
ExtraArgs="$(AppBundleExtraOptions)"
NoSymbolStrip="$(NoSymbolStrip)"
NoDSymUtil="$(NoDSymUtil)"
PackageDebugSymbols="$(PackageDebugSymbols)"
Expand Down
2 changes: 1 addition & 1 deletion msbuild/Xamarin.Shared/Xamarin.iOS.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ Copyright (C) 2013-2016 Xamarin. All rights reserved.
ExecutableName="$(_ExecutableName)"
CompiledEntitlements="$(_CompiledEntitlements)"
Debug="$(_BundlerDebug)"
ExtraArgs="$(_BundlerArguments)"
ExtraArgs="$(AppBundleExtraOptions)"
FastDev="$(MtouchFastDev)"
HttpClientHandler="$(MtouchHttpClientHandler)"
I18n="$(MtouchI18n)"
Expand Down
Loading

0 comments on commit 84638c6

Please sign in to comment.