Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Example/Tuist/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ let openSwiftUICoreTarget = Target.target(
cxxSettings: sharedCxxSettings,
swiftSettings: sharedSwiftSettings + [
.unsafeFlags(["-module-abi-name", "OpenSwiftUI"]),
.unsafeFlags(["-import-underlying-module"], .when(platforms: .darwinPlatforms)),
],
linkerSettings: computeCondition ? [
.linkedLibrary("c++", .when(platforms: .darwinPlatforms)),
Expand Down Expand Up @@ -713,7 +714,9 @@ let openSwiftUITarget = Target.target(
],
cSettings: sharedCSettings,
cxxSettings: sharedCxxSettings,
swiftSettings: sharedSwiftSettings,
swiftSettings: sharedSwiftSettings + [
.unsafeFlags(["-import-underlying-module"], .when(platforms: .darwinPlatforms)),
],
linkerSettings: [
// Force Objective-C categories from static COpenSwiftUI into the final framework.
.unsafeFlags(["-Xlinker", "-ObjC"], .when(platforms: .darwinPlatforms)),
Expand Down
80 changes: 80 additions & 0 deletions Scripts/CI/Tests/framework-header-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const test = require("node:test");

const { installFrameworkHeaders } = require("../../install_framework_headers.js");

function fixture(t, versioned) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "framework headers "));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const source = path.join(root, "Public");
const framework = path.join(root, "OpenSwiftUI.framework");
const contents = versioned ? path.join(framework, "Versions/A") : framework;
fs.mkdirSync(source, { recursive: true });
fs.mkdirSync(path.join(source, "Details"));
fs.mkdirSync(path.join(contents, "Headers"), { recursive: true });
if (versioned) {
fs.symlinkSync("A", path.join(framework, "Versions/Current"));
fs.symlinkSync("Versions/Current/Headers", path.join(framework, "Headers"));
}
fs.writeFileSync(path.join(source, "OpenSwiftUI.h"), '#include "Details/Exports.h"\n');
fs.writeFileSync(path.join(source, "Details/Exports.h"), "#define PUBLIC_VALUE 42\n");
fs.writeFileSync(path.join(source, "OpenSwiftUI_Metal.h"), "#if defined(__METAL_VERSION__)\nnamespace OpenSwiftUI {}\n#endif\n");
fs.writeFileSync(path.join(root, "Internal.h"), "#define INTERNAL_VALUE 7\n");
fs.writeFileSync(path.join(source, "module.modulemap"), `module OpenSwiftUI {
umbrella header "OpenSwiftUI.h"
textual header "OpenSwiftUI_Metal.h"
export *
module * { export * }
}
`);
fs.writeFileSync(path.join(contents, "Headers/OpenSwiftUI-Swift.h"), "// Generated Swift declarations\n");
return { source, framework, contents };
}

for (const versioned of [false, true]) {
test(`installs a self-contained public module in a ${versioned ? "versioned" : "flat"} framework`, t => {
const f = fixture(t, versioned);
installFrameworkHeaders("OpenSwiftUI", f.framework, f.source);
const headers = path.join(f.contents, "Headers");
assert.equal(fs.readFileSync(path.join(headers, "Details/Exports.h"), "utf8"), "#define PUBLIC_VALUE 42\n");
assert.equal(fs.existsSync(path.join(headers, "OpenSwiftUI-Swift.h")), false);
assert.equal(fs.readFileSync(path.join(headers, "OpenSwiftUI_Metal.h"), "utf8"), fs.readFileSync(path.join(f.source, "OpenSwiftUI_Metal.h"), "utf8"));
assert.equal(fs.existsSync(path.join(headers, "Internal.h")), false);
assert.equal(fs.existsSync(path.join(headers, "module.modulemap")), false);
const map = fs.readFileSync(path.join(f.contents, "Modules/module.modulemap"), "utf8");
assert.match(map, /^framework module OpenSwiftUI \{/);
assert.match(map, /umbrella header "OpenSwiftUI.h"/);
assert.match(map, /textual header "OpenSwiftUI_Metal.h"/);
assert.equal(map.includes("OpenSwiftUI.Swift"), false);
assert.equal(map.includes("OpenSwiftUI-Swift.h"), false);
assert.equal(map.includes(f.source), false);
installFrameworkHeaders("OpenSwiftUI", f.framework, f.source);
assert.equal(fs.readFileSync(path.join(f.contents, "Modules/module.modulemap"), "utf8"), map);
});
}

test("installs public headers without a generated Swift header", t => {
const f = fixture(t, false);
fs.unlinkSync(path.join(f.contents, "Headers/OpenSwiftUI-Swift.h"));
installFrameworkHeaders("OpenSwiftUI", f.framework, f.source);
assert.equal(fs.existsSync(path.join(f.contents, "Headers/OpenSwiftUI.h")), true);
assert.equal(fs.existsSync(path.join(f.contents, "Headers/OpenSwiftUI-Swift.h")), false);
});

test("rejects a missing public umbrella before changing the framework", t => {
const f = fixture(t, false);
fs.unlinkSync(path.join(f.source, "OpenSwiftUI.h"));
assert.throws(() => installFrameworkHeaders("OpenSwiftUI", f.framework, f.source), /OpenSwiftUI.h/);
assert.equal(fs.existsSync(path.join(f.contents, "Headers/OpenSwiftUI-Swift.h")), true);
assert.equal(fs.existsSync(path.join(f.contents, "Headers/OpenSwiftUI_Metal.h")), false);
});

test("rejects a module map for a different module", t => {
const f = fixture(t, false);
fs.writeFileSync(path.join(f.source, "module.modulemap"), 'module Internal { header "Internal.h" }\n');
assert.throws(() => installFrameworkHeaders("OpenSwiftUI", f.framework, f.source), /OpenSwiftUI/);
assert.equal(fs.existsSync(path.join(f.contents, "Headers/OpenSwiftUI.h")), false);
});
6 changes: 6 additions & 0 deletions Scripts/build_xcframework.sh
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,12 @@ build_framework() {
ln -s Versions/Current/Modules "$framework/Modules"
fi

case "$scheme" in
OpenSwiftUI|OpenSwiftUICore)
node "$SCRIPT_DIR/install_framework_headers.js" "$scheme" "$framework"
;;
esac

flatten_framework_resource_bundles "$scheme" "$framework"
node "$SCRIPT_DIR/normalize_swiftinterface.js" "$modules_path"
strip_release_metadata "$modules_path"
Expand Down
39 changes: 39 additions & 0 deletions Scripts/install_framework_headers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const fs = require("node:fs");
const path = require("node:path");

function installFrameworkHeaders(name, framework, source) {
const contents = fs.existsSync(path.join(framework, "Versions"))
? path.join(framework, "Versions/Current") : framework;
const headers = path.join(contents, "Headers");
const modules = path.join(contents, "Modules");
const sourceMap = fs.readFileSync(path.join(source, "module.modulemap"), "utf8");
const declaration = `module ${name} {`;
if (!sourceMap.startsWith(declaration)) {
throw new Error(`Expected a public module map for ${name}.`);
}
fs.accessSync(path.join(source, `${name}.h`));

// Keep the source and framework exports in one module map. Only their layout differs.
const frameworkMap = sourceMap.replace(declaration, `framework ${declaration}`);
fs.cpSync(source, headers, {
recursive: true,
dereference: true,
filter: file => fs.statSync(file).isDirectory() || file.endsWith(".h"),
});
fs.rmSync(path.join(headers, `${name}-Swift.h`), { force: true });
fs.mkdirSync(modules, { recursive: true });
fs.writeFileSync(path.join(modules, "module.modulemap"), frameworkMap);
}

if (require.main === module) {
const carriers = { OpenSwiftUI: "COpenSwiftUI", OpenSwiftUICore: "OpenSwiftUI_SPI" };
const [name, framework] = process.argv.slice(2);
if (process.argv.length !== 4 || !carriers[name]) {
console.error("Usage: node Scripts/install_framework_headers.js <OpenSwiftUI|OpenSwiftUICore> <framework>");
process.exitCode = 64;
} else {
installFrameworkHeaders(name, framework, path.join(__dirname, "../Sources", carriers[name], "Public"));
}
}

module.exports = { installFrameworkHeaders };
22 changes: 22 additions & 0 deletions Sources/COpenSwiftUI/Public/OpenSwiftUI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// OpenSwiftUI.h
// OpenSwiftUI

#ifndef OPENSWIFTUI_PUBLIC_H
#define OPENSWIFTUI_PUBLIC_H

#if defined(__METAL_VERSION__)
#include "OpenSwiftUI_Metal.h"
#elif defined(__APPLE__)
#import <TargetConditionals.h>

#if TARGET_OS_OSX && __has_include(<AppKit/AppKit.h>)
#import <AppKit/AppKit.h>
#endif

#if !TARGET_OS_OSX && __has_include(<UIKit/UIKit.h>)
#import <UIKit/UIKit.h>
#endif
#endif

#endif /* OPENSWIFTUI_PUBLIC_H */
31 changes: 31 additions & 0 deletions Sources/COpenSwiftUI/Public/OpenSwiftUI_Metal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// OpenSwiftUI_Metal.h
// OpenSwiftUI

#ifndef OPENSWIFTUI_METAL_H
#define OPENSWIFTUI_METAL_H

#if defined(__METAL_VERSION__)

#include <metal_stdlib>

namespace OpenSwiftUI {

/// A region of a rasterized layer.
struct Layer {
metal::texture2d<half> tex;
float2 info[5];

/// Samples the layer at a position in user coordinates with linear filtering.
/// Returns premultiplied RGBA in the layer's working color space.
half4 sample(float2 p) const {
p = metal::fma(p.x, info[0], metal::fma(p.y, info[1], info[2]));
p = metal::clamp(p, info[3], info[4]);
return tex.sample(metal::sampler(metal::filter::linear), p);
}
};

} // namespace OpenSwiftUI

#endif /* defined(__METAL_VERSION__) */
#endif /* OPENSWIFTUI_METAL_H */
6 changes: 6 additions & 0 deletions Sources/COpenSwiftUI/Public/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module OpenSwiftUI {
umbrella header "OpenSwiftUI.h"
textual header "OpenSwiftUI_Metal.h"
export *
module * { export * }
}
8 changes: 8 additions & 0 deletions Sources/COpenSwiftUI/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module COpenSwiftUI {
umbrella "."
exclude header "Public/OpenSwiftUI.h"
exclude header "Public/OpenSwiftUI_Metal.h"
export *
}

extern module OpenSwiftUI "Public/module.modulemap"
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import OpenSwiftUICore
// MARK: - NSEvent + KeyEventProvider

extension NSEvent: KeyEventProvider {
func resolve(phase: EventPhase) -> KeyEvent? {
package func resolve(phase: EventPhase) -> KeyEvent? {
KeyEvent(
phase: isARepeat ? .active : phase,
timestamp: Time(seconds: timestamp),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import UIKit
// MARK: - UIPress + KeyEventProvider

extension UIPress: KeyEventProvider {
func resolve(phase: EventPhase) -> KeyEvent? {
package func resolve(phase: EventPhase) -> KeyEvent? {
guard let key else {
return nil
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/OpenSwiftUI/Export.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ package typealias UniqueID = OpenSwiftUICore.UniqueID
@_exported import CoreGraphics
#endif

#if canImport(CoreTransferable)
@_exported import CoreTransferable
#endif

#if canImport(DeveloperToolsSupport)
@_exported import DeveloperToolsSupport
#endif

#if canImport(OpenCombine)
public import OpenCombine
public typealias Published = OpenCombine.Published
Expand Down
2 changes: 1 addition & 1 deletion Sources/OpenSwiftUI/Macro/PreviewMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// OpenSwiftUI

#if canImport(DeveloperToolsSupport)
@_exported import DeveloperToolsSupport
public import DeveloperToolsSupport
import OpenSwiftUICore

/// Creates an Xcode preview from an OpenSwiftUI view.
Expand Down
25 changes: 25 additions & 0 deletions Sources/OpenSwiftUI_SPI/Public/OpenSwiftUICore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// OpenSwiftUICore.h
// OpenSwiftUICore

#ifndef OPENSWIFTUICORE_PUBLIC_H
#define OPENSWIFTUICORE_PUBLIC_H

#if defined(__METAL_VERSION__)
#include "OpenSwiftUI_Metal.h"
#elif defined(__APPLE__)
#import <TargetConditionals.h>
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <CoreText/CoreText.h>

#if __has_include(<QuartzCore/QuartzCore.h>)
#import <QuartzCore/QuartzCore.h>
#endif

#if TARGET_OS_VISION && __has_include(<Spatial/Spatial.h>)
#import <Spatial/Spatial.h>
#endif
#endif

#endif /* OPENSWIFTUICORE_PUBLIC_H */
31 changes: 31 additions & 0 deletions Sources/OpenSwiftUI_SPI/Public/OpenSwiftUI_Metal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// OpenSwiftUI_Metal.h
// OpenSwiftUI

#ifndef OPENSWIFTUI_METAL_H
#define OPENSWIFTUI_METAL_H

#if defined(__METAL_VERSION__)

#include <metal_stdlib>

namespace OpenSwiftUI {

/// A region of a rasterized layer.
struct Layer {
metal::texture2d<half> tex;
float2 info[5];

/// Samples the layer at a position in user coordinates with linear filtering.
/// Returns premultiplied RGBA in the layer's working color space.
half4 sample(float2 p) const {
p = metal::fma(p.x, info[0], metal::fma(p.y, info[1], info[2]));
p = metal::clamp(p, info[3], info[4]);
return tex.sample(metal::sampler(metal::filter::linear), p);
}
};

} // namespace OpenSwiftUI

#endif /* defined(__METAL_VERSION__) */
#endif /* OPENSWIFTUI_METAL_H */
6 changes: 6 additions & 0 deletions Sources/OpenSwiftUI_SPI/Public/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module OpenSwiftUICore {
umbrella header "OpenSwiftUICore.h"
textual header "OpenSwiftUI_Metal.h"
export *
module * { export * }
}
2 changes: 2 additions & 0 deletions Sources/OpenSwiftUI_SPI/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,5 @@ module UIFoundation_Private {
umbrella "Shims/UIFoundation"
export *
}

extern module OpenSwiftUICore "Public/module.modulemap"
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
import OpenSwiftUITestsSupport
import Testing

@Observable
#if OPENSWIFTUI
@OpenObservation::Observable
#else
@Observation::Observable
#endif
private final class Model {
var value = 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@ struct ObservedObjectCompatibilityTests {
var t2 = false
}

@Observable
#if OPENSWIFTUI
@OpenObservation::Observable
#else
@Observation::Observable
#endif
class Model2 {
var t1 = false
#if OPENSWIFTUI
@OpenObservation::ObservationIgnored var t2 = false
#else
@ObservationIgnored var t2 = false
#endif
}

struct Subview: View {
Expand Down
Loading
Loading