Dactyloidae iOS initial commit

This commit is contained in:
wuggy 2026-06-26 21:04:09 -07:00
commit 7154a0497e
2123 changed files with 197052 additions and 0 deletions

View file

@ -0,0 +1,178 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import Storage
/**
* A handler can be a plain old swift object. It does not need to extend any
* other object, but can.
*
* Handlers should register for tab events with the `registerFor` method, and
* cleanup with the `unregister` method.
*
* ```
* class HandoffHandler {
* var tabObservers: TabObservers!
*
* init() {
* tabObservers = registerFor(.didLoadFavicon, .didLoadPageMetadata)
* }
*
* deinit {
* unregister(tabObservers)
* }
* }
* ```
*
* Handlers can implement any or all `TabEventHandler` methods. If you
* implement a method, you should probably `registerFor` the event above.
*
* ```
* extension HandoffHandler: TabEventHandler {
* func tab(_ tab: Tab, didLoadPageMetadata metadata: PageMetadata) {
* print("\(tab) has \(pageMetadata)")
* }
*
* func tab(_ tab: Tab, didLoadFavicon favicon: Favicon) {
* print("\(tab) has \(favicon)")
* }
* }
* ```
*
* Tab events should probably be only posted from one place, to avoid cycles.
*
* ```
* TabEvent.post(.didLoadPageMetadata(aPageMetadata), for: tab)
* ```
*
* In this manner, we are able to use the notification center and have type safety.
*
*/
// As we want more events we add more here.
// Each event needs:
// 1. a method in the TabEventHandler.
// 2. a default implementation of the method so not everyone needs to implement it
// 3. a TabEventLabel, which is needed for registration
// 4. a TabEvent, with whatever parameters are needed.
// i) a case to map the event to the event label (var label)
// ii) a case to map the event to the event handler (func handle:with:)
protocol TabEventHandler {
func tab(_ tab: Tab, didChangeURL url: URL)
func tab(_ tab: Tab, didLoadPageMetadata metadata: PageMetadata)
func tab(_ tab: Tab, didLoadFavicon favicon: Favicon?, with: Data?)
func tabDidGainFocus(_ tab: Tab)
func tabDidLoseFocus(_ tab: Tab)
func tabDidClose(_ tab: Tab)
}
// Provide default implmentations, because we don't want to litter the code with
// empty methods, and `@objc optional` doesn't really work very well.
extension TabEventHandler {
func tab(_ tab: Tab, didChangeURL url: URL) {}
func tab(_ tab: Tab, didLoadPageMetadata metadata: PageMetadata) {}
func tab(_ tab: Tab, didLoadFavicon favicon: Favicon?, with: Data?) {}
func tabDidGainFocus(_ tab: Tab) {}
func tabDidLoseFocus(_ tab: Tab) {}
func tabDidClose(_ tab: Tab) {}
}
enum TabEventLabel: String {
case didChangeURL
case didLoadPageMetadata
case didLoadFavicon
case didGainFocus
case didLoseFocus
case didClose
}
enum TabEvent {
case didChangeURL(URL)
case didLoadPageMetadata(PageMetadata)
case didLoadFavicon(Favicon?, with: Data?)
case didGainFocus
case didLoseFocus
case didClose
var label: TabEventLabel {
switch self {
case .didChangeURL:
return .didChangeURL
case .didLoadPageMetadata:
return .didLoadPageMetadata
case .didLoadFavicon:
return .didLoadFavicon
case .didGainFocus:
return .didGainFocus
case .didLoseFocus:
return .didLoseFocus
case .didClose:
return .didClose
}
}
func handle(_ tab: Tab, with handler: TabEventHandler) {
switch self {
case .didChangeURL(let url):
handler.tab(tab, didChangeURL: url)
case .didLoadPageMetadata(let metadata):
handler.tab(tab, didLoadPageMetadata: metadata)
case .didLoadFavicon(let favicon, let data):
handler.tab(tab, didLoadFavicon: favicon, with: data)
case .didGainFocus:
handler.tabDidGainFocus(tab)
case .didLoseFocus:
handler.tabDidLoseFocus(tab)
case .didClose:
handler.tabDidClose(tab)
}
}
}
// Hide some of the machinery away from the boiler plate above.
////////////////////////////////////////////////////////////////////////////////////////
extension TabEventLabel {
var name: Notification.Name {
return Notification.Name(self.rawValue)
}
}
extension TabEvent {
func notification(for tab: Any) -> Notification {
return Notification(name: label.name, object: tab, userInfo: ["payload": self])
}
/// Use this method to post notifications to any concerned listeners.
static func post(_ event: TabEvent, for tab: Any) {
center.post(event.notification(for: tab))
}
}
// These methods are used by TabEventHandler implementers.
// Their usage remains consistent, even as we add more event types and handler methods.
////////////////////////////////////////////////////////////////////////////////////////
private let center = NotificationCenter()
typealias TabObservers = [NSObjectProtocol]
extension TabEventHandler {
/// Implementations of handles should use this method to register for events.
/// `TabObservers` should be preserved for unregistering later.
func registerFor(_ tabEvents: TabEventLabel..., queue: OperationQueue? = nil) -> TabObservers {
return tabEvents.map { eventType in
center.addObserver(forName: eventType.name, object: nil, queue: queue) { notification in
guard let tab = notification.object as? Tab,
let event = notification.userInfo?["payload"] as? TabEvent else {
return
}
event.handle(tab, with: self)
}
}
}
func unregister(_ observers: TabObservers) {
observers.forEach { observer in
center.removeObserver(observer)
}
}
}