├── .gitignore ├── .swiftpm └── xcode │ └── xcshareddata │ └── xcschemes │ ├── JudoSDK.xcscheme │ └── JudoServiceTests.xcscheme ├── JudoSample ├── JudoSample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcshareddata │ │ └── xcschemes │ │ └── JudoSample.xcscheme └── JudoSample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── JudoSample.entitlements │ ├── SceneDelegate.swift │ ├── ViewController.swift │ ├── ar.lproj │ └── LaunchScreen.strings │ ├── de.lproj │ └── LaunchScreen.strings │ ├── es.lproj │ └── LaunchScreen.strings │ ├── fr.lproj │ └── LaunchScreen.strings │ ├── he.lproj │ └── LaunchScreen.strings │ ├── hi.lproj │ └── LaunchScreen.strings │ ├── ja.lproj │ └── LaunchScreen.strings │ ├── ko.lproj │ └── LaunchScreen.strings │ ├── pl.lproj │ └── LaunchScreen.strings │ ├── zh-Hans.lproj │ └── LaunchScreen.strings │ └── zh-Hant.lproj │ └── LaunchScreen.strings ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Sources ├── JudoModel │ ├── CodingUserInfoKey.swift │ ├── DecodingCoordinator.swift │ ├── Experience.swift │ ├── Extensions │ │ ├── CGPoint+Hashable.swift │ │ ├── Error+debugDescription.swift │ │ ├── JSONSerialization+valueForKeyPath.swift │ │ ├── KeyedDecodingContainer+nodes.swift │ │ ├── NSRegularExpression+utils.swift │ │ └── OSLog+JudoModel.swift │ ├── Node.swift │ ├── Nodes │ │ ├── Audio.swift │ │ ├── Carousel.swift │ │ ├── Collection.swift │ │ ├── Conditional.swift │ │ ├── DataSource.swift │ │ ├── Divider.swift │ │ ├── HStack.swift │ │ ├── Icon.swift │ │ ├── Image.swift │ │ ├── Layer.swift │ │ ├── NavBar.swift │ │ ├── NavBarButton.swift │ │ ├── PageControl.swift │ │ ├── Rectangle.swift │ │ ├── Screen.swift │ │ ├── ScrollContainer.swift │ │ ├── Spacer.swift │ │ ├── Text.swift │ │ ├── VStack.swift │ │ ├── Video.swift │ │ ├── WebView.swift │ │ └── ZStack.swift │ └── Value Types │ │ ├── Accessibility.swift │ │ ├── Action.swift │ │ ├── BackButtonStyle.swift │ │ ├── Background.swift │ │ ├── Border.swift │ │ ├── Color.swift │ │ ├── ColorVariants.swift │ │ ├── Condition.swift │ │ ├── DynamicGradient.swift │ │ ├── Fill.swift │ │ ├── Font.swift │ │ ├── FontResource.swift │ │ ├── Frame.swift │ │ ├── Gradient.swift │ │ ├── GradientVariants.swift │ │ ├── Metadata.swift │ │ ├── ModalPresentationStyle.swift │ │ ├── NamedIcon.swift │ │ ├── Overlay.swift │ │ ├── Padding.swift │ │ ├── SegueStyle.swift │ │ ├── Shadow.swift │ │ ├── StatusBarStyle.swift │ │ ├── StringTable.swift │ │ └── SwiftUIValues.swift └── JudoSDK │ ├── Analytics.swift │ ├── AssetsDownloader.swift │ ├── Configuration.swift │ ├── DownloadService.swift │ ├── Events.swift │ ├── ExperienceViewController.swift │ ├── Extensions │ ├── Collection+items.swift │ ├── Condition+isSatisfied.swift │ ├── Error+debugDescription.swift │ ├── Node+hierarchy.swift │ ├── OSLog+JudoSDK.swift │ ├── String+interpolation.swift │ ├── StringTable+resolve.swift │ ├── UIApplication+present.swift │ ├── UIControl+action.swift │ ├── URL+API.swift │ ├── URL+cache.swift │ ├── URLSession+Result.swift │ └── URLSession+dataPublisher.swift │ ├── Judo.swift │ ├── JudoRepository.swift │ ├── MainQueue.swift │ ├── Meta.swift │ ├── Model │ └── ViewID.swift │ ├── RecoverableError.swift │ ├── Resources │ └── en.lproj │ │ └── Localizable.strings │ ├── UI │ ├── CarouselState.swift │ ├── EnvironmentValues+Judo.swift │ ├── ImageFetcher.swift │ ├── Model │ │ ├── Action+handler.swift │ │ ├── Color+named.swift │ │ ├── Color+uiValues.swift │ │ ├── ColorVariants+uikit.swift │ │ ├── Font+uikit.swift │ │ ├── Gradient+swiftUI.swift │ │ ├── RealizeColor.swift │ │ └── UIColor+named.swift │ ├── Modifiers │ │ ├── AccessibilityModifier.swift │ │ ├── ActionModifier.swift │ │ ├── AspectRatioModifier.swift │ │ ├── BackgroundModifier.swift │ │ ├── FontModifier.swift │ │ ├── FrameModifier.swift │ │ ├── IgnoresSafeAreaModifier.swift │ │ ├── LayoutPriorityModifier.swift │ │ ├── MaskModifier.swift │ │ ├── OffsetModifier.swift │ │ ├── OpacityModifier.swift │ │ ├── OverlayModifier.swift │ │ ├── PaddingModifier.swift │ │ └── ShadowModifier.swift │ ├── NavBarViewController.swift │ ├── ScreenViewController.swift │ ├── UIKitExtensions.swift │ └── Views │ │ ├── AnimatedImage.swift │ │ ├── AudioView.swift │ │ ├── CarouselView.swift │ │ ├── CollectionView.swift │ │ ├── ConditionalView.swift │ │ ├── DataSourceView.swift │ │ ├── DividerView.swift │ │ ├── HStackView.swift │ │ ├── IconView.swift │ │ ├── ImageView.swift │ │ ├── LayerView.swift │ │ ├── PageControlView.swift │ │ ├── RectangleView.swift │ │ ├── ScrollContainerView.swift │ │ ├── TextView.swift │ │ ├── VStackView.swift │ │ ├── VideoView.swift │ │ ├── WebViewView.swift │ │ └── ZStackView.swift │ └── Vendor │ ├── Introspect.swift │ ├── JSON.swift │ └── UIImage+Blurhash.swift └── Tests └── JudoServiceTests ├── AuthorizationTests.swift ├── ClipServiceTest.swift ├── JSONSerializationTests.swift └── StringInterpolationTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | -------------------------------------------------------------------------------- /.swiftpm/xcode/xcshareddata/xcschemes/JudoSDK.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /.swiftpm/xcode/xcshareddata/xcschemes/JudoServiceTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /JudoSample/JudoSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JudoSample/JudoSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /JudoSample/JudoSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Mocker", 6 | "repositoryURL": "https://github.com/WeTransfer/Mocker", 7 | "state": { 8 | "branch": null, 9 | "revision": "8ff37ffda243669ba7827f639f91f99b53fa4b49", 10 | "version": null 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /JudoSample/JudoSample.xcodeproj/xcshareddata/xcschemes/JudoSample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BGTaskSchedulerPermittedIdentifiers 6 | 7 | app.judo.background.refresh 8 | 9 | CFBundleDevelopmentRegion 10 | $(DEVELOPMENT_LANGUAGE) 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleTypeRole 27 | Viewer 28 | CFBundleURLName 29 | app.judo.SampleApp 30 | CFBundleURLSchemes 31 | 32 | myapp 33 | 34 | 35 | 36 | CFBundleVersion 37 | 1 38 | LSRequiresIPhoneOS 39 | 40 | UIApplicationSceneManifest 41 | 42 | UIApplicationSupportsMultipleScenes 43 | 44 | UISceneConfigurations 45 | 46 | UIWindowSceneSessionRoleApplication 47 | 48 | 49 | UISceneConfigurationName 50 | Default Configuration 51 | UISceneDelegateClassName 52 | $(PRODUCT_MODULE_NAME).SceneDelegate 53 | UISceneStoryboardFile 54 | Main 55 | 56 | 57 | 58 | 59 | UIApplicationSupportsIndirectInputEvents 60 | 61 | UIBackgroundModes 62 | 63 | fetch 64 | remote-notification 65 | 66 | UILaunchStoryboardName 67 | LaunchScreen 68 | UIMainStoryboardFile 69 | Main 70 | UIRequiredDeviceCapabilities 71 | 72 | armv7 73 | 74 | UISupportedInterfaceOrientations 75 | 76 | UIInterfaceOrientationPortrait 77 | UIInterfaceOrientationLandscapeLeft 78 | UIInterfaceOrientationLandscapeRight 79 | 80 | UISupportedInterfaceOrientations~ipad 81 | 82 | UIInterfaceOrientationPortrait 83 | UIInterfaceOrientationPortraitUpsideDown 84 | UIInterfaceOrientationLandscapeLeft 85 | UIInterfaceOrientationLandscapeRight 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/JudoSample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.associated-domains 8 | 9 | applinks:myapp.judo.app 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import UIKit 17 | import JudoSDK 18 | import os.log 19 | 20 | @available(iOS 13.0, *) 21 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 22 | 23 | var window: UIWindow? 24 | 25 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 26 | if let userActivity = connectionOptions.userActivities.first { 27 | Judo.sharedInstance.continueUserActivity(userActivity, animated: false) 28 | } else if let urlContext = connectionOptions.urlContexts.first { 29 | Judo.sharedInstance.openURL(urlContext.url, animated: false) 30 | } 31 | } 32 | 33 | func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { 34 | if let context = URLContexts.first { 35 | Judo.sharedInstance.openURL(context.url, animated: true) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import UIKit 17 | //import JudoModel 18 | import JudoSDK 19 | 20 | class ViewController: UIViewController { 21 | @IBAction func presentExperience(_ sender: Any) { 22 | let url = URL(string: "")! 23 | Judo.sharedInstance.openURL(url, animated: true) 24 | } 25 | 26 | @IBAction func identify(_ sender: Any) { 27 | // Pass user ID and custom properties to Judo for personalization 28 | struct UserTraits: Codable { 29 | let name: String 30 | let pointsBalance: Int 31 | let premiumTier: Bool 32 | let tags: [String] 33 | } 34 | 35 | Judo.sharedInstance.identify( 36 | userID: "john@example.com", 37 | traits: UserTraits( 38 | name: "John Doe", 39 | pointsBalance: 50_000, 40 | premiumTier: true, 41 | tags: ["foo", "bar", "baz"] 42 | ) 43 | ) 44 | } 45 | 46 | @IBAction func reset(_ sender: Any) { 47 | // Clear user ID and custom properties 48 | Judo.sharedInstance.reset() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/ar.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/de.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/es.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/fr.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/he.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/hi.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/ja.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/ko.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/pl.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/zh-Hans.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JudoSample/JudoSample/zh-Hant.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | copy, modify, and distribute this software in source code or binary form for use 4 | in connection with the web services and APIs provided by Rover. 5 | 6 | This copyright notice shall be included in all copies or substantial portions of 7 | the software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Mocker", 6 | "repositoryURL": "https://github.com/WeTransfer/Mocker", 7 | "state": { 8 | "branch": null, 9 | "revision": "8ff37ffda243669ba7827f639f91f99b53fa4b49", 10 | "version": null 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "JudoSDK", 7 | defaultLocalization: "en", 8 | platforms: [ 9 | .iOS(.v11) 10 | ], 11 | products: [ 12 | .library( 13 | name: "JudoSDK", 14 | // TODO: consider commenting `type: .dynamic` before public release. it's here to use Xcode Previews in Sample app, to work around an Xcode bug. 15 | // type: .dynamic, 16 | targets: ["JudoSDK", "JudoModel"] 17 | ) 18 | ], 19 | dependencies: [ 20 | .package(url: "https://github.com/WeTransfer/Mocker", .revision("8ff37ffda243669ba7827f639f91f99b53fa4b49")) 21 | ], 22 | targets: [ 23 | .target( 24 | name: "JudoSDK", 25 | dependencies: ["JudoModel"], 26 | resources: [.process("Resources")] 27 | ), 28 | .target( 29 | name: "JudoModel" 30 | ), 31 | .testTarget( 32 | name: "JudoServiceTests", 33 | dependencies: ["JudoSDK", "Mocker"] 34 | ) 35 | ] 36 | ) 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | This SDK is no longer maintained. It has been replaced with the Judo SwiftUI SDK: https://github.com/judoapp/judo-swiftui. 3 | 4 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 5 | -------------------------------------------------------------------------------- /Sources/JudoModel/CodingUserInfoKey.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | extension CodingUserInfoKey { 19 | static let decodingCoordinator = CodingUserInfoKey(rawValue: "decodingCoordinator")! 20 | } 21 | -------------------------------------------------------------------------------- /Sources/JudoModel/Experience.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public struct Experience: Decodable { 19 | public enum Appearance: String, Decodable { 20 | case light 21 | case dark 22 | case auto 23 | } 24 | 25 | /// A unique identifier for the Experience. 26 | public let id: String 27 | public let name: String 28 | public let revisionID: String 29 | /// A set of nodes contained in the document. Use `initialScreenID` to determine the initial node to render. 30 | public let nodes: [Node] 31 | public let localization: StringTable 32 | /// Fonts download URLs 33 | public let fonts: [URL] 34 | /// The ID of the initial node to render. 35 | public let initialScreenID: Screen.ID 36 | public let appearance: Appearance 37 | 38 | public init(id: String, name: String, revisionID: String, nodes: [Node], localization: StringTable, fonts: [URL], initialScreenID: Screen.ID, appearance: Appearance) { 39 | self.id = id 40 | self.name = name 41 | self.revisionID = revisionID 42 | self.nodes = nodes 43 | self.initialScreenID = initialScreenID 44 | self.localization = localization 45 | self.fonts = fonts 46 | self.appearance = appearance 47 | } 48 | 49 | /// Initialize Experience from data (JSON) 50 | /// - Parameter data: Experience data. 51 | /// - Throws: Throws error on failure. 52 | public init(decode data: Data) throws { 53 | let decoder = JSONDecoder() 54 | let coordinator = DecodingCoordinator() 55 | decoder.userInfo[.decodingCoordinator] = coordinator 56 | self = try decoder.decode(Self.self, from: data) 57 | } 58 | 59 | enum CodingKeys: String, CodingKey { 60 | case id 61 | case name 62 | case revisionID 63 | case nodes 64 | case fonts 65 | case initialScreenID 66 | case localization 67 | case appearance 68 | } 69 | 70 | public init(from decoder: Decoder) throws { 71 | let container = try decoder.container(keyedBy: CodingKeys.self) 72 | 73 | let id = try container.decode(String.self, forKey: .id) 74 | let revisionID = try container.decode(String.self, forKey: .revisionID) 75 | let name = try container.decode(String.self, forKey: .name) 76 | let initialScreenID = try container.decode(String.self, forKey: .initialScreenID) 77 | let localization = try container.decode(StringTable.self, forKey: .localization) 78 | let fonts = try container.decode([FontResource].self, forKey: .fonts) 79 | 80 | let coordinator = decoder.userInfo[.decodingCoordinator] as! DecodingCoordinator 81 | 82 | let nodes = try container.decodeNodes(forKey: .nodes) 83 | coordinator.resolveRelationships(nodes: nodes) 84 | 85 | let fontURLs = fonts.map { $0.url } 86 | let appearance = try container.decode(Appearance.self, forKey: .appearance) 87 | self.init(id: id, name: name, revisionID: revisionID, nodes: nodes, localization: localization, fonts: fontURLs, initialScreenID: initialScreenID, appearance: appearance) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Sources/JudoModel/Extensions/CGPoint+Hashable.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | 18 | extension CGPoint: Hashable { 19 | public func hash(into hasher: inout Hasher) { 20 | hasher.combine(x) 21 | hasher.combine(y) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/JudoModel/Extensions/Error+debugDescription.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | extension Error { 19 | /// Get a string giving a much more comprehensive explanation of the error, particularly when coming from certain platform types (Codable, in particular). 20 | var debugDescription: String { 21 | return "Error: \(self.localizedDescription), details: \((self as NSError).userInfo.debugDescription)" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/JudoModel/Extensions/NSRegularExpression+utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension NSRegularExpression { 4 | func matches(_ string: String) -> Bool { 5 | let range = NSRange(location: 0, length: string.utf16.count) 6 | return firstMatch(in: string, range: range) != nil 7 | } 8 | 9 | func matches(in string: String, options: NSRegularExpression.MatchingOptions = []) -> [NSTextCheckingResult] { 10 | let range = NSRange(location: 0, length: string.utf16.count) 11 | return matches(in: string, options: options, range: range) 12 | } 13 | 14 | func matches(in string: String, groupIndex: Int) -> String? { 15 | guard let result = matches(in: string).first else { 16 | return nil 17 | } 18 | 19 | guard let range = Range(result.range(at: groupIndex), in: string) else { 20 | return nil 21 | } 22 | return String(string[range]) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/JudoModel/Extensions/OSLog+JudoModel.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import os.log 18 | 19 | private var _judoLog = OSLog(subsystem: "app.judo.JudoSDK", category: "JudoModel") 20 | 21 | extension OSLog { 22 | static var judoLog: OSLog { _judoLog } 23 | } 24 | 25 | private func judoLoggingEnabled() -> Bool { 26 | ProcessInfo.processInfo.environment["JUDO_VERBOSE"] != nil 27 | } 28 | 29 | func judo_log(_ type: OSLogType, _ message: StaticString, _ args: CVarArg...) { 30 | guard judoLoggingEnabled() || type == .error else { 31 | return 32 | } 33 | 34 | // lack of splat means this mess: 35 | switch args.count { 36 | case 0: 37 | os_log(message, log: .judoLog, type: type) 38 | case 1: 39 | os_log(message, log: .judoLog, type: type, args[0]) 40 | case 2: 41 | os_log(message, log: .judoLog, type: type, args[0], args[1]) 42 | case 3: 43 | os_log(message, log: .judoLog, type: type, args[0], args[1], args[2]) 44 | case 4: 45 | os_log(message, log: .judoLog, type: type, args[0], args[1], args[2], args[3]) 46 | case 5: 47 | os_log(message, log: .judoLog, type: type, args[0], args[1], args[2], args[3], args[4]) 48 | default: 49 | os_log(message, log: .judoLog, type: type, args[0], args[1], args[2], args[3], args[4], args[5]) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Audio.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | 17 | import CoreGraphics 18 | import Foundation 19 | 20 | public class Audio: Layer { 21 | 22 | /// Audio URL 23 | public let sourceURL: String 24 | 25 | /// When true the audio will begin playing when the Screen is displayed. 26 | public let autoPlay: Bool 27 | 28 | /// When true the video will loop. 29 | public let looping: Bool 30 | 31 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, sourceURL: String, autoPlay: Bool, looping: Bool) { 32 | self.sourceURL = sourceURL 33 | self.autoPlay = autoPlay 34 | self.looping = looping 35 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 36 | } 37 | 38 | private enum CodingKeys: String, CodingKey { 39 | case sourceURL 40 | case autoPlay 41 | case looping 42 | } 43 | 44 | required init(from decoder: Decoder) throws { 45 | let container = try decoder.container(keyedBy: CodingKeys.self) 46 | sourceURL = try container.decode(String.self, forKey: .sourceURL) 47 | autoPlay = try container.decode(Bool.self, forKey: .autoPlay) 48 | looping = try container.decode(Bool.self, forKey: .looping) 49 | try super.init(from: decoder) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Carousel.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class Carousel: Layer { 20 | /// Indicates whether the Carousel continually wraps around to the first/last elements as you scroll. 21 | public let isLoopEnabled: Bool 22 | 23 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, isLoopEnabled: Bool) { 24 | self.isLoopEnabled = isLoopEnabled 25 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 26 | } 27 | 28 | // MARK: Decodable 29 | 30 | private enum CodingKeys: String, CodingKey { 31 | case isLoopEnabled 32 | } 33 | 34 | required init(from decoder: Decoder) throws { 35 | let container = try decoder.container(keyedBy: CodingKeys.self) 36 | isLoopEnabled = try container.decode(Bool.self, forKey: .isLoopEnabled) 37 | try super.init(from: decoder) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Collection.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class Collection: Layer { 20 | public struct SortDescriptor: Decodable { 21 | public var keyPath: String 22 | public var ascending: Bool 23 | 24 | public init(keyPath: String, ascending: Bool = true) { 25 | self.keyPath = keyPath 26 | self.ascending = ascending 27 | } 28 | } 29 | 30 | public struct Limit: Decodable { 31 | public var show: Int 32 | public var startAt: Int 33 | 34 | public init(show: Int, startAt: Int) { 35 | self.show = show 36 | self.startAt = startAt 37 | } 38 | } 39 | 40 | public let keyPath: String 41 | public let filters: [Condition] 42 | public let sortDescriptors: [SortDescriptor] 43 | public let limit: Limit? 44 | 45 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, keyPath: String, filters: [Condition] = [], sortDescriptors: [SortDescriptor] = [], limit: Limit? = nil) { 46 | self.keyPath = keyPath 47 | self.filters = filters 48 | self.sortDescriptors = sortDescriptors 49 | self.limit = limit 50 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 51 | } 52 | 53 | // MARK: Decodable 54 | 55 | private enum CodingKeys: String, CodingKey { 56 | case keyPath 57 | case filters 58 | case sortDescriptors 59 | case limit 60 | } 61 | 62 | required init(from decoder: Decoder) throws { 63 | let container = try decoder.container(keyedBy: CodingKeys.self) 64 | keyPath = try container.decode(String.self, forKey: .keyPath) 65 | filters = try container.decode([Condition].self, forKey: .filters) 66 | sortDescriptors = try container.decode([SortDescriptor].self, forKey: .sortDescriptors) 67 | limit = try container.decodeIfPresent(Limit.self, forKey: .limit) 68 | try super.init(from: decoder) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Conditional.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class Conditional: Layer { 20 | public let conditions: [Condition] 21 | 22 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, conditions: [Condition] = []) { 23 | self.conditions = conditions 24 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 25 | } 26 | 27 | // MARK: Decodable 28 | 29 | private enum CodingKeys: String, CodingKey { 30 | case conditions 31 | } 32 | 33 | required init(from decoder: Decoder) throws { 34 | let container = try decoder.container(keyedBy: CodingKeys.self) 35 | conditions = try container.decode([Condition].self, forKey: .conditions) 36 | try super.init(from: decoder) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/DataSource.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import Combine 18 | 19 | public class DataSource: Layer, ObservableObject { 20 | public enum HTTPMethod: String, Codable, CaseIterable { 21 | case get = "GET" 22 | case post = "POST" 23 | case put = "PUT" 24 | } 25 | 26 | public struct Header: Codable, Hashable { 27 | public var key: String 28 | public var value: String 29 | 30 | public init(key: String, value: String) { 31 | self.key = key 32 | self.value = value 33 | } 34 | } 35 | 36 | public let url: String 37 | public let httpMethod: HTTPMethod 38 | public let httpBody: String? 39 | public let headers: [Header] 40 | public let pollInterval: Int? 41 | 42 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, url: String, httpMethod: HTTPMethod = .get, httpBody: String? = nil, headers: [Header], pollInterval: Int?) { 43 | self.url = url 44 | self.httpMethod = httpMethod 45 | self.httpBody = httpBody 46 | self.headers = headers 47 | self.pollInterval = pollInterval 48 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 49 | } 50 | 51 | // MARK: Decodable 52 | 53 | private enum CodingKeys: String, CodingKey { 54 | case url 55 | case httpMethod 56 | case httpBody 57 | case headers 58 | case pollInterval 59 | } 60 | 61 | required init(from decoder: Decoder) throws { 62 | let container = try decoder.container(keyedBy: CodingKeys.self) 63 | url = try container.decode(String.self, forKey: .url) 64 | httpMethod = try container.decode(HTTPMethod.self, forKey: .httpMethod) 65 | httpBody = try container.decodeIfPresent(String.self, forKey: .httpBody) 66 | headers = try container.decode([Header].self, forKey: .headers) 67 | pollInterval = try container.decodeIfPresent(Int.self, forKey: .pollInterval) 68 | try super.init(from: decoder) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Divider.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class Divider: Layer { 20 | public let backgroundColor: ColorVariants 21 | 22 | // MARK: Decodable 23 | 24 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, backgroundColor: ColorVariants) { 25 | self.backgroundColor = backgroundColor 26 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 27 | } 28 | 29 | private enum CodingKeys: String, CodingKey { 30 | case backgroundColor 31 | } 32 | 33 | required init(from decoder: Decoder) throws { 34 | let container = try decoder.container(keyedBy: CodingKeys.self) 35 | backgroundColor = try container.decode(ColorVariants.self, forKey: .backgroundColor) 36 | try super.init(from: decoder) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/HStack.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class HStack: Layer { 20 | public let alignment: VerticalAlignment 21 | /// The gap that should be placed between each item in the stack. 22 | public let spacing: CGFloat 23 | 24 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, alignment: VerticalAlignment, spacing: CGFloat) { 25 | self.alignment = alignment 26 | self.spacing = spacing 27 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 28 | } 29 | 30 | // MARK: Decodable 31 | 32 | private enum CodingKeys: String, CodingKey { 33 | case alignment 34 | case spacing 35 | } 36 | 37 | required init(from decoder: Decoder) throws { 38 | let container = try decoder.container(keyedBy: CodingKeys.self) 39 | alignment = try container.decode(VerticalAlignment.self, forKey: .alignment) 40 | spacing = try container.decode(CGFloat.self, forKey: .spacing) 41 | try super.init(from: decoder) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Icon.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class Icon: Layer { 20 | public let icon: NamedIcon 21 | public let pointSize: Int 22 | public let color: ColorVariants 23 | 24 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, icon: NamedIcon, size: Int, color: ColorVariants) { 25 | self.icon = icon 26 | self.pointSize = size 27 | self.color = color 28 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 29 | } 30 | 31 | // MARK: Decodable 32 | 33 | private enum CodingKeys: String, CodingKey { 34 | case icon 35 | case pointSize 36 | case color 37 | } 38 | 39 | required public init(from decoder: Decoder) throws { 40 | let container = try decoder.container(keyedBy: CodingKeys.self) 41 | icon = try container.decode(NamedIcon.self, forKey: .icon) 42 | color = try container.decode(ColorVariants.self, forKey: .color) 43 | pointSize = try container.decode(Int.self, forKey: .pointSize) 44 | try super.init(from: decoder) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Layer.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | public class Layer: Node { 17 | // 18 | } 19 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/NavBarButton.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class NavBarButton: Node { 20 | public enum Placement: String, Codable { 21 | case leading 22 | case trailing 23 | } 24 | 25 | public enum Style: String, Codable { 26 | case custom 27 | case done 28 | case close 29 | } 30 | 31 | public let placement: Placement 32 | public let style: Style 33 | public let title: String? 34 | public let icon: NamedIcon? 35 | 36 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, placement: Placement, style: Style, title: String?, icon: NamedIcon?) { 37 | 38 | self.placement = placement 39 | self.style = style 40 | self.title = title 41 | self.icon = icon 42 | 43 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 44 | } 45 | 46 | // MARK: Decodable 47 | 48 | private enum CodingKeys: String, CodingKey { 49 | case placement 50 | case style 51 | case title 52 | case icon 53 | } 54 | 55 | required init(from decoder: Decoder) throws { 56 | let container = try decoder.container(keyedBy: CodingKeys.self) 57 | placement = try container.decode(Placement.self, forKey: .placement) 58 | style = try container.decode(Style.self, forKey: .style) 59 | title = try container.decodeIfPresent(String.self, forKey: .title) 60 | icon = try container.decodeIfPresent(NamedIcon.self, forKey: .icon) 61 | try super.init(from: decoder) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Rectangle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class Rectangle: Layer { 20 | public let fill: Fill 21 | public let border: Border? 22 | public let cornerRadius: Int 23 | 24 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, fill: Fill, border: Border?, cornerRadius: Int) { 25 | self.fill = fill 26 | self.border = border 27 | self.cornerRadius = cornerRadius 28 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 29 | } 30 | 31 | // MARK: Decodable 32 | 33 | private enum CodingKeys: String, CodingKey { 34 | case fill 35 | case border 36 | case cornerRadius 37 | } 38 | 39 | required init(from decoder: Decoder) throws { 40 | let container = try decoder.container(keyedBy: CodingKeys.self) 41 | fill = try container.decode(Fill.self, forKey: .fill) 42 | border = try container.decodeIfPresent(Border.self, forKey: .border) 43 | cornerRadius = try container.decodeIfPresent(Int.self, forKey: .cornerRadius) ?? 0 44 | 45 | try super.init(from: decoder) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Screen.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class Screen: Node { 20 | public let statusBarStyle: StatusBarStyle 21 | public let backButtonStyle: BackButtonStyle 22 | public let backgroundColor: ColorVariants 23 | 24 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, statusBarStyle: StatusBarStyle, backButtonStyle: BackButtonStyle, backgroundColor: ColorVariants) { 25 | 26 | self.statusBarStyle = statusBarStyle 27 | self.backButtonStyle = backButtonStyle 28 | self.backgroundColor = backgroundColor 29 | 30 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 31 | } 32 | 33 | // MARK: Decodable 34 | 35 | private enum CodingKeys: String, CodingKey { 36 | case statusBarStyle 37 | case backButtonStyle 38 | case backgroundColor 39 | } 40 | 41 | required init(from decoder: Decoder) throws { 42 | let container = try decoder.container(keyedBy: CodingKeys.self) 43 | statusBarStyle = try container.decode(StatusBarStyle.self, forKey: .statusBarStyle) 44 | backButtonStyle = try container.decode(BackButtonStyle.self, forKey: .backButtonStyle) 45 | backgroundColor = try container.decode(ColorVariants.self, forKey: .backgroundColor) 46 | try super.init(from: decoder) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/ScrollContainer.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class ScrollContainer: Layer { 20 | /// Indicates the scroll direction. 21 | public let axis: Axis 22 | /// When true the scroll container will not display the scroll bar indicating the user's current scroll position. 23 | public let disableScrollBar: Bool 24 | 25 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, axis: Axis, disableScrollBar: Bool) { 26 | self.axis = axis 27 | self.disableScrollBar = disableScrollBar 28 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 29 | } 30 | 31 | // MARK: Decodable 32 | 33 | private enum CodingKeys: String, CodingKey { 34 | case axis 35 | case disableScrollBar 36 | } 37 | 38 | required init(from decoder: Decoder) throws { 39 | let container = try decoder.container(keyedBy: CodingKeys.self) 40 | axis = try container.decode(Axis.self, forKey: .axis) 41 | disableScrollBar = try container.decode(Bool.self, forKey: .disableScrollBar) 42 | try super.init(from: decoder) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Spacer.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public final class Spacer: Layer { 19 | // 20 | } 21 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Text.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class Text: Layer { 20 | public enum Transform: String, Codable { 21 | case uppercase 22 | case lowercase 23 | } 24 | /// The text content to render. 25 | public let text: String 26 | public let font: Font 27 | public let textColor: ColorVariants 28 | /// The alignment behavior of the Text. 29 | public let textAlignment: TextAlignment 30 | public let lineLimit: Int? 31 | public let transform: Transform? 32 | 33 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, text: String, font: Font, textColor: ColorVariants, textAlignment: TextAlignment, lineLimit: Int?, transform: Text.Transform?) { 34 | self.text = text 35 | self.font = font 36 | self.textColor = textColor 37 | self.textAlignment = textAlignment 38 | self.lineLimit = lineLimit 39 | self.transform = transform 40 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 41 | } 42 | 43 | // MARK: Decodable 44 | 45 | private enum CodingKeys: String, CodingKey { 46 | case text 47 | case font 48 | case textColor 49 | case textAlignment 50 | case lineLimit 51 | case transform 52 | } 53 | 54 | required init(from decoder: Decoder) throws { 55 | let container = try decoder.container(keyedBy: CodingKeys.self) 56 | text = try container.decode(String.self, forKey: .text) 57 | font = try container.decode(Font.self, forKey: .font) 58 | textColor = try container.decode(ColorVariants.self, forKey: .textColor) 59 | textAlignment = try container.decode(TextAlignment.self, forKey: .textAlignment) 60 | lineLimit = try container.decodeIfPresent(Int.self, forKey: .lineLimit) 61 | transform = try container.decodeIfPresent(Transform.self, forKey: .transform) 62 | try super.init(from: decoder) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/VStack.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class VStack: Layer { 20 | public let alignment: HorizontalAlignment 21 | /// The gap that should be placed between each item in the stack. 22 | public let spacing: CGFloat 23 | 24 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, alignment: HorizontalAlignment, spacing: CGFloat) { 25 | self.alignment = alignment 26 | self.spacing = spacing 27 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 28 | } 29 | 30 | // MARK: Decodable 31 | 32 | private enum CodingKeys: String, CodingKey { 33 | case alignment 34 | case spacing 35 | } 36 | 37 | required init(from decoder: Decoder) throws { 38 | let container = try decoder.container(keyedBy: CodingKeys.self) 39 | alignment = try container.decode(HorizontalAlignment.self, forKey: .alignment) 40 | spacing = try container.decode(CGFloat.self, forKey: .spacing) 41 | try super.init(from: decoder) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/Video.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public class Video: Layer { 20 | 21 | public enum ResizingMode: String, Decodable { 22 | case scaleToFit 23 | case scaleToFill 24 | } 25 | 26 | /// Video URL 27 | public let sourceURL: String 28 | 29 | /// Poster image URL 30 | public let posterImageURL: String? 31 | 32 | /// Resizing mode 33 | public let resizingMode: ResizingMode 34 | 35 | /// When true the media player shown in the Judo layer will feature playback/transport controls. 36 | public let showControls: Bool 37 | 38 | /// When true the video will begin playing when the Screen is displayed. 39 | public let autoPlay: Bool 40 | 41 | /// When true the video will loop. 42 | public let looping: Bool 43 | 44 | /// When true audio track is inhibited from playback. 45 | public let removeAudio: Bool 46 | 47 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, sourceURL: String, posterImageURL: String?, resizingMode: ResizingMode, showControls: Bool, autoPlay: Bool, looping: Bool, removeAudio: Bool) { 48 | self.sourceURL = sourceURL 49 | self.posterImageURL = posterImageURL 50 | self.resizingMode = resizingMode 51 | self.showControls = showControls 52 | self.autoPlay = autoPlay 53 | self.looping = looping 54 | self.removeAudio = removeAudio 55 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 56 | } 57 | 58 | private enum CodingKeys: String, CodingKey { 59 | case sourceURL 60 | case posterImageURL 61 | case resizingMode 62 | case showControls 63 | case autoPlay 64 | case looping 65 | case removeAudio 66 | } 67 | 68 | required init(from decoder: Decoder) throws { 69 | let container = try decoder.container(keyedBy: CodingKeys.self) 70 | sourceURL = try container.decode(String.self, forKey: .sourceURL) 71 | posterImageURL = try container.decodeIfPresent(String.self, forKey: .posterImageURL) 72 | resizingMode = try container.decode(ResizingMode.self, forKey: .resizingMode) 73 | showControls = try container.decode(Bool.self, forKey: .showControls) 74 | autoPlay = try container.decode(Bool.self, forKey: .autoPlay) 75 | looping = try container.decode(Bool.self, forKey: .looping) 76 | removeAudio = try container.decode(Bool.self, forKey: .removeAudio) 77 | try super.init(from: decoder) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/WebView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class WebView: Layer { 20 | public enum Source: Equatable { 21 | case url(String) 22 | case html(String) 23 | } 24 | 25 | public let source: Source 26 | public let isScrollEnabled: Bool 27 | 28 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, source: Source, isScrollEnabled: Bool) { 29 | self.source = source 30 | self.isScrollEnabled = isScrollEnabled 31 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 32 | } 33 | 34 | // MARK: Decodable 35 | 36 | private enum CodingKeys: String, CodingKey { 37 | case source 38 | case isScrollEnabled 39 | } 40 | 41 | required init(from decoder: Decoder) throws { 42 | let container = try decoder.container(keyedBy: CodingKeys.self) 43 | source = try container.decode(Source.self, forKey: .source) 44 | isScrollEnabled = try container.decode(Bool.self, forKey: .isScrollEnabled) 45 | 46 | try super.init(from: decoder) 47 | } 48 | } 49 | 50 | extension WebView.Source: Decodable { 51 | private enum CodingKeys: String, CodingKey { 52 | case typeName = "__typeName" 53 | case value 54 | } 55 | 56 | public init(from decoder: Decoder) throws { 57 | let container = try decoder.container(keyedBy: CodingKeys.self) 58 | let typeName = try container.decode(String.self, forKey: .typeName) 59 | let value = try container.decode(String.self, forKey: .value) 60 | switch typeName { 61 | case "WebViewURLSource": 62 | self = .url(value) 63 | case "WebViewHTMLSource": 64 | self = .html(value) 65 | default: 66 | throw DecodingError.dataCorruptedError( 67 | forKey: .typeName, 68 | in: container, 69 | debugDescription: "Invalid value: \(typeName)" 70 | ) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sources/JudoModel/Nodes/ZStack.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public final class ZStack: Layer { 20 | public let alignment: Alignment 21 | 22 | public init(id: String = UUID().uuidString, name: String?, parent: Node? = nil, children: [Node] = [], ignoresSafeArea: Set? = nil, aspectRatio: CGFloat? = nil, padding: Padding? = nil, frame: Frame? = nil, layoutPriority: CGFloat? = nil, offset: CGPoint? = nil, shadow: Shadow? = nil, opacity: CGFloat? = nil, background: Background? = nil, overlay: Overlay? = nil, mask: Node? = nil, action: Action? = nil, accessibility: Accessibility? = nil, metadata: Metadata? = nil, alignment: Alignment) { 23 | self.alignment = alignment 24 | super.init(id: id, name: name, parent: parent, children: children, ignoresSafeArea: ignoresSafeArea, aspectRatio: aspectRatio, padding: padding, frame: frame, layoutPriority: layoutPriority, offset: offset, shadow: shadow, opacity: opacity, background: background, overlay: overlay, mask: mask, action: action, accessibility: accessibility, metadata: metadata) 25 | } 26 | 27 | // MARK: Decodable 28 | 29 | private enum CodingKeys: String, CodingKey { 30 | case alignment 31 | } 32 | 33 | required init(from decoder: Decoder) throws { 34 | let container = try decoder.container(keyedBy: CodingKeys.self) 35 | alignment = try container.decode(Alignment.self, forKey: .alignment) 36 | try super.init(from: decoder) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Accessibility.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public struct Accessibility: Decodable, Hashable { 19 | /// Hides the node from system accessibility features. 20 | /// When used on an Image this indicates to features 21 | /// such as VoiceOver that the image is decorative and 22 | /// can be safely ignored. When this property is true, 23 | /// accessibility is essentially disabled and all the other 24 | /// Accessibility properties have no effect. 25 | public let isHidden: Bool 26 | /// A textual description of the node for use in system accessibility 27 | /// features such as VoiceOver. When used on an image, this label is 28 | /// used for what is commonly known as "alt text". 29 | public let label: String? 30 | /// Indicates to system accessibility features the order the node is 31 | /// presented to the user. For example the order read by VoiceOver. 32 | /// Nodes with higher values are presented before nodes with lower values. 33 | /// The default value is 0. 34 | public let sortPriority: Int? 35 | /// Indicates to system accessibility features such as VoiceOver that 36 | /// the node is a header that divides content into sections such as a 37 | /// section title. This enables features like VoiceOver to be able to 38 | /// quickly navigate between sections of content. 39 | public let isHeader: Bool 40 | /// Setting this property on one node per screen enables system 41 | /// accessibility features such as VoiceOver to provide the user with 42 | /// a summary of the screen. 43 | public let isSummary: Bool 44 | /// Indicates to system accessibility features such as VoiceOver that 45 | /// interacting with this node will produce a sound. 46 | public let playsSound: Bool 47 | /// Indicates to system accessibility features such as VoiceOver that 48 | /// interacting with this node will begin playback of multimedia. 49 | public let startsMediaSession: Bool 50 | 51 | public init(isHidden: Bool, label: String?, sortPriority: Int?, isHeader: Bool, isSummary: Bool, playsSound: Bool, startsMediaSession: Bool) { 52 | self.isHidden = isHidden 53 | self.label = label 54 | self.sortPriority = sortPriority 55 | self.isHeader = isHeader 56 | self.isSummary = isSummary 57 | self.playsSound = playsSound 58 | self.startsMediaSession = startsMediaSession 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/BackButtonStyle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public enum BackButtonStyle: Decodable { 19 | case `default`(title: String) 20 | case generic 21 | case minimal 22 | 23 | // MARK: Codable 24 | 25 | private enum CodingKeys: String, CodingKey { 26 | case typeName = "__typeName" 27 | case title 28 | } 29 | 30 | public init(from decoder: Decoder) throws { 31 | let container = try decoder.container(keyedBy: CodingKeys.self) 32 | let typeName = try container.decode(String.self, forKey: .typeName) 33 | switch typeName { 34 | case "DefaultBackButtonStyle": 35 | let title = try container.decode(String.self, forKey: .title) 36 | self = .default(title: title) 37 | case "GenericBackButtonStyle": 38 | self = .generic 39 | case "MinimalBackButtonStyle": 40 | self = .minimal 41 | default: 42 | throw DecodingError.dataCorruptedError( 43 | forKey: .typeName, 44 | in: container, 45 | debugDescription: "Invalid value: \(typeName)" 46 | ) 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Background.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Background: Decodable { 4 | /// Background layer node 5 | public let node: Node 6 | 7 | /// The alignment of the background inside the resulting frame. 8 | public let alignment: Alignment 9 | 10 | public init(_ node: Node, alignment: Alignment = .center) { 11 | self.node = node 12 | self.alignment = alignment 13 | } 14 | 15 | // MARK: Codable 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case node 19 | case alignment 20 | } 21 | 22 | public init(from decoder: Decoder) throws { 23 | let container = try decoder.container(keyedBy: CodingKeys.self) 24 | node = try container.decodeNode(forKey: .node) 25 | alignment = try container.decode(Alignment.self, forKey: .alignment) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Border.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public struct Border: Decodable { 19 | public let color: ColorVariants 20 | public let width: Int 21 | 22 | public init(color: ColorVariants, width: Int) { 23 | self.color = color 24 | self.width = width 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Color.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import CoreGraphics 18 | 19 | public struct Color: Hashable, Codable { 20 | /// A decimal between 0.0...1.0 21 | public let red: CGFloat 22 | /// A decimal between 0.0...1.0 23 | public let green: CGFloat 24 | /// A decimal between 0.0...1.0 25 | public let blue: CGFloat 26 | /// A decimal between 0.0...1.0 27 | public let alpha: CGFloat 28 | 29 | public init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { 30 | self.red = red 31 | self.green = green 32 | self.blue = blue 33 | self.alpha = alpha 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/ColorVariants.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public struct ColorVariants: Codable, Hashable { 19 | /// An iOS system color 20 | public let systemName: String? 21 | /// The default color to use if there is no match for the device's mode. 22 | /// The color to use for when device has light mode enabled. 23 | public let `default`: JudoModel.Color? 24 | /// The color to use when the device has high contrast enabled. 25 | public let highContrast: JudoModel.Color? 26 | /// The color to use when the device has dark mode enabled. 27 | public let darkMode: JudoModel.Color? 28 | /// The color to use when the device has dark mode and high contrast enabled. 29 | public let darkModeHighContrast: JudoModel.Color? 30 | 31 | public init(systemName: String?, default: Color?, highContrast: Color?, darkMode: Color?, darkModeHighContrast: Color?) { 32 | self.systemName = systemName 33 | self.default = `default` 34 | self.highContrast = highContrast 35 | self.darkMode = darkMode 36 | self.darkModeHighContrast = darkModeHighContrast 37 | } 38 | 39 | public static let clear = ColorVariants(systemName: "clear", default: nil, highContrast: nil, darkMode: nil, darkModeHighContrast: nil) 40 | } 41 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Condition.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public struct Condition: Decodable { 19 | public enum Predicate: String, Decodable { 20 | case equals 21 | case doesNotEqual 22 | case isGreaterThan 23 | case isLessThan 24 | case isSet 25 | case isNotSet 26 | case isTrue 27 | case isFalse 28 | } 29 | 30 | public var keyPath: String 31 | public var predicate: Predicate 32 | public var value: Any? 33 | 34 | public init(keyPath: String, predicate: Predicate, value: Any? = nil) { 35 | self.keyPath = keyPath 36 | self.predicate = predicate 37 | self.value = value 38 | } 39 | 40 | private enum CodingKeys: String, CodingKey { 41 | case keyPath 42 | case predicate 43 | case value 44 | } 45 | 46 | public init(from decoder: Decoder) throws { 47 | let container = try decoder.container(keyedBy: CodingKeys.self) 48 | keyPath = try container.decode(String.self, forKey: .keyPath) 49 | predicate = try container.decode(Predicate.self, forKey: .predicate) 50 | 51 | if let value = try? container.decode(String.self, forKey: .value) { 52 | self.value = value 53 | } else if let value = try? container.decode(Double.self, forKey: .value) { 54 | self.value = value 55 | } else if let value = try? container.decode(Bool.self, forKey: .value) { 56 | self.value = value 57 | } else if let value = try? container.decode(Date.self, forKey: .value) { 58 | self.value = value 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/DynamicGradient.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | @available(*, deprecated, message: "Not in SDK") 19 | public struct DynamicGradient: Decodable, Hashable { 20 | public var `default`: JudoModel.Gradient 21 | 22 | // The following gradients should be used, in given order of precedence, if their selectors match the environment. 23 | 24 | public var darkModeHighContrast: JudoModel.Gradient? 25 | 26 | public var darkMode: JudoModel.Gradient? 27 | 28 | public var highContrast: JudoModel.Gradient? 29 | 30 | public init(gradient: JudoModel.Gradient) { 31 | self.default = gradient 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Fill.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import os.log 18 | 19 | public enum Fill: Decodable, Hashable { 20 | case flat(_ color: ColorVariants) 21 | case gradient(_ gradient: GradientVariants) 22 | 23 | // MARK: Decodable 24 | 25 | private enum CodingKeys: String, CodingKey { 26 | case typeName = "__typeName" 27 | case color 28 | case gradient 29 | } 30 | 31 | public init(from decoder: Decoder) throws { 32 | let container = try decoder.container(keyedBy: CodingKeys.self) 33 | let typeName = try container.decode(String.self, forKey: .typeName) 34 | switch typeName { 35 | case "FlatFill": 36 | let color = try container.decode(ColorVariants.self, forKey: .color) 37 | self = .flat(color) 38 | case "GradientFill": 39 | let gradient = try container.decode(GradientVariants.self, forKey: .gradient) 40 | self = .gradient(gradient) 41 | default: 42 | judo_log(.error, "Unsupported fill case name: %@", typeName) 43 | assertionFailure("Unsupported value \(typeName)") 44 | self = .flat(.clear) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/FontResource.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum FontResource: Decodable { 4 | case fontResource(url: URL, fontName: String) 5 | case fontResourceCollection(url: URL, fontNames: [String]) 6 | 7 | public var url: URL { 8 | switch self { 9 | case .fontResource(let url, _): 10 | return url 11 | case .fontResourceCollection(let url, _): 12 | return url 13 | } 14 | } 15 | 16 | enum CodingKeys: String, CodingKey { 17 | case typeName = "__typeName" 18 | case url 19 | case fontName 20 | case fontNames 21 | } 22 | 23 | public init(from decoder: Decoder) throws { 24 | let container = try decoder.container(keyedBy: CodingKeys.self) 25 | let typeName = try container.decode(String.self, forKey: .typeName) 26 | switch typeName { 27 | case "FontResource": 28 | let url = try container.decode(URL.self, forKey: .url) 29 | let fontName = try container.decode(String.self, forKey: .fontName) 30 | self = .fontResource(url: url, fontName: fontName) 31 | case "FontCollectionResource": 32 | let url = try container.decode(URL.self, forKey: .url) 33 | let fontNames = try container.decode([String].self, forKey: .fontNames) 34 | self = .fontResourceCollection(url: url, fontNames: fontNames) 35 | default: 36 | throw DecodingError.dataCorruptedError( 37 | forKey: .typeName, 38 | in: container, 39 | debugDescription: "Invalid value: \(typeName)" 40 | ) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Frame.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | import Foundation 18 | 19 | public struct Frame: Decodable, Equatable { 20 | /// A fixed width for the frame. 21 | public let width: CGFloat? 22 | /// A fixed height for the frame. 23 | public let height: CGFloat? 24 | /// The minimum width of the resulting frame. 25 | public let minWidth: CGFloat? 26 | /// The maximum width of the resulting frame. 27 | public let maxWidth: CGFloat? 28 | /// The minimum height of the resulting frame. 29 | public let minHeight: CGFloat? 30 | /// The maximum height of the resulting frame. 31 | public let maxHeight: CGFloat? 32 | /// The alignment of the node inside the resulting frame. 33 | /// The alignment only applies if the node is smaller than the size of the resulting frame. 34 | public let alignment: Alignment 35 | 36 | public var isFixed: Bool { 37 | minWidth == nil && maxWidth == nil && minHeight == nil && maxHeight == nil 38 | } 39 | 40 | public init(width: CGFloat?, height: CGFloat?, minWidth: CGFloat?, maxWidth: CGFloat?, minHeight: CGFloat?, maxHeight: CGFloat?, alignment: Alignment) { 41 | self.width = width 42 | self.height = height 43 | self.minWidth = minWidth 44 | self.maxWidth = maxWidth 45 | self.minHeight = minHeight 46 | self.maxHeight = maxHeight 47 | self.alignment = alignment 48 | } 49 | 50 | private enum CodingKeys: String, CodingKey { 51 | case width 52 | case height 53 | case minWidth 54 | case maxWidth 55 | case minHeight 56 | case maxHeight 57 | case alignment 58 | } 59 | 60 | public init(from decoder: Decoder) throws { 61 | let container = try decoder.container(keyedBy: CodingKeys.self) 62 | 63 | width = try container.decodeIfPresent(CGFloat.self, forKey: .width) 64 | height = try container.decodeIfPresent(CGFloat.self, forKey: .height) 65 | minWidth = try container.decodeIfPresent(CGFloat.self, forKey: .minWidth) 66 | minHeight = try container.decodeIfPresent(CGFloat.self, forKey: .minHeight) 67 | 68 | if let maxWidthStringValue = try? container.decodeIfPresent(String.self, forKey: .maxWidth), maxWidthStringValue == "inf" { 69 | maxWidth = CGFloat.greatestFiniteMagnitude 70 | } else { 71 | maxWidth = try container.decodeIfPresent(CGFloat.self, forKey: .maxWidth) 72 | } 73 | 74 | if let maxHeightStringValue = try? container.decodeIfPresent(String.self, forKey: .maxHeight), maxHeightStringValue == "inf" { 75 | maxHeight = CGFloat.greatestFiniteMagnitude 76 | } else { 77 | maxHeight = try container.decodeIfPresent(CGFloat.self, forKey: .maxHeight) 78 | } 79 | 80 | alignment = try container.decode(Alignment.self, forKey: .alignment) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Gradient.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | 18 | public struct GradientStop: Hashable, Codable { 19 | /// A decimal between 0-1 indicating where along the gradient the color is reached. 20 | public var position: CGFloat 21 | /// The color that the gradient should pass through at this position. 22 | public var color: JudoModel.Color 23 | 24 | public init(position: CGFloat, color: Color) { 25 | self.position = position 26 | self.color = color 27 | } 28 | } 29 | 30 | public struct Gradient: Hashable, Decodable { 31 | /// In a parametric coordinate space, between 0 and 1. 32 | public let from: CGPoint 33 | /// In a parametric coordinate space, between 0 and 1. 34 | public let to: CGPoint 35 | /// Color and stop point data. 36 | public let stops: [GradientStop] 37 | 38 | public init(from: CGPoint, to: CGPoint, stops: [GradientStop]) { 39 | self.from = from 40 | self.to = to 41 | self.stops = stops 42 | } 43 | 44 | public static var `clear`: Gradient { 45 | return Gradient( 46 | from: CGPoint(x: 0.0, y: 0.5), 47 | to: CGPoint(x: 1.0, y: 0.5), 48 | stops: [ 49 | GradientStop(position: 0, color: Color(red: 0, green: 0, blue: 0, alpha: 0)), 50 | GradientStop(position: 1, color: Color(red: 0, green: 0, blue: 0, alpha: 0)) 51 | ] 52 | ) 53 | } 54 | 55 | // MARK: Decodable 56 | 57 | private enum CodingKeys: String, CodingKey { 58 | case from 59 | case to 60 | case stops 61 | } 62 | 63 | public init(from decoder: Decoder) throws { 64 | let container = try decoder.container(keyedBy: CodingKeys.self) 65 | 66 | let fromArray = try container.decode([CGFloat].self, forKey: .from) 67 | from = CGPoint(x: CGFloat(fromArray[0]), y: CGFloat(fromArray[1])) 68 | 69 | let toArray = try container.decode([CGFloat].self, forKey: .to) 70 | to = CGPoint(x: CGFloat(toArray[0]), y: CGFloat(toArray[1])) 71 | 72 | stops = try container.decode([GradientStop].self, forKey: .stops) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/GradientVariants.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | public struct GradientVariants: Hashable, Decodable { 17 | /// The default gradient to use if there is no match for the device's mode. The gradient for devices with light mode enabled. 18 | public let `default`: JudoModel.Gradient 19 | /// The gradient to use when the device has high contrast enabled. 20 | public let darkMode: JudoModel.Gradient? 21 | /// The gradient to use when the device has high contrast enabled. 22 | public let highContrast: JudoModel.Gradient? 23 | /// The gradient to use when the device has dark mode and high contrast enabled. 24 | public let darkModeHighContrast: JudoModel.Gradient? 25 | 26 | public init(default: Gradient, darkMode: Gradient?, highContrast: Gradient?, darkModeHighContrast: Gradient?) { 27 | self.default = `default` 28 | self.darkMode = darkMode 29 | self.highContrast = highContrast 30 | self.darkModeHighContrast = darkModeHighContrast 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Metadata.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | public struct Metadata: Codable, Hashable { 17 | public var properties: [String: String] = [:] 18 | public var tags: Set = [] 19 | 20 | public init(properties: [String: String] = [:], tags: Set = []) { 21 | self.properties = properties 22 | self.tags = tags 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/ModalPresentationStyle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public enum ModalPresentationStyle: String, Codable { 19 | case sheet 20 | case fullScreen 21 | } 22 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/NamedIcon.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public struct NamedIcon: Decodable, Hashable { 19 | /// The name of a SF Symbol icon to use on iOS. 20 | public let symbolName: String 21 | /// The name of the Material Icons icon to use on Android. 22 | public let materialName: String 23 | 24 | public init(symbolName: String, materialName: String) { 25 | self.symbolName = symbolName 26 | self.materialName = materialName 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Overlay.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Overlay: Decodable { 4 | /// Background layer node 5 | public let node: Node 6 | 7 | /// The alignment of the background inside the resulting frame. 8 | public let alignment: Alignment 9 | 10 | public init(_ node: Node, alignment: Alignment = .center) { 11 | self.node = node 12 | self.alignment = alignment 13 | } 14 | 15 | // MARK: Codable 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case node 19 | case alignment 20 | } 21 | 22 | public init(from decoder: Decoder) throws { 23 | let container = try decoder.container(keyedBy: CodingKeys.self) 24 | node = try container.decodeNode(forKey: .node) 25 | alignment = try container.decode(Alignment.self, forKey: .alignment) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Padding.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import CoreGraphics 17 | 18 | public struct Padding: Decodable, Equatable { 19 | /// Padding distance for the top edge. 20 | public let top: CGFloat 21 | /// Padding distance for the leading edge. 22 | public let leading: CGFloat 23 | /// Padding distance for the bottom edge. 24 | public let bottom: CGFloat 25 | /// Padding distance for the trailing edge. 26 | public let trailing: CGFloat 27 | 28 | public init(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) { 29 | self.top = top 30 | self.leading = leading 31 | self.bottom = bottom 32 | self.trailing = trailing 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/SegueStyle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public enum SegueStyle: String, Decodable { 19 | case push 20 | case modal 21 | } 22 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/Shadow.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public struct Shadow: Decodable, Hashable { 19 | /// The shadow's color. 20 | public let color: ColorVariants 21 | public let blur: Int 22 | public let x: Int 23 | public let y: Int 24 | 25 | public init(color: ColorVariants, blur: Int, x: Int, y: Int) { 26 | self.color = color 27 | self.blur = blur 28 | self.x = x 29 | self.y = y 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/StatusBarStyle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public enum StatusBarStyle: String, Decodable { 19 | case `default` 20 | case light 21 | case dark 22 | case inverted 23 | } 24 | -------------------------------------------------------------------------------- /Sources/JudoModel/Value Types/StringTable.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | public typealias StringKey = String 19 | public typealias LocaleIdentifier = String 20 | 21 | public typealias StringTable = [LocaleIdentifier: [StringKey: String]] 22 | -------------------------------------------------------------------------------- /Sources/JudoSDK/AssetsDownloader.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import os.log 18 | 19 | final class AssetsDownloader { 20 | 21 | private let session: URLSession 22 | 23 | init(cache: URLCache? = nil) { 24 | let configuration = URLSessionConfiguration.default 25 | configuration.httpShouldUsePipelining = true 26 | configuration.networkServiceType = .responsiveData 27 | configuration.waitsForConnectivity = true 28 | configuration.urlCache = cache 29 | configuration.requestCachePolicy = .returnCacheDataElseLoad 30 | session = URLSession(configuration: configuration) 31 | } 32 | 33 | func download(url: URL) { 34 | download(url: url, completion: { _ in }) 35 | } 36 | 37 | func download(url: URL, completion: @escaping (Result) -> Void) { 38 | let request = URLRequest.assetRequest(url: url) 39 | let urlTask = session.dataTask(with: request) { data, response, error in 40 | if let error = error { 41 | completion(.failure(error)) 42 | return 43 | } 44 | 45 | if let data = data { 46 | completion(.success(data)) 47 | } 48 | } 49 | 50 | urlTask.resume() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/JudoSDK/DownloadService.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import os.log 18 | 19 | /// Download service. 20 | final class DownloadService { 21 | internal var persistentFetchedExperienceURLs: Set { 22 | get { 23 | Set((Judo.userDefaults.array(forKey: "persistentFetchedExperienceURLs") as? [String])?.compactMap({ URL(string: $0) }) ?? []) 24 | } 25 | set { 26 | Judo.userDefaults.set(newValue.map(\.absoluteString), forKey: "persistentFetchedExperienceURLs") 27 | } 28 | } 29 | 30 | private var lastSeenCache: URLCache? 31 | private var _urlSession: URLSession 32 | private var urlSession: URLSession { 33 | get { 34 | if let lastSeenCache = lastSeenCache, lastSeenCache !== Judo.sharedInstance.urlCache { 35 | judo_log(.debug, "Judo cache setup changed since last usage of DownloadService, recreating URLSession.") 36 | _urlSession = URLSession(configuration: Self.defaultURLSessionConfiguration(cache: Judo.sharedInstance.urlCache)) 37 | self.lastSeenCache = Judo.sharedInstance.urlCache 38 | } 39 | return _urlSession 40 | } 41 | set { 42 | _urlSession = newValue 43 | } 44 | } 45 | 46 | /// Instantiate instance 47 | /// - Parameter configuration: Service configuration. 48 | init(urlSession: URLSession? = nil) { 49 | self.lastSeenCache = Judo.sharedInstance.urlCache 50 | self._urlSession = urlSession ?? URLSession(configuration: Self.defaultURLSessionConfiguration(cache: Judo.sharedInstance.urlCache)) 51 | } 52 | 53 | static func defaultURLSessionConfiguration(cache: URLCache) -> URLSessionConfiguration { 54 | let configuration = URLSessionConfiguration.default 55 | configuration.urlCache = cache 56 | configuration.networkServiceType = .responsiveData 57 | return configuration 58 | } 59 | 60 | /// Fetch experience asynchronously. 61 | /// - Parameter completion: Fetched experience data 62 | func fetchExperienceData(url: URL, cachePolicy: URLRequest.CachePolicy, completion: @escaping (Result) -> Void) { 63 | var urlRequest = URLRequest.apiRequest(url: url) 64 | urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") 65 | urlRequest.cachePolicy = cachePolicy 66 | urlSession.dataTask(with: urlRequest) { result in 67 | completion(Result { 68 | let data = try result.get() 69 | 70 | // Remember fetched experience urls 71 | self.persistentFetchedExperienceURLs.insert(url) 72 | 73 | return data 74 | }) 75 | }.resume() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Extensions/Collection+items.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | extension Collection { 21 | func items(data: Any?, urlParameters: [String: String], userInfo: [String: Any]) -> [Any] { 22 | guard var result = JSONSerialization.value(forKeyPath: keyPath, data: data, urlParameters: urlParameters, userInfo: userInfo) as? [Any] else { 23 | return [] 24 | } 25 | 26 | filters.forEach { condition in 27 | result = result.filter { data in 28 | condition.isSatisfied( 29 | data: data, 30 | urlParameters: urlParameters, 31 | userInfo: userInfo 32 | ) 33 | } 34 | } 35 | 36 | if !sortDescriptors.isEmpty { 37 | result.sort { a, b in 38 | for descriptor in sortDescriptors { 39 | let a = JSONSerialization.value( 40 | forKeyPath: descriptor.keyPath, 41 | data: a, 42 | urlParameters: urlParameters, 43 | userInfo: userInfo 44 | ) 45 | 46 | let b = JSONSerialization.value( 47 | forKeyPath: descriptor.keyPath, 48 | data: b, 49 | urlParameters: urlParameters, 50 | userInfo: userInfo 51 | ) 52 | 53 | switch (a, b) { 54 | case (let a as String, let b as String) where a != b: 55 | return descriptor.ascending ? a < b : a > b 56 | case (let a as Double, let b as Double) where a != b: 57 | return descriptor.ascending ? a < b : a > b 58 | case (let a as Bool, let b as Bool) where a != b: 59 | return descriptor.ascending ? a == false : a == true 60 | case (let a as Date, let b as Date) where a != b: 61 | return descriptor.ascending ? a < b : a > b 62 | default: 63 | break 64 | } 65 | } 66 | 67 | return false 68 | } 69 | } 70 | 71 | if let limit = limit { 72 | let startAt = max(limit.startAt - 1, 0) 73 | let lowerBound = min(startAt, result.indices.upperBound) 74 | let limitedRange = result.indices.clamped(to: lowerBound.. Bool) -> Node? { 25 | reduce(nil) { result, node in 26 | guard result == nil else { 27 | return result 28 | } 29 | 30 | if predicate(node) { 31 | return node 32 | } 33 | 34 | return node.children.highest(where: predicate) 35 | } 36 | } 37 | 38 | /// Traverses the node graph, starting with the node's children, until it finds a node that matches the 39 | /// supplied predicate, from the bottom of the z-order. 40 | func lowest(where predicate: (Node) -> Bool) -> Node? { 41 | reversed().reduce(nil) { result, node in 42 | guard result == nil else { 43 | return result 44 | } 45 | 46 | if predicate(node) { 47 | return node 48 | } 49 | 50 | return node.children.lowest(where: predicate) 51 | } 52 | } 53 | 54 | func traverse(_ block: (Node) -> Void) { 55 | forEach { node in 56 | block(node) 57 | node.children.traverse(block) 58 | } 59 | } 60 | 61 | func flatten() -> [Node] { 62 | flatMap { node -> [Node] in 63 | [node] + node.children.flatten() 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Extensions/OSLog+JudoSDK.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import os.log 18 | 19 | private var _judoLog = OSLog(subsystem: "app.judo.JudoSDK", category: "JudoSDK") 20 | 21 | extension OSLog { 22 | static var judoLog: OSLog { _judoLog } 23 | } 24 | 25 | private func judoLoggingEnabled() -> Bool { 26 | ProcessInfo.processInfo.environment["JUDO_VERBOSE"] != nil 27 | } 28 | 29 | func judo_log(_ type: OSLogType, _ message: StaticString, _ args: CVarArg...) { 30 | guard judoLoggingEnabled() || type == .error else { 31 | return 32 | } 33 | 34 | // lack of splat means this mess: 35 | switch args.count { 36 | case 0: 37 | os_log(message, log: .judoLog, type: type) 38 | case 1: 39 | os_log(message, log: .judoLog, type: type, args[0]) 40 | case 2: 41 | os_log(message, log: .judoLog, type: type, args[0], args[1]) 42 | case 3: 43 | os_log(message, log: .judoLog, type: type, args[0], args[1], args[2]) 44 | case 4: 45 | os_log(message, log: .judoLog, type: type, args[0], args[1], args[2], args[3]) 46 | case 5: 47 | os_log(message, log: .judoLog, type: type, args[0], args[1], args[2], args[3], args[4]) 48 | default: 49 | os_log(message, log: .judoLog, type: type, args[0], args[1], args[2], args[3], args[4], args[5]) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Extensions/StringTable+resolve.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import JudoModel 18 | 19 | extension StringTable { 20 | func resolve(key: StringKey) -> String { 21 | // A simple (and not complete) attempt at RFC 4647 basic filtering. 22 | 23 | let preferredLocale = Locale.preferredLocale() 24 | 25 | if let matchedLocale = self[preferredLocale.identifier], let translation = matchedLocale[key] { 26 | return translation 27 | } 28 | 29 | guard let languageCode = preferredLocale.languageCode else { 30 | return key 31 | } 32 | 33 | let matchedLanguage = self.first { (languageEntry, string) in 34 | languageEntry == languageCode || languageEntry.starts(with: "\(languageCode)-") || languageEntry.starts(with: "\(languageCode)_") 35 | }?.value 36 | 37 | return matchedLanguage?[key] ?? key 38 | } 39 | } 40 | 41 | fileprivate extension Locale { 42 | static func preferredLocale() -> Locale { 43 | guard let preferredIdentifier = Locale.preferredLanguages.first else { 44 | return Locale.current 45 | } 46 | 47 | switch preferredIdentifier.lowercased(with: Locale(identifier: "en-US")) { 48 | case "zh-hant": 49 | return Locale(identifier: "zh-CN") 50 | case "zh-hans": 51 | return Locale(identifier: "zh-TW") 52 | default: 53 | return Locale(identifier: preferredIdentifier) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Extensions/UIControl+action.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import UIKit 17 | 18 | extension UIControl { 19 | 20 | func addAction(for controlEvents: UIControl.Event, _ action: @escaping (UIControl) -> Void) { 21 | 22 | @objc final class UIControlClosureBox: NSObject { 23 | private let closure: (UIControl) -> Void 24 | 25 | init(_ closure: @escaping (UIControl) -> Void) { 26 | self.closure = closure 27 | } 28 | 29 | @objc func invoke(_ sender: UIControl) { 30 | closure(sender) 31 | } 32 | } 33 | 34 | let box = UIControlClosureBox(action) 35 | addTarget(box, action: #selector(UIControlClosureBox.invoke(_:)), for: controlEvents) 36 | objc_setAssociatedObject(self, "\(UUID())", box, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Extensions/URL+cache.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import UIKit 18 | 19 | extension URL { 20 | func cachedImage() -> UIImage? { 21 | if let image = Judo.sharedInstance.imageCache.object(forKey: self as NSURL) { 22 | return image 23 | } 24 | 25 | if let cacheEntry = Judo.sharedInstance.assetsURLCache.cachedResponse(for: URLRequest(url: self)), let image = UIImage(data: cacheEntry.data) { 26 | return image 27 | } 28 | 29 | return nil 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Extensions/URLSession+dataPublisher.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import JudoModel 17 | import Combine 18 | import Foundation 19 | 20 | @available(iOS 13.0, *) 21 | extension URLSession { 22 | func dataPublisher(for request: URLRequest) -> AnyPublisher, Never> { 23 | dataTaskPublisher(for: request) 24 | .retry(1) 25 | .tryMap { element -> Data in 26 | guard let httpResponse = element.response as? HTTPURLResponse, 27 | httpResponse.statusCode == 200 else { 28 | throw URLError(.badServerResponse) 29 | } 30 | 31 | return element.data 32 | } 33 | .tryMap { data in 34 | try JSONSerialization.jsonObject(with: data) 35 | } 36 | .map { data in 37 | .success(data) 38 | } 39 | .catch { error in 40 | Just(.failure(error)).eraseToAnyPublisher() 41 | } 42 | .receive(on: RunLoop.main) 43 | .eraseToAnyPublisher() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/JudoSDK/JudoRepository.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import JudoModel 18 | import os.log 19 | 20 | final public class JudoRepository { 21 | enum Error: Swift.Error { 22 | case notAvailable 23 | } 24 | 25 | /// Data synchronization service 26 | let downloadService = DownloadService() 27 | 28 | /// Retrieve Experience data. 29 | /// - Parameter url: Experience URL 30 | public func retrieveExperience(url: URL, ignoreCache: Bool = false, completion: @escaping (Result) -> Void) { 31 | guard (NSURLComponents(url: url, resolvingAgainstBaseURL: true)?.host) != nil else { 32 | judo_log(.error, "Attempt to retrieve an Experience for an unknown host.") 33 | DispatchQueue.main.async { 34 | completion(Result.failure(UnsupportedDomainError(domain: ""))) 35 | } 36 | return 37 | } 38 | 39 | downloadService.fetchExperienceData(url: url, cachePolicy: ignoreCache ? .reloadIgnoringLocalAndRemoteCacheData : .useProtocolCachePolicy) { result in 40 | do { 41 | let experienceData = try result.get() 42 | let experience = try Experience(decode: experienceData) 43 | DispatchQueue.main.async { 44 | completion(.success(experience)) 45 | } 46 | } catch { 47 | DispatchQueue.main.async { 48 | completion(.failure(error)) 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | struct UnsupportedDomainError: Error, LocalizedError { 56 | var domain: String 57 | 58 | var errorDescription: String? { 59 | "Unsupported Judo domain: \(domain)" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/JudoSDK/MainQueue.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | private let mainQueueKey: DispatchSpecificKey = { 19 | let key = DispatchSpecificKey() 20 | DispatchQueue.main.setSpecific(key: key, value: "judoMainQueueValue") 21 | return key 22 | }() 23 | 24 | extension DispatchQueue { 25 | 26 | static var isMainQueue: Bool { 27 | DispatchQueue.getSpecific(key: mainQueueKey) != nil 28 | } 29 | 30 | static func toMain(_ block: @escaping () -> Void) { 31 | 32 | // Being on the main thread does not guarantee to be on the main queue. 33 | if DispatchQueue.isMainQueue { 34 | block() 35 | } else { 36 | if Thread.isMainThread { 37 | DispatchQueue.main.async { 38 | block() 39 | } 40 | } else { 41 | // Execution is not on the main queue and thread at this point. 42 | DispatchQueue.main.sync { 43 | block() 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Meta.swift: -------------------------------------------------------------------------------- 1 | enum Meta { 2 | public static let APIVersion: Int = 2 3 | public static let SDKVersion: String = "1.8.6" 4 | } 5 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Model/ViewID.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | 18 | struct ViewID: Hashable { 19 | var nodeID: String 20 | var collectionIndex = 0 21 | } 22 | -------------------------------------------------------------------------------- /Sources/JudoSDK/RecoverableError.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | protocol RecoverableError { 17 | var canRecover: Bool { get } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/JudoSDK/Resources/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judoapp/judo-ios/ccefe31abccb8b3af12504adc30eb2c1f8273cf2/Sources/JudoSDK/Resources/en.lproj/Localizable.strings -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/CarouselState.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Combine 17 | 18 | @available(iOS 13.0, *) 19 | final class CarouselState: ObservableObject { 20 | @Published var currentPageForCarousel: [ViewID: Int] = [:] 21 | @Published var currentNumberOfPagesForCarousel: [ViewID: Int] = [:] 22 | } 23 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Model/Color+uiValues.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import UIKit 18 | import SwiftUI 19 | import JudoModel 20 | 21 | @available(iOS 13.0, *) 22 | extension JudoModel.Color { 23 | var swiftUIColor: SwiftUI.Color { 24 | Color(.displayP3, red: Double(red), green: Double(green), blue: Double(blue), opacity: Double(alpha)) 25 | } 26 | 27 | var uiColor: UIColor { 28 | UIColor(displayP3Red: CGFloat(red), green: CGFloat(green), blue: CGFloat(blue), alpha: CGFloat(alpha)) 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Model/ColorVariants+uikit.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import UIKit 17 | import SwiftUI 18 | import JudoModel 19 | 20 | @available(iOS 13.0, *) 21 | extension ColorVariants { 22 | func uikitUIColor(colorScheme: ColorScheme, colorSchemeContrast: ColorSchemeContrast) -> UIColor? { 23 | if let systemColor = systemName { 24 | return UIColor.named(systemColor) 25 | } else if let highContrast = highContrast, colorScheme != .dark, colorSchemeContrast == .increased { 26 | return highContrast.uiColor 27 | } else if let darkModeHighContrast = darkModeHighContrast, colorScheme == .dark, colorSchemeContrast == .increased { 28 | return darkModeHighContrast.uiColor 29 | } else if let darkMode = darkMode, colorScheme == .dark { 30 | return darkMode.uiColor 31 | } else if let `default` = `default` { 32 | return `default`.uiColor 33 | } else { 34 | return nil 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Model/Gradient+swiftUI.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import SwiftUI 18 | import JudoModel 19 | 20 | // MARK: SwiftUI Value 21 | 22 | @available(iOS 13.0, *) 23 | public extension JudoModel.Gradient { 24 | func swiftUIGradient(startPoint: UnitPoint? = nil, endPoint: UnitPoint? = nil) -> LinearGradient { 25 | LinearGradient( 26 | gradient: Gradient( 27 | stops: stops 28 | .sorted { $0.position < $1.position } 29 | .map { Gradient.Stop(color: $0.color.swiftUIColor, location: CGFloat($0.position)) } 30 | ), 31 | startPoint: startPoint ?? .init(x: from.x, y: from.y), 32 | endPoint: endPoint ?? .init(x: to.x, y: to.y) 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Model/RealizeColor.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | 20 | /// Realize a ColorVariants into a UIColor or SwiftUI.Color. 21 | @available(iOS 13.0, *) 22 | struct RealizeColor: View where Content: View { 23 | @Environment(\.colorScheme) private var colorScheme 24 | @Environment(\.colorSchemeContrast) private var colorSchemeContrast 25 | 26 | private let colorVariants: ColorVariants 27 | private let content: (C) -> Content 28 | 29 | init(_ colorVariants: ColorVariants, @ViewBuilder content: @escaping (C) -> Content) where C == SwiftUI.Color { 30 | self.colorVariants = colorVariants 31 | self.content = content 32 | } 33 | 34 | init(_ colorVariants: ColorVariants, @ViewBuilder content: @escaping (C) -> Content) where C == UIColor { 35 | self.colorVariants = colorVariants 36 | self.content = content 37 | } 38 | 39 | var body: some View { 40 | if C.self == SwiftUI.Color.self { 41 | content(swiftUIColor() as! C) 42 | } else if C.self == UIColor.self { 43 | content(uikitUIColor() as! C) 44 | } 45 | } 46 | 47 | private func swiftUIColor() -> SwiftUI.Color { 48 | if let systemColor = self.colorVariants.systemName { 49 | return SwiftUI.Color.named(systemColor) 50 | } else if let highContrast = self.colorVariants.highContrast, colorScheme != .dark, colorSchemeContrast == .increased { 51 | return highContrast.swiftUIColor 52 | } else if let darkModeHighContrast = self.colorVariants.darkModeHighContrast, colorScheme == .dark, colorSchemeContrast == .increased { 53 | return darkModeHighContrast.swiftUIColor 54 | } else if let darkMode = self.colorVariants.darkMode, colorScheme == .dark { 55 | return darkMode.swiftUIColor 56 | } else if let `default` = self.colorVariants.default { 57 | return `default`.swiftUIColor 58 | } else { 59 | return Color(.clear) 60 | } 61 | } 62 | 63 | private func uikitUIColor() -> UIColor { 64 | self.colorVariants.uikitUIColor(colorScheme: colorScheme, colorSchemeContrast: colorSchemeContrast) ?? .clear 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/ActionModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct ActionModifier: ViewModifier { 21 | var layer: Layer 22 | 23 | @Environment(\.experience) private var experience 24 | @Environment(\.screen) private var screen 25 | @Environment(\.presentAction) private var presentAction 26 | @Environment(\.showAction) private var showAction 27 | @Environment(\.experienceViewController) private var experienceViewControllerHolder 28 | @Environment(\.screenViewController) private var screenViewControllerHolder 29 | @Environment(\.data) private var data 30 | @Environment(\.urlParameters) private var urlParameters 31 | @Environment(\.userInfo) private var userInfo 32 | @Environment(\.authorize) private var authorize 33 | 34 | @ViewBuilder 35 | func body(content: Content) -> some View { 36 | if let action = layer.action, let experience = experience, let screen = screen, let experienceViewController = experienceViewControllerHolder?.experienceViewController, let screenViewController = screenViewControllerHolder?.screenViewController { 37 | Button { 38 | action.handle(experience: experience, node: layer, screen: screen, data: data, urlParameters: urlParameters, userInfo: userInfo, authorize: authorize, experienceViewController: experienceViewController, screenViewController: screenViewController) 39 | } label: { 40 | content 41 | } 42 | .buttonStyle(PlainButtonStyle()) 43 | } else { 44 | content 45 | } 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/AspectRatioModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct AspectRatioModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let aspectRatio = node.aspectRatio { 26 | content.aspectRatio(aspectRatio, contentMode: .fit) 27 | } else { 28 | content 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/BackgroundModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct BackgroundModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let background = node.background, let layer = background.node as? Layer { 26 | content.background( 27 | LayerView(layer: layer).environment(\.isEnabled, false), 28 | alignment: background.alignment.swiftUIValue 29 | ) 30 | } else { 31 | content 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/FontModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import UIKit 18 | import JudoModel 19 | 20 | @available(iOS 13.0, *) 21 | struct FontModifier: ViewModifier { 22 | @Environment(\.sizeCategory) private var sizeCategory 23 | @State private var uiFont: SwiftUI.Font 24 | 25 | var font: JudoModel.Font 26 | 27 | init(font: JudoModel.Font) { 28 | self.font = font 29 | self._uiFont = .init(initialValue: getUIFont(for: font)) 30 | } 31 | 32 | func body(content: Content) -> some View { 33 | content 34 | .font(uiFont) 35 | .onReceive(NotificationCenter.default.publisher(for: Judo.didRegisterCustomFontNotification)) { _ in 36 | uiFont = getUIFont(for: font) 37 | } 38 | } 39 | } 40 | 41 | @available(iOS 13.0, *) 42 | private func getUIFont(for font: JudoModel.Font) -> SwiftUI.Font { 43 | if let uifont = font.uikitFont { 44 | return SwiftUI.Font(uifont) 45 | } 46 | 47 | return .body 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/FrameModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct FrameModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let frame = node.frame { 26 | if frame.isFixed { 27 | content 28 | .frame( 29 | width: frame.width, 30 | height: frame.height, 31 | alignment: frame.alignment.swiftUIValue 32 | ) 33 | } else { 34 | content 35 | .frame( 36 | minWidth: frame.minWidth, 37 | maxWidth: frame.maxWidth, 38 | minHeight: frame.minHeight, 39 | maxHeight: frame.maxHeight, 40 | alignment: frame.alignment.swiftUIValue 41 | ) 42 | } 43 | } else { 44 | content 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/IgnoresSafeAreaModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct IgnoresSafeAreaModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let ignoredSafeAreaEdges = node.ignoresSafeArea { 26 | content.edgesIgnoringSafeArea(SwiftUI.Edge.Set(set: ignoredSafeAreaEdges)) 27 | } else { 28 | content 29 | } 30 | } 31 | } 32 | 33 | @available(iOS 13.0, *) 34 | private extension SwiftUI.Edge.Set { 35 | init(set: Set) { 36 | self = SwiftUI.Edge.Set(set.map { edge in 37 | SwiftUI.Edge.Set.Element(edge.swiftUIValue) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/LayoutPriorityModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct LayoutPriorityModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let layoutPriority = node.layoutPriority { 26 | content.layoutPriority(Double(layoutPriority)) 27 | } else { 28 | content 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/MaskModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct MaskModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let mask = node.mask as? Layer { 26 | content.mask( 27 | LayerView(layer: mask) 28 | .environment(\.isEnabled, false) 29 | ) 30 | } else { 31 | content 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/OffsetModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct OffsetModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let offset = node.offset { 26 | content.offset(x: offset.x, y: offset.y) 27 | } else { 28 | content 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/OpacityModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct OpacityModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let opacity = node.opacity { 26 | content.opacity(Double(opacity)) 27 | } else { 28 | content 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/OverlayModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct OverlayModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func overlayContents(layer: Layer) -> some View { 25 | // Workaround: SwiftUI Overlays over UIkit views that use gesture recognizers other than tap (namely, swipe, such as ScrollViews) will block the input. The workaround is to embed the SwiftUI overlay inside UIKit itself, which, presumably, is able to properly marshal the gesture input. 26 | if node is Carousel || node is ScrollContainer { 27 | SwiftUIWrapper { 28 | LayerView(layer: layer) 29 | } 30 | .environment(\.isEnabled, false) 31 | .allowsHitTesting(false) 32 | } else { 33 | LayerView(layer: layer) 34 | .environment(\.isEnabled, false) 35 | .allowsHitTesting(false) 36 | } 37 | } 38 | 39 | @ViewBuilder 40 | func body(content: Content) -> some View { 41 | if let overlay = node.overlay, let layer = overlay.node as? Layer { 42 | content.overlay( 43 | overlayContents(layer: layer), 44 | alignment: overlay.alignment.swiftUIValue 45 | ) 46 | } else { 47 | content 48 | } 49 | } 50 | } 51 | 52 | @available(iOS 13.0, *) 53 | private struct SwiftUIWrapper: UIViewControllerRepresentable { 54 | let content: () -> T 55 | 56 | func makeUIViewController(context: Context) -> UIHostingController { 57 | let hostingController = UIHostingController(rootView: content()) 58 | hostingController.view.backgroundColor = .clear 59 | return hostingController 60 | } 61 | 62 | func updateUIViewController(_ uiViewController: UIHostingController, context: Context) { 63 | uiViewController.rootView = content() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/PaddingModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct PaddingModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let padding = node.padding { 26 | content.padding( 27 | EdgeInsets( 28 | top: CGFloat(padding.top), 29 | leading: CGFloat(padding.leading), 30 | bottom: CGFloat(padding.bottom), 31 | trailing: CGFloat(padding.trailing) 32 | ) 33 | ) 34 | } else { 35 | content 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Modifiers/ShadowModifier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct ShadowModifier: ViewModifier { 21 | var node: Node 22 | 23 | @ViewBuilder 24 | func body(content: Content) -> some View { 25 | if let shadow = node.shadow { 26 | RealizeColor(shadow.color) { shadowColor in 27 | content 28 | .shadow( 29 | color: shadowColor, 30 | radius: CGFloat(shadow.blur / 2), 31 | x: CGFloat(shadow.x), 32 | y: CGFloat(shadow.y) 33 | ) 34 | } 35 | } else { 36 | content 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/AnimatedImage.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import SwiftUI 18 | import Combine 19 | 20 | @available(iOS 13.0, *) 21 | struct AnimatedImage: View { 22 | @State private var frameIndex = 0 23 | 24 | private let timer: Publishers.Autoconnect 25 | private let images: [UIImage] 26 | 27 | init(uiImage image: UIImage) { 28 | self.images = image.images ?? [image] 29 | self.timer = Timer.publish(every: image.duration / Double(images.count), on: .main, in: .common).autoconnect() 30 | } 31 | 32 | var body: some View { 33 | Image(uiImage: images[frameIndex]) 34 | .resizable() 35 | .onReceive(timer) { _ in 36 | if frameIndex + 1 < images.count { 37 | frameIndex += 1 38 | } else { 39 | frameIndex = 0 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/AudioView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import Foundation 17 | import SwiftUI 18 | import JudoModel 19 | import AVKit 20 | import Combine 21 | 22 | @available(iOS 13.0, *) 23 | struct AudioView: View { 24 | @Environment(\.data) private var data 25 | @Environment(\.urlParameters) private var urlParameters 26 | @Environment(\.userInfo) private var userInfo 27 | 28 | let audio: JudoModel.Audio 29 | @State private var isVisible = true 30 | 31 | var body: some View { 32 | if let urlString = audio.sourceURL.evaluatingExpressions(data: data, urlParameters: urlParameters, userInfo: userInfo), let sourceURL = URL(string: urlString) { 33 | AudioPlayerView( 34 | sourceURL: sourceURL, 35 | looping: audio.looping, 36 | autoPlay: audio.autoPlay, 37 | isVisible: isVisible 38 | ) 39 | .onDisappear { isVisible = false } 40 | .onAppear { isVisible = true } 41 | .modifier(AudioPlayerFrameModifier()) 42 | } 43 | } 44 | } 45 | 46 | @available(iOS 13.0, *) 47 | private struct AudioPlayerView: UIViewControllerRepresentable { 48 | var sourceURL: URL 49 | var looping: Bool 50 | var autoPlay: Bool 51 | var isVisible: Bool 52 | 53 | func makeUIViewController(context: Context) -> AVPlayerViewController { 54 | AudioPlayerViewController(sourceURL: sourceURL, looping: looping) 55 | } 56 | 57 | func updateUIViewController(_ viewController: AVPlayerViewController, context: Context) { 58 | if !isVisible { 59 | viewController.player?.pause() 60 | } else if autoPlay { 61 | viewController.player?.play() 62 | } 63 | } 64 | } 65 | 66 | @available(iOS 13.0, *) 67 | private class AudioPlayerViewController: AVPlayerViewController { 68 | private var looper: AVPlayerLooper? 69 | 70 | init(sourceURL: URL, looping: Bool) { 71 | super.init(nibName: nil, bundle: nil) 72 | 73 | let playerItem = AVPlayerItem(url: sourceURL) 74 | 75 | if looping { 76 | let player = AVQueuePlayer() 77 | self.player = player 78 | self.looper = AVPlayerLooper(player: player, templateItem: playerItem) 79 | } else { 80 | player = AVPlayer(playerItem: playerItem) 81 | } 82 | } 83 | 84 | required init?(coder: NSCoder) { 85 | fatalError("init(coder:) has not been implemented") 86 | } 87 | } 88 | 89 | @available(iOS 13.0, *) 90 | fileprivate struct AudioPlayerFrameModifier: ViewModifier { 91 | 92 | @ViewBuilder 93 | func body(content: Content) -> some View { 94 | if #available(iOS 16.0, *) { 95 | content.frame(height: 110) 96 | } else { 97 | content.frame(height: 44) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/CollectionView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import JudoModel 17 | import SwiftUI 18 | 19 | @available(iOS 13.0, *) 20 | struct CollectionView: View { 21 | @Environment(\.data) private var data 22 | @Environment(\.urlParameters) private var urlParameters 23 | @Environment(\.userInfo) private var userInfo 24 | var collection: Collection 25 | 26 | var body: some View { 27 | if let items = items { 28 | ForEach(Array(zip(items.indices, items)), id: \.0) { index, item in 29 | ForEach(layers) { layer in 30 | LayerView(layer: layer) 31 | .environment(\.collectionIndex, index) 32 | .environment(\.data, item) 33 | } 34 | } 35 | } 36 | } 37 | 38 | private var items: [Any]? { 39 | collection.items( 40 | data: data, 41 | urlParameters: urlParameters, 42 | userInfo: userInfo 43 | ) 44 | } 45 | 46 | private var layers: [Layer] { 47 | collection.children.compactMap { $0 as? Layer } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/ConditionalView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import JudoModel 17 | import SwiftUI 18 | 19 | @available(iOS 13.0, *) 20 | struct ConditionalView: View { 21 | @Environment(\.data) private var data 22 | @Environment(\.urlParameters) private var urlParameters 23 | @Environment(\.userInfo) private var userInfo 24 | var conditional: Conditional 25 | 26 | var body: some View { 27 | if allConditionsSatisfied { 28 | ForEach(conditional.children.compactMap { $0 as? Layer }) { 29 | LayerView(layer: $0) 30 | } 31 | } 32 | } 33 | 34 | private var allConditionsSatisfied: Bool { 35 | conditional.conditions.allSatisfy { condition in 36 | condition.isSatisfied( 37 | data: data, 38 | urlParameters: urlParameters, 39 | userInfo: userInfo 40 | ) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/DividerView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct DividerView: View { 21 | var divider: JudoModel.Divider 22 | 23 | var body: some View { 24 | RealizeColor(divider.backgroundColor) { backgroundColor in 25 | SwiftUI.Divider().background(backgroundColor) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/HStackView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct HStackView: View { 21 | var stack: JudoModel.HStack 22 | 23 | var body: some View { 24 | SwiftUI.HStack(alignment: stack.alignment.swiftUIValue, spacing: stack.spacing) { 25 | ForEach(orderedLayers) { 26 | LayerView(layer: $0) 27 | } 28 | } 29 | } 30 | 31 | private var orderedLayers: [Layer] { 32 | stack.children.compactMap { $0 as? Layer } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/IconView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct IconView: View { 21 | var icon: Icon 22 | 23 | var body: some View { 24 | RealizeColor(icon.color) { color in 25 | SwiftUI.Image(systemName: icon.icon.symbolName) 26 | .foregroundColor(color) 27 | .font(SwiftUI.Font.system(size: CGFloat(icon.pointSize))) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/RectangleView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct RectangleView: View { 21 | var rectangle: JudoModel.Rectangle 22 | 23 | @Environment(\.colorScheme) private var colorScheme 24 | @Environment(\.colorSchemeContrast) private var colorSchemeContrast 25 | 26 | @ViewBuilder 27 | var body: some View { 28 | // For some reason SwiftUI's Rectangle will not allow you apply both a fill and a border so 29 | // we must use a ZStack to apply both. The trailing compositingGroup modifier ensures that 30 | // the ShadowModifier renders properly. 31 | SwiftUI.ZStack { 32 | switch rectangle.fill { 33 | case .flat(let color): 34 | RealizeColor(color) { color in 35 | RoundedRectangle( 36 | cornerRadius: CGFloat(rectangle.cornerRadius), 37 | style: .circular 38 | ) 39 | .fill(color) 40 | } 41 | case .gradient(let gradientVariants): 42 | RoundedRectangle( 43 | cornerRadius: CGFloat(rectangle.cornerRadius), 44 | style: .circular 45 | ) 46 | .fill( 47 | gradientVariants.resolve(colorScheme: colorScheme, colorSchemeContrast: colorSchemeContrast).swiftUIGradient() 48 | ) 49 | } 50 | 51 | if let border = rectangle.border { 52 | RealizeColor(border.color) { borderColor in 53 | RoundedRectangle( 54 | cornerRadius: CGFloat(rectangle.cornerRadius), 55 | style: .circular 56 | ) 57 | .strokeBorder(lineWidth: CGFloat(border.width), antialiased: true) 58 | .foregroundColor(borderColor) 59 | } 60 | } 61 | } 62 | .compositingGroup() 63 | } 64 | } 65 | 66 | @available(iOS 13.0, *) 67 | private extension GradientVariants { 68 | func resolve(colorScheme: ColorScheme, colorSchemeContrast: ColorSchemeContrast) -> JudoModel.Gradient { 69 | if colorScheme == .dark && colorSchemeContrast == .increased { 70 | return darkModeHighContrast ?? self.default 71 | } else if colorScheme == .dark { 72 | return darkMode ?? self.default 73 | } else if colorSchemeContrast == .increased { 74 | return highContrast ?? self.default 75 | } else { 76 | return self.default 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/TextView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct TextView: View { 21 | @Environment(\.data) private var data 22 | @Environment(\.stringTable) private var stringTable 23 | @Environment(\.urlParameters) private var urlParameters 24 | @Environment(\.userInfo) private var userInfo 25 | 26 | var text: JudoModel.Text 27 | 28 | var body: some View { 29 | let textString = stringTable.resolve(key: text.text) 30 | 31 | if let textValue = textString.evaluatingExpressions(data: data, urlParameters: urlParameters, userInfo: userInfo) { 32 | RealizeColor(self.text.textColor) { textColor in 33 | SwiftUI.Text(transformed(textValue)) 34 | .modifier( 35 | FontModifier(font: self.text.font) 36 | ) 37 | .foregroundColor(textColor) 38 | } 39 | .multilineTextAlignment(uiTextAlignment) 40 | .lineLimit(self.text.lineLimit) 41 | .fixedSize(horizontal: false, vertical: true) 42 | } 43 | } 44 | 45 | private func transformed(_ text: String) -> String { 46 | switch self.text.transform { 47 | case .lowercase: 48 | return text.lowercased() 49 | case .uppercase: 50 | return text.uppercased() 51 | case .none: 52 | return text 53 | } 54 | } 55 | 56 | private var uiTextAlignment: SwiftUI.TextAlignment { 57 | switch text.textAlignment { 58 | case .center: 59 | return .center 60 | case .leading: 61 | return .leading 62 | case .trailing: 63 | return .trailing 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/VStackView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct VStackView: View { 21 | var stack: JudoModel.VStack 22 | 23 | var body: some View { 24 | SwiftUI.VStack(alignment: stack.alignment.swiftUIValue, spacing: stack.spacing) { 25 | ForEach(orderedLayers) { 26 | LayerView(layer: $0) 27 | } 28 | } 29 | } 30 | 31 | private var orderedLayers: [Layer] { 32 | stack.children.compactMap { $0 as? Layer } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/JudoSDK/UI/Views/ZStackView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import SwiftUI 17 | import JudoModel 18 | 19 | @available(iOS 13.0, *) 20 | struct ZStackView: View { 21 | var stack: JudoModel.ZStack 22 | 23 | var body: some View { 24 | SwiftUI.ZStack(alignment: stack.alignment.swiftUIValue) { 25 | ForEach(orderedLayers) { 26 | LayerView(layer: $0) 27 | } 28 | } 29 | } 30 | 31 | private var orderedLayers: [Layer] { 32 | stack.children.compactMap { $0 as? Layer }.reversed() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/JudoServiceTests/JSONSerializationTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved. 2 | // You are hereby granted a non-exclusive, worldwide, royalty-free license to use, 3 | // copy, modify, and distribute this software in source code or binary form for use 4 | // in connection with the web services and APIs provided by Rover. 5 | // 6 | // This copyright notice shall be included in all copies or substantial portions of 7 | // the software. 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import XCTest 17 | @testable import JudoSDK 18 | 19 | class JSONSerializationTests: XCTestCase { 20 | 21 | override func setUpWithError() throws { 22 | // Put setup code here. This method is called before the invocation of each test method in the class. 23 | } 24 | 25 | override func tearDownWithError() throws { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | func testValueForKeyPath() throws { 30 | let exampleData = [ 31 | "hello": 42 32 | ] 33 | 34 | let value = JSONSerialization.value(forKeyPath: "data.hello", data: exampleData, urlParameters: [:], userInfo: [:]) 35 | XCTAssertEqual(value as! Int, 42) 36 | } 37 | 38 | func testMissingValueForKeyPath() throws { 39 | let exampleData = [ 40 | "another": 42 41 | ] 42 | 43 | let value = JSONSerialization.value(forKeyPath: "data.missing", data: exampleData, urlParameters: [:], userInfo: [:]) 44 | XCTAssertNil(value) 45 | } 46 | 47 | func testKeyPathTraversal() throws { 48 | let exampleData = [ 49 | "nested": [ 50 | "hello": 42, 51 | "attribute_with.period": 69, 52 | 53 | // because you know some deranged API out there will do this. 54 | "attribute_with_trailing_period.": 1337, 55 | ".attribute_with_leading_period": 7331 56 | ], 57 | "flattened.attributes": [ 58 | "hello": 24 59 | ] 60 | ] 61 | 62 | let nestedValue = JSONSerialization.value(forKeyPath: "data.nested.hello", data: exampleData, urlParameters: [:], userInfo: [:]) 63 | XCTAssertEqual(nestedValue as! Int, 42) 64 | 65 | let dictValue = JSONSerialization.value(forKeyPath: "data.nested", data: exampleData, urlParameters: [:], userInfo: [:]) 66 | XCTAssert(dictValue is [String: Any]) 67 | 68 | let value = JSONSerialization.value(forKeyPath: "data.flattened.attributes.hello", data: exampleData, urlParameters: [:], userInfo: [:]) 69 | XCTAssertEqual(value as! Int, 24) 70 | 71 | let keyWithPeriodValue = JSONSerialization.value(forKeyPath: "data.nested.attribute_with.period", data: exampleData, urlParameters: [:], userInfo: [:]) 72 | XCTAssertEqual(keyWithPeriodValue as! Int, 69) 73 | 74 | let derangedLeadingPeriodValue = JSONSerialization.value(forKeyPath: "data.nested..attribute_with_leading_period", data: exampleData, urlParameters: [:], userInfo: [:]) 75 | XCTAssertEqual(derangedLeadingPeriodValue as! Int, 7331) 76 | 77 | let derangedTrailingPeriodValue = JSONSerialization.value(forKeyPath: "data.nested.attribute_with_trailing_period.", data: exampleData, urlParameters: [:], userInfo: [:]) 78 | XCTAssertEqual(derangedTrailingPeriodValue as! Int, 1337) 79 | } 80 | } 81 | --------------------------------------------------------------------------------