#!/usr/bin/env ruby
require 'xcodeproj'
# Create project
project_path = 'MarkBaseInstaller.xcodeproj'
project = Xcodeproj::Project.new(project_path)
# Create target (use macOS 14.0 as base platform, will update deployment target later)
target = project.new_target(:application, 'MarkBaseInstaller', :macos, '14.0')
# Create group for source files
main_group = project.main_group
sources_group = main_group.new_group('Sources')
# Create AppDelegate.swift file reference
app_delegate_file = sources_group.new_file('AppDelegate.swift')
# Add file to target
target.source_build_phase.add_file_reference(app_delegate_file)
# Configure build settings
target.build_configurations.each do |config|
config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = 'com.momentry.markbase.installer'
config.build_settings['DEVELOPMENT_TEAM'] = 'K3TDMD9Y6B'
config.build_settings['CODE_SIGN_IDENTITY'] = 'Developer ID Application'
config.build_settings['SWIFT_VERSION'] = '6.0'
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '26.0'
config.build_settings['PRODUCT_NAME'] = 'MarkBaseInstaller'
config.build_settings['INFOPLIST_FILE'] = 'Info.plist'
config.build_settings['CODE_SIGN_ENTITLEMENTS'] = 'MarkBaseInstaller.entitlements'
# Disable automatic signing (use manual Developer ID)
config.build_settings['CODE_SIGN_STYLE'] = 'Manual'
end
# Create Info.plist
info_plist_path = 'Info.plist'
File.write(info_plist_path, <<-PLIST)
CFBundleIdentifier
com.momentry.markbase.installer
CFBundleName
MarkBase Installer
CFBundleVersion
1.0.0
CFBundleShortVersionString
1.0.0
CFBundleExecutable
MarkBaseInstaller
CFBundlePackageType
APPL
LSMinimumSystemVersion
26.0
NSHighResolutionCapable
NSPrincipalClass
NSApplication
PLIST
# Create entitlements
entitlements_path = 'MarkBaseInstaller.entitlements'
File.write(entitlements_path, <<-ENTITLEMENTS)
com.apple.developer.system-extension.install
ENTITLEMENTS
# Create AppDelegate.swift
app_delegate_path = 'Sources/AppDelegate.swift'
File.write(app_delegate_path, <<-SWIFT)
import Cocoa
import SystemExtensions
import OSLog
class AppDelegate: NSObject, NSApplicationDelegate {
let logger = Logger(subsystem: "com.momentry.markbase.installer", category: "AppDelegate")
var window: NSWindow!
var statusLabel: NSTextField!
var installButton: NSButton!
var extensionDelegate: ExtensionDelegate?
func applicationDidFinishLaunching(_ aNotification: Notification) {
logger.info("MarkBase Installer started")
createWindow()
setupUI()
window.makeKeyAndOrderFront(nil)
}
func createWindow() {
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 500, height: 400),
styleMask: [.titled, .closable, .miniaturizable],
backing: .buffered,
defer: false
)
window.title = "MarkBase Installer"
window.center()
window.isReleasedWhenClosed = false
}
func setupUI() {
let contentView = window.contentView!
// Title
let titleLabel = NSTextField(labelWithString: "MarkBase System Extension Installer")
titleLabel.font = NSFont.systemFont(ofSize: 18, weight: .bold)
titleLabel.alignment = .center
contentView.addSubview(titleLabel)
// Status Label
statusLabel = NSTextField(wrappingLabelWithString: "Ready to install MarkBaseFS System Extension")
statusLabel.font = NSFont.systemFont(ofSize: 14)
statusLabel.alignment = .center
contentView.addSubview(statusLabel)
// Install Button
installButton = NSButton(title: "Install System Extension", target: self, action: #selector(installExtension))
installButton.bezelStyle = .rounded
installButton.font = NSFont.systemFont(ofSize: 13, weight: .medium)
contentView.addSubview(installButton)
// Layout
titleLabel.translatesAutoresizingMaskIntoConstraints = false
statusLabel.translatesAutoresizingMaskIntoConstraints = false
installButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 50),
statusLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
statusLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 40),
statusLabel.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8),
installButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
installButton.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 40),
installButton.widthAnchor.constraint(equalToConstant: 200),
installButton.heightAnchor.constraint(equalToConstant: 30),
])
}
@objc func installExtension() {
logger.info("Install button clicked")
let extensionID = "com.momentry.markbase.fskit"
logger.info("Requesting activation for extension: \(extensionID)")
updateStatus("Requesting installation...")
installButton.isEnabled = false
let request = OSSystemExtensionRequest.activationRequest(
forExtensionWithIdentifier: extensionID,
queue: .main
)
// Create delegate and store as strong reference
extensionDelegate = ExtensionDelegate(
onSuccess: {
self.logger.info("Extension activated successfully")
self.updateStatus("✅ Installation successful!")
self.installButton.isEnabled = true
self.extensionDelegate = nil
},
onError: { error in
self.logger.error("Extension activation failed: \(error.localizedDescription)")
self.updateStatus("❌ Installation failed: \(error.localizedDescription)")
self.installButton.isEnabled = true
self.extensionDelegate = nil
},
onNeedsApproval: {
self.logger.info("Extension needs user approval")
self.updateStatus("⏳ Waiting for approval in System Settings")
}
)
request.delegate = extensionDelegate
OSSystemExtensionManager.shared.submitRequest(request)
}
func updateStatus(_ message: String) {
DispatchQueue.main.async {
self.statusLabel.stringValue = message
}
}
}
class ExtensionDelegate: NSObject, OSSystemExtensionRequestDelegate {
let onSuccess: () -> Void
let onError: (Error) -> Void
let onNeedsApproval: () -> Void
init(onSuccess: @escaping () -> Void, onError: @escaping (Error) -> Void, onNeedsApproval: @escaping () -> Void) {
self.onSuccess = onSuccess
self.onError = onError
self.onNeedsApproval = onNeedsApproval
super.init()
}
func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
Logger(subsystem: "com.momentry.markbase.installer", category: "ExtensionDelegate")
.info("Request finished with result")
onSuccess()
}
func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
Logger(subsystem: "com.momentry.markbase.installer", category: "ExtensionDelegate")
.error("Request failed with error: \(error.localizedDescription)")
onError(error)
}
func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
Logger(subsystem: "com.momentry.markbase.installer", category: "ExtensionDelegate")
.info("Request needs user approval")
onNeedsApproval()
}
func request(_ request: OSSystemExtensionRequest, actionForReplacingExtension existing: OSSystemExtensionProperties, withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
Logger(subsystem: "com.momentry.markbase.installer", category: "ExtensionDelegate")
.info("Replacing extension")
return .replace
}
}
SWIFT
# Save project
project.save
puts "Project created successfully at #{project_path}"
puts "Files created:"
puts " - Info.plist"
puts " - MarkBaseInstaller.entitlements"
puts " - Sources/AppDelegate.swift"
puts ""
puts "Next step: Build the project with:"
puts " xcodebuild -project #{project_path} -scheme MarkBaseInstaller -configuration Release build"