diff --git a/src/entries/sourcegear_bridge.html b/src/entries/sourcegear_bridge.html new file mode 100644 index 0000000..a19c739 --- /dev/null +++ b/src/entries/sourcegear_bridge.html @@ -0,0 +1,264 @@ +--- +layout: post +title: SourceGear Bridge preview: Swift with .NET in Xcode +date: 2021-09-20 12:00:00 +keywords: swift dotnet front aspnet bridge +teaser: TODO +--- + +
For quite some time I have been working on various +things involving interop between .NET and other ecosystems. +If you follow my blog or tweets, you've seen me talk about "Llama", +and then "Alpaca", and so on. Generally speaking, I have +referred to these efforts as my exploratory projects.
+ +But now, one of those efforts (the one previously called "Alpaca") is +now called "SourceGear Bridge", and the new name reflects +our intention to develop this into a production-ready +solution with support available. (Note that I'm not +saying that it is production-ready now, simply that now we +are focused on getting it there.)
+ +In broad brush strokes, we will describe SourceGear Bridge in terms of +delivering great interop between .NET and other things. +We hope to expand to more languages in the future, but for now the +primary focus is Swift. And for that reason, most of +my attention lately has been on providing the user +experience that Swift developers expect. In +other words, everything needs to work well in Xcode on a +Mac.
+ +Preview release 0.2.0 is now available, and in this +blog entry I will walk through a simple demo. If you +want to follow along on a Mac, you will need Xcode 13, +configured to run Swift 5.5 on the command-line. The +non-Xcode parts of this demo will also work on Linux +if you install a Swift 5.5 development snapshot.
+ +And of course you will need .NET 6 rc1:
+ +https://dotnet.microsoft.com/download/dotnet/6.0
+ +(If you're on a Mac, I recommend using the installer.)
+ +For this blog entry, I'm going to start with the command-line +for a while and then switch to Xcode a bit later.
+ +The .NET command-line interface is called dotnet
, and its
+basic design is similar to swift
, where the first argument is
+the name of a command.
+Building with .NET? Use dotnet build
.
+Building with Swift? Use swift build
.
To create a new web project with .NET,
+you would use dotnet new
:
+mkdir foo +cd foo +dotnet new web -lang C# ++ +
(The -lang C#
argument is usually optional because
+it's the default language for .NET.)
This will result in several files being generated.
+The two important ones are foo.csproj
(the project
+file) and Program.cs
(the source file).
You can take a look at these files if you like, but +I just wanted to mention them and move on to focus on how +to create the equivalent project in Swift.
+ +Many things about the .NET command-line interface
+can be customized, including support for other languages.
+So the first thing we want to do here is to install
+some templates to let dotnet
know about Swift:
+dotnet new --install sourcegear.bridge.swift.templates ++ +
In the command just above, sourcegear.bridge.swift.templates
+is the ID of a package on nuget.org
. NuGet is the package
+manager for .NET.
Having installed the templates, we can can list the templates like this:
+ ++dotnet new --list ++ +
And we can see that a couple of new templates are now available, +both of which are for the Swift language.
+ +Let's use one of those templates to create the same project we did above, +except now in Swift: + +
+mkdir bar +cd bar +dotnet new web -lang Swift ++ +
This should create three things:
+ ++bar.swiftproj +Package.swift +Sources/ ++ +
The presence of the Package.swift
file indicates that this
+directory is now a SwiftPM package, and can be used with any
+Swift tooling that supports Swift Package Manager, including the swift
command
+line tool as well as Xcode.
(For .NET devs: Package.swift
looks like a Swift
+source file but it's actually a project file, the equivalent of
+a csproj
.)
The presence of the bar.swiftproj
file means that this
+directory is also a .NET project, and can be used with
+the dotnet
command-line interface as well.
+This file is the Swift equivalent of the
+foo.csproj
file. It's in a format called MSBuild, but
+Swift developers shouldn't need to worry about that. Mostly
+what this file does is explain to MSBuild how to get the information it
+needs from Package.swift
.
The source code for project is in
+Sources/app/Program.swift
just as one would expect for a
+Swift package. We'll take a look at the code itself
+later when we open it up in Xcode.
Continuing on the command-line a bit more, try swift build
.
+The first time you build the project
+will take longer because it has to compile all the bindings.
+The result of the build should appear as a shared library (on Mac, a dylib) in
+.build/debug/
.
The compilation model here is to build the +Swift code into a shared library. Then the tooling +generates a trivial .NET host program that +(1) initializes the bindings so that Swift +can call .NET APIs, and (2) passes control to the +the Swift code.
+ +Now we can do dotnet run
and you should see messages indicating
+that the web server is running on port 5000:
+TODO replace this +eric@LAPTOP-6A6HQVJ9:~/dev/bridge/swift/projects/minimal$ dotnet run +info: Microsoft.Hosting.Lifetime[14] + Now listening on: http://localhost:5000 +info: Microsoft.Hosting.Lifetime[14] + Now listening on: https://localhost:5001 +info: Microsoft.Hosting.Lifetime[0] + Application started. Press Ctrl+C to shut down. +info: Microsoft.Hosting.Lifetime[0] + Hosting environment: Production +info: Microsoft.Hosting.Lifetime[0] + Content root path: /home/eric/dev/bridge/swift/projects/minimal ++ +
And now you should be able to go to a browser and access http://localhost:5000/
+to see: Hello World!
Okay, let's try opening the Package.swift
file in Xcode,
+either from Finder, or from the command-line, like this:
+open Package.swift ++ +
Here in Xcode, things should mostly work as you would expect, +like any other Swift project, with syntax coloring and code completion +and other niceties.
+ +TODO screen shot of the code in Program.swift + +Most .NET APIs have documentation comments, and that +information is propagated through the bindings to make it +available with Quick Help:
+ +TODO screen shot of Quick Help + +Code completion is especially helpful for a framework +like ASP.NET Core, which has many features and a large +surface area:
+ +TODO screen shot of code completion for a .NET API + +Looking at Program.swift
, there are import
statements, typealias
declarations, some
+error handling, and a function declaration, but the essence of the matter is:
+let app = try WebApplication.Create() + +try app.MapGet( + pattern: "/", + handler: + Func+ ++ { + () -> System.String in + "Hello World!" + } + ); + +try app.Run(); +
Three API calls:
+ +One to create the web app
One to set things up
One to run it
The most interesting call here is the second one, the call to MapGet(pattern:handler:)
, which basically says "when somebody accesses the root URL, run this closure, which returns a string".
One thing you might notice is that the C# version is more concise +than its Swift equivalent. Both are using the same 3 +API calls from the so-called +"minimal" APIs which are new in ASP.NET Core 6. +TODO +https://www.hanselman.com/blog/minimal-apis-at-a-glance-in-net-6 +But currently the Swift incarnation ends up more verbose. +There are some things we can do in the future to +make things tighter. +A full explanation would fill another blog entry, but +for now I'll highlight a few of the ways that the Swift and C# code differ:
+ +It is common in Swift to use named parameters. The MapGet()
call shows this, with pattern:
and handler:
labels for its arguments.
The handler
argument for MapGet(pattern:handler:)
is a Swift closure. In the C# version, the lambda is much shorter, because it makes use of a new C# 10 compiler feature to infer delegate types..
Swift doesn't have namespaces. We cope with this limitation by using nested types (which kinda works), and the Swift typealias
feature (which is actually quite powerful).
If you're a C# developer, I'd like to clarify that Swift doesn't have exceptions even though this code makes it look otherwise.
We have lots to do as we move SourceGear Bridge toward a production-ready release. +Here's a few things in progress but not yet finished:
+ +F#-style object expressions
Support for async/await (new in Swift 5.5)
Fix handling of formatting and other inline tags in documentation comments
Automatic generation of bindings for nuget packages
Test this stuff on Windows and see if it works
Enjoy!
+