@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,688 @@
|
|
|
1
|
+
// Capture for the browser, as far as code scanners use it: a session with
|
|
2
|
+
// the camera as its input, a metadata output that reports machine-readable
|
|
3
|
+
// codes, a photo output, and a preview layer (docs/ops-protocol.md, "Code
|
|
4
|
+
// scanning").
|
|
5
|
+
//
|
|
6
|
+
// The page owns the camera. `startRunning()` asks it to scan (a `scanner`
|
|
7
|
+
// host request with the metadata output's `BarcodeDetector` formats): it
|
|
8
|
+
// opens the camera over the page, or, where the browser cannot scan, asks the
|
|
9
|
+
// viewer to type the code. The code it answers with reaches the metadata
|
|
10
|
+
// output's delegate as an `AVMetadataMachineReadableCodeObject`, on the
|
|
11
|
+
// delegate's queue, as a frame with a code does on iOS. One scan per request:
|
|
12
|
+
// after a code, or when the viewer closes the camera, the session keeps
|
|
13
|
+
// running and its preview layer offers to scan again.
|
|
14
|
+
//
|
|
15
|
+
// The photo output's photo is the camera frame the code was read in (a JPEG
|
|
16
|
+
// the page sends with the code), or a one-pixel placeholder where there was
|
|
17
|
+
// none (a typed code, a headless run). Devices are the page's two cameras,
|
|
18
|
+
// back and front, each a wide-angle camera; their configuration (torch,
|
|
19
|
+
// focus, exposure, zoom) is kept, and the torch is asked of the page when it
|
|
20
|
+
// scans. Authorization is granted: the browser asks the viewer when the
|
|
21
|
+
// camera opens.
|
|
22
|
+
|
|
23
|
+
import Foundation
|
|
24
|
+
import FoundationBridge
|
|
25
|
+
import SwiftUI
|
|
26
|
+
import struct SwiftUI.CGFloat
|
|
27
|
+
import struct SwiftUI.CGPoint
|
|
28
|
+
import struct SwiftUI.CGRect
|
|
29
|
+
import UIKit
|
|
30
|
+
|
|
31
|
+
// MARK: - Devices
|
|
32
|
+
|
|
33
|
+
public enum AVAuthorizationStatus: Int, Sendable {
|
|
34
|
+
case notDetermined = 0, restricted, denied, authorized
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// A camera: the page's back or front camera.
|
|
38
|
+
open class AVCaptureDevice: @unchecked Sendable {
|
|
39
|
+
public struct DeviceType: RawRepresentable, Hashable, Sendable {
|
|
40
|
+
public let rawValue: String
|
|
41
|
+
public init(rawValue: String) { self.rawValue = rawValue }
|
|
42
|
+
public init(_ rawValue: String) { self.rawValue = rawValue }
|
|
43
|
+
public static let builtInWideAngleCamera = DeviceType("AVCaptureDeviceTypeBuiltInWideAngleCamera")
|
|
44
|
+
public static let builtInUltraWideCamera = DeviceType("AVCaptureDeviceTypeBuiltInUltraWideCamera")
|
|
45
|
+
public static let builtInTelephotoCamera = DeviceType("AVCaptureDeviceTypeBuiltInTelephotoCamera")
|
|
46
|
+
public static let builtInDualCamera = DeviceType("AVCaptureDeviceTypeBuiltInDualCamera")
|
|
47
|
+
public static let builtInDualWideCamera = DeviceType("AVCaptureDeviceTypeBuiltInDualWideCamera")
|
|
48
|
+
public static let builtInTripleCamera = DeviceType("AVCaptureDeviceTypeBuiltInTripleCamera")
|
|
49
|
+
public static let builtInTrueDepthCamera = DeviceType("AVCaptureDeviceTypeBuiltInTrueDepthCamera")
|
|
50
|
+
public static let builtInLiDARDepthCamera = DeviceType("AVCaptureDeviceTypeBuiltInLiDARDepthCamera")
|
|
51
|
+
public static let builtInMicrophone = DeviceType("AVCaptureDeviceTypeBuiltInMicrophone")
|
|
52
|
+
public static let microphone = DeviceType("AVCaptureDeviceTypeMicrophone")
|
|
53
|
+
public static let external = DeviceType("AVCaptureDeviceTypeExternal")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public enum Position: Int, Sendable { case unspecified = 0, back, front }
|
|
57
|
+
public enum TorchMode: Int, Sendable { case off = 0, on, auto }
|
|
58
|
+
public enum FlashMode: Int, Sendable { case off = 0, on, auto }
|
|
59
|
+
public enum FocusMode: Int, Sendable { case locked = 0, autoFocus, continuousAutoFocus }
|
|
60
|
+
public enum ExposureMode: Int, Sendable { case locked = 0, autoExpose, continuousAutoExposure, custom }
|
|
61
|
+
|
|
62
|
+
/// A capture format: a 1920 × 1080 video stream with a 65° field of view.
|
|
63
|
+
open class Format: @unchecked Sendable {
|
|
64
|
+
public let formatDescription = CMFormatDescription(dimensions: CMVideoDimensions(width: 1920, height: 1080))
|
|
65
|
+
public let videoFieldOfView: Float = 65
|
|
66
|
+
public let videoMaxZoomFactor: CGFloat = 16
|
|
67
|
+
init() {}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public let deviceType: DeviceType
|
|
71
|
+
public let position: Position
|
|
72
|
+
public let uniqueID: String
|
|
73
|
+
public let localizedName: String
|
|
74
|
+
public let activeFormat = Format()
|
|
75
|
+
public var formats: [Format] { [activeFormat] }
|
|
76
|
+
|
|
77
|
+
/// Asked of the page when it scans (a camera track's torch, where it has one).
|
|
78
|
+
open var torchMode: TorchMode = .off
|
|
79
|
+
open var hasTorch: Bool { position == .back }
|
|
80
|
+
open var isTorchAvailable: Bool { hasTorch }
|
|
81
|
+
open var isTorchActive: Bool { torchMode == .on }
|
|
82
|
+
open var torchLevel: Float { torchMode == .on ? 1 : 0 }
|
|
83
|
+
open var hasFlash: Bool { false }
|
|
84
|
+
open var focusMode: FocusMode = .continuousAutoFocus
|
|
85
|
+
open var exposureMode: ExposureMode = .continuousAutoExposure
|
|
86
|
+
open var focusPointOfInterest = CGPoint(x: 0.5, y: 0.5)
|
|
87
|
+
open var exposurePointOfInterest = CGPoint(x: 0.5, y: 0.5)
|
|
88
|
+
/// The page does not focus on a point.
|
|
89
|
+
open var isFocusPointOfInterestSupported: Bool { false }
|
|
90
|
+
open var isExposurePointOfInterestSupported: Bool { false }
|
|
91
|
+
open var isSmoothAutoFocusSupported: Bool { false }
|
|
92
|
+
open var isSmoothAutoFocusEnabled = false
|
|
93
|
+
open var autoFocusRangeRestriction = 0
|
|
94
|
+
/// Not known: -1, as for a camera that does not report it.
|
|
95
|
+
open var minimumFocusDistance: Int { -1 }
|
|
96
|
+
open var videoZoomFactor: CGFloat = 1
|
|
97
|
+
open var minAvailableVideoZoomFactor: CGFloat { 1 }
|
|
98
|
+
open var maxAvailableVideoZoomFactor: CGFloat { activeFormat.videoMaxZoomFactor }
|
|
99
|
+
open var isConnected: Bool { true }
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
init(position: Position) {
|
|
103
|
+
deviceType = .builtInWideAngleCamera
|
|
104
|
+
self.position = position
|
|
105
|
+
uniqueID = position == .front ? "browser-camera-front" : "browser-camera-back"
|
|
106
|
+
localizedName = position == .front ? "Front Camera" : "Back Camera"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static let back = AVCaptureDevice(position: .back)
|
|
110
|
+
static let front = AVCaptureDevice(position: .front)
|
|
111
|
+
|
|
112
|
+
/// The back camera for video; nil for anything else.
|
|
113
|
+
open class func `default`(for mediaType: AVMediaType) -> AVCaptureDevice? {
|
|
114
|
+
mediaType == .video ? back : nil
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
open class func `default`(_ deviceType: DeviceType, for mediaType: AVMediaType?, position: Position) -> AVCaptureDevice? {
|
|
118
|
+
guard deviceType == .builtInWideAngleCamera, mediaType == nil || mediaType == .video else { return nil }
|
|
119
|
+
return position == .front ? front : back
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
open class func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus { .authorized }
|
|
123
|
+
|
|
124
|
+
/// Granted (the browser asks when the camera opens), on the main queue.
|
|
125
|
+
/// The handler is not `@Sendable`, as Objective-C's block is not.
|
|
126
|
+
open class func requestAccess(for mediaType: AVMediaType, completionHandler handler: @escaping (Bool) -> Void) {
|
|
127
|
+
nonisolated(unsafe) let handler = handler
|
|
128
|
+
FoundationBridge.DispatchQueue.main.async { handler(true) }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@available(*, deprecated, message: "Use AVCaptureDevice.DiscoverySession")
|
|
132
|
+
open class func devices(for mediaType: AVMediaType) -> [AVCaptureDevice] {
|
|
133
|
+
mediaType == .video ? [back, front] : []
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// The cameras of the given types, media type and position.
|
|
137
|
+
open class DiscoverySession: @unchecked Sendable {
|
|
138
|
+
public let devices: [AVCaptureDevice]
|
|
139
|
+
|
|
140
|
+
public init(deviceTypes: [DeviceType], mediaType: AVMediaType?, position: Position) {
|
|
141
|
+
guard deviceTypes.contains(.builtInWideAngleCamera), mediaType == nil || mediaType == .video else {
|
|
142
|
+
devices = []
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
switch position {
|
|
146
|
+
case .back: devices = [AVCaptureDevice.back]
|
|
147
|
+
case .front: devices = [AVCaptureDevice.front]
|
|
148
|
+
case .unspecified: devices = [AVCaptureDevice.back, AVCaptureDevice.front]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
open func hasMediaType(_ mediaType: AVMediaType) -> Bool { mediaType == .video }
|
|
154
|
+
open func lockForConfiguration() throws {}
|
|
155
|
+
open func unlockForConfiguration() {}
|
|
156
|
+
open func isTorchModeSupported(_ torchMode: TorchMode) -> Bool { hasTorch }
|
|
157
|
+
open func setTorchModeOn(level torchLevel: Float) throws { torchMode = .on }
|
|
158
|
+
open func isFocusModeSupported(_ focusMode: FocusMode) -> Bool { true }
|
|
159
|
+
open func isExposureModeSupported(_ exposureMode: ExposureMode) -> Bool { true }
|
|
160
|
+
open func ramp(toVideoZoomFactor factor: CGFloat, withRate rate: Float) { videoZoomFactor = factor }
|
|
161
|
+
open func cancelVideoZoomRamp() {}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// MARK: - Core Media's format descriptions
|
|
165
|
+
|
|
166
|
+
/// The pixel size of a video format.
|
|
167
|
+
public struct CMVideoDimensions: Hashable, Sendable {
|
|
168
|
+
public var width: Int32
|
|
169
|
+
public var height: Int32
|
|
170
|
+
public init(width: Int32, height: Int32) {
|
|
171
|
+
self.width = width
|
|
172
|
+
self.height = height
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// A media format's description: here, a video stream's size.
|
|
177
|
+
public final class CMFormatDescription: @unchecked Sendable {
|
|
178
|
+
let dimensions: CMVideoDimensions
|
|
179
|
+
init(dimensions: CMVideoDimensions) { self.dimensions = dimensions }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
public typealias CMVideoFormatDescription = CMFormatDescription
|
|
183
|
+
|
|
184
|
+
public func CMVideoFormatDescriptionGetDimensions(_ videoDesc: CMFormatDescription) -> CMVideoDimensions { videoDesc.dimensions }
|
|
185
|
+
|
|
186
|
+
// MARK: - Inputs, outputs and connections
|
|
187
|
+
|
|
188
|
+
open class AVCaptureInput: @unchecked Sendable {
|
|
189
|
+
init() {}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// A camera as a session's input.
|
|
193
|
+
open class AVCaptureDeviceInput: AVCaptureInput, @unchecked Sendable {
|
|
194
|
+
public let device: AVCaptureDevice
|
|
195
|
+
|
|
196
|
+
public init(device: AVCaptureDevice) throws {
|
|
197
|
+
self.device = device
|
|
198
|
+
super.init()
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public enum AVCaptureVideoOrientation: Int, Sendable {
|
|
203
|
+
case portrait = 1, portraitUpsideDown, landscapeRight, landscapeLeft
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/// What links a session's input to one of its outputs (or a preview layer).
|
|
207
|
+
open class AVCaptureConnection: @unchecked Sendable {
|
|
208
|
+
public private(set) weak var output: AVCaptureOutput?
|
|
209
|
+
open var isEnabled = true
|
|
210
|
+
open var isActive: Bool { true }
|
|
211
|
+
open var isVideoOrientationSupported: Bool { true }
|
|
212
|
+
open var videoOrientation: AVCaptureVideoOrientation = .portrait
|
|
213
|
+
open var videoRotationAngle: CGFloat = 90
|
|
214
|
+
open var isVideoMirroringSupported: Bool { true }
|
|
215
|
+
open var isVideoMirrored = false
|
|
216
|
+
open var automaticallyAdjustsVideoMirroring = true
|
|
217
|
+
|
|
218
|
+
init(output: AVCaptureOutput?) { self.output = output }
|
|
219
|
+
|
|
220
|
+
open func isVideoRotationAngleSupported(_ videoRotationAngle: CGFloat) -> Bool { true }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
open class AVCaptureOutput: @unchecked Sendable {
|
|
224
|
+
public internal(set) var connections: [AVCaptureConnection] = []
|
|
225
|
+
weak var session: AVCaptureSession?
|
|
226
|
+
|
|
227
|
+
init() {}
|
|
228
|
+
|
|
229
|
+
open func connection(with mediaType: AVMediaType) -> AVCaptureConnection? {
|
|
230
|
+
mediaType == .video ? connections.first : nil
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// MARK: - Metadata
|
|
235
|
+
|
|
236
|
+
/// Something a capture found in a frame; codes are `AVMetadataMachineReadableCodeObject`s.
|
|
237
|
+
open class AVMetadataObject: @unchecked Sendable {
|
|
238
|
+
/// The kinds of machine-readable code (Apple's raw values).
|
|
239
|
+
public struct ObjectType: RawRepresentable, Hashable, Sendable {
|
|
240
|
+
public let rawValue: String
|
|
241
|
+
public init(rawValue: String) { self.rawValue = rawValue }
|
|
242
|
+
public init(_ rawValue: String) { self.rawValue = rawValue }
|
|
243
|
+
|
|
244
|
+
public static let qr = ObjectType(rawValue: "org.iso.QRCode")
|
|
245
|
+
public static let aztec = ObjectType(rawValue: "org.iso.Aztec")
|
|
246
|
+
public static let dataMatrix = ObjectType(rawValue: "org.iso.DataMatrix")
|
|
247
|
+
public static let pdf417 = ObjectType(rawValue: "org.iso.PDF417")
|
|
248
|
+
public static let microQR = ObjectType(rawValue: "org.iso.MicroQR")
|
|
249
|
+
public static let microPDF417 = ObjectType(rawValue: "org.iso.MicroPDF417")
|
|
250
|
+
public static let code39 = ObjectType(rawValue: "org.iso.Code39")
|
|
251
|
+
public static let code39Mod43 = ObjectType(rawValue: "org.iso.Code39Mod43")
|
|
252
|
+
public static let code93 = ObjectType(rawValue: "com.intermec.Code93")
|
|
253
|
+
public static let code128 = ObjectType(rawValue: "org.iso.Code128")
|
|
254
|
+
public static let ean8 = ObjectType(rawValue: "org.gs1.EAN-8")
|
|
255
|
+
public static let ean13 = ObjectType(rawValue: "org.gs1.EAN-13")
|
|
256
|
+
public static let upce = ObjectType(rawValue: "org.gs1.UPC-E")
|
|
257
|
+
public static let interleaved2of5 = ObjectType(rawValue: "org.ansi.Interleaved2of5")
|
|
258
|
+
public static let itf14 = ObjectType(rawValue: "org.gs1.ITF14")
|
|
259
|
+
public static let codabar = ObjectType(rawValue: "org.codabar")
|
|
260
|
+
public static let gs1DataBar = ObjectType(rawValue: "org.gs1.GS1DataBar")
|
|
261
|
+
public static let gs1DataBarExpanded = ObjectType(rawValue: "org.gs1.GS1DataBarExpanded")
|
|
262
|
+
public static let gs1DataBarLimited = ObjectType(rawValue: "org.gs1.GS1DataBarLimited")
|
|
263
|
+
public static let face = ObjectType(rawValue: "face")
|
|
264
|
+
public static let humanBody = ObjectType(rawValue: "mdta/com.apple.quicktime.detected-human-body")
|
|
265
|
+
|
|
266
|
+
/// Every code type the page's `BarcodeDetector` can look for.
|
|
267
|
+
static let codes: [ObjectType] = [.qr, .microQR, .aztec, .dataMatrix, .pdf417, .microPDF417, .code39, .code39Mod43,
|
|
268
|
+
.code93, .code128, .ean8, .ean13, .upce, .interleaved2of5, .itf14, .codabar]
|
|
269
|
+
|
|
270
|
+
/// The `BarcodeDetector` format that finds this kind of code, if any.
|
|
271
|
+
public var _barcodeFormat: String? {
|
|
272
|
+
switch self {
|
|
273
|
+
case .qr, .microQR: "qr_code"
|
|
274
|
+
case .aztec: "aztec"
|
|
275
|
+
case .dataMatrix: "data_matrix"
|
|
276
|
+
case .pdf417, .microPDF417: "pdf417"
|
|
277
|
+
case .code39, .code39Mod43: "code_39"
|
|
278
|
+
case .code93: "code_93"
|
|
279
|
+
case .code128: "code_128"
|
|
280
|
+
case .ean8: "ean_8"
|
|
281
|
+
case .ean13: "ean_13"
|
|
282
|
+
case .upce: "upc_e"
|
|
283
|
+
case .interleaved2of5, .itf14: "itf"
|
|
284
|
+
case .codabar: "codabar"
|
|
285
|
+
default: nil
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
public let type: ObjectType
|
|
291
|
+
public let bounds: CGRect
|
|
292
|
+
public let time: CMTime
|
|
293
|
+
public let duration: CMTime
|
|
294
|
+
|
|
295
|
+
init(type: ObjectType, bounds: CGRect) {
|
|
296
|
+
self.type = type
|
|
297
|
+
self.bounds = bounds
|
|
298
|
+
time = .zero
|
|
299
|
+
duration = .zero
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/// A code read in a frame: its contents and corners.
|
|
304
|
+
open class AVMetadataMachineReadableCodeObject: AVMetadataObject, @unchecked Sendable {
|
|
305
|
+
public let stringValue: String?
|
|
306
|
+
/// The code's corners, in the frame's pixels as the page reports them.
|
|
307
|
+
public let corners: [CGPoint]
|
|
308
|
+
|
|
309
|
+
public init(type: ObjectType, stringValue: String?, corners: [CGPoint]) {
|
|
310
|
+
self.stringValue = stringValue
|
|
311
|
+
self.corners = corners
|
|
312
|
+
let xs = corners.map(\.x), ys = corners.map(\.y)
|
|
313
|
+
let bounds = corners.isEmpty ? CGRect.zero
|
|
314
|
+
: CGRect(x: xs.min()!, y: ys.min()!, width: xs.max()! - xs.min()!, height: ys.max()! - ys.min()!)
|
|
315
|
+
super.init(type: type, bounds: bounds)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/// Main-actor isolated: the page has one thread, and a view controller (as
|
|
320
|
+
/// CodeScanner's is) conforms in Swift 6 as it may to Objective-C's protocol.
|
|
321
|
+
@MainActor
|
|
322
|
+
public protocol AVCaptureMetadataOutputObjectsDelegate: AnyObject {
|
|
323
|
+
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
extension AVCaptureMetadataOutputObjectsDelegate {
|
|
327
|
+
public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/// Reports the codes the page reads to its delegate.
|
|
331
|
+
open class AVCaptureMetadataOutput: AVCaptureOutput, @unchecked Sendable {
|
|
332
|
+
public private(set) weak var metadataObjectsDelegate: (any AVCaptureMetadataOutputObjectsDelegate)?
|
|
333
|
+
public private(set) var metadataObjectsCallbackQueue: FoundationBridge.DispatchQueue?
|
|
334
|
+
/// The kinds of code to look for; only kinds the page can read are available.
|
|
335
|
+
open var metadataObjectTypes: [AVMetadataObject.ObjectType]! = []
|
|
336
|
+
open var availableMetadataObjectTypes: [AVMetadataObject.ObjectType] { session == nil ? [] : AVMetadataObject.ObjectType.codes }
|
|
337
|
+
open var rectOfInterest = CGRect(x: 0, y: 0, width: 1, height: 1)
|
|
338
|
+
|
|
339
|
+
public override init() { super.init() }
|
|
340
|
+
|
|
341
|
+
open func setMetadataObjectsDelegate(_ objectsDelegate: (any AVCaptureMetadataOutputObjectsDelegate)?, queue objectsCallbackQueue: FoundationBridge.DispatchQueue?) {
|
|
342
|
+
metadataObjectsDelegate = objectsDelegate
|
|
343
|
+
metadataObjectsCallbackQueue = objectsCallbackQueue
|
|
344
|
+
session?.outputChanged()
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/// The page's formats for the requested kinds of code.
|
|
348
|
+
var barcodeFormats: [String] {
|
|
349
|
+
var formats: [String] = []
|
|
350
|
+
for type in metadataObjectTypes ?? [] {
|
|
351
|
+
if let format = type._barcodeFormat, !formats.contains(format) { formats.append(format) }
|
|
352
|
+
}
|
|
353
|
+
return formats
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/// The kind of code `format` is among the requested ones (UPC-A, which
|
|
357
|
+
/// `BarcodeDetector` reports on its own, is EAN-13 to AVFoundation).
|
|
358
|
+
func objectType(forFormat format: String?) -> AVMetadataObject.ObjectType {
|
|
359
|
+
let types = metadataObjectTypes ?? []
|
|
360
|
+
let wanted = format == "upc_a" ? "ean_13" : format
|
|
361
|
+
return types.first { $0._barcodeFormat == wanted } ?? types.first ?? .qr
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
func deliver(_ object: AVMetadataObject) {
|
|
365
|
+
guard let delegate = metadataObjectsDelegate, let connection = connections.first else { return }
|
|
366
|
+
nonisolated(unsafe) let held = (delegate, object, connection)
|
|
367
|
+
let send: @Sendable () -> Void = { MainActor.assumeIsolated { held.0.metadataOutput(self, didOutput: [held.1], from: held.2) } }
|
|
368
|
+
if let queue = metadataObjectsCallbackQueue { queue.async(execute: send) } else { send() }
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// MARK: - Photos
|
|
373
|
+
|
|
374
|
+
open class AVCapturePhotoSettings: @unchecked Sendable {
|
|
375
|
+
public let uniqueID: Int64
|
|
376
|
+
open var flashMode: AVCaptureDevice.FlashMode = .off
|
|
377
|
+
open var isHighResolutionPhotoEnabled = false
|
|
378
|
+
nonisolated(unsafe) private static var serial: Int64 = 0
|
|
379
|
+
|
|
380
|
+
public init() {
|
|
381
|
+
Self.serial += 1
|
|
382
|
+
uniqueID = Self.serial
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
public convenience init(from photoSettings: AVCapturePhotoSettings) {
|
|
386
|
+
self.init()
|
|
387
|
+
flashMode = photoSettings.flashMode
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
open class AVCaptureResolvedPhotoSettings: @unchecked Sendable {
|
|
392
|
+
public let uniqueID: Int64
|
|
393
|
+
init(uniqueID: Int64) { self.uniqueID = uniqueID }
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/// A captured photo: the frame the last code was read in.
|
|
397
|
+
open class AVCapturePhoto: @unchecked Sendable {
|
|
398
|
+
let data: Data
|
|
399
|
+
public let resolvedSettings: AVCaptureResolvedPhotoSettings
|
|
400
|
+
public let timestamp = CMTime.zero
|
|
401
|
+
public var isRawPhoto: Bool { false }
|
|
402
|
+
|
|
403
|
+
init(data: Data, resolvedSettings: AVCaptureResolvedPhotoSettings) {
|
|
404
|
+
self.data = data
|
|
405
|
+
self.resolvedSettings = resolvedSettings
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
open func fileDataRepresentation() -> Data? { data }
|
|
409
|
+
open func cgImageRepresentation() -> CGImage? { UIImage(data: data)?.cgImage }
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/// Main-actor isolated, as `AVCaptureMetadataOutputObjectsDelegate` is.
|
|
413
|
+
@MainActor
|
|
414
|
+
public protocol AVCapturePhotoCaptureDelegate: AnyObject {
|
|
415
|
+
func photoOutput(_ output: AVCapturePhotoOutput, willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings)
|
|
416
|
+
func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings)
|
|
417
|
+
func photoOutput(_ output: AVCapturePhotoOutput, didCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings)
|
|
418
|
+
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: (any Error)?)
|
|
419
|
+
func photoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: (any Error)?)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
extension AVCapturePhotoCaptureDelegate {
|
|
423
|
+
public func photoOutput(_ output: AVCapturePhotoOutput, willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) {}
|
|
424
|
+
public func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {}
|
|
425
|
+
public func photoOutput(_ output: AVCapturePhotoOutput, didCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {}
|
|
426
|
+
public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: (any Error)?) {}
|
|
427
|
+
public func photoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: (any Error)?) {}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/// Takes a photo: the camera frame of the last code the session read, on the main queue.
|
|
431
|
+
open class AVCapturePhotoOutput: AVCaptureOutput, @unchecked Sendable {
|
|
432
|
+
open var isHighResolutionCaptureEnabled = false
|
|
433
|
+
open var maxPhotoQualityPrioritization = 0
|
|
434
|
+
open var supportedFlashModes: [AVCaptureDevice.FlashMode] { [.off] }
|
|
435
|
+
|
|
436
|
+
public override init() { super.init() }
|
|
437
|
+
|
|
438
|
+
open func capturePhoto(with settings: AVCapturePhotoSettings, delegate: any AVCapturePhotoCaptureDelegate) {
|
|
439
|
+
let resolved = AVCaptureResolvedPhotoSettings(uniqueID: settings.uniqueID)
|
|
440
|
+
let photo = AVCapturePhoto(data: session?.lastFrame ?? _placeholderPhoto, resolvedSettings: resolved)
|
|
441
|
+
nonisolated(unsafe) let held = delegate
|
|
442
|
+
FoundationBridge.DispatchQueue.main.async {
|
|
443
|
+
MainActor.assumeIsolated {
|
|
444
|
+
held.photoOutput(self, willBeginCaptureFor: resolved)
|
|
445
|
+
held.photoOutput(self, willCapturePhotoFor: resolved)
|
|
446
|
+
held.photoOutput(self, didCapturePhotoFor: resolved)
|
|
447
|
+
held.photoOutput(self, didFinishProcessingPhoto: photo, error: nil)
|
|
448
|
+
held.photoOutput(self, didFinishCaptureFor: resolved, error: nil)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/// A one-pixel white PNG: the photo when no frame came with the code.
|
|
455
|
+
let _placeholderPhoto = Data(base64Encoded: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4//8/AAX+Av4N70a4AAAAAElFTkSuQmCC")!
|
|
456
|
+
|
|
457
|
+
// MARK: - Sessions
|
|
458
|
+
|
|
459
|
+
/// A capture session: while it runs, the page scans for its metadata output.
|
|
460
|
+
open class AVCaptureSession: @unchecked Sendable {
|
|
461
|
+
public struct Preset: RawRepresentable, Hashable, Sendable {
|
|
462
|
+
public let rawValue: String
|
|
463
|
+
public init(rawValue: String) { self.rawValue = rawValue }
|
|
464
|
+
public static let high = Preset(rawValue: "AVCaptureSessionPresetHigh")
|
|
465
|
+
public static let medium = Preset(rawValue: "AVCaptureSessionPresetMedium")
|
|
466
|
+
public static let low = Preset(rawValue: "AVCaptureSessionPresetLow")
|
|
467
|
+
public static let photo = Preset(rawValue: "AVCaptureSessionPresetPhoto")
|
|
468
|
+
public static let hd1280x720 = Preset(rawValue: "AVCaptureSessionPreset1280x720")
|
|
469
|
+
public static let hd1920x1080 = Preset(rawValue: "AVCaptureSessionPreset1920x1080")
|
|
470
|
+
public static let vga640x480 = Preset(rawValue: "AVCaptureSessionPreset640x480")
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/// Where the session is with the page's scanner, drawn by its preview layers.
|
|
474
|
+
public enum _ScanState: Equatable, Sendable {
|
|
475
|
+
/// Not running, or waiting to be asked to scan again.
|
|
476
|
+
case idle
|
|
477
|
+
/// The page is scanning (its camera or a typed code is up).
|
|
478
|
+
case scanning
|
|
479
|
+
/// The last scan delivered a code.
|
|
480
|
+
case found
|
|
481
|
+
/// The page could not scan; the message says why.
|
|
482
|
+
case failed(String)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
open var sessionPreset: Preset = .high
|
|
486
|
+
public private(set) var inputs: [AVCaptureInput] = []
|
|
487
|
+
public private(set) var outputs: [AVCaptureOutput] = []
|
|
488
|
+
public private(set) var isRunning = false
|
|
489
|
+
public private(set) var _scanState: _ScanState = .idle { didSet { if _scanState != oldValue { notifyPreviews() } } }
|
|
490
|
+
/// The camera frame of the last code the page read (a JPEG), for photo outputs.
|
|
491
|
+
var lastFrame: Data?
|
|
492
|
+
private var previews: [WeakPreview] = []
|
|
493
|
+
|
|
494
|
+
public init() {}
|
|
495
|
+
|
|
496
|
+
open var connections: [AVCaptureConnection] { outputs.flatMap(\.connections) }
|
|
497
|
+
|
|
498
|
+
open func canAddInput(_ input: AVCaptureInput) -> Bool { !inputs.contains { $0 === input } }
|
|
499
|
+
open func addInput(_ input: AVCaptureInput) { if canAddInput(input) { inputs.append(input) } }
|
|
500
|
+
open func removeInput(_ input: AVCaptureInput) { inputs.removeAll { $0 === input } }
|
|
501
|
+
|
|
502
|
+
open func canAddOutput(_ output: AVCaptureOutput) -> Bool { !outputs.contains { $0 === output } && output.session == nil }
|
|
503
|
+
|
|
504
|
+
open func addOutput(_ output: AVCaptureOutput) {
|
|
505
|
+
guard canAddOutput(output) else { return }
|
|
506
|
+
outputs.append(output)
|
|
507
|
+
output.session = self
|
|
508
|
+
output.connections = [AVCaptureConnection(output: output)]
|
|
509
|
+
outputChanged()
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
open func removeOutput(_ output: AVCaptureOutput) {
|
|
513
|
+
outputs.removeAll { $0 === output }
|
|
514
|
+
output.session = nil
|
|
515
|
+
output.connections = []
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
open func beginConfiguration() {}
|
|
519
|
+
open func commitConfiguration() {}
|
|
520
|
+
|
|
521
|
+
/// Starts the session; the page is asked to scan for the metadata output.
|
|
522
|
+
open func startRunning() {
|
|
523
|
+
guard !isRunning else { return }
|
|
524
|
+
isRunning = true
|
|
525
|
+
_scanState = .idle
|
|
526
|
+
_scan()
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/// Stops the session. A scan the page has open finishes; its code is dropped.
|
|
530
|
+
open func stopRunning() {
|
|
531
|
+
guard isRunning else { return }
|
|
532
|
+
isRunning = false
|
|
533
|
+
if _scanState != .scanning { _scanState = .idle }
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/// A metadata output that wants codes, if the session has a camera.
|
|
537
|
+
private var scanningOutput: AVCaptureMetadataOutput? {
|
|
538
|
+
guard inputs.contains(where: { $0 is AVCaptureDeviceInput }) else { return nil }
|
|
539
|
+
return outputs.lazy.compactMap { $0 as? AVCaptureMetadataOutput }.first {
|
|
540
|
+
$0.metadataObjectsDelegate != nil && !$0.barcodeFormats.isEmpty
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
private var camera: AVCaptureDevice? {
|
|
545
|
+
inputs.lazy.compactMap { ($0 as? AVCaptureDeviceInput)?.device }.first
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/// Asks the page for a code (see the top of the file); the preview
|
|
549
|
+
/// layer's button calls it again after a scan.
|
|
550
|
+
public func _scan() {
|
|
551
|
+
guard isRunning, _scanState != .scanning, let output = scanningOutput else { return }
|
|
552
|
+
_scanState = .scanning
|
|
553
|
+
let params: _JSON = .object([
|
|
554
|
+
"action": .string("scan"),
|
|
555
|
+
"formats": .array(output.barcodeFormats.map { .string($0) }),
|
|
556
|
+
"torch": .bool(camera?.torchMode == .on),
|
|
557
|
+
"vibrate": .bool(false),
|
|
558
|
+
"facing": .string(camera?.position == .front ? "user" : "environment"),
|
|
559
|
+
])
|
|
560
|
+
Task { @MainActor in
|
|
561
|
+
do {
|
|
562
|
+
let answer = try await _CaptureHost.request(params)
|
|
563
|
+
self.finishScan(answer, output: output)
|
|
564
|
+
} catch {
|
|
565
|
+
let message = (error as? _HostRequestError)?.message ?? String(describing: error)
|
|
566
|
+
self._scanState = self.isRunning ? .failed(message) : .idle
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private func finishScan(_ answer: _JSON, output: AVCaptureMetadataOutput) {
|
|
572
|
+
guard isRunning, case .object(let fields) = answer, case .string(let string)? = fields["string"] else {
|
|
573
|
+
_scanState = .idle
|
|
574
|
+
return
|
|
575
|
+
}
|
|
576
|
+
var format: String?
|
|
577
|
+
if case .string(let value)? = fields["format"] { format = value }
|
|
578
|
+
var corners: [CGPoint] = []
|
|
579
|
+
if case .array(let points)? = fields["corners"] {
|
|
580
|
+
for point in points {
|
|
581
|
+
if case .object(let xy) = point, case .number(let x)? = xy["x"], case .number(let y)? = xy["y"] {
|
|
582
|
+
corners.append(CGPoint(x: x, y: y))
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
lastFrame = nil
|
|
587
|
+
if case .string(let image)? = fields["image"] { lastFrame = Data(base64Encoded: image) }
|
|
588
|
+
_scanState = .found
|
|
589
|
+
output.deliver(AVMetadataMachineReadableCodeObject(type: output.objectType(forFormat: format), stringValue: string, corners: corners))
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
func outputChanged() {
|
|
593
|
+
// A delegate set after `startRunning()` (from another queue on iOS) starts the scan.
|
|
594
|
+
if isRunning, _scanState == .idle { _scan() }
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
fileprivate func register(_ preview: AVCaptureVideoPreviewLayer) {
|
|
598
|
+
previews.removeAll { $0.layer == nil || $0.layer === preview }
|
|
599
|
+
previews.append(WeakPreview(layer: preview))
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private func notifyPreviews() {
|
|
603
|
+
for preview in previews { preview.layer?._changed() }
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
private struct WeakPreview {
|
|
607
|
+
weak var layer: AVCaptureVideoPreviewLayer?
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/// Sends a `scanner` host request; tests substitute it.
|
|
612
|
+
@MainActor public enum _CaptureHost {
|
|
613
|
+
public static var request: (_ params: _JSON) async throws -> _JSON = { params in
|
|
614
|
+
try await _Runtime.shared._hostRequest(kind: "scanner", params: params)
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// MARK: - Preview
|
|
619
|
+
|
|
620
|
+
/// Shows a session: the page's camera is over the page while it scans, so
|
|
621
|
+
/// the layer draws what to do next, filling its view (a button to scan again
|
|
622
|
+
/// after a code, or after the viewer closed the camera).
|
|
623
|
+
open class AVCaptureVideoPreviewLayer: CALayer, @unchecked Sendable {
|
|
624
|
+
open var session: AVCaptureSession? { didSet { if let session { session.register(self) }; _changed() } }
|
|
625
|
+
open var videoGravity: AVLayerVideoGravity = .resizeAspect
|
|
626
|
+
public private(set) lazy var connection: AVCaptureConnection? = AVCaptureConnection(output: nil)
|
|
627
|
+
|
|
628
|
+
public required init() { super.init() }
|
|
629
|
+
|
|
630
|
+
public init(session: AVCaptureSession) {
|
|
631
|
+
super.init()
|
|
632
|
+
self.session = session
|
|
633
|
+
session.register(self)
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
public init(sessionWithNoConnection session: AVCaptureSession) {
|
|
637
|
+
super.init()
|
|
638
|
+
self.session = session
|
|
639
|
+
session.register(self)
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
open func setSessionWithNoConnection(_ session: AVCaptureSession) { self.session = session }
|
|
643
|
+
|
|
644
|
+
/// Layer and capture coordinates are the same here (the page has the frames).
|
|
645
|
+
open func captureDevicePointConverted(fromLayerPoint pointInLayer: CGPoint) -> CGPoint { pointInLayer }
|
|
646
|
+
open func layerPointConverted(fromCaptureDevicePoint captureDevicePointOfInterest: CGPoint) -> CGPoint { captureDevicePointOfInterest }
|
|
647
|
+
open func metadataOutputRectConverted(fromLayerRect rectInLayerCoordinates: CGRect) -> CGRect { rectInLayerCoordinates }
|
|
648
|
+
open func layerRectConverted(fromMetadataOutputRect rectInMetadataOutputCoordinates: CGRect) -> CGRect { rectInMetadataOutputCoordinates }
|
|
649
|
+
open func transformedMetadataObject(for metadataObject: AVMetadataObject) -> AVMetadataObject? { metadataObject }
|
|
650
|
+
|
|
651
|
+
open override var _drawing: AnyView? {
|
|
652
|
+
guard let session else { return nil }
|
|
653
|
+
return AnyView(_CapturePreview(state: session.isRunning ? session._scanState : .idle, running: session.isRunning) { [weak session] in
|
|
654
|
+
session?._scan()
|
|
655
|
+
})
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/// What a preview layer draws: black, as a camera preview starts, and what to do next.
|
|
660
|
+
struct _CapturePreview: View {
|
|
661
|
+
let state: AVCaptureSession._ScanState
|
|
662
|
+
let running: Bool
|
|
663
|
+
let scan: @MainActor @Sendable () -> Void
|
|
664
|
+
|
|
665
|
+
var body: some View {
|
|
666
|
+
ZStack {
|
|
667
|
+
Color.black
|
|
668
|
+
VStack(spacing: 16) {
|
|
669
|
+
switch state {
|
|
670
|
+
case .scanning:
|
|
671
|
+
ProgressView().tint(.white)
|
|
672
|
+
Text("Scanning…").foregroundColor(.white)
|
|
673
|
+
case .failed(let message):
|
|
674
|
+
Image(systemName: "camera.badge.ellipsis").font(.largeTitle).foregroundColor(.white)
|
|
675
|
+
Text(message).font(.footnote).foregroundColor(.white).multilineTextAlignment(.center)
|
|
676
|
+
Button("Try Again", systemImage: "arrow.clockwise", action: scan).buttonStyle(.borderedProminent)
|
|
677
|
+
case .idle, .found:
|
|
678
|
+
if running {
|
|
679
|
+
Image(systemName: "qrcode.viewfinder").font(.system(size: 64)).foregroundColor(.white)
|
|
680
|
+
Button(state == .found ? "Scan Again" : "Scan a Code", systemImage: "camera", action: scan)
|
|
681
|
+
.buttonStyle(.borderedProminent)
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
.padding(24)
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|