WindowsアプリをSwiftで作成する。
前回はこちらです。
方法に関して
Windows向けのSwiftツールチェーンがダウンロード可能になったことによりツールチェーンを使って様々な方法でアプリを作成することができると思います。
自分の場合はGUIが必要でしたので方法として情報の多いCMakeとninjaを使った方法で作成していくことにしました。
今回はCMakeとninjaを使った方法でのアプリのビルドを行います。
自身はCMake初心者なので設定内容を詳しく把握しておりません。そのため実行は自己責任でお願いします。基本的にはこちらのリポジトリの記述内容を参考にしています。
最低限の「Swiftファイルのコンパイルと実行ファイルの作成」のみを行いたい方はこちらの動画を参考にしていただきたいです。
環境
OS: Windows 11 Version 22H2
Tool Chain: Swift 5.7
Editor: Visual Studio Code(以下VSCode)
前回からWindowsバージョンを11に上げましたが準備としては前回と同じようにできます。
リポジトリのクローン
今回CMakeとninjaを使った方法でのアプリのビルドを行うにあたり、GUIを簡単に作成できる下記のリポジトリを利用することにしました。
こちらのreadme通りに進めれば既に準備を行っている環境でしたら問題なくUICatalog.exeが生成され、実行もできます。
readmeの翻訳では少しつまらないので、新規でCMakeListにビルド対象をクローンしたリポジトリ内に加えてビルドを行おうと思います。
お好きなフォルダにクローンができたらVSCodeで「swift-win32」フォルダを開きます。
新規でCMakeListにビルド対象をクローンしたリポジトリ内に加える
VSCodeで「swift-win32/Examples/」を開きます。この配下に作りたいアプリ名でフォルダを追加します。ここではDemoAppという名前で作成していきます。
このフォルダ内に追加するファイルはCMakeLists.txt, Info.plist, DemoApp.exe.manifest, DemoApp.swift4つです。
CMakeLists.txt
add_executable(DemoApp
DemoApp.swift)
add_custom_command(TARGET DemoApp POST_BUILD
COMMAND
mt -nologo -manifest ${CMAKE_CURRENT_SOURCE_DIR}/DemoApp.exe.manifest -outputresource:$<TARGET_FILE:DemoApp>
COMMAND
${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist $<TARGET_FILE_DIR:DemoApp>)
# FIXME(SR-12683) `@main` requires `-parse-as-library`
target_compile_options(DemoApp PRIVATE
-parse-as-library)
target_link_libraries(DemoApp PRIVATE
SwiftWin32)
Info.plist
<?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>ApplicationSceneManifest</key>
<dict>
<key>ApplicationSupportsMultipleScenes</key>
<false/>
<key>SceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>SceneConfigurationName</key>
<string>Default Configuration</string>
<key>SceneDelegateClassName</key>
<string>DemoApp.DemoApp</string>
</dict>
</array>
</dict>
</dict>
</dict>
</plist>
DemoApp.exe.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<asmv1:assembly
manifestVersion="1.0"
xmlns:asmv1="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<!-- assembly identity -->
<asmv1:assemblyIdentity
name="org.compnerd.swift-win32.DemoApp"
processorArchitecture="*"
type="win32"
version="1.0.0.0"/>
<!-- application specific settings -->
<asmv3:application xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<windowsSettings>
<dpiAwareness xmlns="">PerMonitorV2</dpiAwareness>
<dpiAware>true</dpiAware>
</windowsSettings>
</asmv3:application>
<asmv1:description>DemoApp</asmv1:description>
<!-- Common Control Support -->
<asmv1:dependency>
<asmv1:dependentAssembly>
<asmv1:assemblyIdentity
language="*"
name="Microsoft.Windows.Common-Controls"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
type="win32"
version="6.0.0.0"
/>
</asmv1:dependentAssembly>
</asmv1:dependency>
</asmv1:assembly>
DemoApp.swift
import SwiftWin32
import Foundation
@main
final class DemoApp: ApplicationDelegate, SceneDelegate {
var window: Window!
lazy var label = Label(frame: Rect(x: 150.0, y: 220.0, width: 400.0, height: 80.0))
func scene(_ scene: Scene, willConnectTo session: SceneSession, options: Scene.ConnectionOptions) {
guard let windowScene = scene as? WindowScene else { return }
let size: Size = Size(width: 500, height: 500)
windowScene.sizeRestrictions?.minimumSize = size
windowScene.sizeRestrictions?.maximumSize = size
self.window = Window(windowScene: windowScene)
window.rootViewController = ViewController()
window.rootViewController?.title = "Demo App"
window.addSubview(self.label)
self.label.text = "Hello, World"
if let consolasFont = Font(name: "Consolas", size: 24) {
self.label.font = consolasFont
} else {
self.label.font = Font(name: "Cascadia Code", size: 24)
}
window.makeKeyAndVisible()
}
func sceneDidBecomeActive(_: Scene) {
print("Good morning!")
}
func sceneWillResignActive(_: Scene) {
print("Good night!")
}
func applicationWillTerminate(_: Application) {
print("Goodbye cruel world!")
}
}
swift-win32/Examples/CMakeLists.txt
「swift-win32/Examples/CMakeLists.txt」に下記を追加します。
add_subdirectory(Calculator)
add_subdirectory(UICatalog)
+ add_subdirectory(DemoApp)
ビルドする
スタートから「x64 Native Tools Command Prompt for VS 2022」を探し出しクリックで実行します。
ウィンドウが開いたらcdコマンドで「swift-win32」配下まで移動します。
CMakeのビルド
cmake -B build -D BUILD_SHARED_LIBS=YES -D CMAKE_BUILD_TYPE=Release -D CMAKE_Swift_FLAGS="-sdk %SDKROOT%" -G Ninja -S .
「Build files have been written to:」でビルドした内容が格納されたパスが表示されたら成功です。
Ninjaのビルド
ninja -C build SwiftWin32 DemoApp
作成中からコマンド入力可能状態になればビルドは完了です。もしエラーが出た場合はその指示に従ってファイルを修正します。
アプリの起動
CMakeのビルド時に「Build files have been written to:」で示されたパス内のbinの中に.exeがついた実行ファイル---ここではDemoApp.exe---が格納されているのでダブルクリックします。
中心あたりに「Hello, World」とあるウィンドウが開けば成功です。
次回はswift-win32で実現されているUIパーツについて書く予定です。
Discussion