├── Example
├── WindowManagementProject
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── WindowManagementProject.entitlements
│ ├── CodeFileDocument.swift
│ ├── Info.plist
│ ├── ContentView.swift
│ └── WindowManagementProjectApp.swift
└── WindowManagementProject.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
│ └── project.pbxproj
├── .gitignore
├── Sources
└── WindowManagement
│ ├── DocumentGroup
│ ├── NSDocumentWatcher.swift
│ ├── AutoDismissView.swift
│ ├── NSApp+openDocument.swift
│ └── NSDocumentGroup.swift
│ ├── OpenWindow
│ ├── NSMenuItem+OpenWindowAction.swift
│ ├── SceneID.swift
│ ├── NSApp+openWindow.swift
│ └── OpenWindowCommand.swift
│ ├── Scene+Environment.swift
│ ├── NSApp+RestoreWindow.swift
│ ├── NSWindowEnvironmentKey.swift
│ ├── WindowManagement.swift
│ ├── View+Window.swift
│ ├── NSWindow+Swizzle.swift
│ ├── NSWindow+Extensions.swift
│ └── Scene+Window.swift
├── Tests
└── WindowManagementTests
│ └── WindowManagementTests.swift
├── LICENSE
├── Package.swift
└── README.md
/Example/WindowManagementProject/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject/WindowManagementProject.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/DocumentGroup/NSDocumentWatcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | enum NSDocumentMapper {
11 | static var mapping: [URL: () -> NSDocument?] = [:]
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/OpenWindow/NSMenuItem+OpenWindowAction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | extension NSMenuItem {
11 | static var openWindowAction: ((SceneID?, (any Codable & Hashable)?) -> Void)?
12 | }
13 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/OpenWindow/SceneID.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import Foundation
9 |
10 | /// Unique ID of each scene.
11 | public struct SceneID {
12 | public let id: String
13 |
14 | public init(_ id: String) {
15 | self.id = id
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/Scene+Environment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public extension SwiftUI.Scene {
11 | @inlinable
12 | func modifier(_ modifier: T) -> ModifiedContent {
13 | return .init(content: self, modifier: modifier)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/WindowManagementTests/WindowManagementTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import WindowManagement
3 |
4 | final class WindowManagementTests: XCTestCase {
5 | func testExample() throws {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "swiftui-windowmanagement",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/Wouter01/SwiftUI-WindowManagement",
7 | "state" : {
8 | "revision" : "03642ad06a3aa51e8284eb22146a208269cdc1ca",
9 | "version" : "2.1.0"
10 | }
11 | }
12 | ],
13 | "version" : 2
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/NSApp+RestoreWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public extension App {
11 |
12 | /// Enable or disable window restoring after quitting. This is the same behavior as when an app is restored after reboot or force quit.
13 | func enableWindowSizeSaveOnQuit(_ enabled: Bool = true) {
14 | UserDefaults.standard.setValue(enabled, forKey: "NSQuitAlwaysKeepsWindows")
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/NSWindowEnvironmentKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 06/01/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public extension EnvironmentValues {
11 | var window: NSWindowEnvironmentKey.Value {
12 | get { self[NSWindowEnvironmentKey.self] }
13 | set { self[NSWindowEnvironmentKey.self] = newValue }
14 | }
15 | }
16 |
17 | public struct NSWindowEnvironmentKey: EnvironmentKey {
18 | public static var defaultValue = NSWindow()
19 | }
20 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject/CodeFileDocument.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeFileDocument.swift
3 | // WindowManagementProject
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import AppKit
9 | import WindowManagement
10 |
11 | @objc(CodeFileDocument)
12 | final class CodeFileDocument: NSDocument {
13 |
14 | override nonisolated func read(from data: Data, ofType typeName: String) throws {
15 |
16 | }
17 |
18 | override func makeWindowControllers() {
19 | if let window = NSApp.openDocument(self), let windowController = window.windowController {
20 | addWindowController(windowController)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/DocumentGroup/AutoDismissView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AutoDismissView.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct AutoDismissView: View {
11 | @Environment(\.dismiss) var dismissWindow
12 |
13 | var defaultAction: (() -> Void)?
14 |
15 | var body: some View {
16 | VStack {}
17 | .task {
18 | Task {
19 | if let defaultAction {
20 | defaultAction()
21 | } else {
22 | NSDocumentController.shared.openDocument(nil)
23 | }
24 | }
25 | dismissWindow()
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDocumentTypes
6 |
7 |
8 | CFBundleTypeExtensions
9 |
10 | h
11 |
12 | CFBundleTypeName
13 | C header file
14 | CFBundleTypeOSTypes
15 |
16 | TEXT
17 | utxt
18 | TUTX
19 | ****
20 |
21 | CFBundleTypeRole
22 | Editor
23 | NSDocumentClass
24 | CodeFileDocument
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // WindowManagementProject
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 | import WindowManagement
10 |
11 | struct ContentView: View {
12 | @Environment(\.window) var window
13 | var body: some View {
14 | VStack {
15 | Text("Window title: \(window.title)")
16 | Button("Open Document (only .h file supported)") {
17 | NSDocumentController.shared.openDocument(nil)
18 | }
19 |
20 | Button("Open Settings") {
21 | NSApp.openSettings()
22 | }
23 | }
24 | .padding()
25 | }
26 | }
27 |
28 | struct ContentView_Previews: PreviewProvider {
29 | static var previews: some View {
30 | ContentView()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/WindowManagement.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct WM {
4 | static var currentIdentifier: String = "" {
5 | didSet {
6 | NSWindow.swizzleAll()
7 | }
8 | }
9 | static var availableWindows: [NSWindow] = []
10 | static var modifications: [String: WindowModifications] = [:]
11 | static var didSwizzle = false
12 | }
13 |
14 | struct WindowModifications {
15 | var styleMask: NSWindow.StyleMask?
16 | var windowButtonsEnabled: [NSWindow.ButtonType: WindowButton] = [:]
17 | var collectionBehavior: NSWindow.CollectionBehavior?
18 | var movableByBackground: Bool?
19 | var movable: Bool?
20 | var tabbingMode: NSWindow.TabbingMode?
21 | var backgroundColor: NSColor?
22 | var titlebarAppearsTransparent: Bool?
23 | var disableRestoreOnLaunch: Bool?
24 | var animationBehavior: NSWindow.AnimationBehavior?
25 |
26 | struct WindowButton {
27 | var enabled = true
28 | var isHidden = false
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Wouter Hennen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/OpenWindow/NSApp+openWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSApp+openWindow.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public extension NSApplication {
11 | /// Open a SwiftUI Window with given ID.
12 | func openWindow(_ id: SceneID) {
13 | NSMenuItem.openWindowAction?(id, nil)
14 | }
15 |
16 | /// Open a SwiftUI Window with given ID and value.
17 | func openWindow(_ id: SceneID, value: any Codable & Hashable) {
18 | NSMenuItem.openWindowAction?(id, value)
19 | }
20 |
21 | /// Open a SwiftUI Window with given value.
22 | func openWindow(value: any Codable & Hashable) {
23 | NSMenuItem.openWindowAction?(nil, value)
24 | }
25 |
26 | /// Open the SwiftUI Settings window.
27 | func openSettings() {
28 | let eventSource = CGEventSource(stateID: .hidSystemState)
29 | let keyCommand = CGEvent(keyboardEventSource: eventSource, virtualKey: 0x2B, keyDown: true)
30 | guard let keyCommand else { return }
31 |
32 | keyCommand.flags = .maskCommand
33 | let event = NSEvent(cgEvent: keyCommand)
34 | guard let event else { return }
35 |
36 | NSApp.sendEvent(event)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.7
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "WindowManagement",
8 | platforms: [
9 | .macOS(.v12)
10 | ],
11 | products: [
12 | // Products define the executables and libraries a package produces, and make them visible to other packages.
13 | .library(
14 | name: "WindowManagement",
15 | targets: ["WindowManagement"]),
16 | ],
17 | dependencies: [
18 | // Dependencies declare other packages that this package depends on.
19 | // .package(url: /* package url */, from: "1.0.0"),
20 | ],
21 | targets: [
22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
23 | // Targets can depend on other targets in this package, and on products in packages this package depends on.
24 | .target(
25 | name: "WindowManagement",
26 | dependencies: [],
27 | swiftSettings: []),
28 | .testTarget(
29 | name: "WindowManagementTests",
30 | dependencies: ["WindowManagement"]),
31 | ]
32 | )
33 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/OpenWindow/OpenWindowCommand.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @available(macOS 13.0, *)
11 | struct OpenWindowCommand: Commands {
12 | @Environment(\.openWindow) var openWindow
13 |
14 | public var body: some Commands {
15 | CommandGroup(after: .windowArrangement) {
16 | Divider()
17 | .hidden()
18 | .task {
19 | NSMenuItem.openWindowAction = { scene, value in
20 | switch (scene, value) {
21 | case (.some(let id), .none):
22 | openWindow(id: id.id)
23 | case (.none, .some(let data)):
24 | openWindow(value: data)
25 | case let (.some(id), .some(data)):
26 | openWindow(id: id.id, value: data)
27 | default:
28 | break
29 | }
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
36 | public extension Scene {
37 | /// Required to enable window opening from NSApp. Call this method only once, from one scene.
38 | @available(macOS 13.0, *)
39 | func enableOpenWindow() -> some Scene {
40 | commands {
41 | OpenWindowCommand()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/DocumentGroup/NSApp+openDocument.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSApp+openDocument.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import AppKit
9 |
10 | public extension SceneID {
11 |
12 | static func document(_ type: Document.Type) -> SceneID {
13 | SceneID("document" + String(describing: type))
14 | }
15 | }
16 |
17 | public extension NSApplication {
18 | /// Open a new NSDocumentGroup Window with given type.
19 | func openDocument(_ type: Document.Type, _ document: Document) -> NSWindow? {
20 | guard let fileURL = document.fileURL else { return nil }
21 | NSDocumentMapper.mapping[fileURL] = { [weak document] in document }
22 | NSApp.openWindow(.document(type), value: fileURL)
23 |
24 | let window = NSApp
25 | .windows
26 | .filter { $0.identifier?.rawValue.starts(with: "document" + String(describing: Document.self)) ?? false }
27 | .filter { $0.windowController?.document == nil }
28 | .first
29 |
30 | window?.title = fileURL.absoluteString
31 | window?.representedURL = fileURL
32 | window?.identifier = .init(fileURL.absoluteString)
33 |
34 | return window
35 | }
36 |
37 | /// Open a new NSDocumentGroup Window.
38 | func openDocument(_ document: Document) -> NSWindow? {
39 | openDocument(Document.self, document)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/DocumentGroup/NSDocumentGroup.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSDocumentGroup.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | /// Creates a SwiftUI WindowGroup for an NSDocument class. This works similar to SwiftUI's DocumentGroup.
11 | /// The defaultAction closure gets called when the user clicks the "+" button on a window tab bar.
12 | @available(macOS 13.0, *)
13 | public struct NSDocumentGroup: Scene {
14 |
15 | var type: NSDocumentType.Type
16 | var content: (NSDocumentType) -> Content
17 | var defaultAction: (() -> Void)?
18 |
19 | public init(for type: NSDocumentType.Type, content: @escaping (NSDocumentType) -> Content, defaultAction: (() -> Void)? = nil) {
20 | self.type = type
21 | self.content = content
22 | self.defaultAction = defaultAction
23 | }
24 |
25 | public var body: some Scene {
26 | let id = "document" + String(describing: type)
27 | WindowGroup(id: id, for: URL.self) { url in
28 | if let url = url.wrappedValue {
29 | let document = NSDocumentMapper.mapping[url]
30 | if let document = document?() as? NSDocumentType {
31 | content(document)
32 | } else {
33 | Text("Could not open document (\(url))")
34 | }
35 | } else {
36 | AutoDismissView(defaultAction: defaultAction)
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/View+Window.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 06/01/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public extension View {
11 | /// Inject the settings window into the environment.
12 | func injectSettingsWindow() -> some View {
13 | if #available(macOS 13, *) {
14 | return self.modifier(Injectwindow(identifier: "com_apple_SwiftUI_Settings_window"))
15 | } else {
16 | return self.modifier(Injectwindow(identifier: "SwiftUIWindow"))
17 | }
18 | }
19 |
20 | /// Inject the current window into the environment.
21 | func injectWindow(_ identifier: String) -> some View {
22 | return self.modifier(Injectwindow(identifier: identifier))
23 | }
24 |
25 | /// Inject the current window into the environment.
26 | func injectWindow(_ identifier: SceneID) -> some View {
27 | return self.modifier(Injectwindow(identifier: identifier.id))
28 | }
29 | }
30 |
31 | struct Injectwindow: ViewModifier {
32 | var identifier: String
33 | @State var window = NSWindow()
34 |
35 | func body(content: Content) -> some View {
36 | content
37 | .environment(\.window, window)
38 | .task {
39 | if window.identifier == nil {
40 | window = WM.availableWindows.first { $0.identifier?.rawValue.starts(with: identifier) ?? false } ?? NSWindow()
41 | WM.availableWindows = Array(WM.availableWindows.dropFirst())
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/NSWindow+Swizzle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 06/01/2023.
6 | //
7 |
8 | import AppKit
9 |
10 | extension NSWindow {
11 |
12 | static var didSwizzle = false
13 |
14 | /// Always use an active appearance for the window.
15 | /// This allows SwiftUI Materials to have an always active state, instead of following the window key state.
16 | /// WARNING: Enabling this can have unwanted sideeffects.
17 | /// This option is enabled globally for all windows.
18 | public static var alwaysUseActiveAppearance = false
19 |
20 | static func swizzleAll() {
21 | if !didSwizzle {
22 | didSwizzle = true
23 | swizzleMethod(#selector(setter: NSWindow.identifier), #selector(NSWindow.setSwizzledIdentifier))
24 | swizzleMethod(#selector(setter: NSWindow.styleMask), #selector(NSWindow.setSwizzledStyleMask))
25 |
26 | if alwaysUseActiveAppearance {
27 | swizzleMethod(Selector(("hasKeyAppearance")), #selector(getter: NSWindow.hasKeyAppearanceOverride))
28 | swizzleMethod(Selector(("hasMainAppearance")), #selector(getter: NSWindow.hasKeyAppearanceOverride))
29 | }
30 | }
31 | }
32 |
33 | @objc var hasKeyAppearanceOverride: Bool {
34 | true
35 | }
36 |
37 | static func swizzleMethod(_ original: Selector, _ replacement: Selector) {
38 |
39 | let originalMethodSet = class_getInstanceMethod(self, original)
40 | let swizzledMethodSet = class_getInstanceMethod(self, replacement)
41 |
42 | method_exchangeImplementations(originalMethodSet!, swizzledMethodSet!)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject/WindowManagementProjectApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WindowManagementProjectApp.swift
3 | // WindowManagementProject
4 | //
5 | // Created by Wouter Hennen on 11/06/2023.
6 | //
7 |
8 | import SwiftUI
9 | import WindowManagement
10 |
11 | extension SceneID {
12 | static let firstWindowGroup = SceneID("firstWindowGroup")
13 | }
14 |
15 | @main
16 | struct WindowManagementProjectApp: App {
17 |
18 | @NSApplicationDelegateAdaptor var delegate: AppDelegate
19 |
20 | init() {
21 | enableWindowSizeSaveOnQuit(true)
22 | }
23 |
24 | var body: some Scene {
25 | Group {
26 | WindowGroup(id: SceneID.firstWindowGroup.id) {
27 | ContentView()
28 | .frame(maxWidth: .infinity, maxHeight: .infinity)
29 | .background(.regularMaterial)
30 | .injectWindow(.firstWindowGroup)
31 | }
32 | .register(.firstWindowGroup)
33 | .titlebarAppearsTransparent()
34 | .movableByBackground()
35 | .windowButton(.closeButton, hidden: true)
36 | .backgroundColor(.systemGray.withAlphaComponent(0.001))
37 |
38 | Settings {
39 | VStack {
40 | Text("HEllo")
41 | }
42 | .frame(minWidth: 300, minHeight: 300)
43 | }
44 | .enableOpenWindow()
45 |
46 | NSDocumentGroup(for: CodeFileDocument.self) { document in
47 | Text(document.fileURL?.absoluteString ?? "")
48 | }
49 | .register(.document(CodeFileDocument.self))
50 | .transition(.documentWindow)
51 | .movableByBackground()
52 | .windowButton(.miniaturizeButton, hidden: true)
53 | }
54 | .environment(\.controlSize, .large)
55 | }
56 | }
57 |
58 | class AppDelegate: NSObject, NSApplicationDelegate {
59 | func applicationDidFinishLaunching(_ notification: Notification) {
60 | // NSApp.openSettings()
61 | // NSApp.openWindow(.firstWindowGroup)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/NSWindow+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 06/01/2023.
6 | //
7 |
8 | import AppKit
9 |
10 | extension NSWindow {
11 |
12 | @objc func setSwizzledStyleMask(_ newValue: StyleMask) {
13 | if let id = identifier {
14 | let key = id.rawValue.prefix(while: { $0 != "-"})
15 | let value = WM.modifications[String(key)]
16 | if let styleMask = value?.styleMask {
17 | self.setSwizzledStyleMask(styleMask.union(newValue.intersection(.fullScreen)))
18 | } else {
19 | self.setSwizzledStyleMask(newValue)
20 | }
21 | } else {
22 | self.setSwizzledStyleMask(newValue)
23 | }
24 | }
25 |
26 | @objc func setSwizzledIdentifier(_ newValue: NSUserInterfaceItemIdentifier?) {
27 | self.setSwizzledIdentifier(newValue)
28 |
29 | if !WM.availableWindows.contains(self) {
30 | WM.availableWindows.append(self)
31 | }
32 |
33 | if let id = newValue {
34 |
35 | let key = id.rawValue.prefix(while: { $0 != "-"})
36 |
37 | let value = WM.modifications[String(key)]
38 |
39 | guard let value else { return }
40 |
41 | if let styleMask = value.styleMask {
42 | self.setSwizzledStyleMask(styleMask.union(.titled)) // .titled is required for SwiftUI
43 | }
44 |
45 | if let behavior = value.collectionBehavior {
46 | collectionBehavior = behavior
47 | }
48 |
49 | if let movableByBackground = value.movableByBackground {
50 | isMovableByWindowBackground = movableByBackground
51 | }
52 |
53 | if let movable = value.movable {
54 | isMovable = movable
55 | }
56 |
57 | if let tabbingMode = value.tabbingMode {
58 | self.tabbingMode = tabbingMode
59 | }
60 |
61 | if let bgColor = value.backgroundColor {
62 | self.backgroundColor = bgColor
63 | }
64 |
65 | if let transparent = value.titlebarAppearsTransparent {
66 | self.titlebarAppearsTransparent = transparent
67 | }
68 |
69 | if let disableRestore = value.disableRestoreOnLaunch {
70 | self.isRestorable = !disableRestore
71 | }
72 |
73 | if let animationBehavior = value.animationBehavior {
74 | self.animationBehavior = animationBehavior
75 | }
76 |
77 | value.windowButtonsEnabled.forEach {
78 | standardWindowButton($0)?.isHidden = $1.isHidden
79 | standardWindowButton($0)?.isEnabled = $1.enabled
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Sources/WindowManagement/Scene+Window.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wouter Hennen on 06/01/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public extension Scene {
11 |
12 | /// Registers a scene to be modified by the following scene modifiers.
13 | /// The identifier should be the same as in the Scene initializer.
14 | /// This modifier should be called before other modifiers of this package.
15 | func register(_ identifier: String) -> some Scene {
16 | WM.currentIdentifier = identifier
17 | WM.modifications[identifier] = WindowModifications()
18 | return self
19 | }
20 |
21 | /// Registers a scene to be modified by the following scene modifiers.
22 | /// The identifier should be the same as in the Scene initializer.
23 | /// This modifier should be called before other modifiers of this package.
24 | func register(_ identifier: SceneID) -> some Scene {
25 | WM.currentIdentifier = identifier.id
26 | WM.modifications[identifier.id] = WindowModifications()
27 | return self
28 | }
29 |
30 | /// Registers the settings scene to be modified by the following scene modifiers.
31 | /// The identifier should be the same as in the Scene initializer.
32 | /// This modifier should be called before other modifiers of this package.
33 | func registerSettingsWindow() -> some Scene {
34 | WM.currentIdentifier = "com_apple_SwiftUI_Settings_window"
35 | WM.modifications[WM.currentIdentifier] = WindowModifications()
36 | return self
37 | }
38 |
39 | /// Indicates whether the window can be moved by clicking and dragging its background.
40 | func movableByBackground(_ value: Bool = true) -> some Scene {
41 | WM.modifications[WM.currentIdentifier]?.movableByBackground = value
42 | return self
43 | }
44 |
45 | /// Indicates whether the view can be moved.
46 | func movable(_ value: Bool) -> some Scene {
47 | WM.modifications[WM.currentIdentifier]?.movable = value
48 | return self
49 | }
50 |
51 | /// Apply a certain stylemask to the window.
52 | /// Note that `NSWindow.StyleMask.titled` is always included, otherwise SwiftUI will crash.
53 | func styleMask(_ styleMask: NSWindow.StyleMask) -> some Scene {
54 | WM.modifications[WM.currentIdentifier]?.styleMask = styleMask
55 | return self
56 | }
57 |
58 | /// Indicated whether a window button should be enabled or not.
59 | /// If disabled, the button will be greyed out but still visible.
60 | /// The keyboard shortcut of the button won't be active.
61 | /// Use `windowButton(_:hidden:)` to hide a window button.
62 | func windowButton(_ button: NSWindow.ButtonType, enabled: Bool) -> some Scene {
63 | WM.modifications[WM.currentIdentifier]?.windowButtonsEnabled[button, default: .init()].enabled = enabled
64 | return self
65 | }
66 |
67 | /// Indicated whether a window button should be hidden or not.
68 | /// A hidden button can still be triggered with it's keyboard shortcut, e.g. Cmd+w for closing a window.
69 | /// Use `windowButton(_:hidden:)` to disable a window button.
70 | func windowButton(_ button: NSWindow.ButtonType, hidden: Bool) -> some Scene {
71 | WM.modifications[WM.currentIdentifier]?.windowButtonsEnabled[button, default: .init()].isHidden = hidden
72 | return self
73 | }
74 |
75 | /// Apply a certain collectionBehavior to the window.
76 | func collectionBehavior(_ behavior: NSWindow.CollectionBehavior) -> some Scene {
77 | WM.modifications[WM.currentIdentifier]?.collectionBehavior = behavior
78 | return self
79 | }
80 |
81 | /// Set the tabbingMode for the window.
82 | /// If set to preferred, new windows will be opened as tabs.
83 | func tabbingMode(_ mode: NSWindow.TabbingMode) -> some Scene {
84 | WM.modifications[WM.currentIdentifier]?.tabbingMode = mode
85 | return self
86 | }
87 |
88 | /// Sets the background color of the window.
89 | func backgroundColor(_ color: NSColor) -> some Scene {
90 | WM.modifications[WM.currentIdentifier]?.backgroundColor = color
91 | return self
92 | }
93 |
94 | /// Makes the titlebar transparent.
95 | func titlebarAppearsTransparent(_ value: Bool = true) -> some Scene {
96 | WM.modifications[WM.currentIdentifier]?.titlebarAppearsTransparent = value
97 | return self
98 | }
99 |
100 | /// This will stop windows from relaunching if they were open in the last active app state.
101 | func disableRestoreOnLaunch() -> some Scene {
102 | WM.modifications[WM.currentIdentifier]?.disableRestoreOnLaunch = true
103 | return self
104 | }
105 |
106 | func transition(_ transition: NSWindow.AnimationBehavior) -> some Scene {
107 | WM.modifications[WM.currentIdentifier]?.animationBehavior = transition
108 | return self
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftUI-WindowManagement
2 |
3 | The WindowManagement package allows you to control window behaviors of SwiftUI Scenes without doing weird tricks.
4 | Views can also access the NSWindow through the Environment.
5 |
6 |
7 |
8 |
9 | ## Notice
10 | > This package uses and modifies some internals of SwiftUI. As SwiftUI changes frequently, implementations might break.
11 |
12 | > Note: DocumentGroup is not supported (yet)
13 |
14 | ## Compatibility
15 | - macOS 12.0 (Untested, but should work)
16 | - macOS 13.0
17 | - macOS 14.0
18 |
19 | ## Opening SwiftUI Windows from anywhere in your app.
20 | Using SwiftUI windows can be difficult when parts of your app rely on AppKit types. For example, you can't open a SwiftUI window from an `AppDelegate`. This package adds some functions to `NSApp` to allow this kind of behavior.
21 |
22 | #### Usage
23 | First, define a SceneID for each SwiftUI scene:
24 | ```swift
25 | extension SceneID {
26 | static let myWindow = SceneID("myWindow")
27 | }
28 | ```
29 | Important: you must add `.enableOpenWindow()` to one scene (only one is required) to enable the functionality.
30 | ```swift
31 | @main
32 | struct MyApp: App {
33 | var body: some Scene {
34 | WindowGroup(id: SceneID.myWindow.id) {
35 | ...
36 | }
37 | .enableOpenWindow()
38 | }
39 | }
40 | ```
41 | You can open a window by calling the following function:
42 | ```swift
43 | func applicationDidFinishLaunching(_ notification: Notification) {
44 | NSApp.openWindow(.myWindow)
45 | }
46 | ```
47 |
48 | You can also open a SwiftUI Settings window (also works on macOS Sonoma):
49 | ```swift
50 | func applicationDidFinishLaunching(_ notification: Notification) {
51 | NSApp.openSettings()
52 | }
53 | ```
54 |
55 | ## Passing an Environment Value to multiple Scenes
56 | ```swift
57 | var body: some Scene {
58 | Group {
59 | WindowGroup {
60 | ...
61 | }
62 |
63 | Settings {
64 | ...
65 | }
66 |
67 | NSDocumentGroup(for: ...) { _ in
68 | ...
69 | }
70 | }
71 | .environment(\.controlSize, .large)
72 | }
73 | ```
74 |
75 | ## Scene Window UI Modifiers
76 | #### register
77 | This modifier enables the modification of the underlying `NSWindow` for this scene.
78 | It should always be called before any of the other modifiers.
79 | The identifier must be the same as the one set in the Scene initializer.
80 | For the `Settings` Scene, use `registerSettingsWindow`.
81 |
82 | ```swift
83 | func register(_ identifier: String)
84 |
85 | func register(_ identifier: SceneID)
86 |
87 | func registerSettingsWindow()
88 | ```
89 |
90 | #### movableByBackground
91 | Indicates whether the window can be moved by clicking and dragging its background.
92 | ```swift
93 | func movableByBackground(_ value: Bool)
94 | ```
95 |
96 | #### movable
97 | Indicates whether the view can be moved.
98 | ```swift
99 | func movable(_ value: Bool)
100 | ```
101 |
102 | #### styleMask
103 | Apply a certain stylemask to the window.
104 | Note that `NSWindow.StyleMask.titled` is always included, otherwise SwiftUI will crash.
105 | ```swift
106 | func styleMask(_ styleMask: NSWindow.StyleMask)
107 | ```
108 |
109 | #### windowButton
110 | Indicated whether a window button should be enabled or not.
111 | If disabled, the button will be greyed out but still visible.
112 | The keyboard shortcut of the button won't be active.
113 | Use `windowButton(_:hidden:)` to hide a window button.
114 | ```swift
115 | func windowButton(_ button: NSWindow.ButtonType, enabled: Bool)
116 | ```
117 |
118 | Indicated whether a window button should be hidden or not.
119 | A hidden button can still be triggered with it's keyboard shortcut, e.g. Cmd+w for closing a window.
120 | Use `windowButton(_:hidden:)` to disable a window button.
121 | ```swift
122 | func windowButton(_ button: NSWindow.ButtonType, hidden: Bool)
123 | ```
124 |
125 | #### collectionBehavior
126 | Apply a certain collectionBehavior to the window.
127 | ```swift
128 | func collectionBehavior(_ behavior: NSWindow.CollectionBehavior)
129 | ```
130 |
131 | #### tabbingMode
132 | Set the tabbingMode for the window.
133 | If set to preferred, new windows will be opened as tabs.
134 |
135 | ```swift
136 | func tabbingMode(_ mode: NSWindow.TabbingMode)
137 | ```
138 |
139 | #### backgroundColor
140 | Sets the background color of the window.
141 | ```swift
142 | func backgroundColor(_ color: NSColor)
143 | ```
144 |
145 | #### titlebarAppearsTransparent
146 | Makes the titlebar transparent.
147 | ```swift
148 | func titlebarAppearsTransparent(_ value: Bool)
149 | ```
150 |
151 | #### disableRestoreOnLaunch
152 | This will stop windows from relaunching if they were open in the last active app state.
153 | > Note: While this should work fine, I can't guarantee it.
154 | ```swift
155 | func disableRestoreOnLaunch()
156 | ```
157 |
158 | #### transition
159 | Configure the NSWindow.AnimationBehavior for the Scene
160 | ```swift
161 | func transition(_ transition: NSWindow.AnimationBehavior)
162 | ```
163 |
164 | ## Available View modifiers
165 | Inject the current window into the environment.
166 | ```swift
167 | func injectWindow(_ identifier: String)
168 | ```
169 |
170 | Inject the settings window into the environment.
171 | ```swift
172 | func injectSettingsWindow()
173 | ```
174 |
175 | Get the NSWindow from a View.
176 | ```swift
177 | @Environment(\.window) var window
178 | ```
179 |
180 | ## NSDocumentGroup
181 | NSDocumentGroup is an alternative to SwiftUI's documentgroup. It allows you to use a SwiftUI Window with an NSDocument. This is useful for cases where you need SwiftUI (for example, to make `.focusedValue` work), but `FileDocument` or `ReferenceFileDocument` are not meeting your requirements. One of the issues I ran into with these types is that they become slow when opening large folders (for example, a node_modules folder). Using an NSDocument allows you to optimize this.
182 |
183 | #### Usage
184 | See the example folder for a working sample project.
185 |
186 | First, create a NSDocument class (make sure to add the filetype to the project config).
187 | Override the `makeWindowControllers()` type and open a new SwiftUI window by calling `openDocument`.
188 | ```swift
189 | override func makeWindowControllers() {
190 | if let window = NSApp.openDocument(self), let windowController = window.windowController {
191 | addWindowController(windowController)
192 | }
193 | }
194 | ```
195 | Add a new NSDocumentGroup Scene to your app. The scene provides a reference to the opened NSDocument.
196 | ```swift
197 | NSDocumentGroup(for: CodeFileDocument.self) { document in
198 | Text(document.fileURL?.absoluteString ?? "")
199 | }
200 | ```
201 |
202 | ## Extras
203 |
204 | Make SwiftUI Materials always active
205 | > Note: Set this once in the initializer of your implementation of the `App` protocol
206 | ```swift
207 | NSWindow.alwaysUseActiveAppearance = true
208 | ```
209 |
210 | ## Example
211 | An example project can be found in the project repository.
212 |
--------------------------------------------------------------------------------
/Example/WindowManagementProject.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6C11A8C02A36610500E86728 /* CodeFileDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C11A8BF2A36610500E86728 /* CodeFileDocument.swift */; };
11 | 6C11A8C42A368C3F00E86728 /* WindowManagement in Frameworks */ = {isa = PBXBuildFile; productRef = 6C11A8C32A368C3F00E86728 /* WindowManagement */; };
12 | 6C11A8C72A368E7B00E86728 /* WindowManagement in Frameworks */ = {isa = PBXBuildFile; productRef = 6C11A8C62A368E7B00E86728 /* WindowManagement */; };
13 | 6CC243D12A3648FE003F9C1A /* WindowManagementProjectApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC243D02A3648FE003F9C1A /* WindowManagementProjectApp.swift */; };
14 | 6CC243D32A3648FE003F9C1A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC243D22A3648FE003F9C1A /* ContentView.swift */; };
15 | 6CC243D52A3648FF003F9C1A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CC243D42A3648FF003F9C1A /* Assets.xcassets */; };
16 | 6CC243D82A364900003F9C1A /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CC243D72A364900003F9C1A /* Preview Assets.xcassets */; };
17 | 6CC243E12A36491D003F9C1A /* WindowManagement in Frameworks */ = {isa = PBXBuildFile; productRef = 6CC243E02A36491D003F9C1A /* WindowManagement */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | 6C11A8BF2A36610500E86728 /* CodeFileDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeFileDocument.swift; sourceTree = ""; };
22 | 6CC243CD2A3648FE003F9C1A /* WindowManagementProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WindowManagementProject.app; sourceTree = BUILT_PRODUCTS_DIR; };
23 | 6CC243D02A3648FE003F9C1A /* WindowManagementProjectApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManagementProjectApp.swift; sourceTree = ""; };
24 | 6CC243D22A3648FE003F9C1A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
25 | 6CC243D42A3648FF003F9C1A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
26 | 6CC243D72A364900003F9C1A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
27 | 6CC243D92A364900003F9C1A /* WindowManagementProject.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WindowManagementProject.entitlements; sourceTree = ""; };
28 | 6CC243E22A365406003F9C1A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 6CC243CA2A3648FE003F9C1A /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | 6C11A8C72A368E7B00E86728 /* WindowManagement in Frameworks */,
37 | 6CC243E12A36491D003F9C1A /* WindowManagement in Frameworks */,
38 | 6C11A8C42A368C3F00E86728 /* WindowManagement in Frameworks */,
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXFrameworksBuildPhase section */
43 |
44 | /* Begin PBXGroup section */
45 | 6CC243C42A3648FE003F9C1A = {
46 | isa = PBXGroup;
47 | children = (
48 | 6CC243CF2A3648FE003F9C1A /* WindowManagementProject */,
49 | 6CC243CE2A3648FE003F9C1A /* Products */,
50 | );
51 | sourceTree = "";
52 | };
53 | 6CC243CE2A3648FE003F9C1A /* Products */ = {
54 | isa = PBXGroup;
55 | children = (
56 | 6CC243CD2A3648FE003F9C1A /* WindowManagementProject.app */,
57 | );
58 | name = Products;
59 | sourceTree = "";
60 | };
61 | 6CC243CF2A3648FE003F9C1A /* WindowManagementProject */ = {
62 | isa = PBXGroup;
63 | children = (
64 | 6CC243E22A365406003F9C1A /* Info.plist */,
65 | 6CC243D02A3648FE003F9C1A /* WindowManagementProjectApp.swift */,
66 | 6C11A8BF2A36610500E86728 /* CodeFileDocument.swift */,
67 | 6CC243D22A3648FE003F9C1A /* ContentView.swift */,
68 | 6CC243D42A3648FF003F9C1A /* Assets.xcassets */,
69 | 6CC243D92A364900003F9C1A /* WindowManagementProject.entitlements */,
70 | 6CC243D62A364900003F9C1A /* Preview Content */,
71 | );
72 | path = WindowManagementProject;
73 | sourceTree = "";
74 | };
75 | 6CC243D62A364900003F9C1A /* Preview Content */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 6CC243D72A364900003F9C1A /* Preview Assets.xcassets */,
79 | );
80 | path = "Preview Content";
81 | sourceTree = "";
82 | };
83 | /* End PBXGroup section */
84 |
85 | /* Begin PBXNativeTarget section */
86 | 6CC243CC2A3648FE003F9C1A /* WindowManagementProject */ = {
87 | isa = PBXNativeTarget;
88 | buildConfigurationList = 6CC243DC2A364900003F9C1A /* Build configuration list for PBXNativeTarget "WindowManagementProject" */;
89 | buildPhases = (
90 | 6CC243C92A3648FE003F9C1A /* Sources */,
91 | 6CC243CA2A3648FE003F9C1A /* Frameworks */,
92 | 6CC243CB2A3648FE003F9C1A /* Resources */,
93 | );
94 | buildRules = (
95 | );
96 | dependencies = (
97 | );
98 | name = WindowManagementProject;
99 | packageProductDependencies = (
100 | 6CC243E02A36491D003F9C1A /* WindowManagement */,
101 | 6C11A8C32A368C3F00E86728 /* WindowManagement */,
102 | 6C11A8C62A368E7B00E86728 /* WindowManagement */,
103 | );
104 | productName = WindowManagementProject;
105 | productReference = 6CC243CD2A3648FE003F9C1A /* WindowManagementProject.app */;
106 | productType = "com.apple.product-type.application";
107 | };
108 | /* End PBXNativeTarget section */
109 |
110 | /* Begin PBXProject section */
111 | 6CC243C52A3648FE003F9C1A /* Project object */ = {
112 | isa = PBXProject;
113 | attributes = {
114 | BuildIndependentTargetsInParallel = 1;
115 | LastSwiftUpdateCheck = 1500;
116 | LastUpgradeCheck = 1500;
117 | TargetAttributes = {
118 | 6CC243CC2A3648FE003F9C1A = {
119 | CreatedOnToolsVersion = 15.0;
120 | };
121 | };
122 | };
123 | buildConfigurationList = 6CC243C82A3648FE003F9C1A /* Build configuration list for PBXProject "WindowManagementProject" */;
124 | compatibilityVersion = "Xcode 14.0";
125 | developmentRegion = en;
126 | hasScannedForEncodings = 0;
127 | knownRegions = (
128 | en,
129 | Base,
130 | );
131 | mainGroup = 6CC243C42A3648FE003F9C1A;
132 | packageReferences = (
133 | 6C11A8C52A368E7B00E86728 /* XCRemoteSwiftPackageReference "SwiftUI-WindowManagement" */,
134 | );
135 | productRefGroup = 6CC243CE2A3648FE003F9C1A /* Products */;
136 | projectDirPath = "";
137 | projectRoot = "";
138 | targets = (
139 | 6CC243CC2A3648FE003F9C1A /* WindowManagementProject */,
140 | );
141 | };
142 | /* End PBXProject section */
143 |
144 | /* Begin PBXResourcesBuildPhase section */
145 | 6CC243CB2A3648FE003F9C1A /* Resources */ = {
146 | isa = PBXResourcesBuildPhase;
147 | buildActionMask = 2147483647;
148 | files = (
149 | 6CC243D82A364900003F9C1A /* Preview Assets.xcassets in Resources */,
150 | 6CC243D52A3648FF003F9C1A /* Assets.xcassets in Resources */,
151 | );
152 | runOnlyForDeploymentPostprocessing = 0;
153 | };
154 | /* End PBXResourcesBuildPhase section */
155 |
156 | /* Begin PBXSourcesBuildPhase section */
157 | 6CC243C92A3648FE003F9C1A /* Sources */ = {
158 | isa = PBXSourcesBuildPhase;
159 | buildActionMask = 2147483647;
160 | files = (
161 | 6CC243D32A3648FE003F9C1A /* ContentView.swift in Sources */,
162 | 6CC243D12A3648FE003F9C1A /* WindowManagementProjectApp.swift in Sources */,
163 | 6C11A8C02A36610500E86728 /* CodeFileDocument.swift in Sources */,
164 | );
165 | runOnlyForDeploymentPostprocessing = 0;
166 | };
167 | /* End PBXSourcesBuildPhase section */
168 |
169 | /* Begin XCBuildConfiguration section */
170 | 6CC243DA2A364900003F9C1A /* Debug */ = {
171 | isa = XCBuildConfiguration;
172 | buildSettings = {
173 | ALWAYS_SEARCH_USER_PATHS = NO;
174 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
175 | CLANG_ANALYZER_NONNULL = YES;
176 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
177 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
178 | CLANG_ENABLE_MODULES = YES;
179 | CLANG_ENABLE_OBJC_ARC = YES;
180 | CLANG_ENABLE_OBJC_WEAK = YES;
181 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
182 | CLANG_WARN_BOOL_CONVERSION = YES;
183 | CLANG_WARN_COMMA = YES;
184 | CLANG_WARN_CONSTANT_CONVERSION = YES;
185 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
186 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
187 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
188 | CLANG_WARN_EMPTY_BODY = YES;
189 | CLANG_WARN_ENUM_CONVERSION = YES;
190 | CLANG_WARN_INFINITE_RECURSION = YES;
191 | CLANG_WARN_INT_CONVERSION = YES;
192 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
193 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
194 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
195 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
196 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
197 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
198 | CLANG_WARN_STRICT_PROTOTYPES = YES;
199 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
200 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
201 | CLANG_WARN_UNREACHABLE_CODE = YES;
202 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
203 | COPY_PHASE_STRIP = NO;
204 | DEBUG_INFORMATION_FORMAT = dwarf;
205 | ENABLE_STRICT_OBJC_MSGSEND = YES;
206 | ENABLE_TESTABILITY = YES;
207 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
208 | GCC_C_LANGUAGE_STANDARD = gnu17;
209 | GCC_DYNAMIC_NO_PIC = NO;
210 | GCC_NO_COMMON_BLOCKS = YES;
211 | GCC_OPTIMIZATION_LEVEL = 0;
212 | GCC_PREPROCESSOR_DEFINITIONS = (
213 | "DEBUG=1",
214 | "$(inherited)",
215 | );
216 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
217 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
218 | GCC_WARN_UNDECLARED_SELECTOR = YES;
219 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
220 | GCC_WARN_UNUSED_FUNCTION = YES;
221 | GCC_WARN_UNUSED_VARIABLE = YES;
222 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
223 | MACOSX_DEPLOYMENT_TARGET = 14.0;
224 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
225 | MTL_FAST_MATH = YES;
226 | ONLY_ACTIVE_ARCH = YES;
227 | SDKROOT = macosx;
228 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
229 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
230 | };
231 | name = Debug;
232 | };
233 | 6CC243DB2A364900003F9C1A /* Release */ = {
234 | isa = XCBuildConfiguration;
235 | buildSettings = {
236 | ALWAYS_SEARCH_USER_PATHS = NO;
237 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
238 | CLANG_ANALYZER_NONNULL = YES;
239 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
240 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
241 | CLANG_ENABLE_MODULES = YES;
242 | CLANG_ENABLE_OBJC_ARC = YES;
243 | CLANG_ENABLE_OBJC_WEAK = YES;
244 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
245 | CLANG_WARN_BOOL_CONVERSION = YES;
246 | CLANG_WARN_COMMA = YES;
247 | CLANG_WARN_CONSTANT_CONVERSION = YES;
248 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
249 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
250 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
251 | CLANG_WARN_EMPTY_BODY = YES;
252 | CLANG_WARN_ENUM_CONVERSION = YES;
253 | CLANG_WARN_INFINITE_RECURSION = YES;
254 | CLANG_WARN_INT_CONVERSION = YES;
255 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
256 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
257 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
258 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
259 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
260 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
261 | CLANG_WARN_STRICT_PROTOTYPES = YES;
262 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
263 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
264 | CLANG_WARN_UNREACHABLE_CODE = YES;
265 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
266 | COPY_PHASE_STRIP = NO;
267 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
268 | ENABLE_NS_ASSERTIONS = NO;
269 | ENABLE_STRICT_OBJC_MSGSEND = YES;
270 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
271 | GCC_C_LANGUAGE_STANDARD = gnu17;
272 | GCC_NO_COMMON_BLOCKS = YES;
273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
275 | GCC_WARN_UNDECLARED_SELECTOR = YES;
276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
277 | GCC_WARN_UNUSED_FUNCTION = YES;
278 | GCC_WARN_UNUSED_VARIABLE = YES;
279 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
280 | MACOSX_DEPLOYMENT_TARGET = 14.0;
281 | MTL_ENABLE_DEBUG_INFO = NO;
282 | MTL_FAST_MATH = YES;
283 | SDKROOT = macosx;
284 | SWIFT_COMPILATION_MODE = wholemodule;
285 | };
286 | name = Release;
287 | };
288 | 6CC243DD2A364900003F9C1A /* Debug */ = {
289 | isa = XCBuildConfiguration;
290 | buildSettings = {
291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
292 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
293 | CODE_SIGN_ENTITLEMENTS = WindowManagementProject/WindowManagementProject.entitlements;
294 | CODE_SIGN_STYLE = Automatic;
295 | COMBINE_HIDPI_IMAGES = YES;
296 | CURRENT_PROJECT_VERSION = 1;
297 | DEVELOPMENT_ASSET_PATHS = "\"WindowManagementProject/Preview Content\"";
298 | DEVELOPMENT_TEAM = 2MMGJGVTB4;
299 | ENABLE_HARDENED_RUNTIME = YES;
300 | ENABLE_PREVIEWS = YES;
301 | GENERATE_INFOPLIST_FILE = YES;
302 | INFOPLIST_FILE = WindowManagementProject/Info.plist;
303 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
304 | LD_RUNPATH_SEARCH_PATHS = (
305 | "$(inherited)",
306 | "@executable_path/../Frameworks",
307 | );
308 | MARKETING_VERSION = 1.0;
309 | PRODUCT_BUNDLE_IDENTIFIER = com.tweety.WindowManagementProject;
310 | PRODUCT_NAME = "$(TARGET_NAME)";
311 | SWIFT_EMIT_LOC_STRINGS = YES;
312 | SWIFT_VERSION = 5.0;
313 | };
314 | name = Debug;
315 | };
316 | 6CC243DE2A364900003F9C1A /* Release */ = {
317 | isa = XCBuildConfiguration;
318 | buildSettings = {
319 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
320 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
321 | CODE_SIGN_ENTITLEMENTS = WindowManagementProject/WindowManagementProject.entitlements;
322 | CODE_SIGN_STYLE = Automatic;
323 | COMBINE_HIDPI_IMAGES = YES;
324 | CURRENT_PROJECT_VERSION = 1;
325 | DEVELOPMENT_ASSET_PATHS = "\"WindowManagementProject/Preview Content\"";
326 | DEVELOPMENT_TEAM = 2MMGJGVTB4;
327 | ENABLE_HARDENED_RUNTIME = YES;
328 | ENABLE_PREVIEWS = YES;
329 | GENERATE_INFOPLIST_FILE = YES;
330 | INFOPLIST_FILE = WindowManagementProject/Info.plist;
331 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
332 | LD_RUNPATH_SEARCH_PATHS = (
333 | "$(inherited)",
334 | "@executable_path/../Frameworks",
335 | );
336 | MARKETING_VERSION = 1.0;
337 | PRODUCT_BUNDLE_IDENTIFIER = com.tweety.WindowManagementProject;
338 | PRODUCT_NAME = "$(TARGET_NAME)";
339 | SWIFT_EMIT_LOC_STRINGS = YES;
340 | SWIFT_VERSION = 5.0;
341 | };
342 | name = Release;
343 | };
344 | /* End XCBuildConfiguration section */
345 |
346 | /* Begin XCConfigurationList section */
347 | 6CC243C82A3648FE003F9C1A /* Build configuration list for PBXProject "WindowManagementProject" */ = {
348 | isa = XCConfigurationList;
349 | buildConfigurations = (
350 | 6CC243DA2A364900003F9C1A /* Debug */,
351 | 6CC243DB2A364900003F9C1A /* Release */,
352 | );
353 | defaultConfigurationIsVisible = 0;
354 | defaultConfigurationName = Release;
355 | };
356 | 6CC243DC2A364900003F9C1A /* Build configuration list for PBXNativeTarget "WindowManagementProject" */ = {
357 | isa = XCConfigurationList;
358 | buildConfigurations = (
359 | 6CC243DD2A364900003F9C1A /* Debug */,
360 | 6CC243DE2A364900003F9C1A /* Release */,
361 | );
362 | defaultConfigurationIsVisible = 0;
363 | defaultConfigurationName = Release;
364 | };
365 | /* End XCConfigurationList section */
366 |
367 | /* Begin XCRemoteSwiftPackageReference section */
368 | 6C11A8C52A368E7B00E86728 /* XCRemoteSwiftPackageReference "SwiftUI-WindowManagement" */ = {
369 | isa = XCRemoteSwiftPackageReference;
370 | repositoryURL = "https://github.com/Wouter01/SwiftUI-WindowManagement";
371 | requirement = {
372 | kind = upToNextMajorVersion;
373 | minimumVersion = 2.0.0;
374 | };
375 | };
376 | /* End XCRemoteSwiftPackageReference section */
377 |
378 | /* Begin XCSwiftPackageProductDependency section */
379 | 6C11A8C32A368C3F00E86728 /* WindowManagement */ = {
380 | isa = XCSwiftPackageProductDependency;
381 | productName = WindowManagement;
382 | };
383 | 6C11A8C62A368E7B00E86728 /* WindowManagement */ = {
384 | isa = XCSwiftPackageProductDependency;
385 | package = 6C11A8C52A368E7B00E86728 /* XCRemoteSwiftPackageReference "SwiftUI-WindowManagement" */;
386 | productName = WindowManagement;
387 | };
388 | 6CC243E02A36491D003F9C1A /* WindowManagement */ = {
389 | isa = XCSwiftPackageProductDependency;
390 | productName = WindowManagement;
391 | };
392 | /* End XCSwiftPackageProductDependency section */
393 | };
394 | rootObject = 6CC243C52A3648FE003F9C1A /* Project object */;
395 | }
396 |
--------------------------------------------------------------------------------