---
title: Support External Display #osaka_swift
tags:  #osaka_swift  
author: [Kazuya Ueoka](https://convert.docswell.com/user/fromkk)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/YJ9KPLXDJ3.jpg?width=480
description: Osaka.swift #1 で開催したクイズ大会の説明資料です。
published: January 14, 25
canonical: https://convert.docswell.com/s/fromkk/5R29GW-osaka_swift
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/YJ9KPLXDJ3.jpg)

Support External Display
@fromkk


# Page. 2

![Page Image](https://bcdn.docswell.com/page/GJ8RDX257D.jpg)

•
阪 まれ、 阪育ち、今は埼
に住んでいます。
• 1986/02/20
県所沢市
まれ
• @fromkk
(X/note/Instagram/GitHub/
Zenn/Qiita etc…)
玉
生
大
己
2
生
自
大
紹介


# Page. 3

![Page Image](https://bcdn.docswell.com/page/LJLKM8237R.jpg)

クイズアプリを作りました
• 名前はQuiz Match
• オーナーがクイズを作成
• メンバーは招待コードからクイズに参加すること
ができる
• 1.1.0からChatGPT経由でお題を作成できる
ように
3


# Page. 4

![Page Image](https://bcdn.docswell.com/page/47M2Y68M7W.jpg)

外部ディスプレイ対応
• 前提: SwiftUIベース・TCA使
• @UIApplicationDelegateAdaptorでAppDelegate
対応
@UIApplicationDelegateAdaptor(AppDelegate.self)
var delegate: AppDelegate
用
4


# Page. 5

![Page Image](https://bcdn.docswell.com/page/P7RM9P5LJ9.jpg)

Info.plistにApplication Scene Manifestを追加
5


# Page. 6

![Page Image](https://bcdn.docswell.com/page/PJXVQ3X6EX.jpg)

AppDelegateでSceneDelegateをサポート
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -&gt; UISceneConfiguration {
if connectingSceneSession.role == .windowExternalDisplayNonInteractive {
return UISceneConfiguration(
name: &quot;External Configuration&quot;,
sessionRole: connectingSceneSession.role)
} else {
return UISceneConfiguration(
name: &quot;Default Configuration&quot;,
sessionRole: connectingSceneSession.role)
}
}
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/3JKQ9YWGJD.jpg)

SceneDelegateで外部ディスプレイの場合のUIを作成
import ComposableArchitecture
import SwiftUI
import UIKit
final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
guard session.configuration.role == .windowExternalDisplayNonInteractive else {
return
}
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(
rootView: QuizExternalView(
store: Store(
initialState: QuizExternal.State()
) {
QuizExternal()
}))
window.isHidden = false
self.window = window
}
}
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/LE3PW915J5.jpg)

困った点
• 本体側のViewと外部ディスプレイに表
る必要がある
する内容を同期させ
• 通常のSwiftUIなら@EnvironmentObjectを利
参考: https://useyourloaf.com/blog/swiftuisupporting-external-screens/
用
示
8


# Page. 9

![Page Image](https://bcdn.docswell.com/page/8EDLKGXYJG.jpg)

本体と外部ディスプレイのデータの同期
• TCAではSharing Stateが利
できそう
参考: https://pointfreeco.github.io/swiftcomposable-architecture/main/
documentation/composablearchitecture/
sharingstate/
• 1.17.0からはSharingが別パッケージに
用
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/V7P2K3P278.jpg)

実装
extension SharedReaderKey where Self == InMemoryKey&lt;CurrentQuestion.State?&gt; {
static var currentQuestion: Self {
inMemory(&quot;currentQuestion&quot;)
}
}
開催中のクイズ画
外部ディスプレイ
@Reducer
struct QuizOpening {
@ObservableState
struct State: Equatable, Sendable {
@Shared(.currentQuestion)
var currentQuestion: CurrentQuestion.State?
@Reducer
struct QuizExternal {
@ObservableState
struct State: Equatable, Sendable {
@SharedReader(.currentQuestion)
var currentQuestion: CurrentQuestion.State?
}
func setCurrentQuestion(_ currentQuestion: CurrentQuestion.State?) {
$currentQuestion.withLock {
$0 = currentQuestion
}
}
}
面
}


# Page. 11

![Page Image](https://bcdn.docswell.com/page/2JVDV42XJQ.jpg)

UIの 夫
• 外部ディスプレイのサイズは様々
• どのサイズでも同じように
えて欲しい
• GeometryReaderでサイズを取得し、基準となるサイズから
計算
var body: some View {
GeometryReader { context in
Text(currentQuestion.question.text)
.font(.system(size: 48 * aspect(context)))
.frame(maxWidth: .infinity, alignment: .leading)
}
}
private func aspect(_ context: GeometryProxy) -&gt; CGFloat {
context.size.width / 1920
}
見
工
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/5EGYL1RREL.jpg)



# Page. 13

![Page Image](https://bcdn.docswell.com/page/4JQMYDVY7P.jpg)

まとめ
• クイズアプリで外部ディスプレイ対応をしてみました
• 本体と外部ディスプレイの同期にTCAのSharingを利
みました
• UIの調整にはGeometryReaderを利
して計算
用
用
13
して


# Page. 14

![Page Image](https://bcdn.docswell.com/page/K74VWZMZJ1.jpg)

大
ここからは@akidon0000作の
クイズ 会🎉


