@swiftbrowser/swift 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Package.swift +200 -9
- package/README.md +7 -9
- package/Sources/AVFoundation/AVFoundation.swift +131 -0
- package/Sources/AVFoundation/Asset.swift +487 -0
- package/Sources/AVFoundation/AudioSession.swift +144 -0
- package/Sources/AVFoundation/CMTime.swift +303 -0
- package/Sources/AVFoundation/Capture.swift +688 -0
- package/Sources/AVFoundation/Player.swift +749 -0
- package/Sources/AVFoundation/PlayerView.swift +117 -0
- package/Sources/AVKit/AVKit.swift +132 -0
- package/Sources/Accelerate/Accelerate.swift +122 -0
- package/Sources/AppIntents/AppIntents.swift +144 -0
- package/Sources/AppIntents/Parameters.swift +220 -0
- package/Sources/AppIntents/Shortcuts.swift +159 -0
- package/Sources/AudioToolbox/AudioToolbox.swift +55 -0
- package/Sources/AuthenticationServices/AuthenticationServices.swift +257 -0
- package/Sources/AuthenticationServices/Authorization.swift +251 -0
- package/Sources/Charts/Charts.swift +0 -4
- package/Sources/Combine/AnyCancellable.swift +65 -0
- package/Sources/Combine/AnyPublisher.swift +148 -0
- package/Sources/Combine/AnySubscriber.swift +230 -0
- package/Sources/Combine/Cancellable.swift +35 -0
- package/Sources/Combine/Codable.swift +29 -0
- package/Sources/Combine/CombineIdentifier.swift +45 -0
- package/Sources/Combine/Concurrency/ConcurrencyHelpers.swift +24 -0
- package/Sources/Combine/Concurrency/Future+Concurrency.swift +136 -0
- package/Sources/Combine/Concurrency/GENERATED-Publisher+Concurrency.swift +397 -0
- package/Sources/Combine/CurrentValueSubject.swift +252 -0
- package/Sources/Combine/CustomCombineIdentifierConvertible.swift +28 -0
- package/Sources/Combine/Future.swift +228 -0
- package/Sources/Combine/GENERATED-RootProtocols.swift +426 -0
- package/Sources/Combine/Helpers/ConduitBase.swift +40 -0
- package/Sources/Combine/Helpers/ConduitList.swift +59 -0
- package/Sources/Combine/Helpers/DebugHook.swift +177 -0
- package/Sources/Combine/Helpers/FilterProducer.swift +174 -0
- package/Sources/Combine/Helpers/Locking.swift +30 -0
- package/Sources/Combine/Helpers/PartialCompletion.swift +26 -0
- package/Sources/Combine/Helpers/ReduceProducer.swift +249 -0
- package/Sources/Combine/Helpers/SubjectSubscriber.swift +96 -0
- package/Sources/Combine/Helpers/SubscriberTap.swift +69 -0
- package/Sources/Combine/Helpers/SubscriptionStatus.swift +33 -0
- package/Sources/Combine/Helpers/SubscriptionTap.swift +33 -0
- package/Sources/Combine/Helpers/Utils.swift +30 -0
- package/Sources/Combine/Helpers/Violations.swift +33 -0
- package/Sources/Combine/ImmediateScheduler.swift +178 -0
- package/Sources/Combine/LICENSE +21 -0
- package/Sources/Combine/NotificationPublisher.swift +48 -0
- package/Sources/Combine/ObjectWillChange.swift +46 -0
- package/Sources/Combine/PassthroughSubject.swift +222 -0
- package/Sources/Combine/Publisher+Subscribe.swift +53 -0
- package/Sources/Combine/Publishers/Deferred.swift +48 -0
- package/Sources/Combine/Publishers/Empty.swift +54 -0
- package/Sources/Combine/Publishers/Fail.swift +42 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Catch.swift +673 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Encode.swift +388 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.MapKeyPath.swift +384 -0
- package/Sources/Combine/Publishers/Just.swift +313 -0
- package/Sources/Combine/Publishers/Optional.Publisher.swift +315 -0
- package/Sources/Combine/Publishers/Publishers.AllSatisfy.swift +217 -0
- package/Sources/Combine/Publishers/Publishers.AssertNoFailure.swift +158 -0
- package/Sources/Combine/Publishers/Publishers.Autoconnect.swift +193 -0
- package/Sources/Combine/Publishers/Publishers.Breakpoint.swift +231 -0
- package/Sources/Combine/Publishers/Publishers.Buffer.swift +356 -0
- package/Sources/Combine/Publishers/Publishers.Collect.swift +102 -0
- package/Sources/Combine/Publishers/Publishers.CollectByCount.swift +195 -0
- package/Sources/Combine/Publishers/Publishers.CompactMap.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.Comparison.swift +371 -0
- package/Sources/Combine/Publishers/Publishers.Concatenate.swift +474 -0
- package/Sources/Combine/Publishers/Publishers.Contains.swift +293 -0
- package/Sources/Combine/Publishers/Publishers.Count.swift +87 -0
- package/Sources/Combine/Publishers/Publishers.Debounce.swift +332 -0
- package/Sources/Combine/Publishers/Publishers.Delay.swift +269 -0
- package/Sources/Combine/Publishers/Publishers.Drop.swift +155 -0
- package/Sources/Combine/Publishers/Publishers.DropUntilOutput.swift +283 -0
- package/Sources/Combine/Publishers/Publishers.DropWhile.swift +378 -0
- package/Sources/Combine/Publishers/Publishers.Filter.swift +248 -0
- package/Sources/Combine/Publishers/Publishers.First.swift +261 -0
- package/Sources/Combine/Publishers/Publishers.FlatMap.swift +533 -0
- package/Sources/Combine/Publishers/Publishers.HandleEvents.swift +260 -0
- package/Sources/Combine/Publishers/Publishers.IgnoreOutput.swift +109 -0
- package/Sources/Combine/Publishers/Publishers.Last.swift +252 -0
- package/Sources/Combine/Publishers/Publishers.MakeConnectable.swift +82 -0
- package/Sources/Combine/Publishers/Publishers.Map.swift +351 -0
- package/Sources/Combine/Publishers/Publishers.MapError.swift +140 -0
- package/Sources/Combine/Publishers/Publishers.MeasureInterval.swift +187 -0
- package/Sources/Combine/Publishers/Publishers.Multicast.swift +284 -0
- package/Sources/Combine/Publishers/Publishers.Output.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.PrefixUntilOutput.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.PrefixWhile.swift +176 -0
- package/Sources/Combine/Publishers/Publishers.Print.swift +206 -0
- package/Sources/Combine/Publishers/Publishers.ReceiveOn.swift +227 -0
- package/Sources/Combine/Publishers/Publishers.Reduce.swift +204 -0
- package/Sources/Combine/Publishers/Publishers.RemoveDuplicates.swift +264 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceEmpty.swift +201 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceError.swift +211 -0
- package/Sources/Combine/Publishers/Publishers.Retry.swift +274 -0
- package/Sources/Combine/Publishers/Publishers.Scan.swift +331 -0
- package/Sources/Combine/Publishers/Publishers.Sequence.swift +437 -0
- package/Sources/Combine/Publishers/Publishers.SetFailureType.swift +120 -0
- package/Sources/Combine/Publishers/Publishers.Share.swift +104 -0
- package/Sources/Combine/Publishers/Publishers.SubscribeOn.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.SwitchToLatest.swift +444 -0
- package/Sources/Combine/Publishers/Publishers.Throttle.swift +359 -0
- package/Sources/Combine/Publishers/Publishers.Timeout.swift +301 -0
- package/Sources/Combine/Publishers/Publishers.Zip.swift +720 -0
- package/Sources/Combine/Publishers/Publishers.swift +13 -0
- package/Sources/Combine/Publishers/Record.swift +237 -0
- package/Sources/Combine/Publishers/Result.Publisher.swift +337 -0
- package/Sources/Combine/Result.swift +46 -0
- package/Sources/Combine/Scheduler.swift +89 -0
- package/Sources/Combine/Schedulers.swift +211 -0
- package/Sources/Combine/Subject+Void.swift +17 -0
- package/Sources/Combine/Subscriber+Void.swift +20 -0
- package/Sources/Combine/Subscribers/Subscribers.Assign.swift +175 -0
- package/Sources/Combine/Subscribers/Subscribers.Completion.swift +90 -0
- package/Sources/Combine/Subscribers/Subscribers.Demand.swift +476 -0
- package/Sources/Combine/Subscribers/Subscribers.Sink.swift +206 -0
- package/Sources/Combine/Subscribers/Subscribers.swift +9 -0
- package/Sources/Combine/Subscription.swift +22 -0
- package/Sources/Combine/Subscriptions.swift +42 -0
- package/Sources/Combine/SwiftUIObservation.swift +10 -0
- package/Sources/Combine/_Introspection.swift +107 -0
- package/Sources/CommonCrypto/CommonCrypto.swift +80 -0
- package/Sources/Compression/Compression.swift +365 -0
- package/Sources/Compression/Deflate.swift +407 -0
- package/Sources/Compression/Zlib.swift +179 -0
- package/Sources/CoreFoundationTypes/CoreFoundationTypes.swift +101 -0
- package/Sources/CoreGraphics/CoreGraphics.swift +411 -0
- package/Sources/CoreGraphics/ProjectionTransforms.swift +28 -0
- package/Sources/CoreHaptics/CoreHaptics.swift +399 -0
- package/Sources/CoreImage/CIColor.swift +41 -0
- package/Sources/CoreImage/CIContext.swift +31 -0
- package/Sources/CoreImage/CIDetector.swift +57 -0
- package/Sources/CoreImage/CIFilter.swift +243 -0
- package/Sources/CoreImage/CIFilterBuiltins.swift +136 -0
- package/Sources/CoreImage/CIImage.swift +162 -0
- package/Sources/CoreImage/QRCode.swift +393 -0
- package/Sources/CoreLocation/Coordinate.swift +30 -0
- package/Sources/CoreLocation/Location.swift +65 -0
- package/Sources/CoreML/Evaluation.swift +414 -0
- package/Sources/CoreML/FeatureValue.swift +266 -0
- package/Sources/CoreML/Model.swift +269 -0
- package/Sources/CoreML/MultiArray.swift +92 -0
- package/Sources/CoreML/Specification.swift +233 -0
- package/Sources/CoreText/CoreText.swift +64 -0
- package/Sources/CoreTransferable/CoreTransferable.swift +606 -0
- package/Sources/CryptoKit/ChaChaPoly.swift +258 -0
- package/Sources/CryptoKit/CryptoKit.swift +375 -0
- package/Sources/CryptoKit/Curve25519.swift +317 -0
- package/Sources/CryptoKit/P256.swift +321 -0
- package/Sources/FoundationBridge/AppStorage.swift +165 -0
- package/Sources/FoundationBridge/AssetResources.swift +90 -0
- package/Sources/FoundationBridge/AsyncBytes.swift +128 -0
- package/Sources/FoundationBridge/AsyncImage.swift +116 -0
- package/Sources/FoundationBridge/AttributedStrings.swift +16 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/Bundle.swift +41 -4
- package/Sources/FoundationBridge/CombineExports.swift +8 -0
- package/Sources/FoundationBridge/CoreFoundation.swift +77 -0
- package/Sources/FoundationBridge/Dispatch.swift +458 -0
- package/Sources/FoundationBridge/EnvironmentFoundation.swift +41 -0
- package/Sources/FoundationBridge/FileImporter.swift +128 -0
- package/Sources/FoundationBridge/FileSystem.swift +68 -0
- package/Sources/FoundationBridge/FoundationBridge.swift +24 -72
- package/Sources/FoundationBridge/HostResolution.swift +31 -0
- package/Sources/FoundationBridge/ItemProvider.swift +655 -0
- package/Sources/FoundationBridge/KeyValueObserving.swift +138 -0
- package/Sources/FoundationBridge/Links.swift +127 -0
- package/Sources/FoundationBridge/ListFormatter.swift +32 -0
- package/Sources/FoundationBridge/Localization.swift +149 -0
- package/Sources/FoundationBridge/Locks.swift +50 -0
- package/Sources/FoundationBridge/Markdown.swift +527 -0
- package/Sources/FoundationBridge/MeasurementFormat.swift +95 -0
- package/Sources/FoundationBridge/NotificationCenter.swift +154 -0
- package/Sources/FoundationBridge/OpenURLHandlers.swift +60 -0
- package/Sources/FoundationBridge/Operation.swift +195 -0
- package/Sources/FoundationBridge/ProcessInfo.swift +32 -0
- package/Sources/FoundationBridge/RelativeDateTimeFormatter.swift +136 -0
- package/Sources/FoundationBridge/SceneStorage.swift +78 -0
- package/Sources/FoundationBridge/SecurityExports.swift +8 -0
- package/Sources/FoundationBridge/Selector.swift +226 -0
- package/Sources/FoundationBridge/StringComparators.swift +17 -0
- package/Sources/FoundationBridge/Table.swift +211 -0
- package/Sources/FoundationBridge/TextAttributes.swift +42 -0
- package/Sources/FoundationBridge/Timer.swift +6 -1
- package/Sources/FoundationBridge/URLSession.swift +718 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/UserDefaults.swift +17 -4
- package/Sources/FoundationBridge/WebAuthRedirects.swift +227 -0
- package/Sources/FoundationBridge/WebSocket.swift +187 -0
- package/Sources/FoundationModels/FoundationModels.swift +1098 -0
- package/Sources/ImageIO/GIF.swift +72 -0
- package/Sources/ImageIO/ImageIO.swift +248 -0
- package/Sources/ImageIO/ImageTypes.swift +28 -0
- package/Sources/LinkPresentation/LinkPresentation.swift +249 -0
- package/Sources/LocalAuthentication/LAContext.swift +86 -0
- package/Sources/MapKit/Geometry.swift +223 -0
- package/Sources/MapKit/Map.swift +638 -0
- package/Sources/MapKit/MapView.swift +229 -0
- package/Sources/MobileCoreServices/MobileCoreServices.swift +28 -0
- package/Sources/NaturalLanguage/LanguageIdentifier.swift +317 -0
- package/Sources/NaturalLanguage/NaturalLanguage.swift +135 -0
- package/Sources/OSLog/OSLog.swift +6 -0
- package/Sources/Photos/Photos.swift +572 -0
- package/Sources/PhotosUI/PhotosPicker.swift +248 -0
- package/Sources/PhotosUI/PhotosPickerItem.swift +261 -0
- package/Sources/QuickLook/QuickLook.swift +168 -0
- package/Sources/SafariServices/SafariServices.swift +106 -0
- package/Sources/Security/Security.swift +332 -0
- package/Sources/StoreKit/AppStore.swift +207 -0
- package/Sources/StoreKit/Products.swift +280 -0
- package/Sources/StoreKit/RequestReview.swift +28 -0
- package/Sources/StoreKit/StoreKit1.swift +486 -0
- package/Sources/StoreKit/StoreViews.swift +428 -0
- package/Sources/StoreKit/Transactions.swift +197 -0
- package/Sources/SwiftData/Macros.swift +58 -0
- package/Sources/SwiftData/ModelActor.swift +38 -0
- package/Sources/SwiftData/ModelContainer.swift +129 -0
- package/Sources/SwiftData/ModelContext.swift +451 -0
- package/Sources/SwiftData/PersistentModel.swift +238 -0
- package/Sources/SwiftData/Properties.swift +261 -0
- package/Sources/SwiftData/Query.swift +256 -0
- package/Sources/SwiftData/Schema.swift +192 -0
- package/Sources/SwiftData/Store.swift +146 -0
- package/Sources/SwiftData/StringPredicates.swift +125 -0
- package/Sources/SwiftDataMacros/EntryMacro.swift +82 -0
- package/Sources/SwiftDataMacros/GenerableMacro.swift +183 -0
- package/Sources/SwiftDataMacros/ModelActorMacro.swift +46 -0
- package/Sources/SwiftDataMacros/ModelMacro.swift +260 -0
- package/Sources/SwiftDataMacros/Plugin.swift +18 -0
- package/Sources/SwiftDataMacros/PredicateMacro.swift +1244 -0
- package/Sources/SwiftUI/API/Accessibility.swift +55 -6
- package/Sources/SwiftUI/API/AccessibilityExtras.swift +33 -0
- package/Sources/SwiftUI/API/AccessibilityFocus.swift +63 -0
- package/Sources/SwiftUI/API/Alert.swift +1 -1
- package/Sources/SwiftUI/API/AlignmentGuides.swift +68 -0
- package/Sources/SwiftUI/API/Animation.swift +37 -5
- package/Sources/SwiftUI/API/App.swift +31 -1
- package/Sources/SwiftUI/API/BindingCollections.swift +35 -0
- package/Sources/SwiftUI/API/BindingForEach.swift +27 -0
- package/Sources/SwiftUI/API/Bitmaps.swift +254 -0
- package/Sources/SwiftUI/API/ButtonStyles.swift +96 -0
- package/Sources/SwiftUI/API/Canvas.swift +337 -0
- package/Sources/SwiftUI/API/Color.swift +104 -28
- package/Sources/SwiftUI/API/ColorEffects.swift +52 -0
- package/Sources/SwiftUI/API/ColorPickerAndLabeledContent.swift +138 -0
- package/Sources/SwiftUI/API/ColorSpaces.swift +161 -0
- package/Sources/SwiftUI/API/Commands.swift +106 -0
- package/Sources/SwiftUI/API/ContainerBackground.swift +28 -0
- package/Sources/SwiftUI/API/ContentShape.swift +33 -0
- package/Sources/SwiftUI/API/ContentSizeCategory.swift +35 -0
- package/Sources/SwiftUI/API/ContentUnavailable.swift +101 -0
- package/Sources/SwiftUI/API/Controls.swift +59 -27
- package/Sources/SwiftUI/API/CustomLayout.swift +49 -10
- package/Sources/SwiftUI/API/CustomToolbarContent.swift +46 -0
- package/Sources/SwiftUI/API/Decorations.swift +1 -1
- package/Sources/SwiftUI/API/EditMode.swift +25 -82
- package/Sources/SwiftUI/API/Effects.swift +125 -10
- package/Sources/SwiftUI/API/Entry.swift +8 -0
- package/Sources/SwiftUI/API/Environment.swift +100 -9
- package/Sources/SwiftUI/API/EnvironmentSendable.swift +6 -0
- package/Sources/SwiftUI/API/Focus.swift +11 -3
- package/Sources/SwiftUI/API/Gauge.swift +26 -10
- package/Sources/SwiftUI/API/GeometryEffects.swift +232 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +13 -2
- package/Sources/SwiftUI/API/GestureState.swift +65 -0
- package/Sources/SwiftUI/API/Gestures.swift +1 -1
- package/Sources/SwiftUI/API/Glass.swift +127 -0
- package/Sources/SwiftUI/API/Grid.swift +1 -1
- package/Sources/SwiftUI/API/GroupBoxAndControlGroup.swift +130 -0
- package/Sources/SwiftUI/API/KeyPress.swift +54 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +13 -6
- package/Sources/SwiftUI/API/Layout.swift +71 -12
- package/Sources/SwiftUI/API/LazyStacks.swift +49 -0
- package/Sources/SwiftUI/API/LegacyAlert.swift +97 -0
- package/Sources/SwiftUI/API/Lists.swift +61 -6
- package/Sources/SwiftUI/API/Menu.swift +8 -1
- package/Sources/SwiftUI/API/Modifiers.swift +88 -12
- package/Sources/SwiftUI/API/MoreModifiers.swift +100 -0
- package/Sources/SwiftUI/API/Namespace.swift +44 -0
- package/Sources/SwiftUI/API/Navigation.swift +24 -9
- package/Sources/SwiftUI/API/NavigationDestinationBindings.swift +55 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +16 -2
- package/Sources/SwiftUI/API/Path.swift +543 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +48 -31
- package/Sources/SwiftUI/API/PhaseAnimator.swift +130 -0
- package/Sources/SwiftUI/API/Picker.swift +14 -4
- package/Sources/SwiftUI/API/PointerAndKeyboard.swift +77 -0
- package/Sources/SwiftUI/API/Popover.swift +41 -0
- package/Sources/SwiftUI/API/Preferences.swift +262 -0
- package/Sources/SwiftUI/API/Presentation.swift +17 -4
- package/Sources/SwiftUI/API/PresentationExtras.swift +60 -0
- package/Sources/SwiftUI/API/Primitives.swift +168 -39
- package/Sources/SwiftUI/API/Publishers.swift +4 -4
- package/Sources/SwiftUI/API/Redaction.swift +75 -0
- package/Sources/SwiftUI/API/RowActions.swift +3 -2
- package/Sources/SwiftUI/API/Scenes.swift +131 -0
- package/Sources/SwiftUI/API/ScrollModifiers.swift +132 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +3 -3
- package/Sources/SwiftUI/API/ScrollViewReader.swift +144 -0
- package/Sources/SwiftUI/API/SectionSupplements.swift +86 -0
- package/Sources/SwiftUI/API/Shape.swift +214 -0
- package/Sources/SwiftUI/API/ShapeStyle.swift +187 -8
- package/Sources/SwiftUI/API/ShapeTrim.swift +78 -0
- package/Sources/SwiftUI/API/Sharing.swift +193 -0
- package/Sources/SwiftUI/API/StackLayouts.swift +199 -0
- package/Sources/SwiftUI/API/State.swift +18 -1
- package/Sources/SwiftUI/API/StatusGaps.swift +121 -0
- package/Sources/SwiftUI/API/SymbolEffects.swift +203 -0
- package/Sources/SwiftUI/API/TabContent.swift +841 -0
- package/Sources/SwiftUI/API/Table.swift +382 -0
- package/Sources/SwiftUI/API/Tabs.swift +148 -4
- package/Sources/SwiftUI/API/Tasks.swift +2 -2
- package/Sources/SwiftUI/API/Text.swift +386 -0
- package/Sources/SwiftUI/API/TextDates.swift +61 -0
- package/Sources/SwiftUI/API/TextDecorations.swift +52 -0
- package/Sources/SwiftUI/API/TextFieldAxis.swift +103 -0
- package/Sources/SwiftUI/API/TextFieldCommit.swift +52 -0
- package/Sources/SwiftUI/API/Toolbar.swift +52 -16
- package/Sources/SwiftUI/API/Transfer.swift +145 -0
- package/Sources/SwiftUI/API/UnevenRoundedRectangle.swift +66 -0
- package/Sources/SwiftUI/API/Video.swift +64 -0
- package/Sources/SwiftUI/API/View.swift +46 -2
- package/Sources/SwiftUI/API/ViewModifier.swift +4 -1
- package/Sources/SwiftUI/API/VisualEffect.swift +74 -1
- package/Sources/SwiftUI/API/Windows.swift +25 -0
- package/Sources/SwiftUI/Charts/Axis.swift +7 -1
- package/Sources/SwiftUI/Charts/AxisFormatsAndRanges.swift +68 -0
- package/Sources/SwiftUI/Charts/Chart.swift +17 -3
- package/Sources/SwiftUI/Charts/ChartProxy.swift +57 -0
- package/Sources/SwiftUI/Localization/Localization.swift +524 -0
- package/Sources/SwiftUI/Localization/PluralRules.swift +127 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +2 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +37 -1
- package/Sources/SwiftUI/Runtime/ImageSource.swift +310 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +76 -4
- package/Sources/SwiftUI/Runtime/LazyRows.swift +99 -0
- package/Sources/SwiftUI/Runtime/Measurements.swift +140 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +58 -12
- package/Sources/SwiftUI/Runtime/Reconciler.swift +10 -1
- package/Sources/SwiftUI/Runtime/Replay.swift +382 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +977 -198
- package/Sources/SwiftUI/Runtime/Sameness.swift +152 -0
- package/Sources/SwiftUI/Runtime/ViewPath.swift +89 -0
- package/Sources/SwiftUI/Runtime/ViewTraits.swift +175 -0
- package/Sources/SwiftUI/Runtime/WebAuthHeadless.swift +49 -0
- package/Sources/Symbols/Symbols.swift +25 -0
- package/Sources/Translation/Translation.swift +423 -0
- package/Sources/UIKit/Activity.swift +213 -0
- package/Sources/UIKit/Alerts.swift +69 -0
- package/Sources/UIKit/AppDelegate.swift +222 -0
- package/Sources/UIKit/Appearance.swift +84 -0
- package/Sources/UIKit/ColorConversions.swift +70 -0
- package/Sources/UIKit/Controls.swift +225 -0
- package/Sources/UIKit/Device.swift +200 -0
- package/Sources/UIKit/Feedback.swift +65 -0
- package/Sources/UIKit/Font.swift +307 -0
- package/Sources/UIKit/Graphics.swift +204 -0
- package/Sources/UIKit/HostingController.swift +185 -0
- package/Sources/UIKit/Identity.swift +24 -0
- package/Sources/UIKit/Introspectable.swift +510 -0
- package/Sources/UIKit/Pasteboard.swift +363 -0
- package/Sources/UIKit/PhotoAlbum.swift +81 -0
- package/Sources/UIKit/RemoteNotifications.swift +15 -0
- package/Sources/UIKit/Representable.swift +288 -0
- package/Sources/UIKit/SafeArea.swift +12 -0
- package/Sources/UIKit/SceneDelegates.swift +132 -0
- package/Sources/UIKit/Scenes.swift +40 -0
- package/Sources/UIKit/ScrollView.swift +157 -0
- package/Sources/UIKit/ShortcutItems.swift +67 -0
- package/Sources/UIKit/SymbolConfiguration.swift +124 -0
- package/Sources/UIKit/TargetAction.swift +123 -0
- package/Sources/UIKit/TextPaste.swift +161 -0
- package/Sources/UIKit/TextView.swift +538 -0
- package/Sources/UIKit/UIKit.swift +292 -0
- package/Sources/UIKit/UIView.swift +855 -0
- package/Sources/UIKit/ViewTranslation.swift +311 -0
- package/Sources/UniformTypeIdentifiers/UTType.swift +196 -0
- package/Sources/UserNotifications/Content.swift +370 -0
- package/Sources/UserNotifications/NotificationCenter.swift +309 -0
- package/Sources/WeatherKit/WeatherKit.swift +228 -0
- package/Sources/WebKit/WebKit.swift +245 -0
- package/Sources/os/os.swift +376 -0
- package/Sources/zlib/zlib.swift +6 -0
- package/index.d.ts +1 -1
- package/index.mjs +2 -2
- package/manifest.d.mts +2 -0
- package/manifest.mjs +41 -0
- package/package.json +5 -3
- package/sync-sources.mjs +21 -7
- package/Sources/FoundationLite/Common.swift +0 -3
- package/Sources/FoundationLite/FoundationLite.swift +0 -171
- package/Sources/FoundationLite/Interaction.swift +0 -146
- package/Sources/FoundationLite/Timer.swift +0 -48
- package/Sources/_FoundationCommon/_FoundationCommon.swift +0 -10
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
// UserNotifications' value types: what a notification says, when it fires,
|
|
2
|
+
// and what the viewer allowed. Shapes and names follow Apple's framework;
|
|
3
|
+
// the center that schedules and shows them is NotificationCenter.swift.
|
|
4
|
+
|
|
5
|
+
import Foundation
|
|
6
|
+
|
|
7
|
+
// MARK: Authorization
|
|
8
|
+
|
|
9
|
+
/// What an app asks permission for. The browser has one permission for all of
|
|
10
|
+
/// it; the options are recorded and otherwise ignored.
|
|
11
|
+
public struct UNAuthorizationOptions: OptionSet, Sendable, Hashable {
|
|
12
|
+
public let rawValue: UInt
|
|
13
|
+
public init(rawValue: UInt) { self.rawValue = rawValue }
|
|
14
|
+
|
|
15
|
+
public static let badge = UNAuthorizationOptions(rawValue: 1 << 0)
|
|
16
|
+
public static let sound = UNAuthorizationOptions(rawValue: 1 << 1)
|
|
17
|
+
public static let alert = UNAuthorizationOptions(rawValue: 1 << 2)
|
|
18
|
+
public static let carPlay = UNAuthorizationOptions(rawValue: 1 << 3)
|
|
19
|
+
public static let criticalAlert = UNAuthorizationOptions(rawValue: 1 << 4)
|
|
20
|
+
public static let providesAppNotificationSettings = UNAuthorizationOptions(rawValue: 1 << 5)
|
|
21
|
+
public static let provisional = UNAuthorizationOptions(rawValue: 1 << 6)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// The browser's `Notification.permission`: `default` is `.notDetermined`,
|
|
25
|
+
/// `granted` `.authorized`, `denied` (or no Notification API) `.denied`.
|
|
26
|
+
public enum UNAuthorizationStatus: Int, Sendable {
|
|
27
|
+
case notDetermined = 0
|
|
28
|
+
case denied = 1
|
|
29
|
+
case authorized = 2
|
|
30
|
+
case provisional = 3
|
|
31
|
+
case ephemeral = 4
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public enum UNNotificationSetting: Int, Sendable {
|
|
35
|
+
case notSupported = 0
|
|
36
|
+
case disabled = 1
|
|
37
|
+
case enabled = 2
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public enum UNAlertStyle: Int, Sendable {
|
|
41
|
+
case none = 0
|
|
42
|
+
case banner = 1
|
|
43
|
+
case alert = 2
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public enum UNShowPreviewsSetting: Int, Sendable {
|
|
47
|
+
case always = 0
|
|
48
|
+
case whenAuthenticated = 1
|
|
49
|
+
case never = 2
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// What `getNotificationSettings` reports. Every per-kind setting follows the
|
|
53
|
+
/// one browser permission: enabled when authorized, else disabled.
|
|
54
|
+
public final class UNNotificationSettings: NSObject, @unchecked Sendable {
|
|
55
|
+
public let authorizationStatus: UNAuthorizationStatus
|
|
56
|
+
public let soundSetting: UNNotificationSetting
|
|
57
|
+
public let badgeSetting: UNNotificationSetting
|
|
58
|
+
public let alertSetting: UNNotificationSetting
|
|
59
|
+
public let notificationCenterSetting: UNNotificationSetting
|
|
60
|
+
public let lockScreenSetting: UNNotificationSetting
|
|
61
|
+
public let carPlaySetting: UNNotificationSetting
|
|
62
|
+
public let criticalAlertSetting: UNNotificationSetting
|
|
63
|
+
public let timeSensitiveSetting: UNNotificationSetting
|
|
64
|
+
public let scheduledDeliverySetting: UNNotificationSetting
|
|
65
|
+
public let directMessagesSetting: UNNotificationSetting
|
|
66
|
+
public let announcementSetting: UNNotificationSetting
|
|
67
|
+
public let alertStyle: UNAlertStyle
|
|
68
|
+
public let showPreviewsSetting: UNShowPreviewsSetting
|
|
69
|
+
public let providesAppNotificationSettings: Bool
|
|
70
|
+
|
|
71
|
+
init(authorizationStatus: UNAuthorizationStatus) {
|
|
72
|
+
self.authorizationStatus = authorizationStatus
|
|
73
|
+
let shown: UNNotificationSetting = authorizationStatus == .authorized ? .enabled : .disabled
|
|
74
|
+
soundSetting = shown
|
|
75
|
+
badgeSetting = shown
|
|
76
|
+
alertSetting = shown
|
|
77
|
+
notificationCenterSetting = shown
|
|
78
|
+
lockScreenSetting = shown
|
|
79
|
+
carPlaySetting = .notSupported
|
|
80
|
+
criticalAlertSetting = .notSupported
|
|
81
|
+
timeSensitiveSetting = .notSupported
|
|
82
|
+
scheduledDeliverySetting = .notSupported
|
|
83
|
+
directMessagesSetting = .notSupported
|
|
84
|
+
announcementSetting = .notSupported
|
|
85
|
+
alertStyle = authorizationStatus == .authorized ? .banner : .none
|
|
86
|
+
showPreviewsSetting = .always
|
|
87
|
+
providesAppNotificationSettings = false
|
|
88
|
+
super.init()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Errors the center reports. In the browser: `.notificationsNotAllowed` when
|
|
93
|
+
/// the page has no Notification API (an iPhone browser tab that is not on the
|
|
94
|
+
/// Home Screen, for one).
|
|
95
|
+
public struct UNError: Error, Hashable, Sendable, CustomStringConvertible {
|
|
96
|
+
public enum Code: Int, Sendable {
|
|
97
|
+
case notificationsNotAllowed = 1
|
|
98
|
+
case attachmentInvalidURL = 100
|
|
99
|
+
case attachmentUnrecognizedType = 101
|
|
100
|
+
case attachmentInvalidFileSize = 102
|
|
101
|
+
case attachmentNotInDataStore = 103
|
|
102
|
+
case attachmentMoveIntoDataStoreFailed = 104
|
|
103
|
+
case attachmentCorrupt = 105
|
|
104
|
+
case notificationInvalidNoDate = 1400
|
|
105
|
+
case notificationInvalidNoContent = 1401
|
|
106
|
+
case contentProvidingObjectNotAllowed = 1500
|
|
107
|
+
case contentProvidingInvalid = 1501
|
|
108
|
+
case badgeInputInvalid = 1600
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public let code: Code
|
|
112
|
+
|
|
113
|
+
public init(_ code: Code) { self.code = code }
|
|
114
|
+
|
|
115
|
+
public static let notificationsNotAllowed = Code.notificationsNotAllowed
|
|
116
|
+
public static let notificationInvalidNoDate = Code.notificationInvalidNoDate
|
|
117
|
+
public static let notificationInvalidNoContent = Code.notificationInvalidNoContent
|
|
118
|
+
|
|
119
|
+
public var description: String {
|
|
120
|
+
switch code {
|
|
121
|
+
case .notificationsNotAllowed: "Notifications are not allowed for this application"
|
|
122
|
+
default: "UNErrorDomain error \(code.rawValue)"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public var localizedDescription: String { description }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// MARK: Content
|
|
130
|
+
|
|
131
|
+
public struct UNNotificationSoundName: RawRepresentable, Hashable, Sendable {
|
|
132
|
+
public let rawValue: String
|
|
133
|
+
public init(rawValue: String) { self.rawValue = rawValue }
|
|
134
|
+
public init(_ rawValue: String) { self.rawValue = rawValue }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// A notification's sound. The browser plays its own, or none: a notification
|
|
138
|
+
/// without a sound is shown `silent`.
|
|
139
|
+
public final class UNNotificationSound: NSObject, @unchecked Sendable {
|
|
140
|
+
public let name: UNNotificationSoundName?
|
|
141
|
+
let critical: Bool
|
|
142
|
+
|
|
143
|
+
init(name: UNNotificationSoundName?, critical: Bool = false) {
|
|
144
|
+
self.name = name
|
|
145
|
+
self.critical = critical
|
|
146
|
+
super.init()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public class var `default`: UNNotificationSound { UNNotificationSound(name: nil) }
|
|
150
|
+
public class var defaultCritical: UNNotificationSound { UNNotificationSound(name: nil, critical: true) }
|
|
151
|
+
public class var defaultRingtone: UNNotificationSound { UNNotificationSound(name: nil) }
|
|
152
|
+
|
|
153
|
+
public class func defaultCriticalSound(withAudioVolume volume: Float) -> UNNotificationSound { .defaultCritical }
|
|
154
|
+
public class func soundNamed(_ name: UNNotificationSoundName) -> UNNotificationSound { UNNotificationSound(name: name) }
|
|
155
|
+
public class func criticalSoundNamed(_ name: UNNotificationSoundName) -> UNNotificationSound { UNNotificationSound(name: name, critical: true) }
|
|
156
|
+
public class func criticalSoundNamed(_ name: UNNotificationSoundName, withAudioVolume volume: Float) -> UNNotificationSound {
|
|
157
|
+
UNNotificationSound(name: name, critical: true)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public enum UNNotificationInterruptionLevel: UInt, Sendable {
|
|
162
|
+
case passive = 0
|
|
163
|
+
case active = 1
|
|
164
|
+
case timeSensitive = 2
|
|
165
|
+
case critical = 3
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// What a notification says. Immutable; `UNMutableNotificationContent` is the
|
|
169
|
+
/// one apps fill in. The browser shows the title, and the subtitle and body
|
|
170
|
+
/// as the notification's text, one line each.
|
|
171
|
+
open class UNNotificationContent: NSObject, NSCopying, NSMutableCopying, @unchecked Sendable {
|
|
172
|
+
fileprivate var storedTitle = ""
|
|
173
|
+
fileprivate var storedSubtitle = ""
|
|
174
|
+
fileprivate var storedBody = ""
|
|
175
|
+
fileprivate var storedBadge: NSNumber?
|
|
176
|
+
fileprivate var storedSound: UNNotificationSound?
|
|
177
|
+
fileprivate var storedUserInfo: [AnyHashable: Any] = [:]
|
|
178
|
+
fileprivate var storedCategoryIdentifier = ""
|
|
179
|
+
fileprivate var storedThreadIdentifier = ""
|
|
180
|
+
fileprivate var storedLaunchImageName = ""
|
|
181
|
+
fileprivate var storedTargetContentIdentifier: String?
|
|
182
|
+
fileprivate var storedInterruptionLevel = UNNotificationInterruptionLevel.active
|
|
183
|
+
fileprivate var storedRelevanceScore = 0.0
|
|
184
|
+
fileprivate var storedFilterCriteria: String?
|
|
185
|
+
|
|
186
|
+
open var title: String { storedTitle }
|
|
187
|
+
open var subtitle: String { storedSubtitle }
|
|
188
|
+
open var body: String { storedBody }
|
|
189
|
+
open var badge: NSNumber? { storedBadge }
|
|
190
|
+
open var sound: UNNotificationSound? { storedSound }
|
|
191
|
+
open var userInfo: [AnyHashable: Any] { storedUserInfo }
|
|
192
|
+
open var categoryIdentifier: String { storedCategoryIdentifier }
|
|
193
|
+
open var threadIdentifier: String { storedThreadIdentifier }
|
|
194
|
+
open var launchImageName: String { storedLaunchImageName }
|
|
195
|
+
open var targetContentIdentifier: String? { storedTargetContentIdentifier }
|
|
196
|
+
open var interruptionLevel: UNNotificationInterruptionLevel { storedInterruptionLevel }
|
|
197
|
+
open var relevanceScore: Double { storedRelevanceScore }
|
|
198
|
+
open var filterCriteria: String? { storedFilterCriteria }
|
|
199
|
+
|
|
200
|
+
public override init() {
|
|
201
|
+
super.init()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fileprivate func fill(from other: UNNotificationContent) {
|
|
205
|
+
storedTitle = other.storedTitle
|
|
206
|
+
storedSubtitle = other.storedSubtitle
|
|
207
|
+
storedBody = other.storedBody
|
|
208
|
+
storedBadge = other.storedBadge
|
|
209
|
+
storedSound = other.storedSound
|
|
210
|
+
storedUserInfo = other.storedUserInfo
|
|
211
|
+
storedCategoryIdentifier = other.storedCategoryIdentifier
|
|
212
|
+
storedThreadIdentifier = other.storedThreadIdentifier
|
|
213
|
+
storedLaunchImageName = other.storedLaunchImageName
|
|
214
|
+
storedTargetContentIdentifier = other.storedTargetContentIdentifier
|
|
215
|
+
storedInterruptionLevel = other.storedInterruptionLevel
|
|
216
|
+
storedRelevanceScore = other.storedRelevanceScore
|
|
217
|
+
storedFilterCriteria = other.storedFilterCriteria
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// An immutable copy.
|
|
221
|
+
open func copy(with zone: NSZone? = nil) -> Any {
|
|
222
|
+
let copy = UNNotificationContent()
|
|
223
|
+
copy.fill(from: self)
|
|
224
|
+
return copy
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// A `UNMutableNotificationContent` with the same values.
|
|
228
|
+
open func mutableCopy(with zone: NSZone? = nil) -> Any {
|
|
229
|
+
let copy = UNMutableNotificationContent()
|
|
230
|
+
copy.fill(from: self)
|
|
231
|
+
return copy
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
open class UNMutableNotificationContent: UNNotificationContent, @unchecked Sendable {
|
|
236
|
+
open override var title: String { get { storedTitle } set { storedTitle = newValue } }
|
|
237
|
+
open override var subtitle: String { get { storedSubtitle } set { storedSubtitle = newValue } }
|
|
238
|
+
open override var body: String { get { storedBody } set { storedBody = newValue } }
|
|
239
|
+
/// Sets the app badge (`navigator.setAppBadge`, where the browser has it) when delivered; 0 clears it.
|
|
240
|
+
open override var badge: NSNumber? { get { storedBadge } set { storedBadge = newValue } }
|
|
241
|
+
open override var sound: UNNotificationSound? { get { storedSound } set { storedSound = newValue } }
|
|
242
|
+
open override var userInfo: [AnyHashable: Any] { get { storedUserInfo } set { storedUserInfo = newValue } }
|
|
243
|
+
open override var categoryIdentifier: String { get { storedCategoryIdentifier } set { storedCategoryIdentifier = newValue } }
|
|
244
|
+
open override var threadIdentifier: String { get { storedThreadIdentifier } set { storedThreadIdentifier = newValue } }
|
|
245
|
+
open override var launchImageName: String { get { storedLaunchImageName } set { storedLaunchImageName = newValue } }
|
|
246
|
+
open override var targetContentIdentifier: String? { get { storedTargetContentIdentifier } set { storedTargetContentIdentifier = newValue } }
|
|
247
|
+
open override var interruptionLevel: UNNotificationInterruptionLevel { get { storedInterruptionLevel } set { storedInterruptionLevel = newValue } }
|
|
248
|
+
open override var relevanceScore: Double { get { storedRelevanceScore } set { storedRelevanceScore = newValue } }
|
|
249
|
+
open override var filterCriteria: String? { get { storedFilterCriteria } set { storedFilterCriteria = newValue } }
|
|
250
|
+
|
|
251
|
+
public override init() {
|
|
252
|
+
super.init()
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// MARK: Triggers
|
|
257
|
+
|
|
258
|
+
/// When a request is delivered. A request without a trigger is delivered at once.
|
|
259
|
+
open class UNNotificationTrigger: NSObject, @unchecked Sendable {
|
|
260
|
+
public let repeats: Bool
|
|
261
|
+
|
|
262
|
+
init(repeats: Bool) {
|
|
263
|
+
self.repeats = repeats
|
|
264
|
+
super.init()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// The first delivery after `date` (the scheduling uses this; the
|
|
268
|
+
/// subclasses' `nextTriggerDate()` asks it about now).
|
|
269
|
+
func nextFireDate(after date: Date) -> Date? { nil }
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/// Fires `timeInterval` seconds after the request is added, and every
|
|
273
|
+
/// `timeInterval` seconds after that when it repeats.
|
|
274
|
+
public final class UNTimeIntervalNotificationTrigger: UNNotificationTrigger, @unchecked Sendable {
|
|
275
|
+
public let timeInterval: TimeInterval
|
|
276
|
+
|
|
277
|
+
/// As on iOS, the interval must be positive, and at least 60 seconds when
|
|
278
|
+
/// the trigger repeats.
|
|
279
|
+
public init(timeInterval: TimeInterval, repeats: Bool) {
|
|
280
|
+
precondition(timeInterval > 0, "time interval must be greater than 0")
|
|
281
|
+
precondition(!repeats || timeInterval >= 60, "time interval must be at least 60 if repeating")
|
|
282
|
+
self.timeInterval = timeInterval
|
|
283
|
+
super.init(repeats: repeats)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
public func nextTriggerDate() -> Date? { nextFireDate(after: Date()) }
|
|
287
|
+
|
|
288
|
+
override func nextFireDate(after date: Date) -> Date? { date.addingTimeInterval(timeInterval) }
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/// Fires at the next date matching `dateComponents` (in their calendar and
|
|
292
|
+
/// time zone, else the current ones), and at each match after that when it
|
|
293
|
+
/// repeats: `hour = 9` is every day at 9:00, `weekday = 2, hour = 9` every
|
|
294
|
+
/// Monday.
|
|
295
|
+
public final class UNCalendarNotificationTrigger: UNNotificationTrigger, @unchecked Sendable {
|
|
296
|
+
public let dateComponents: DateComponents
|
|
297
|
+
|
|
298
|
+
public init(dateMatching dateComponents: DateComponents, repeats: Bool) {
|
|
299
|
+
self.dateComponents = dateComponents
|
|
300
|
+
super.init(repeats: repeats)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
public func nextTriggerDate() -> Date? { nextFireDate(after: Date()) }
|
|
304
|
+
|
|
305
|
+
override func nextFireDate(after date: Date) -> Date? {
|
|
306
|
+
var calendar = dateComponents.calendar ?? Calendar.current
|
|
307
|
+
if let timeZone = dateComponents.timeZone { calendar.timeZone = timeZone }
|
|
308
|
+
var matching = dateComponents
|
|
309
|
+
matching.calendar = nil
|
|
310
|
+
matching.timeZone = nil
|
|
311
|
+
return calendar.nextDate(after: date, matching: matching, matchingPolicy: .nextTime)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// MARK: Requests
|
|
316
|
+
|
|
317
|
+
public final class UNNotificationRequest: NSObject, @unchecked Sendable {
|
|
318
|
+
public let identifier: String
|
|
319
|
+
public let content: UNNotificationContent
|
|
320
|
+
public let trigger: UNNotificationTrigger?
|
|
321
|
+
|
|
322
|
+
/// `content` is copied, as on iOS: changing it afterwards changes nothing.
|
|
323
|
+
public init(identifier: String, content: UNNotificationContent, trigger: UNNotificationTrigger?) {
|
|
324
|
+
self.identifier = identifier
|
|
325
|
+
self.content = content.copy() as! UNNotificationContent
|
|
326
|
+
self.trigger = trigger
|
|
327
|
+
super.init()
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/// A delivered notification.
|
|
332
|
+
public final class UNNotification: NSObject, @unchecked Sendable {
|
|
333
|
+
public let date: Date
|
|
334
|
+
public let request: UNNotificationRequest
|
|
335
|
+
|
|
336
|
+
init(date: Date, request: UNNotificationRequest) {
|
|
337
|
+
self.date = date
|
|
338
|
+
self.request = request
|
|
339
|
+
super.init()
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/// How to present a notification while the app is open, as a delegate's
|
|
344
|
+
/// `willPresent` answers.
|
|
345
|
+
public struct UNNotificationPresentationOptions: OptionSet, Sendable, Hashable {
|
|
346
|
+
public let rawValue: UInt
|
|
347
|
+
public init(rawValue: UInt) { self.rawValue = rawValue }
|
|
348
|
+
|
|
349
|
+
public static let badge = UNNotificationPresentationOptions(rawValue: 1 << 0)
|
|
350
|
+
public static let sound = UNNotificationPresentationOptions(rawValue: 1 << 1)
|
|
351
|
+
public static let alert = UNNotificationPresentationOptions(rawValue: 1 << 2)
|
|
352
|
+
public static let list = UNNotificationPresentationOptions(rawValue: 1 << 3)
|
|
353
|
+
public static let banner = UNNotificationPresentationOptions(rawValue: 1 << 4)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/// The viewer's answer to a notification. The browser never reports one (a
|
|
357
|
+
/// click on a notification focuses the page), so `didReceive` is not called.
|
|
358
|
+
public final class UNNotificationResponse: NSObject, @unchecked Sendable {
|
|
359
|
+
public let notification: UNNotification
|
|
360
|
+
public let actionIdentifier: String
|
|
361
|
+
|
|
362
|
+
init(notification: UNNotification, actionIdentifier: String) {
|
|
363
|
+
self.notification = notification
|
|
364
|
+
self.actionIdentifier = actionIdentifier
|
|
365
|
+
super.init()
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
public let UNNotificationDefaultActionIdentifier = "com.apple.UNNotificationDefaultActionIdentifier"
|
|
370
|
+
public let UNNotificationDismissActionIdentifier = "com.apple.UNNotificationDismissActionIdentifier"
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// UserNotifications over the browser's Notification API. The center keeps
|
|
2
|
+
// the pending requests and schedules each with a timer of its own (the
|
|
3
|
+
// executor's, which the page drives with `setTimeout`); when one fires, the
|
|
4
|
+
// page shows it with `new Notification(…)`. Permission is the page's
|
|
5
|
+
// `Notification.permission`, asked for with `Notification.requestPermission()`.
|
|
6
|
+
//
|
|
7
|
+
// Every exchange with the page is a host request of kind `notification`
|
|
8
|
+
// (docs/ops-protocol.md, "Notifications"). Timers only run while the page is
|
|
9
|
+
// open: a notification scheduled for tomorrow is shown only if the app is
|
|
10
|
+
// still open tomorrow (in a background tab, a few seconds late at most).
|
|
11
|
+
// Headless runs answer deterministically: permission is "default" until the
|
|
12
|
+
// app asks, then "granted", and deliveries happen on the virtual clock.
|
|
13
|
+
|
|
14
|
+
import Foundation
|
|
15
|
+
import SwiftUI
|
|
16
|
+
|
|
17
|
+
public protocol UNUserNotificationCenterDelegate: AnyObject {
|
|
18
|
+
/// Asked before a notification is shown while the app is open. In the
|
|
19
|
+
/// browser the app is open whenever a notification can fire, so a center
|
|
20
|
+
/// without a delegate shows every notification (iOS would show none).
|
|
21
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,
|
|
22
|
+
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
|
|
23
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions
|
|
24
|
+
/// Never called in the browser: the page gets no answer from a notification.
|
|
25
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse,
|
|
26
|
+
withCompletionHandler completionHandler: @escaping () -> Void)
|
|
27
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async
|
|
28
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
extension UNUserNotificationCenterDelegate {
|
|
32
|
+
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,
|
|
33
|
+
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
34
|
+
completionHandler(UNUserNotificationCenter.showEverything)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
|
|
38
|
+
await withCheckedContinuation { continuation in
|
|
39
|
+
userNotificationCenter(center, willPresent: notification) { continuation.resume(returning: $0) }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse,
|
|
44
|
+
withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
45
|
+
completionHandler()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {}
|
|
49
|
+
|
|
50
|
+
public func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
open class UNUserNotificationCenter: NSObject, @unchecked Sendable {
|
|
54
|
+
/// Sends one `notification` request to the page; `headless` is the answer
|
|
55
|
+
/// a headless run gives instead. Tests substitute it.
|
|
56
|
+
typealias Transport = @MainActor @Sendable (_ params: _JSON, _ headless: _JSON) async throws -> _JSON
|
|
57
|
+
|
|
58
|
+
private static let shared = UNUserNotificationCenter(transport: { params, headless in
|
|
59
|
+
try await _Runtime.shared._hostRequest(kind: "notification", params: params, headless: headless)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
/// The app's notification center.
|
|
63
|
+
open class func current() -> UNUserNotificationCenter { shared }
|
|
64
|
+
|
|
65
|
+
weak open var delegate: (any UNUserNotificationCenterDelegate)?
|
|
66
|
+
|
|
67
|
+
open var supportsContentExtensions: Bool { false }
|
|
68
|
+
|
|
69
|
+
static let showEverything: UNNotificationPresentationOptions = [.banner, .list, .sound, .badge]
|
|
70
|
+
|
|
71
|
+
/// Timers sleep at most a day at a time: the page's `setTimeout` cannot
|
|
72
|
+
/// wait longer than 24.8 days.
|
|
73
|
+
static let longestSleep: TimeInterval = 86_400
|
|
74
|
+
|
|
75
|
+
private struct Scheduled {
|
|
76
|
+
let request: UNNotificationRequest
|
|
77
|
+
/// Nil when the trigger never matches again; the request stays pending, as on iOS.
|
|
78
|
+
let fireDate: Date?
|
|
79
|
+
let token: Int
|
|
80
|
+
let timer: Task<Void, Never>?
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private let transport: Transport
|
|
84
|
+
private let lock = NSLock()
|
|
85
|
+
private var pending: [Scheduled] = []
|
|
86
|
+
private var delivered: [UNNotification] = []
|
|
87
|
+
private var nextToken = 1
|
|
88
|
+
/// The last permission the page reported, and what a headless run answers.
|
|
89
|
+
private var knownPermission = "default"
|
|
90
|
+
|
|
91
|
+
init(transport: @escaping Transport) {
|
|
92
|
+
self.transport = transport
|
|
93
|
+
super.init()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// MARK: Authorization
|
|
97
|
+
|
|
98
|
+
open func requestAuthorization(options: UNAuthorizationOptions = [],
|
|
99
|
+
completionHandler: @escaping @Sendable (Bool, (any Error)?) -> Void) {
|
|
100
|
+
Task { @MainActor in
|
|
101
|
+
do {
|
|
102
|
+
completionHandler(try await self.requestAuthorization(options: options), nil)
|
|
103
|
+
} catch {
|
|
104
|
+
completionHandler(false, error)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Asks the page for permission (`Notification.requestPermission()`):
|
|
110
|
+
/// `true` when granted. Without a Notification API it throws
|
|
111
|
+
/// `UNError(.notificationsNotAllowed)`.
|
|
112
|
+
open func requestAuthorization(options: UNAuthorizationOptions = []) async throws -> Bool {
|
|
113
|
+
let permission = await askPermission(action: "request")
|
|
114
|
+
if permission == "unsupported" { throw UNError(.notificationsNotAllowed) }
|
|
115
|
+
return permission == "granted"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
open func getNotificationSettings(completionHandler: @escaping @Sendable (UNNotificationSettings) -> Void) {
|
|
119
|
+
Task { @MainActor in completionHandler(await self.notificationSettings()) }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
open func notificationSettings() async -> UNNotificationSettings {
|
|
123
|
+
UNNotificationSettings(authorizationStatus: Self.status(of: await askPermission(action: "status")))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static func status(of permission: String) -> UNAuthorizationStatus {
|
|
127
|
+
switch permission {
|
|
128
|
+
case "granted": .authorized
|
|
129
|
+
case "default": .notDetermined
|
|
130
|
+
default: .denied
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// `status` reads `Notification.permission`; `request` asks the viewer.
|
|
135
|
+
@MainActor private func askPermission(action: String) async -> String {
|
|
136
|
+
let headless = action == "request" ? "granted" : lock.withLock { knownPermission }
|
|
137
|
+
let answer = try? await transport(.object(["action": .string(action)]), .object(["permission": .string(headless)]))
|
|
138
|
+
guard case .object(let fields)? = answer, case .string(let permission)? = fields["permission"] else { return "unsupported" }
|
|
139
|
+
lock.withLock { knownPermission = permission }
|
|
140
|
+
return permission
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// MARK: Scheduling
|
|
144
|
+
|
|
145
|
+
/// Schedules the request, replacing a pending one with the same identifier.
|
|
146
|
+
/// Delivered when its trigger fires, at once without one, if the page is
|
|
147
|
+
/// still open then.
|
|
148
|
+
open func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: (@Sendable ((any Error)?) -> Void)? = nil) {
|
|
149
|
+
schedule(request, now: Date())
|
|
150
|
+
if let completionHandler { Task { @MainActor in completionHandler(nil) } }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
open func add(_ request: UNNotificationRequest) async throws {
|
|
154
|
+
schedule(request, now: Date())
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
open func getPendingNotificationRequests(completionHandler: @escaping @Sendable ([UNNotificationRequest]) -> Void) {
|
|
158
|
+
let requests = pendingNotificationRequestsNow()
|
|
159
|
+
Task { @MainActor in completionHandler(requests) }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
open func pendingNotificationRequests() async -> [UNNotificationRequest] {
|
|
163
|
+
pendingNotificationRequestsNow()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
open func removePendingNotificationRequests(withIdentifiers identifiers: [String]) {
|
|
167
|
+
let removed: [Scheduled] = lock.withLock {
|
|
168
|
+
let removed = pending.filter { identifiers.contains($0.request.identifier) }
|
|
169
|
+
pending.removeAll { identifiers.contains($0.request.identifier) }
|
|
170
|
+
return removed
|
|
171
|
+
}
|
|
172
|
+
for entry in removed { entry.timer?.cancel() }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
open func removeAllPendingNotificationRequests() {
|
|
176
|
+
let removed: [Scheduled] = lock.withLock {
|
|
177
|
+
defer { pending.removeAll() }
|
|
178
|
+
return pending
|
|
179
|
+
}
|
|
180
|
+
for entry in removed { entry.timer?.cancel() }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// When each pending request fires next (nil: never), for tests.
|
|
184
|
+
func pendingFireDates() -> [String: Date?] {
|
|
185
|
+
lock.withLock { Dictionary(pending.map { ($0.request.identifier, $0.fireDate) }, uniquingKeysWith: { _, last in last }) }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private func pendingNotificationRequestsNow() -> [UNNotificationRequest] {
|
|
189
|
+
lock.withLock { pending.map(\.request) }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private func schedule(_ request: UNNotificationRequest, now: Date) {
|
|
193
|
+
let fireDate: Date? = request.trigger.map { $0.nextFireDate(after: now) } ?? now
|
|
194
|
+
let replaced: Scheduled? = lock.withLock {
|
|
195
|
+
let token = nextToken
|
|
196
|
+
nextToken += 1
|
|
197
|
+
let old = pending.first { $0.request.identifier == request.identifier }
|
|
198
|
+
pending.removeAll { $0.request.identifier == request.identifier }
|
|
199
|
+
let timer = fireDate.map { startTimer(identifier: request.identifier, token: token, delay: $0.timeIntervalSince(now)) }
|
|
200
|
+
pending.append(Scheduled(request: request, fireDate: fireDate, token: token, timer: timer))
|
|
201
|
+
return old
|
|
202
|
+
}
|
|
203
|
+
replaced?.timer?.cancel()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private func startTimer(identifier: String, token: Int, delay: TimeInterval) -> Task<Void, Never> {
|
|
207
|
+
Task { @MainActor [weak self] in
|
|
208
|
+
var remaining = max(0, delay)
|
|
209
|
+
repeat {
|
|
210
|
+
let step = min(remaining, Self.longestSleep)
|
|
211
|
+
do { try await Task.sleep(for: .milliseconds(Int64((step * 1000).rounded()))) } catch { return }
|
|
212
|
+
remaining -= step
|
|
213
|
+
} while remaining > 0
|
|
214
|
+
await self?.fire(identifier: identifier, token: token)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/// Delivers a pending request whose timer ran out (unless it was removed
|
|
219
|
+
/// or replaced meanwhile), and schedules its next delivery if it repeats.
|
|
220
|
+
@MainActor private func fire(identifier: String, token: Int) async {
|
|
221
|
+
let now = Date()
|
|
222
|
+
let notification: UNNotification? = lock.withLock {
|
|
223
|
+
guard let index = pending.firstIndex(where: { $0.request.identifier == identifier && $0.token == token }) else { return nil }
|
|
224
|
+
let entry = pending.remove(at: index)
|
|
225
|
+
let notification = UNNotification(date: now, request: entry.request)
|
|
226
|
+
delivered.removeAll { $0.request.identifier == identifier }
|
|
227
|
+
delivered.append(notification)
|
|
228
|
+
if let trigger = entry.request.trigger, trigger.repeats {
|
|
229
|
+
// After the date it was due, not just now: a timer may run a
|
|
230
|
+
// few milliseconds early, and the same match must not fire twice.
|
|
231
|
+
let after = max(now, entry.fireDate ?? now)
|
|
232
|
+
let next = trigger.nextFireDate(after: after)
|
|
233
|
+
let nextToken = self.nextToken
|
|
234
|
+
self.nextToken += 1
|
|
235
|
+
let timer = next.map { startTimer(identifier: identifier, token: nextToken, delay: $0.timeIntervalSince(now)) }
|
|
236
|
+
pending.insert(Scheduled(request: entry.request, fireDate: next, token: nextToken, timer: timer), at: index)
|
|
237
|
+
}
|
|
238
|
+
return notification
|
|
239
|
+
}
|
|
240
|
+
guard let notification else { return }
|
|
241
|
+
var options = Self.showEverything
|
|
242
|
+
if let delegate { options = await delegate.userNotificationCenter(self, willPresent: notification) }
|
|
243
|
+
await present(notification, options: options)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@MainActor private func present(_ notification: UNNotification, options: UNNotificationPresentationOptions) async {
|
|
247
|
+
let content = notification.request.content
|
|
248
|
+
var params: [String: _JSON] = ["action": .string("show"), "id": .string(notification.request.identifier)]
|
|
249
|
+
if !options.isDisjoint(with: [.banner, .alert, .list]) {
|
|
250
|
+
params["title"] = .string(content.title)
|
|
251
|
+
params["subtitle"] = .string(content.subtitle)
|
|
252
|
+
params["body"] = .string(content.body)
|
|
253
|
+
params["silent"] = .bool(content.sound == nil || !options.contains(.sound))
|
|
254
|
+
}
|
|
255
|
+
if options.contains(.badge), let badge = content.badge { params["badge"] = .number(badge.doubleValue) }
|
|
256
|
+
guard params["title"] != nil || params["badge"] != nil else { return }
|
|
257
|
+
let granted = lock.withLock { knownPermission } == "granted"
|
|
258
|
+
_ = try? await transport(.object(params), .object(["shown": .bool(granted)]))
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// MARK: Delivered notifications and the badge
|
|
262
|
+
|
|
263
|
+
open func getDeliveredNotifications(completionHandler: @escaping @Sendable ([UNNotification]) -> Void) {
|
|
264
|
+
let notifications = lock.withLock { delivered }
|
|
265
|
+
Task { @MainActor in completionHandler(notifications) }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
open func deliveredNotifications() async -> [UNNotification] {
|
|
269
|
+
lock.withLock { delivered }
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/// Closes the browser notifications still on screen for these identifiers.
|
|
273
|
+
open func removeDeliveredNotifications(withIdentifiers identifiers: [String]) {
|
|
274
|
+
let removed: [String] = lock.withLock {
|
|
275
|
+
let ids = delivered.map(\.request.identifier).filter { identifiers.contains($0) }
|
|
276
|
+
delivered.removeAll { identifiers.contains($0.request.identifier) }
|
|
277
|
+
return ids
|
|
278
|
+
}
|
|
279
|
+
close(removed)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
open func removeAllDeliveredNotifications() {
|
|
283
|
+
let removed: [String] = lock.withLock {
|
|
284
|
+
defer { delivered.removeAll() }
|
|
285
|
+
return delivered.map(\.request.identifier)
|
|
286
|
+
}
|
|
287
|
+
close(removed)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private func close(_ identifiers: [String]) {
|
|
291
|
+
guard !identifiers.isEmpty else { return }
|
|
292
|
+
let transport = self.transport
|
|
293
|
+
Task { @MainActor in
|
|
294
|
+
_ = try? await transport(.object(["action": .string("close"), "ids": .array(identifiers.map { .string($0) })]), .object([:]))
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/// The app badge (`navigator.setAppBadge`; 0 clears it), where the browser has one.
|
|
299
|
+
open func setBadgeCount(_ newBadgeCount: Int, withCompletionHandler completionHandler: (@Sendable ((any Error)?) -> Void)? = nil) {
|
|
300
|
+
Task { @MainActor in
|
|
301
|
+
try? await self.setBadgeCount(newBadgeCount)
|
|
302
|
+
completionHandler?(nil)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
open func setBadgeCount(_ newBadgeCount: Int) async throws {
|
|
307
|
+
_ = try? await transport(.object(["action": .string("badge"), "badge": .number(Double(newBadgeCount))]), .object([:]))
|
|
308
|
+
}
|
|
309
|
+
}
|