     
     // MARK: Private Properties
     
+    @objc private let glyph: String
+    @objc private let unicodeName: String
+    @objc private let unicodeBlockName: String?
+    @objc private let unicodeCategoryName: String?
+    @objc private let unicode: String
+    
+    @objc private let characterColor: NSColor
+    
     private var closingCueObserver: AnyCancellable?
-    
-    @objc private dynamic var glyph: String?
-    @objc private dynamic var unicodeName: String?
-    @objc private dynamic var unicodeBlockName: String?
-    @objc private dynamic var unicodeCategoryName: String?
-    @objc private dynamic var unicode: String = ""
-    
-    @objc private dynamic var characterColor: NSColor = .labelColor
     
     
     

    /// - Parameter info: The CharacterInfo instance to display.
    static func instantiate(for info: CharacterInfo) -> Self {
        
        let identifier: NSStoryboard.SceneIdentifier? = info.isComplex ? "ComplexCharacterPopoverController" : nil
        
        let instance = self.instantiate(storyboard: "CharacterPopover", identifier: identifier)
        instance.setup(characterInfo: info)
        
        return instance
    }
    
    
    
    // MARK: Public Methods
    
    /// Show the popover anchored to the specified view.
    ///
    /// - Parameters:
    ///   - parentView: The view relative to which the popover should be positioned.
    /// - Returns: A popover instance.
    func showPopover(relativeTo positioningRect: NSRect, of parentView: NSView) {
        
        assert(self.glyph != nil)
        
        let popover = NSPopover()
        popover.contentViewController = self
        popover.delegate = self
        popover.behavior = .semitransient
        popover.show(relativeTo: positioningRect, of: parentView, preferredEdge: .minY)
        
        // auto-close popover if selection is changed
        if let textView = parentView as? NSTextView {
            self.closingCueObserver = NotificationCenter.default.publisher(for: NSTextView.didChangeSelectionNotification, object: textView)
                .sink { [weak popover] _ in popover?.performClose(nil) }
        }
    }
    
    
    
    // MARK: Private Methods
    
    /// Initialize view with character info.
    ///
    /// - Parameter info: The CharacterInfo instance to display.
    private func setup(characterInfo info: CharacterInfo) {
        
        let unicodes = info.string.unicodeScalars
        

        
        self.unicode = codePoints.joined(separator: "\n")
        self.characterColor = (info.pictureString != nil) ? .tertiaryLabelColor : .labelColor
        
        super.init(coder: coder)
    }
    
    
    required init?(coder: NSCoder) {
        
        fatalError("init(coder:) has not been implemented")
    }
    
    
    
    // MARK: Public Methods
    
    /// Show the popover anchored to the specified view.
    ///
    /// - Parameters:
    ///   - parentView: The view relative to which the popover should be positioned.
    /// - Returns: A popover instance.
    func showPopover(relativeTo positioningRect: NSRect, of parentView: NSView) {
        
        let popover = NSPopover()
        popover.contentViewController = self
        popover.delegate = self
        popover.behavior = .semitransient
        popover.show(relativeTo: positioningRect, of: parentView, preferredEdge: .minY)
        
        // auto-close popover if selection is changed
        if let textView = parentView as? NSTextView {
            self.closingCueObserver = NotificationCenter.default.publisher(for: NSTextView.didChangeSelectionNotification, object: textView)
                .sink { [weak popover] _ in popover?.performClose(nil) }
        }
    }
    
}
