This guide helps you migrate your existing legacy Panorama code to version 1.0, which includes Swift 5.9+ modernization and API improvements.
Panorama 1.0 brings significant improvements while maintaining the core architecture:
- Swift 5.9+ requirement (previously Swift 3.0.2)
- Modernized APIs with better type safety
- Improved documentation and code organization
- Breaking changes to some property names and method signatures
- XPlatform dependency replaces ZKit
Before (1.x):
- Swift 3.0.2+
- Xcode 8.2+
After (1.0):
- Swift 5.9+
- Xcode 15.0+
- iOS 13.0+ / macOS 10.13+
Before:
if viewlet.enabled {
// Handle enabled state
}After:
if viewlet.isEnabled {
// Handle enabled state
}Before:
let viewlet = panorama.findViewlet(point: location)After:
let viewlet = panorama.findViewlet(at: location)Before:
view.sendSubview(toBack: subview)
view.bringSubview(toFront: subview)
view.replaceSubview(subview: oldView, with: newView)After:
view.sendSubview(toBack: subview)
view.bringSubview(toFront: subview)
// replaceSubview is now provided by AppKit directly
view.replaceSubview(oldView, with: newView)Before:
style.foregroundColors = [.normal: XColor.white]
style.backgroundFills = [.normal: ViewletFill.solid(.blue)]After:
style.setForegroundColor(.white, for: .normal)
style.setBackgroundFill(.solid(.blue), for: .normal)Before:
let gradient = Gradient(locations: [(0, XColor.red), (1, XColor.blue)])After:
let gradient = Gradient(colorStops: [(0, XColor.red), (1, XColor.blue)])Before:
class Gradient {
// Reference type
}After:
struct Gradient {
// Value type - provides better performance and safety
}Before:
fill.fill(rect: bounds, context: context)After:
fill.fill(rect: bounds, in: context)Before:
import ZKitAfter:
// Remove ZKit import - functionality is built-in or use XPlatform if neededFull keyboard input support with focus management:
let textField = TextFieldViewlet(frame: CGRect(x: 50, y: 100, width: 200, height: 30))
textField.placeholder = "Enter text"
textField.onTextChange = { text in
print("Text: \(text)")
}Draggable card components:
let card = NoteCardViewlet(frame: CGRect(x: 100, y: 100, width: 200, height: 150))
card.text = "Drag me!"
card.cardColor = .yellowComplete form layout example with multiple fields.
- Fixed infinite recursion in findViewlet - Added bounds checking
- Fixed touch handling recursion - Prevented forwarding to nested Panoramas
- Fixed touch location stack overflow - Direct content view access
- Fixed iOS upside-down rendering - Removed unnecessary coordinate flip
- Improved iOS text rendering - Now uses UIKit's native string drawing for consistent results
- All enums now conform to
CaseIterable - Better optional handling with guard statements
- Reduced force unwrapping
// New gradient directions
let fill = ViewletFill.linearGradient(
direction: .diagonal, // New: diagonal gradients
colors: [.red, .blue]
)
// Better style inheritance
let childStyle = ViewletStyle(name: "Child", parent: parentStyle)- Proper
[weak self]in closures - Fixed potential retain cycles
- Cleaner parent-child relationships
// Property access with modern syntax
private(set) var scrollView: XScrollView
// Better error handling patterns
guard let viewlet = findViewlet(at: point) else { return }
// Cleaner optional chaining
panorama?.setNeedsDisplay()Package.swift:
dependencies: [
// Remove ZKit
// .package(url: "https://github.com/codelynx/ZKit", from: "1.0.0")
// Add XPlatform if needed
.package(url: "https://github.com/codelynx/XPlatform", from: "1.0.0"),
// Update Panorama
.package(url: "https://github.com/codelynx/Panorama", from: "1.0.0")
]Search and replace throughout your codebase:
viewlet.enabled→viewlet.isEnabledfindViewlet(point:→findViewlet(at:Gradient(locations:→Gradient(colorStops:
Before:
let style = ViewletStyle()
style.foregroundColors[.normal] = .white
style.backgroundFills[.highlighted] = .solid(.blue)After:
let style = ViewletStyle()
style.setForegroundColor(.white, for: .normal)
style.setBackgroundFill(.solid(.blue), for: .highlighted)Before:
override func draw(in context: CGContext) {
backgroundFill?.fill(rect: bounds, context: context)
}After:
override func draw(in context: CGContext) {
backgroundFill?.fill(rect: bounds, in: context)
}Remove any import ZKit statements from your code. The functionality you need is either built into Panorama or available through XPlatform.
- Build your project and fix any remaining compilation errors
- Run your test suite
- Test zoom and pan functionality
- Verify event handling works correctly on both platforms
- Check that custom viewlets render properly
Solution: Replace with isEnabled
Solution: Check the migration guide for the specific method and update the parameter labels
Solution: Use the public setter methods instead of direct property access
Solution: Gradient is now a struct. Check if you're trying to use reference semantics and adjust accordingly
-
Use Modern Swift Patterns:
// Good guard let panorama = self.panorama else { return } // Avoid if let panorama = self.panorama { // lots of code }
-
Leverage Type Safety:
// Use CaseIterable for enums for state in ViewletState.allCases { // Process all states }
-
Proper Memory Management:
Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false) { [weak self] _ in guard let self = self else { return } self.handleTimeout() }
If you encounter issues during migration:
- Check the API Reference for detailed documentation
- Review the CHANGELOG for all changes
- Open an issue on GitHub
While Panorama 1.0 includes several breaking changes, the migration process is straightforward. The improvements in type safety, performance, and modern Swift support make the upgrade worthwhile. Most projects can be migrated in a few hours by following this guide.
The core architecture remains the same, so your existing knowledge of Panorama still applies. The changes primarily improve the developer experience and code maintainability.