├── .gitignore ├── First R ├── First R.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── First R │ └── main.swift ├── LICENSE.txt ├── README.md ├── SwiftDensity ├── .gitignore ├── SwiftDensity.xcodeproj │ └── project.pbxproj └── SwiftDensity │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon_128x128.png │ │ ├── icon_128x128@2x.png │ │ ├── icon_16x16.png │ │ ├── icon_16x16@2x.png │ │ ├── icon_256x256.png │ │ ├── icon_256x256@2x.png │ │ ├── icon_32x32.png │ │ ├── icon_32x32@2x.png │ │ ├── icon_512x512.png │ │ └── icon_512x512@2x.png │ ├── Contents.json │ └── codeError.colorset │ │ └── Contents.json │ ├── ContentView.swift │ ├── HelperViews.swift │ ├── Info.plist │ ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json │ ├── SwiftDensity-Bridging-Header.h │ ├── SwiftDensity.entitlements │ ├── SwiftDensityApp.swift │ ├── en.lproj │ └── Localizable.strings │ ├── globals.swift │ ├── model.swift │ ├── models.swift │ ├── r-scripts │ └── density.rstats │ ├── r-utils.swift │ ├── sexp-utils.swift │ ├── swift-r-bridge.c │ ├── swift-r-bridge.h │ ├── undo.swift │ └── utils.swift ├── data └── inflammation-01.csv ├── ephemerids-2 ├── ephemerids.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcshareddata │ │ └── xcschemes │ │ └── ephemerids.xcscheme └── ephemerids │ ├── ephemerids-Bridging-Header.h │ ├── ephemerids.entitlements │ ├── main.swift │ ├── r-utils.swift │ ├── sexp-utils.swift │ ├── sunriseset.swift │ ├── swift-r-bridge.c │ ├── swift-r-bridge.h │ └── utils.swift ├── ephemerids-3 ├── ephemerids.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcshareddata │ │ └── xcschemes │ │ └── ephemerids.xcscheme └── ephemerids │ ├── Info.plist │ ├── ephemerids-Bridging-Header.h │ ├── ephemerids.entitlements │ ├── main.swift │ ├── r-utils.swift │ ├── sexp-utils.swift │ ├── sunriseset.swift │ ├── swift-r-bridge.c │ ├── swift-r-bridge.h │ └── utils.swift ├── ephemerids ├── ephemerids.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── ephemerids.xcscheme └── ephemerids │ ├── ephemerids-Bridging-Header.h │ ├── ephemerids.entitlements │ ├── main.swift │ ├── r-utils.swift │ ├── sexp-utils.swift │ ├── sunriseset.swift │ ├── swift-r-bridge.c │ ├── swift-r-bridge.h │ └── utils.swift ├── random ├── random.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── random │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── ContentView.swift │ ├── Info.plist │ ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json │ ├── RandomModel.swift │ ├── random-Bridging-Header.h │ ├── random.entitlements │ └── randomApp.swift ├── scriptr-2 ├── scriptr.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── scriptr │ ├── Info.plist │ ├── main.swift │ ├── r-utils.swift │ ├── scriptr-Bridging-Header.h │ ├── scriptr.entitlements │ ├── sexp-utils.swift │ ├── swift-r-bridge.c │ ├── swift-r-bridge.h │ └── utils.swift ├── scriptr ├── scriptr.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── scriptr │ ├── Info.plist │ ├── main.swift │ ├── r-utils.swift │ ├── scriptr-Bridging-Header.h │ ├── scriptr.entitlements │ ├── sexp-utils.swift │ ├── swift-r-bridge.c │ ├── swift-r-bridge.h │ └── utils.swift ├── secondr ├── secondr.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── secondr │ ├── main.swift │ ├── secondr-Bridging-Header.h │ ├── swift-r-bridge.c │ └── swift-r-bridge.h └── shiny ├── shiny.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── shiny.xcscheme └── shiny ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── ContentView.swift ├── Info.plist ├── Preview Content └── Preview Assets.xcassets │ └── Contents.json ├── ShinyModel.swift ├── WebView.swift ├── process.swift ├── shiny-Bridging-Header.h ├── shiny-app.rscript ├── shiny.entitlements └── shinyApp.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Gcc Patch 26 | /*.gcno 27 | 28 | # General 29 | .DS_Store 30 | .AppleDouble 31 | .LSOverride 32 | 33 | # Icon must end with two \r 34 | Icon 35 | 36 | 37 | # Thumbnails 38 | ._* 39 | 40 | # Files that might appear in the root of a volume 41 | .DocumentRevisions-V100 42 | .fseventsd 43 | .Spotlight-V100 44 | .TemporaryItems 45 | .Trashes 46 | .VolumeIcon.icns 47 | .com.apple.timemachine.donotpresent 48 | 49 | # Directories potentially created on remote AFP share 50 | .AppleDB 51 | .AppleDesktop 52 | Network Trash Folder 53 | Temporary Items 54 | .apdisk 55 | -------------------------------------------------------------------------------- /First R/First R.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 01872DAB259F8F7B00E9CE7B /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01872DAA259F8F7B00E9CE7B /* main.swift */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 01872DA5259F8F7B00E9CE7B /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 01872DA7259F8F7B00E9CE7B /* First R */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "First R"; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 01872DAA259F8F7B00E9CE7B /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | 01872DA4259F8F7B00E9CE7B /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | 01872D9E259F8F7B00E9CE7B = { 42 | isa = PBXGroup; 43 | children = ( 44 | 01872DA9259F8F7B00E9CE7B /* First R */, 45 | 01872DA8259F8F7B00E9CE7B /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | 01872DA8259F8F7B00E9CE7B /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 01872DA7259F8F7B00E9CE7B /* First R */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | 01872DA9259F8F7B00E9CE7B /* First R */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 01872DAA259F8F7B00E9CE7B /* main.swift */, 61 | ); 62 | path = "First R"; 63 | sourceTree = ""; 64 | }; 65 | /* End PBXGroup section */ 66 | 67 | /* Begin PBXNativeTarget section */ 68 | 01872DA6259F8F7B00E9CE7B /* First R */ = { 69 | isa = PBXNativeTarget; 70 | buildConfigurationList = 01872DAE259F8F7B00E9CE7B /* Build configuration list for PBXNativeTarget "First R" */; 71 | buildPhases = ( 72 | 01872DA3259F8F7B00E9CE7B /* Sources */, 73 | 01872DA4259F8F7B00E9CE7B /* Frameworks */, 74 | 01872DA5259F8F7B00E9CE7B /* CopyFiles */, 75 | ); 76 | buildRules = ( 77 | ); 78 | dependencies = ( 79 | ); 80 | name = "First R"; 81 | productName = "First R"; 82 | productReference = 01872DA7259F8F7B00E9CE7B /* First R */; 83 | productType = "com.apple.product-type.tool"; 84 | }; 85 | /* End PBXNativeTarget section */ 86 | 87 | /* Begin PBXProject section */ 88 | 01872D9F259F8F7B00E9CE7B /* Project object */ = { 89 | isa = PBXProject; 90 | attributes = { 91 | LastSwiftUpdateCheck = 1230; 92 | LastUpgradeCheck = 1230; 93 | TargetAttributes = { 94 | 01872DA6259F8F7B00E9CE7B = { 95 | CreatedOnToolsVersion = 12.3; 96 | }; 97 | }; 98 | }; 99 | buildConfigurationList = 01872DA2259F8F7B00E9CE7B /* Build configuration list for PBXProject "First R" */; 100 | compatibilityVersion = "Xcode 9.3"; 101 | developmentRegion = en; 102 | hasScannedForEncodings = 0; 103 | knownRegions = ( 104 | en, 105 | Base, 106 | ); 107 | mainGroup = 01872D9E259F8F7B00E9CE7B; 108 | productRefGroup = 01872DA8259F8F7B00E9CE7B /* Products */; 109 | projectDirPath = ""; 110 | projectRoot = ""; 111 | targets = ( 112 | 01872DA6259F8F7B00E9CE7B /* First R */, 113 | ); 114 | }; 115 | /* End PBXProject section */ 116 | 117 | /* Begin PBXSourcesBuildPhase section */ 118 | 01872DA3259F8F7B00E9CE7B /* Sources */ = { 119 | isa = PBXSourcesBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | 01872DAB259F8F7B00E9CE7B /* main.swift in Sources */, 123 | ); 124 | runOnlyForDeploymentPostprocessing = 0; 125 | }; 126 | /* End PBXSourcesBuildPhase section */ 127 | 128 | /* Begin XCBuildConfiguration section */ 129 | 01872DAC259F8F7B00E9CE7B /* Debug */ = { 130 | isa = XCBuildConfiguration; 131 | buildSettings = { 132 | ALWAYS_SEARCH_USER_PATHS = NO; 133 | CLANG_ANALYZER_NONNULL = YES; 134 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 135 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 136 | CLANG_CXX_LIBRARY = "libc++"; 137 | CLANG_ENABLE_MODULES = YES; 138 | CLANG_ENABLE_OBJC_ARC = YES; 139 | CLANG_ENABLE_OBJC_WEAK = YES; 140 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 141 | CLANG_WARN_BOOL_CONVERSION = YES; 142 | CLANG_WARN_COMMA = YES; 143 | CLANG_WARN_CONSTANT_CONVERSION = YES; 144 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 145 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 146 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 147 | CLANG_WARN_EMPTY_BODY = YES; 148 | CLANG_WARN_ENUM_CONVERSION = YES; 149 | CLANG_WARN_INFINITE_RECURSION = YES; 150 | CLANG_WARN_INT_CONVERSION = YES; 151 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 152 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 153 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 154 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 155 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 156 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 157 | CLANG_WARN_STRICT_PROTOTYPES = YES; 158 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 159 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 160 | CLANG_WARN_UNREACHABLE_CODE = YES; 161 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 162 | COPY_PHASE_STRIP = NO; 163 | DEBUG_INFORMATION_FORMAT = dwarf; 164 | ENABLE_STRICT_OBJC_MSGSEND = YES; 165 | ENABLE_TESTABILITY = YES; 166 | GCC_C_LANGUAGE_STANDARD = gnu11; 167 | GCC_DYNAMIC_NO_PIC = NO; 168 | GCC_NO_COMMON_BLOCKS = YES; 169 | GCC_OPTIMIZATION_LEVEL = 0; 170 | GCC_PREPROCESSOR_DEFINITIONS = ( 171 | "DEBUG=1", 172 | "$(inherited)", 173 | ); 174 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 175 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 176 | GCC_WARN_UNDECLARED_SELECTOR = YES; 177 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 178 | GCC_WARN_UNUSED_FUNCTION = YES; 179 | GCC_WARN_UNUSED_VARIABLE = YES; 180 | MACOSX_DEPLOYMENT_TARGET = 11.1; 181 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 182 | MTL_FAST_MATH = YES; 183 | ONLY_ACTIVE_ARCH = YES; 184 | SDKROOT = macosx; 185 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 186 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 187 | }; 188 | name = Debug; 189 | }; 190 | 01872DAD259F8F7B00E9CE7B /* Release */ = { 191 | isa = XCBuildConfiguration; 192 | buildSettings = { 193 | ALWAYS_SEARCH_USER_PATHS = NO; 194 | CLANG_ANALYZER_NONNULL = YES; 195 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 196 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 197 | CLANG_CXX_LIBRARY = "libc++"; 198 | CLANG_ENABLE_MODULES = YES; 199 | CLANG_ENABLE_OBJC_ARC = YES; 200 | CLANG_ENABLE_OBJC_WEAK = YES; 201 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 202 | CLANG_WARN_BOOL_CONVERSION = YES; 203 | CLANG_WARN_COMMA = YES; 204 | CLANG_WARN_CONSTANT_CONVERSION = YES; 205 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 206 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 207 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 208 | CLANG_WARN_EMPTY_BODY = YES; 209 | CLANG_WARN_ENUM_CONVERSION = YES; 210 | CLANG_WARN_INFINITE_RECURSION = YES; 211 | CLANG_WARN_INT_CONVERSION = YES; 212 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 213 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 214 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 216 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 217 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 218 | CLANG_WARN_STRICT_PROTOTYPES = YES; 219 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 220 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 221 | CLANG_WARN_UNREACHABLE_CODE = YES; 222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 223 | COPY_PHASE_STRIP = NO; 224 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 225 | ENABLE_NS_ASSERTIONS = NO; 226 | ENABLE_STRICT_OBJC_MSGSEND = YES; 227 | GCC_C_LANGUAGE_STANDARD = gnu11; 228 | GCC_NO_COMMON_BLOCKS = YES; 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | MACOSX_DEPLOYMENT_TARGET = 11.1; 236 | MTL_ENABLE_DEBUG_INFO = NO; 237 | MTL_FAST_MATH = YES; 238 | SDKROOT = macosx; 239 | SWIFT_COMPILATION_MODE = wholemodule; 240 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 241 | }; 242 | name = Release; 243 | }; 244 | 01872DAF259F8F7B00E9CE7B /* Debug */ = { 245 | isa = XCBuildConfiguration; 246 | buildSettings = { 247 | CODE_SIGN_STYLE = Automatic; 248 | DEVELOPMENT_TEAM = CBY22P58G8; 249 | ENABLE_HARDENED_RUNTIME = YES; 250 | PRODUCT_NAME = "$(TARGET_NAME)"; 251 | SWIFT_VERSION = 5.0; 252 | }; 253 | name = Debug; 254 | }; 255 | 01872DB0259F8F7B00E9CE7B /* Release */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | CODE_SIGN_STYLE = Automatic; 259 | DEVELOPMENT_TEAM = CBY22P58G8; 260 | ENABLE_HARDENED_RUNTIME = YES; 261 | PRODUCT_NAME = "$(TARGET_NAME)"; 262 | SWIFT_VERSION = 5.0; 263 | }; 264 | name = Release; 265 | }; 266 | /* End XCBuildConfiguration section */ 267 | 268 | /* Begin XCConfigurationList section */ 269 | 01872DA2259F8F7B00E9CE7B /* Build configuration list for PBXProject "First R" */ = { 270 | isa = XCConfigurationList; 271 | buildConfigurations = ( 272 | 01872DAC259F8F7B00E9CE7B /* Debug */, 273 | 01872DAD259F8F7B00E9CE7B /* Release */, 274 | ); 275 | defaultConfigurationIsVisible = 0; 276 | defaultConfigurationName = Release; 277 | }; 278 | 01872DAE259F8F7B00E9CE7B /* Build configuration list for PBXNativeTarget "First R" */ = { 279 | isa = XCConfigurationList; 280 | buildConfigurations = ( 281 | 01872DAF259F8F7B00E9CE7B /* Debug */, 282 | 01872DB0259F8F7B00E9CE7B /* Release */, 283 | ); 284 | defaultConfigurationIsVisible = 0; 285 | defaultConfigurationName = Release; 286 | }; 287 | /* End XCConfigurationList section */ 288 | }; 289 | rootObject = 01872D9F259F8F7B00E9CE7B /* Project object */; 290 | } 291 | -------------------------------------------------------------------------------- /First R/First R.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /First R/First R.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /First R/First R.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /First R/First R/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | print("Hello, World!") 4 | 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | 204 | 205 | ## Runtime Library Exception to the Apache 2.0 License: ## 206 | 207 | 208 | As an exception, if you use this Software to compile your source code and 209 | portions of this Software are embedded into the binary product as a result, 210 | you may redistribute such product without providing attribution as would 211 | otherwise be required by Sections 4(a), 4(b) and 4(d) of the License. 212 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a repo of files that go along with SwiftR — Using R from Swift 2 | 3 | https://rud.is/books/swiftr/ 4 | -------------------------------------------------------------------------------- /SwiftDensity/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/swift,xcode,macos 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=swift,xcode,macos 4 | 5 | ### macOS ### 6 | # General 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | ### Swift ### 34 | # Xcode 35 | # 36 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 37 | 38 | ## User settings 39 | xcuserdata/ 40 | 41 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 42 | *.xcscmblueprint 43 | *.xccheckout 44 | 45 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 46 | build/ 47 | DerivedData/ 48 | *.moved-aside 49 | *.pbxuser 50 | !default.pbxuser 51 | *.mode1v3 52 | !default.mode1v3 53 | *.mode2v3 54 | !default.mode2v3 55 | *.perspectivev3 56 | !default.perspectivev3 57 | 58 | ## Obj-C/Swift specific 59 | *.hmap 60 | 61 | ## App packaging 62 | *.ipa 63 | *.dSYM.zip 64 | *.dSYM 65 | 66 | ## Playgrounds 67 | timeline.xctimeline 68 | playground.xcworkspace 69 | 70 | # Swift Package Manager 71 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 72 | # Packages/ 73 | # Package.pins 74 | # Package.resolved 75 | # *.xcodeproj 76 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 77 | # hence it is not needed unless you have added a package configuration file to your project 78 | # .swiftpm 79 | 80 | .build/ 81 | 82 | # CocoaPods 83 | # We recommend against adding the Pods directory to your .gitignore. However 84 | # you should judge for yourself, the pros and cons are mentioned at: 85 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 86 | # Pods/ 87 | # Add this line if you want to avoid checking in source code from the Xcode workspace 88 | # *.xcworkspace 89 | 90 | # Carthage 91 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 92 | # Carthage/Checkouts 93 | 94 | Carthage/Build/ 95 | 96 | # Accio dependency management 97 | Dependencies/ 98 | .accio/ 99 | 100 | # fastlane 101 | # It is recommended to not store the screenshots in the git repo. 102 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 103 | # For more information about the recommended setup visit: 104 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 105 | 106 | fastlane/report.xml 107 | fastlane/Preview.html 108 | fastlane/screenshots/**/*.png 109 | fastlane/test_output 110 | 111 | # Code Injection 112 | # After new code Injection tools there's a generated folder /iOSInjectionProject 113 | # https://github.com/johnno1962/injectionforxcode 114 | 115 | iOSInjectionProject/ 116 | 117 | ### Xcode ### 118 | # Xcode 119 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 120 | 121 | 122 | 123 | 124 | ## Gcc Patch 125 | /*.gcno 126 | 127 | ### Xcode Patch ### 128 | *.xcodeproj/* 129 | !*.xcodeproj/project.pbxproj 130 | !*.xcodeproj/xcshareddata/ 131 | !*.xcworkspace/contents.xcworkspacedata 132 | **/xcshareddata/WorkspaceSettings.xcsettings 133 | 134 | # End of https://www.toptal.com/developers/gitignore/api/swift,xcode,macos 135 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/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 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "scale" : "1x", 6 | "filename" : "icon_16x16.png", 7 | "idiom" : "mac" 8 | }, 9 | { 10 | "scale" : "2x", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "icon_32x32.png", 17 | "scale" : "1x", 18 | "size" : "32x32", 19 | "idiom" : "mac" 20 | }, 21 | { 22 | "scale" : "2x", 23 | "size" : "32x32", 24 | "filename" : "icon_32x32@2x.png", 25 | "idiom" : "mac" 26 | }, 27 | { 28 | "filename" : "icon_128x128.png", 29 | "size" : "128x128", 30 | "scale" : "1x", 31 | "idiom" : "mac" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "2x", 36 | "size" : "128x128", 37 | "filename" : "icon_128x128@2x.png" 38 | }, 39 | { 40 | "filename" : "icon_256x256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "filename" : "icon_256x256@2x.png", 48 | "scale" : "2x", 49 | "idiom" : "mac" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "filename" : "icon_512x512.png", 54 | "scale" : "1x", 55 | "idiom" : "mac" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "filename" : "icon_512x512@2x.png", 60 | "scale" : "2x", 61 | "idiom" : "mac" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrbrmstr/swiftr-book-examples/927dd18ab9c7daa441b52962e3a84353c49cceb4/SwiftDensity/SwiftDensity/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Assets.xcassets/codeError.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "platform" : "osx", 6 | "reference" : "systemRedColor" 7 | }, 8 | "idiom" : "universal" 9 | }, 10 | { 11 | "appearances" : [ 12 | { 13 | "appearance" : "luminosity", 14 | "value" : "dark" 15 | } 16 | ], 17 | "color" : { 18 | "platform" : "osx", 19 | "reference" : "systemYellowColor" 20 | }, 21 | "idiom" : "universal" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | 5 | @ObservedObject var densityApp = DensityModel() 6 | 7 | var body: some View { 8 | 9 | VStack { 10 | 11 | HStack { 12 | 13 | // MARK: Kernel radio buttons 14 | 15 | VStack { 16 | Text(LocalizationStrings.DENSITY_ESTIMATION_KERNEL) 17 | Picker(selection: $densityApp.kernel, label: Text("")) { 18 | ForEach(densityApp.availableKernels, id: \.self) { kernel in 19 | Text(kernel) 20 | } 21 | } 22 | .pickerStyle(RadioGroupPickerStyle()) 23 | .onChange(of: densityApp.kernel, perform: { value in 24 | densityApp.updatePlot() 25 | }) 26 | } 27 | .help(LocalizationStrings.KERNEL_HELP) 28 | .padding() 29 | 30 | // MARK: Kernel bandwidth slider 31 | 32 | VStack { 33 | Group { 34 | Text("\(LocalizationStrings.DENSITY_ESTIMATION_BANDWIDTH): \(String(densityApp.bandwidth.roundToDecimal(AppGlobals.BANDWIDTH_DISPLAY_DECIMALS)))") 35 | Slider( 36 | value: $densityApp.bandwidth, 37 | in: AppGlobals.BANDWIDTH_RANGE, 38 | onEditingChanged: { editing in 39 | if (!editing) { densityApp.updatePlot() } 40 | } 41 | ) 42 | .padding() 43 | } 44 | .help(LocalizationStrings.BANDWIDTH_HELP) 45 | 46 | Spacer() 47 | 48 | // MARK: R data generation script 49 | 50 | VStack { 51 | Text(LocalizationStrings.R_CMD_LABEL) 52 | Group { 53 | UndoProvider($densityApp.rCmd) { rCmd in 54 | TextField( 55 | "", 56 | text: rCmd, 57 | onEditingChanged: { tap in 58 | if (!tap) { densityApp.validateRCmd() } 59 | }, 60 | onCommit: { 61 | densityApp.validateRCmd() 62 | if (densityApp.rCmdOK) { densityApp.updatePlot() } 63 | } 64 | ) 65 | .font(.system(size: 12, design: .monospaced)) 66 | .disableAutocorrection(true) 67 | .lineLimit(3) 68 | .padding(4) 69 | } 70 | } .border((densityApp.rCmdOK) ? Color.codeOK : Color.codeError) 71 | } 72 | .help(LocalizationStrings.RCMD_HELP) 73 | 74 | }.padding() 75 | 76 | } 77 | 78 | Divider() 79 | 80 | HStack { // MARK: Plot pane 81 | Image(nsImage: NSImage(data: densityApp.plotData) ?? NSImage()) 82 | .resizable() 83 | .aspectRatio(contentMode: .fit) 84 | .frame( 85 | width: AppGlobals.PLOT_WIDTH, 86 | height: AppGlobals.PLOT_HEIGHT, 87 | alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/ 88 | ) 89 | .help(LocalizationStrings.PLOT_HELP) 90 | .focusable() 91 | .contextMenu(ContextMenu(menuItems: { 92 | Button(action: { 93 | NSPasteboard.general.clearContents() 94 | NSPasteboard.general.writeObjects([ (NSImage(data: densityApp.plotData) ?? NSImage()) ]) 95 | }) { 96 | Text("Copy") 97 | Image(systemName: "doc.on.doc") 98 | } 99 | })) 100 | .onCopyCommand(perform: { 101 | let item = [ 102 | NSItemProvider( 103 | item: (NSImage(data: densityApp.plotData) ?? NSImage()).tiffRepresentation as NSSecureCoding?, 104 | typeIdentifier: kUTTypeTIFF as String 105 | ) 106 | ] 107 | return(item) 108 | }) 109 | .border(Color.primary) 110 | } 111 | 112 | }.padding() 113 | 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/HelperViews.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | 4 | struct InstallPackagesView: View { 5 | var packages: [String] 6 | var body: some View { 7 | VStack { 8 | Text("\(LocalizationStrings.MISSING_PACKAGES_ERROR):").padding() 9 | ForEach (packages, id: \.self) { Text($0) } .padding() 10 | } 11 | .padding() 12 | } 13 | } 14 | 15 | struct EmbeddedRErrorView: View { 16 | var body: some View { 17 | VStack { 18 | Text(LocalizationStrings.R_INITIALIZATION_FAILED).padding() 19 | } 20 | .padding() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSApplicationCategoryType 22 | public.app-category.education 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | 26 | 27 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/SwiftDensity-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #include "swift-r-bridge.h" 6 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/SwiftDensity.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.disable-library-validation 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/SwiftDensityApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct SwiftDensityApp: App { 5 | 6 | @ObservedObject var packagesModel: PackagesModel = PackagesModel() 7 | 8 | var body: some Scene { 9 | 10 | WindowGroup { 11 | 12 | if (!packagesModel.embeddedROK) { // MARK: R startup was 😭 13 | 14 | EmbeddedRErrorView() 15 | .navigationTitle(LocalizationStrings.INIT_FAILURE) 16 | .fixedSize() 17 | 18 | } else if (packagesModel.missingPackages.count > 0) { // MARK: We're missing some 📦 19 | 20 | InstallPackagesView(packages: packagesModel.missingPackages) 21 | .navigationTitle(LocalizationStrings.INSTALL_PKGS) 22 | .fixedSize() 23 | .onReceive(NotificationCenter.default.publisher(for: NSApplication.willTerminateNotification)) { _ in 24 | Rf_endEmbeddedR(0) 25 | } 26 | 27 | } else { // MARK: All is 👍🏽 28 | 29 | ContentView() 30 | .navigationTitle(LocalizationStrings.MAIN_TITLE) 31 | .fixedSize() 32 | .onReceive(NotificationCenter.default.publisher(for: NSApplication.willTerminateNotification)) { _ in 33 | Rf_endEmbeddedR(0) 34 | } 35 | 36 | } 37 | } 38 | .commands { 39 | CommandGroup(replacing: CommandGroupPlacement.newItem) { } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "KERNEL-HELP" = "Smoothing kernel to be used. Run `help('density')` in an interactive R session for more information."; 2 | "BANDWIDTH-HELP" = "The smoothing bandwidth to be used. The kernels are scaled such that this is the standard deviation of the smoothing kernel."; 3 | "RCMD-HELP" = "R code that will be used to generate a length>2 numeric vector for the kernel density computation. Hit or / will cause a re-evaluation of the code. If there are errors parsing or evaluating the expression the input box will be hilighted."; 4 | "PLOT-HELP" = "The line is the computed density and the dots are the values generated by evaluating the R code above the plot."; 5 | "DENSITY-SCRIPT-MISSING" = "The R script that computes the density and products the plot cannot be found in the application bundle. Please reinstall the application."; 6 | "DENSITY-SCRIPT-MODIFIED" = "Density R script has been modified. Please reinstall the application."; 7 | 8 | "DENSITY-ESTIMATION-KERNEL" = "Density Estimation kernel"; 9 | "DENSITY-ESTIMATION-BANDWIDTH" = "Density estimation bandwidth"; 10 | "R-CMD-LABEL" = "R Command for random data creation"; 11 | 12 | "MISSING-PACKAGES-ERROR" = "Please install the following packages and restart the application"; 13 | "R-INITIALIZATION-FAILED" = "Initialization of embedded R failed."; 14 | 15 | "INIT-FAILURE" = "Initialization Failure"; 16 | "INSTALL-PKGS" = "Please Install Missing Packages"; 17 | "MAIN-TITLE" = "Swift & SwiftR Demo: Density Estimation"; 18 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/globals.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class AppGlobals { 4 | 5 | static let PLOT_WIDTH: CGFloat = 600 6 | static let PLOT_HEIGHT: CGFloat = 350 7 | 8 | static let MIN_BANDWIDTH: Double = 0.05 9 | static let MAX_BANDWIDTH: Double = 2 10 | static let BANDWIDTH_RANGE: ClosedRange = MIN_BANDWIDTH...MAX_BANDWIDTH // Range for our bandwidth slider 11 | static let DEFAULT_BANDWIDTH: Double = 1 12 | static let BANDWIDTH_DISPLAY_DECIMALS: Int = 4 13 | 14 | static let DENSITY_SCRIPT_SHA2565: String = "SHA256 digest: 161ba060f6c40c2bb70d9e02e3389d26ae8c47d0ad1a5e3985a46ae97c4a409b" 15 | static let DEFAULT_R_CMD: String = "c(rnorm(100, 0, 1), rnorm(50, 5, 1))\n\n\n" 16 | 17 | } 18 | 19 | class RGlobals { 20 | static let plot_width:String = "plot_width" 21 | static let plot_height:String = "plot_height" 22 | static let bw_digits:String = "bw_digits" 23 | static let x:String = "x" 24 | static let bw:String = "bw" 25 | static let kernel:String = "kernel" 26 | } 27 | 28 | class LocalizationStrings { 29 | // help 30 | static let DENSITY_SCRIPT_MISSING: String = "DENSITY-SCRIPT-MISSING".localized 31 | static let DENSITY_SCRIPT_MODIFIED: String = "DENSITY-SCRIPT-MODIFIED".localized 32 | static let BANDWIDTH_HELP: String = "BANDWIDTH-HELP".localized 33 | static let KERNEL_HELP: String = "KERNEL-HELP".localized 34 | static let RCMD_HELP: String = "RCMD-HELP".localized 35 | static let PLOT_HELP: String = "PLOT-HELP".localized 36 | // labels 37 | static let DENSITY_ESTIMATION_KERNEL: String = "DENSITY-ESTIMATION-KERNEL".localized 38 | static let DENSITY_ESTIMATION_BANDWIDTH: String = "DENSITY-ESTIMATION-BANDWIDTH".localized 39 | static let R_CMD_LABEL: String = "R-CMD-LABEL".localized 40 | // error 41 | static let MISSING_PACKAGES_ERROR: String = "MISSING-PACKAGES-ERROR".localized 42 | static let R_INITIALIZATION_FAILED: String = "R-INITIALIZATION-FAILED".localized 43 | // window titles 44 | static let INIT_FAILURE: String = "INIT-FAILURE".localized 45 | static let INSTALL_PKGS: String = "INSTALL-PKGS".localized 46 | static let MAIN_TITLE: String = "MAIN-TITLE".localized 47 | } 48 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/model.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class DensityModel : ObservableObject { 4 | 5 | @Published var plotData: Data = Data() 6 | @Published var kernel = "gaussian" 7 | @Published var bandwidth = 100.0 8 | @Published var rCmd = "c(rnorm(100, 0, 1), rnorm(50, 5, 1))" 9 | 10 | init() { 11 | 12 | initEmbeddedR() 13 | 14 | let _ = safeSilentEvalParseString(""" 15 | suppressPackageStartupMessages({ 16 | library(ggplot2) 17 | library(magick) 18 | library(hrbrthemes) 19 | }) 20 | """, showMessage: false) 21 | 22 | updatePlot() 23 | 24 | } 25 | 26 | func updatePlot() { 27 | 28 | if (parseStatus(rCmd) != PARSE_OK) { return() } 29 | 30 | debugPrint("Plotting") 31 | 32 | let res = safeSilentEvalParseString(""" 33 | suppressMessages(suppressWarnings(tryCatch({ 34 | 35 | bw <- \(bandwidth) 36 | kernel <- "\(kernel)" 37 | x <- \(rCmd) 38 | 39 | ggplot() + 40 | stat_density( 41 | aes(x), 42 | bw = bw/100, 43 | kernel = kernel, 44 | geom = "line" 45 | ) + 46 | geom_point( 47 | aes(x, rep(0, length(x))), alpha = 1/4 48 | ) + 49 | scale_x_continuous(limits = range(x) + c(-2, 2)) + 50 | labs( 51 | title = sprintf("Kernel: %s", kernel), 52 | x = sprintf("N = %s • Bandwidth = %s", length(x), bw/100), 53 | y = "Density" 54 | ) + 55 | theme_ipsum_gs(grid="XY") -> gg 56 | 57 | image_graph( 58 | width = 480*2, 59 | height = 240*2, 60 | pointsize = 12, 61 | res = 144 62 | ) -> plt 63 | 64 | print(gg) 65 | 66 | dev.off() 67 | 68 | image_write(plt, path = NULL, format = "jpeg") 69 | 70 | }))) 71 | """, showMessage: true) 72 | 73 | // if everything is OK, change up plot data, otherwise keep current plot data 74 | 75 | if (res.count >= 0) { 76 | if (res.last!.isRAW) { 77 | plotData = Data(res.last!)! // there will be ~8 evaluated expressions 78 | } 79 | } 80 | 81 | R_gc() 82 | 83 | } 84 | 85 | deinit { 86 | Rf_endEmbeddedR(0) 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/models.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CryptoKit 3 | 4 | // TODO: Unify the models 5 | 6 | // MARK: Model for view that handles missing packages 7 | class PackagesModel: ObservableObject{ 8 | 9 | @Published var missingPackages: [String] = [] 10 | @Published var embeddedROK: Bool 11 | 12 | init() { 13 | 14 | let res = initEmbeddedR("/Library/Frameworks/R.framework/Resources") 15 | 16 | embeddedROK = (res == 1) // unfortunately this is always the case 17 | 18 | if (embeddedROK) { 19 | do { 20 | 21 | let ggplotAvailable = try require("ggplot2") 22 | if (ggplotAvailable) { 23 | let hrbrthemesAvailable = try require("hrbrthemes") 24 | if (!hrbrthemesAvailable) { missingPackages.append("hrbrthemes") } 25 | } else { 26 | if (!ggplotAvailable) { missingPackages.append("ggplot2") } 27 | } 28 | 29 | let magickAvailable = try require("magick") 30 | if (!magickAvailable) { missingPackages.append("magick") } 31 | 32 | } catch { 33 | // Something rly went wrong if we got here so we eventually need to handle this 34 | } 35 | } 36 | 37 | } 38 | 39 | } 40 | 41 | // MARK: Model for the Density viewer application 42 | class DensityModel : ObservableObject { 43 | 44 | @Published var plotData: Data = Data() 45 | @Published var kernel: String = "" 46 | @Published var availableKernels: [String] = [String]() 47 | @Published var bandwidth = AppGlobals.DEFAULT_BANDWIDTH 48 | @Published var rCmd = AppGlobals.DEFAULT_R_CMD 49 | @Published var rCmdOK: Bool = true 50 | 51 | private var parsedRScript: SEXP = R_NilValue 52 | 53 | init() { 54 | 55 | // do the initial eval on the default R values generation code 56 | let res: [SEXP] = safeSilentEvalParseString("eval(formals(density.default)$kernel)") 57 | 58 | if (res.last != R_NilValue) { // R is working 59 | 60 | defer { res.UNPROTECT() } 61 | 62 | // get the kernel strings 63 | let kernels = [String](res.last!)! 64 | 65 | // make them available to swift and the content view 66 | self.availableKernels = kernels.map { $0.capitalizingFirstLetter() } 67 | self.kernel = self.availableKernels.first! 68 | 69 | // define some R globals (that are more 'constant-y') 70 | defineGlobal(RGlobals.plot_width, Double(AppGlobals.PLOT_WIDTH).SEXP) 71 | defineGlobal(RGlobals.plot_height, Double(AppGlobals.PLOT_HEIGHT).SEXP) 72 | defineGlobal(RGlobals.bw_digits, 4.SEXP) 73 | 74 | validateRCmd() // generate the initial data and assign it (technically we should set the seed too) 75 | 76 | // load the R script from the bundle 77 | guard let densityScriptPath = Bundle.main.path(forResource: "density", ofType: "rstats") else { 78 | fatalError(LocalizationStrings.DENSITY_SCRIPT_MISSING) 79 | } 80 | 81 | // read it in 82 | // try! is a little dangerous but it should be safe here 83 | let densityScript = try! String(contentsOfFile: densityScriptPath) 84 | 85 | // this is far from a perfect way to ensure the script has not been modified 86 | // (go to a terminal, find the SwiftDensity binary and do 87 | // $ strings | grep 43fff23f4d5d86a62d743a96c477b78a8239ab8fc8dc7df2482447fc1c49ec01 88 | // if you don't believe me. 89 | // dealing with resource protection in a more thorough way is beyond the scope of this exercise. 90 | if (SHA256.hash(data: densityScript.data(using: .utf8)!).description != AppGlobals.DENSITY_SCRIPT_SHA2565) { 91 | fatalError(LocalizationStrings.DENSITY_SCRIPT_MODIFIED) 92 | } 93 | 94 | // parse the R script only once 95 | parsedRScript = parseRScript(densityScript).protectedSEXP // need to ensure we RUNPROTECT this 96 | 97 | } 98 | 99 | updatePlot() // we want to start with a plot 100 | 101 | } 102 | 103 | /// Called to when rCmd is updated to determine if it is parse-able R code 104 | /// 105 | /// 106 | /// - Does the input R expressions parse? 107 | /// - Does it eval OK? 108 | /// - Does it have a compatible type? 109 | /// - Is the length of the vector >= 2? 110 | /// 111 | /// There is a side-effect of the R global "x" being populated with the evaluated, parsed statement.s 112 | func validateRCmd() { 113 | let res = safeSilentEvalParseString(rCmd, showMessage: true) 114 | defer { res.UNPROTECT() } 115 | rCmdOK = (res.first != R_NilValue) && ((res.first!.isREAL) || (res.first!.isINTEGER)) && (res.count >= 1) 116 | if (rCmdOK) { 117 | defineGlobal("x", res.first!) 118 | } 119 | } 120 | 121 | /// Called when the plot panel should be updated 122 | func updatePlot() { 123 | 124 | defineGlobal(RGlobals.bw, bandwidth.SEXP) // update bandwidth 125 | defineGlobal(RGlobals.kernel, kernel.lowercased().SEXP) // update kernel 126 | 127 | // at least we're not parsing it each time 128 | let res: [SEXP] = evalSEXP(parsedRScript) 129 | defer { res.UNPROTECT() } 130 | 131 | // if everything is OK, change up plot data, otherwise keep current plot data 132 | 133 | if (res.count >= 0) { 134 | if (res.last!.isRAW) { 135 | plotData = Data(res.last!)! // there will be ~8 evaluated expressions 136 | } 137 | } 138 | 139 | R_gc() 140 | 141 | } 142 | 143 | deinit { 144 | RUNPROTECT(1) // parsedRScript from init() 145 | Rf_endEmbeddedR(0) 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/r-scripts/density.rstats: -------------------------------------------------------------------------------- 1 | suppressMessages(suppressWarnings(tryCatch({ 2 | 3 | set.seed(Sys.Date()) 4 | 5 | # make the plot 6 | 7 | ggplot() + 8 | stat_density( 9 | aes(x), 10 | bw = bw, 11 | kernel = kernel, 12 | geom = "line" 13 | ) + 14 | geom_point( 15 | aes(x, rep(0, length(x))), alpha = 1/4 16 | ) + 17 | scale_x_continuous(limits = range(x) + c(-2, 2)) + 18 | labs( 19 | title = sprintf("Kernel: %s", kernel), 20 | x = sprintf("N = %s • Bandwidth = %s", length(x), round(bw, digits = bw_digits)), 21 | y = "Density" 22 | ) + 23 | theme_ipsum_gs(grid="XY") + 24 | theme(plot.margin = margin(6, 6, 6, 6, unit = "pt")) -> gg 25 | 26 | # start some magick 27 | 28 | image_graph( 29 | width = plot_width * 2, 30 | height = plot_height * 2, 31 | pointsize = 12, 32 | res = 144 33 | ) -> plt 34 | 35 | # incant it 36 | 37 | print(gg) 38 | dev.off() 39 | image_write(plt, path = NULL, format = "jpeg") # no file round trip 40 | 41 | }))) 42 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/r-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Rf_protect wrapper since the PROTECT macro is unusable from Swift 4 | /// 5 | /// Defined in case muscle memory forgets to use RPROTECT from the C bridge 6 | let PROTECT = Rf_protect 7 | 8 | /// Rf_unprotect wrapper since the UNPROTECT macro is unusable from Swift 9 | /// 10 | /// Defined in case muscle memory forgets to use RUNPROTECT from the C bridge 11 | let UNPROTECT = Rf_unprotect 12 | 13 | /// R-related exceptions 14 | enum RError: Error { 15 | case tryEvalError(String) // we only have one R error exception type for the moment. 16 | } 17 | 18 | // MARK: Swift helper to varName <- value 19 | 20 | /// Define a global object within R from the passed in SEXP 21 | /// 22 | /// - Parameters: 23 | /// - varName: name of the object 24 | /// - value: SEXP value to assign the object 25 | func defineGlobal(_ varName: String, _ value: SEXP) { 26 | Rf_defineVar(Rf_install(varName), value, R_GlobalEnv) 27 | } 28 | 29 | // MARK: Swift helper to require(package) 30 | 31 | /// Equivalent of R's library(pkg) 32 | /// 33 | /// - Parameter pkg: R package to load 34 | /// - Throws: An `RError.tryEvalError` 35 | func require(_ pkg: String) throws -> Bool { 36 | 37 | var rErr: CInt = 0 // holds our error return value from evaluation 38 | 39 | let requireCall: SEXP = RPROTECT(Rf_allocVector(SEXPTYPE(LANGSXP), 3+1))! 40 | defer { RUNPROTECT(1) } 41 | SETCAR(requireCall, Rf_install("require")) 42 | 43 | var reqParams: SEXP = CDR(requireCall); 44 | SETCAR(reqParams, RPROTECT(Rf_mkString(pkg))) 45 | defer { RUNPROTECT(1) } 46 | SET_TAG(reqParams, Rf_install("package")) 47 | 48 | reqParams = CDR(reqParams) 49 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 50 | defer { RUNPROTECT(1) } 51 | SET_TAG(reqParams, Rf_install("warn.conflicts")) 52 | 53 | reqParams = CDR(reqParams) 54 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 55 | defer { RUNPROTECT(1) } 56 | SET_TAG(reqParams, Rf_install("quietly")) 57 | 58 | // requireCall is PROTECTed above but Rf_install() may allocate which could kill it so make sure it is safe 59 | let suppressWarningsCall = RPROTECT(Rf_lang2(Rf_install("suppressWarnings"), requireCall)) 60 | defer { RUNPROTECT(1) } 61 | 62 | let suppressPackageStartupMessagesCall = RPROTECT(Rf_lang2(Rf_install("suppressPackageStartupMessages"), suppressWarningsCall)) 63 | defer { RUNPROTECT(1) } 64 | 65 | let res: SEXP = RPROTECT(R_tryEvalSilent(suppressPackageStartupMessagesCall, R_GlobalEnv, &rErr))! 66 | defer { RUNPROTECT(1) } 67 | 68 | if (rErr == 0) { 69 | if (SEXPTYPE(TYPEOF(res)) == LGLSXP) { 70 | return(Rf_asLogical(res) == 1) 71 | } else { 72 | throw RError.tryEvalError( 73 | "R evaluation error attempting to load. Expected TRUE/FALSE, but found '\(String(cString: Rf_type2char(SEXPTYPE(TYPEOF(res)))))" 74 | ) 75 | } 76 | } else { 77 | throw RError.tryEvalError("R evaluation error attempting to load \(pkg): \(String(cString: R_curErrorBuf()))") 78 | } 79 | 80 | } 81 | 82 | // MARK: Parse a string of R code and return the parse status 83 | 84 | /// Parse a string of R code and return the parse status 85 | /// 86 | /// - Parameters 87 | /// - string: The R code to parse 88 | /// - Returns: ParseStatus enum value 89 | func parseStatus(_ string: String) -> ParseStatus { 90 | 91 | let vectorToParse = [ string ].protectedSEXP 92 | defer { RUNPROTECT(1) } 93 | 94 | var status: ParseStatus = PARSE_OK 95 | let _ = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 96 | RUNPROTECT(1) 97 | 98 | return(status) 99 | 100 | } 101 | 102 | /// Parse an R script/code that is stored in a string 103 | /// 104 | /// - Parameters 105 | /// - string: The R code to parse 106 | /// 107 | /// The returned SEXP will NOT be protected 108 | func parseRScript(_ string: String) -> SEXP { 109 | 110 | let vectorToParse = [ string ].protectedSEXP 111 | defer { RUNPROTECT(1) } 112 | 113 | var status: ParseStatus = PARSE_OK 114 | let res = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 115 | RUNPROTECT(1) 116 | 117 | return((status == PARSE_OK) ? res! : R_NilValue) 118 | 119 | } 120 | 121 | /// Evaluate parsed R code 122 | /// 123 | /// - Parameters 124 | /// - parsedSEXP: The parsed R code to evaluate 125 | /// 126 | /// Each SEXP element of the returned Swift array of SEXPs WILL BE protected. 127 | /// 128 | /// - Important: The caller will be expected to call the SEXP Array `UNPROTECT()` method. 129 | func evalSEXP(_ parsedSEXP: SEXP) -> [SEXP] { 130 | 131 | var rErr: CInt = 0 132 | var res: [SEXP] = [SEXP]() 133 | 134 | res.reserveCapacity(Int(Rf_length(parsedSEXP))) 135 | 136 | for idx in 0.. [SEXP] { 162 | 163 | let vectorToParse = vec.protectedSEXP 164 | defer { RUNPROTECT(1)} 165 | var status: ParseStatus = PARSE_OK 166 | let parsed = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 167 | defer { RUNPROTECT(1)} 168 | 169 | if (status == PARSE_OK) { 170 | var rErr: CInt = 0 171 | var res: [SEXP] = [SEXP]() 172 | res.reserveCapacity(Int(Rf_length(parsed))) 173 | for idx in 0.. [SEXP] { 201 | 202 | let vectorToParse = [ string ].protectedSEXP 203 | defer { RUNPROTECT(1) } 204 | var status: ParseStatus = PARSE_OK 205 | let parsed = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 206 | defer { RUNPROTECT(1) } 207 | 208 | if (status == PARSE_OK) { 209 | var rErr: CInt = 0 210 | var res: [SEXP] = [SEXP]() 211 | res.reserveCapacity(Int(Rf_length(parsed))) 212 | for idx in 0.. SEXP { 240 | 241 | var err: CInt = 0 242 | 243 | let call_ƒ = RPROTECT(Rf_lang1(call.protectedSEXP)) 244 | defer { RUNPROTECT(2) } 245 | 246 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 247 | defer { RUNPROTECT(1) } 248 | 249 | if (err != 0) { 250 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 251 | } 252 | 253 | return(res) 254 | 255 | } 256 | 257 | /// - SeeAlso: Rlang1 258 | func Rlang2(_ call: SEXP, x: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 259 | 260 | var err: CInt = 0 261 | 262 | let call_ƒ = RPROTECT( 263 | Rf_lang2( 264 | call.protectedSEXP, 265 | x.protectedSEXP 266 | ) 267 | ) 268 | defer { RUNPROTECT(3) } 269 | 270 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 271 | defer { RUNPROTECT(1) } 272 | 273 | if (err != 0) { 274 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 275 | } 276 | 277 | return(res) 278 | 279 | } 280 | 281 | /// - SeeAlso: Rlang1 282 | func Rlang3(_ call: SEXP, x: SEXP, y: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 283 | 284 | var err: CInt = 0 285 | 286 | let call_ƒ = RPROTECT( 287 | Rf_lang3( 288 | call.protectedSEXP, 289 | x.protectedSEXP, 290 | y.protectedSEXP 291 | ) 292 | ) 293 | defer { RUNPROTECT(4) } 294 | 295 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 296 | defer { RUNPROTECT(1) } 297 | 298 | if (err != 0) { 299 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 300 | } 301 | 302 | return(res) 303 | 304 | } 305 | 306 | /// - SeeAlso: Rlang1 307 | func Rlang4(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 308 | 309 | var err: CInt = 0 310 | 311 | let call_ƒ = RPROTECT( 312 | Rf_lang4( 313 | call.protectedSEXP, 314 | x.protectedSEXP, 315 | y.protectedSEXP, 316 | z.protectedSEXP 317 | ) 318 | ) 319 | defer { RUNPROTECT(5) } 320 | 321 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 322 | defer { RUNPROTECT(1) } 323 | 324 | if (err != 0) { 325 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 326 | } 327 | 328 | return(res) 329 | 330 | } 331 | 332 | /// - SeeAlso: Rlang1 333 | func Rlang5(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, a: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 334 | 335 | var err: CInt = 0 336 | 337 | let call_ƒ = RPROTECT( 338 | Rf_lang5( 339 | call.protectedSEXP, 340 | x.protectedSEXP, 341 | y.protectedSEXP, 342 | z.protectedSEXP, 343 | a.protectedSEXP 344 | ) 345 | ) 346 | defer { RUNPROTECT(6) } 347 | 348 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 349 | defer { RUNPROTECT(1) } 350 | 351 | if (err != 0) { 352 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 353 | } 354 | 355 | return(res) 356 | 357 | } 358 | 359 | /// - SeeAlso: Rlang1 360 | func Rlang6(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, a: SEXP, b: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 361 | 362 | var err: CInt = 0 363 | 364 | let call_ƒ = RPROTECT( 365 | Rf_lang6( 366 | call.protectedSEXP, 367 | x.protectedSEXP, 368 | y.protectedSEXP, 369 | z.protectedSEXP, 370 | a.protectedSEXP, 371 | b.protectedSEXP 372 | ) 373 | ) 374 | defer { RUNPROTECT(7) } 375 | 376 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 377 | defer { RUNPROTECT(1) } 378 | 379 | if (err != 0) { 380 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 381 | } 382 | 383 | return(res) 384 | 385 | } 386 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/sexp-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Data { 4 | 5 | /// Create a Data from an RAWSXP 6 | public init?(_ sexp: SEXP) { 7 | if (sexp != R_NilValue) { 8 | switch (TYPEOF(sexp)) { 9 | case RAWSXP: self = Data(bytes: RAW(sexp), count: Int(sexp.count)) 10 | default: return(nil) 11 | } 12 | } else { 13 | return(nil) 14 | } 15 | } 16 | 17 | } 18 | 19 | extension String { 20 | 21 | /// Create a String from an STRSXP 22 | init?(_ sexp: SEXP) { 23 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 24 | switch (TYPEOF(sexp)) { 25 | case STRSXP: self = String(cString: CHAR_Rf_asChar(sexp)) 26 | default: return(nil) 27 | } 28 | } else { 29 | return(nil) 30 | } 31 | } 32 | 33 | /// Provide a SEXP representation (STRSXP) of this String 34 | var SEXP: SEXP { return(Rf_mkString(self)) } 35 | 36 | /// Provide a protected SEXP representation of this String's SEXP 37 | /// - Important: the caller is responsible for UNPROTECTing it 38 | var protectedSEXP: SEXP { 39 | return(SEXP.protectedSEXP) 40 | } 41 | } 42 | 43 | extension Bool { 44 | 45 | /// Create a Bool from an LGLSXP 46 | init?(_ sexp: SEXP) { 47 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 48 | switch (TYPEOF(sexp)) { 49 | case LGLSXP: self = (Rf_asLogical(sexp) == 1) 50 | default: return(nil) 51 | } 52 | } else { 53 | return(nil) 54 | } 55 | } 56 | 57 | /// Provide a SEXP representation (LGLSXP) of this Bool 58 | var SEXP: SEXP { return(Rf_ScalarLogical(self ? 1 : 0)) } 59 | 60 | /// Provide a protected SEXP representation of this Bool's SEXP 61 | /// - Important: the caller is responsible for UNPROTECTing it 62 | var protectedSEXP: SEXP { 63 | return(SEXP.protectedSEXP) 64 | } 65 | } 66 | 67 | extension Int { 68 | 69 | /// Create an Int from an INTSXP 70 | init?(_ sexp: SEXP) { 71 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 72 | switch (TYPEOF(sexp)) { 73 | case INTSXP: self = Int(Rf_asInteger(sexp)) 74 | default: return(nil) 75 | } 76 | } else { 77 | return(nil) 78 | } 79 | } 80 | 81 | /// Provide a SEXP representation (INTSXP) of this Int 82 | var SEXP: SEXP { return(Rf_ScalarInteger(CInt(self))) } 83 | 84 | /// Provide a protected SEXP representation of this Int's SEXP 85 | /// - Important: the caller is responsible for UNPROTECTing it 86 | var protectedSEXP: SEXP { 87 | return(SEXP.protectedSEXP) 88 | } 89 | } 90 | 91 | extension Double { 92 | 93 | /// Create a Double from a REALXSP, INTSXP, or STRSXP 94 | init?(_ sexp: SEXP) { 95 | 96 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 97 | switch (TYPEOF(sexp)) { 98 | case REALSXP: self = Rf_asReal(sexp) 99 | case INTSXP: self = Double(Int(sexp)!) // a tad more dangerous 100 | case STRSXP: self = Double(String(sexp)!)! // a tad, tad more dangerous 101 | default: return(nil) 102 | } 103 | } else { 104 | return(nil) 105 | } 106 | 107 | } 108 | 109 | /// Provide a SEXP representation (REALSXP) of this Double 110 | var SEXP: SEXP { return(Rf_ScalarReal(self)) } 111 | 112 | /// Provide a protected SEXP representation of this Double's SEXP 113 | /// - Important: the caller is responsible for UNPROTECTing it 114 | var protectedSEXP: SEXP { 115 | return(SEXP.protectedSEXP) 116 | } 117 | 118 | } 119 | 120 | extension Array where Element == Bool { 121 | 122 | /// Create a Swift Bool Array from an R logical vector 123 | init?(_ sexp: SEXP) { 124 | if (sexp.isLOGICAL) { 125 | var val : [Bool] = [Bool]() 126 | val.reserveCapacity(Int(sexp.count)) 127 | let LOGICALVEC = LOGICAL(sexp) 128 | for idx in 0.. 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | //int initEmbeddedR(void); 13 | int initEmbeddedR(const char *r_home); 14 | void printFromR(const char *message); 15 | SEXP RPROTECT(SEXP x); 16 | void RUNPROTECT(int x); 17 | const char *CHAR_Rf_asChar(SEXP x); 18 | const char *RVecElToCstr(SEXP x, R_xlen_t i); 19 | 20 | #endif /* swift_r_bridge_h */ 21 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/undo.swift: -------------------------------------------------------------------------------- 1 | // https://github.com/LostMoa/UndoProviderExample 2 | 3 | import Foundation 4 | import SwiftUI 5 | 6 | struct Provider: View where WrappedView: View { 7 | 8 | var wrappedView: () -> WrappedView 9 | 10 | init(@ViewBuilder wrappedView: @escaping () -> WrappedView) { 11 | self.wrappedView = wrappedView 12 | } 13 | 14 | var body: some View { 15 | wrappedView() 16 | } 17 | } 18 | 19 | struct BindingInterceptedProvider: View where WrappedView: View { 20 | 21 | var wrappedView: (Binding) -> WrappedView 22 | 23 | var binding: Binding 24 | 25 | init( 26 | _ binding: Binding, 27 | @ViewBuilder wrappedView: @escaping (Binding) -> WrappedView 28 | ) { 29 | self.binding = binding 30 | self.wrappedView = wrappedView 31 | } 32 | 33 | var interceptedBinding: Binding { 34 | Binding { 35 | self.binding.wrappedValue 36 | } set: { newValue in 37 | print("\(newValue) is about to override \(self.binding.wrappedValue)") 38 | self.binding.wrappedValue = newValue 39 | } 40 | } 41 | 42 | var body: some View { 43 | wrappedView(self.interceptedBinding) 44 | } 45 | } 46 | 47 | struct UndoProvider: View where WrappedView: View { 48 | 49 | @Environment(\.undoManager) 50 | var undoManager 51 | 52 | @StateObject 53 | var handler: UndoHandler = UndoHandler() 54 | 55 | var wrappedView: (Binding) -> WrappedView 56 | 57 | var binding: Binding 58 | 59 | init(_ binding: Binding, @ViewBuilder wrappedView: @escaping (Binding) -> WrappedView) { 60 | self.binding = binding 61 | self.wrappedView = wrappedView 62 | } 63 | 64 | var interceptedBinding: Binding { 65 | Binding { 66 | self.binding.wrappedValue 67 | } set: { newValue in 68 | self.handler.registerUndo(from: self.binding.wrappedValue, to: newValue) 69 | self.binding.wrappedValue = newValue 70 | } 71 | } 72 | 73 | var body: some View { 74 | wrappedView(self.interceptedBinding).onAppear { 75 | self.handler.binding = self.binding 76 | self.handler.undoManger = self.undoManager 77 | }.onChange(of: self.undoManager) { undoManager in 78 | self.handler.undoManger = undoManager 79 | } 80 | } 81 | } 82 | 83 | class UndoHandler: ObservableObject { 84 | var binding: Binding? 85 | weak var undoManger: UndoManager? 86 | 87 | func registerUndo(from oldValue: Value, to newValue: Value) { 88 | undoManger?.registerUndo(withTarget: self) { handler in 89 | handler.registerUndo(from: newValue, to: oldValue) 90 | handler.binding?.wrappedValue = oldValue 91 | } 92 | } 93 | 94 | init() {} 95 | } 96 | 97 | -------------------------------------------------------------------------------- /SwiftDensity/SwiftDensity/utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | 4 | extension Color { 5 | static let codeError = Color("codeError") 6 | static let codeOK = clear 7 | } 8 | 9 | extension Date { 10 | 11 | /// Enables passing in of e.g. "2021-01-01" to get a Date 12 | init(fromISODate: String) { 13 | self = Date.iso8601Formatter.date(from: fromISODate)! 14 | } 15 | 16 | /// Provides an e.g. "2021-01-01" string representation of a Date 17 | var ISODate: String { 18 | Date.iso8601Formatter.string(from: self) 19 | } 20 | 21 | static let iso8601Formatter: ISO8601DateFormatter = { 22 | let fmt = ISO8601DateFormatter() 23 | fmt.formatOptions = [ .withFullDate, .withDashSeparatorInDate ] 24 | return(fmt) 25 | }() 26 | 27 | } 28 | 29 | extension Double { 30 | 31 | /// Provides e.g. 3 from 3.14 32 | var whole: Self { rounded(.toNearestOrAwayFromZero) } 33 | 34 | /// Provides e.g. 0.14 from 3.14 35 | var fraction: Self { truncatingRemainder(dividingBy: 1) } 36 | 37 | /// Provides e.g. "05:30" from 5.5 38 | var decimalTimeToHM: String { 39 | return(String(format: "%02d:%02d", Int(whole), Int(fraction*60))) 40 | } 41 | 42 | /// Rounds a double to a specified number of decimal digits 43 | func roundToDecimal(_ fractionDigits: Int) -> Double { 44 | let multiplier = pow(10, Double(fractionDigits)) 45 | return Darwin.round(self * multiplier) / multiplier 46 | } 47 | 48 | } 49 | 50 | extension String { 51 | func capitalizingFirstLetter() -> String { 52 | return prefix(1).capitalized + dropFirst() 53 | } 54 | 55 | mutating func capitalizeFirstLetter() { 56 | self = self.capitalizingFirstLetter() 57 | } 58 | 59 | var localized: String { 60 | NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /data/inflammation-01.csv: -------------------------------------------------------------------------------- 1 | 0,0,1,3,1,2,4,7,8,3,3,3,10,5,7,4,7,7,12,18,6,13,11,11,7,7,4,6,8,8,4,4,5,7,3,4,2,3,0,0 2 | 0,1,2,1,2,1,3,2,2,6,10,11,5,9,4,4,7,16,8,6,18,4,12,5,12,7,11,5,11,3,3,5,4,4,5,5,1,1,0,1 3 | 0,1,1,3,3,2,6,2,5,9,5,7,4,5,4,15,5,11,9,10,19,14,12,17,7,12,11,7,4,2,10,5,4,2,2,3,2,2,1,1 4 | 0,0,2,0,4,2,2,1,6,7,10,7,9,13,8,8,15,10,10,7,17,4,4,7,6,15,6,4,9,11,3,5,6,3,3,4,2,3,2,1 5 | 0,1,1,3,3,1,3,5,2,4,4,7,6,5,3,10,8,10,6,17,9,14,9,7,13,9,12,6,7,7,9,6,3,2,2,4,2,0,1,1 6 | 0,0,1,2,2,4,2,1,6,4,7,6,6,9,9,15,4,16,18,12,12,5,18,9,5,3,10,3,12,7,8,4,7,3,5,4,4,3,2,1 7 | 0,0,2,2,4,2,2,5,5,8,6,5,11,9,4,13,5,12,10,6,9,17,15,8,9,3,13,7,8,2,8,8,4,2,3,5,4,1,1,1 8 | 0,0,1,2,3,1,2,3,5,3,7,8,8,5,10,9,15,11,18,19,20,8,5,13,15,10,6,10,6,7,4,9,3,5,2,5,3,2,2,1 9 | 0,0,0,3,1,5,6,5,5,8,2,4,11,12,10,11,9,10,17,11,6,16,12,6,8,14,6,13,10,11,4,6,4,7,6,3,2,1,0,0 10 | 0,1,1,2,1,3,5,3,5,8,6,8,12,5,13,6,13,8,16,8,18,15,16,14,12,7,3,8,9,11,2,5,4,5,1,4,1,2,0,0 11 | 0,1,0,0,4,3,3,5,5,4,5,8,7,10,13,3,7,13,15,18,8,15,15,16,11,14,12,4,10,10,4,3,4,5,5,3,3,2,2,1 12 | 0,1,0,0,3,4,2,7,8,5,2,8,11,5,5,8,14,11,6,11,9,16,18,6,12,5,4,3,5,7,8,3,5,4,5,5,4,0,1,1 13 | 0,0,2,1,4,3,6,4,6,7,9,9,3,11,6,12,4,17,13,15,13,12,8,7,4,7,12,9,5,6,5,4,7,3,5,4,2,3,0,1 14 | 0,0,0,0,1,3,1,6,6,5,5,6,3,6,13,3,10,13,9,16,15,9,11,4,6,4,11,11,12,3,5,8,7,4,6,4,1,3,0,0 15 | 0,1,2,1,1,1,4,1,5,2,3,3,10,7,13,5,7,17,6,9,12,13,10,4,12,4,6,7,6,10,8,2,5,1,3,4,2,0,2,0 16 | 0,1,1,0,1,2,4,3,6,4,7,5,5,7,5,10,7,8,18,17,9,8,12,11,11,11,14,6,11,2,10,9,5,6,5,3,4,2,2,0 17 | 0,0,0,0,2,3,6,5,7,4,3,2,10,7,9,11,12,5,12,9,13,19,14,17,5,13,8,11,5,10,9,8,7,5,3,1,4,0,2,1 18 | 0,0,0,1,2,1,4,3,6,7,4,2,12,6,12,4,14,7,8,14,13,19,6,9,12,6,4,13,6,7,2,3,6,5,4,2,3,0,1,0 19 | 0,0,2,1,2,5,4,2,7,8,4,7,11,9,8,11,15,17,11,12,7,12,7,6,7,4,13,5,7,6,6,9,2,1,1,2,2,0,1,0 20 | 0,1,2,0,1,4,3,2,2,7,3,3,12,13,11,13,6,5,9,16,9,19,16,11,8,9,14,12,11,9,6,6,6,1,1,2,4,3,1,1 21 | 0,1,1,3,1,4,4,1,8,2,2,3,12,12,10,15,13,6,5,5,18,19,9,6,11,12,7,6,3,6,3,2,4,3,1,5,4,2,2,0 22 | 0,0,2,3,2,3,2,6,3,8,7,4,6,6,9,5,12,12,8,5,12,10,16,7,14,12,5,4,6,9,8,5,6,6,1,4,3,0,2,0 23 | 0,0,0,3,4,5,1,7,7,8,2,5,12,4,10,14,5,5,17,13,16,15,13,6,12,9,10,3,3,7,4,4,8,2,6,5,1,0,1,0 24 | 0,1,1,1,1,3,3,2,6,3,9,7,8,8,4,13,7,14,11,15,14,13,5,13,7,14,9,10,5,11,5,3,5,1,1,4,4,1,2,0 25 | 0,1,1,1,2,3,5,3,6,3,7,10,3,8,12,4,12,9,15,5,17,16,5,10,10,15,7,5,3,11,5,5,6,1,1,1,1,0,2,1 26 | 0,0,2,1,3,3,2,7,4,4,3,8,12,9,12,9,5,16,8,17,7,11,14,7,13,11,7,12,12,7,8,5,7,2,2,4,1,1,1,0 27 | 0,0,1,2,4,2,2,3,5,7,10,5,5,12,3,13,4,13,7,15,9,12,18,14,16,12,3,11,3,2,7,4,8,2,2,1,3,0,1,1 28 | 0,0,1,1,1,5,1,5,2,2,4,10,4,8,14,6,15,6,12,15,15,13,7,17,4,5,11,4,8,7,9,4,5,3,2,5,4,3,2,1 29 | 0,0,2,2,3,4,6,3,7,6,4,5,8,4,7,7,6,11,12,19,20,18,9,5,4,7,14,8,4,3,7,7,8,3,5,4,1,3,1,0 30 | 0,0,0,1,4,4,6,3,8,6,4,10,12,3,3,6,8,7,17,16,14,15,17,4,14,13,4,4,12,11,6,9,5,5,2,5,2,1,0,1 31 | 0,1,1,0,3,2,4,6,8,6,2,3,11,3,14,14,12,8,8,16,13,7,6,9,15,7,6,4,10,8,10,4,2,6,5,5,2,3,2,1 32 | 0,0,2,3,3,4,5,3,6,7,10,5,10,13,14,3,8,10,9,9,19,15,15,6,8,8,11,5,5,7,3,6,6,4,5,2,2,3,0,0 33 | 0,1,2,2,2,3,6,6,6,7,6,3,11,12,13,15,15,10,14,11,11,8,6,12,10,5,12,7,7,11,5,8,5,2,5,5,2,0,2,1 34 | 0,0,2,1,3,5,6,7,5,8,9,3,12,10,12,4,12,9,13,10,10,6,10,11,4,15,13,7,3,4,2,9,7,2,4,2,1,2,1,1 35 | 0,0,1,2,4,1,5,5,2,3,4,8,8,12,5,15,9,17,7,19,14,18,12,17,14,4,13,13,8,11,5,6,6,2,3,5,2,1,1,1 36 | 0,0,0,3,1,3,6,4,3,4,8,3,4,8,3,11,5,7,10,5,15,9,16,17,16,3,8,9,8,3,3,9,5,1,6,5,4,2,2,0 37 | 0,1,2,2,2,5,5,1,4,6,3,6,5,9,6,7,4,7,16,7,16,13,9,16,12,6,7,9,10,3,6,4,5,4,6,3,4,3,2,1 38 | 0,1,1,2,3,1,5,1,2,2,5,7,6,6,5,10,6,7,17,13,15,16,17,14,4,4,10,10,10,11,9,9,5,4,4,2,1,0,1,0 39 | 0,1,0,3,2,4,1,1,5,9,10,7,12,10,9,15,12,13,13,6,19,9,10,6,13,5,13,6,7,2,5,5,2,1,1,1,1,3,0,1 40 | 0,1,1,3,1,1,5,5,3,7,2,2,3,12,4,6,8,15,16,16,15,4,14,5,13,10,7,10,6,3,2,3,6,3,3,5,4,3,2,1 41 | 0,0,0,2,2,1,3,4,5,5,6,5,5,12,13,5,7,5,11,15,18,7,9,10,14,12,11,9,10,3,2,9,6,2,2,5,3,0,0,1 42 | 0,0,1,3,3,1,2,1,8,9,2,8,10,3,8,6,10,13,11,17,19,6,4,11,6,12,7,5,5,4,4,8,2,6,6,4,2,2,0,0 43 | 0,1,1,3,4,5,2,1,3,7,9,6,10,5,8,15,11,12,15,6,12,16,6,4,14,3,12,9,6,11,5,8,5,5,6,1,2,1,2,0 44 | 0,0,1,3,1,4,3,6,7,8,5,7,11,3,6,11,6,10,6,19,18,14,6,10,7,9,8,5,8,3,10,2,5,1,5,4,2,1,0,1 45 | 0,1,1,3,3,4,4,6,3,4,9,9,7,6,8,15,12,15,6,11,6,18,5,14,15,12,9,8,3,6,10,6,8,7,2,5,4,3,1,1 46 | 0,1,2,2,4,3,1,4,8,9,5,10,10,3,4,6,7,11,16,6,14,9,11,10,10,7,10,8,8,4,5,8,4,4,5,2,4,1,1,0 47 | 0,0,2,3,4,5,4,6,2,9,7,4,9,10,8,11,16,12,15,17,19,10,18,13,15,11,8,4,7,11,6,7,6,5,1,3,1,0,0,0 48 | 0,1,1,3,1,4,6,2,8,2,10,3,11,9,13,15,5,15,6,10,10,5,14,15,12,7,4,5,11,4,6,9,5,6,1,1,2,1,2,1 49 | 0,0,1,3,2,5,1,2,7,6,6,3,12,9,4,14,4,6,12,9,12,7,11,7,16,8,13,6,7,6,10,7,6,3,1,5,4,3,0,0 50 | 0,0,1,2,3,4,5,7,5,4,10,5,12,12,5,4,7,9,18,16,16,10,15,15,10,4,3,7,5,9,4,6,2,4,1,4,2,2,2,1 51 | 0,1,2,1,1,3,5,3,6,3,10,10,11,10,13,10,13,6,6,14,5,4,5,5,9,4,12,7,7,4,7,9,3,3,6,3,4,1,2,0 52 | 0,1,2,2,3,5,2,4,5,6,8,3,5,4,3,15,15,12,16,7,20,15,12,8,9,6,12,5,8,3,8,5,4,1,3,2,1,3,1,0 53 | 0,0,0,2,4,4,5,3,3,3,10,4,4,4,14,11,15,13,10,14,11,17,9,11,11,7,10,12,10,10,10,8,7,5,2,2,4,1,2,1 54 | 0,0,2,1,1,4,4,7,2,9,4,10,12,7,6,6,11,12,9,15,15,6,6,13,5,12,9,6,4,7,7,6,5,4,1,4,2,2,2,1 55 | 0,1,2,1,1,4,5,4,4,5,9,7,10,3,13,13,8,9,17,16,16,15,12,13,5,12,10,9,11,9,4,5,5,2,2,5,1,0,0,1 56 | 0,0,1,3,2,3,6,4,5,7,2,4,11,11,3,8,8,16,5,13,16,5,8,8,6,9,10,10,9,3,3,5,3,5,4,5,3,3,0,1 57 | 0,1,1,2,2,5,1,7,4,2,5,5,4,6,6,4,16,11,14,16,14,14,8,17,4,14,13,7,6,3,7,7,5,6,3,4,2,2,1,1 58 | 0,1,1,1,4,1,6,4,6,3,6,5,6,4,14,13,13,9,12,19,9,10,15,10,9,10,10,7,5,6,8,6,6,4,3,5,2,1,1,1 59 | 0,0,0,1,4,5,6,3,8,7,9,10,8,6,5,12,15,5,10,5,8,13,18,17,14,9,13,4,10,11,10,8,8,6,5,5,2,0,2,0 60 | 0,0,1,0,3,2,5,4,8,2,9,3,3,10,12,9,14,11,13,8,6,18,11,9,13,11,8,5,5,2,8,5,3,5,4,1,3,1,1,0 61 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "swift-argument-parser", 6 | "repositoryURL": "https://github.com/apple/swift-argument-parser", 7 | "state": { 8 | "branch": null, 9 | "revision": "92646c0cdbaca076c8d3d0207891785b3379cbff", 10 | "version": "0.3.1" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids.xcodeproj/xcshareddata/xcschemes/ephemerids.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 44 | 46 | 52 | 53 | 54 | 55 | 61 | 63 | 69 | 70 | 71 | 72 | 74 | 75 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/ephemerids-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #include "swift-r-bridge.h" 6 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/ephemerids.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.disable-library-validation 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ArgumentParser 3 | 4 | struct Sunriset: ParsableCommand { 5 | 6 | static var configuration = CommandConfiguration( 7 | abstract: "Outputs sunrise/sunset times for a given longitude/latitude for today or a speficied date. ", 8 | discussion: "Use -- before positional parameters if any are negative.\ne.g. ephemerids -- -70.8636 43.2683" 9 | ) 10 | 11 | @Option(help: "A ISO3601 date. — e.g. 2021-01-01") var date: String = Date().ISODate // default --date= to today 12 | 13 | @Argument(help: "Longitude, decimal — e.g. -70.8636. ") 14 | var lng: Double = -70.8636 // provide a default 15 | 16 | @Argument(help: "Latitude, decimal — e.g. 43.2683. Use -- before positional parameters if any are negative.") 17 | var lat: Double = 43.2683 // provide a default 18 | 19 | mutating func run() throws { 20 | doSunRiseSet(date: Date(fromISODate: date), lng: lng, lat: lat) 21 | } 22 | 23 | } 24 | 25 | initEmbeddedR() 26 | 27 | Sunriset.main() 28 | 29 | Rf_endEmbeddedR(0) 30 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/r-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let PROTECT = Rf_protect 4 | let UNPROTECT = Rf_unprotect 5 | 6 | // we only have one R error exception type for the moment. 7 | enum RError: Error { 8 | case tryEvalError(String) 9 | } 10 | 11 | // Swift helper to require(package) 12 | func require(_ pkg: String) throws -> Bool { 13 | 14 | var rErr: CInt = 0 // holds our error return value from evaluation 15 | 16 | let requireCall: SEXP = RPROTECT(Rf_allocVector(SEXPTYPE(LANGSXP), 3+1))! 17 | defer { RUNPROTECT(1) } 18 | SETCAR(requireCall, Rf_install("require")) 19 | 20 | var reqParams: SEXP = CDR(requireCall); 21 | SETCAR(reqParams, RPROTECT(Rf_mkString(pkg))) 22 | defer { RUNPROTECT(1) } 23 | SET_TAG(reqParams, Rf_install("package")) 24 | 25 | reqParams = CDR(reqParams) 26 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 27 | defer { RUNPROTECT(1) } 28 | SET_TAG(reqParams, Rf_install("warn.conflicts")) 29 | 30 | reqParams = CDR(reqParams) 31 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 32 | defer { RUNPROTECT(1) } 33 | SET_TAG(reqParams, Rf_install("quietly")) 34 | 35 | // requireCall is PROTECTed above but Rf_install() may allocate which could kill it so make sure it is safe 36 | let suppressWarningsCall = RPROTECT(Rf_lang2(Rf_install("suppressWarnings"), RPROTECT(requireCall))) 37 | defer { RUNPROTECT(2) } 38 | 39 | let res: SEXP = RPROTECT(R_tryEvalSilent(suppressWarningsCall, R_GlobalEnv, &rErr))! 40 | defer { RUNPROTECT(1) } 41 | 42 | if (rErr == 0) { 43 | if (SEXPTYPE(TYPEOF(res)) == LGLSXP) { 44 | return(Rf_asLogical(res) == 1) 45 | } else { 46 | throw RError.tryEvalError( 47 | "R evaluation error attempting to load. Expected TRUE/FALSE, but found '\(String(cString: Rf_type2char(SEXPTYPE(TYPEOF(res)))))" 48 | ) 49 | } 50 | } else { 51 | throw RError.tryEvalError("R evaluation error attempting to load \(pkg): \(String(cString: R_curErrorBuf()))") 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/sexp-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | init?(_ sexp: SEXP) { 5 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 6 | switch (TYPEOF(sexp)) { 7 | case STRSXP: self = String(cString: CHAR_Rf_asChar(sexp)) 8 | default: return(nil) 9 | } 10 | } else { 11 | return(nil) 12 | } 13 | } 14 | var SEXP: SEXP { return(Rf_mkString(self)) } 15 | var protectedSEXP: SEXP { return(RPROTECT(Rf_mkString(self))) } 16 | } 17 | 18 | extension Int { 19 | init?(_ sexp: SEXP) { 20 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 21 | switch (TYPEOF(sexp)) { 22 | case INTSXP: self = Int(Rf_asInteger(sexp)) 23 | default: return(nil) 24 | } 25 | } else { 26 | return(nil) 27 | } 28 | } 29 | var SEXP: SEXP { return(Rf_ScalarInteger(CInt(self))) } 30 | var protectedSEXP: SEXP { return(RPROTECT(Rf_ScalarInteger(CInt(self)))) } 31 | } 32 | 33 | extension Double { 34 | 35 | init?(_ sexp: SEXP) { 36 | 37 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 38 | switch (TYPEOF(sexp)) { 39 | case REALSXP: self = Rf_asReal(sexp) 40 | case INTSXP: self = Double(Int(sexp)!) // a tad more dangerous 41 | case STRSXP: self = Double(String(sexp)!)! // a tad more dangerous 42 | default: return(nil) 43 | } 44 | } else { 45 | return(nil) 46 | } 47 | 48 | } 49 | 50 | var SEXP: SEXP { return(Rf_ScalarReal(self)) } 51 | var protectedSEXP: SEXP { return(RPROTECT(Rf_ScalarReal(self))) } 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/sunriseset.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct sunriset { 4 | let rise: Double 5 | let set: Double 6 | } 7 | 8 | // sun_rise_set("2021-01-02", lon = -70.8636, lat = 43.2683) 9 | // ## $rise 10 | // ## [1] 12.25761 11 | // ## 12 | // ## $set 13 | // ## [1] 21.28906 14 | func sunRiseSet(date: Date, lng: Double, lat: Double) -> sunriset { 15 | 16 | var err: CInt = 0 17 | 18 | let call_ƒ = RPROTECT( 19 | Rf_lang4( 20 | Rf_install("sun_rise_set"), 21 | date.ISODate.protectedSEXP, 22 | lng.protectedSEXP, 23 | lat.protectedSEXP 24 | ) 25 | ) 26 | defer { RUNPROTECT(4) } 27 | 28 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, R_GlobalEnv, &err)) ?? R_NilValue 29 | defer { RUNPROTECT(1) } 30 | 31 | return(sunriset( 32 | rise: Double(VECTOR_ELT(res, 0)) ?? -1, 33 | set: Double(VECTOR_ELT(res, 1)) ?? -1 34 | )) 35 | 36 | } 37 | 38 | func doSunRiseSet(date: Date, lng: Double, lat: Double) { 39 | 40 | do { 41 | if try require("daybreak") { 42 | let res = sunRiseSet(date: date, lng: lng, lat: lat) 43 | print("Sunrise 🌅: \(res.rise.decimalTimeToHM) GMT\n Sunset 🌇: \(res.set.decimalTimeToHM) GMT") 44 | } else { 45 | print("The {daybreak} package is not available. Please remotes::install_github('hrbrmstr/daybreak')") 46 | } 47 | } catch { 48 | print("Error: \(error)") 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/swift-r-bridge.c: -------------------------------------------------------------------------------- 1 | #include "swift-r-bridge.h" 2 | 3 | void initEmbeddedR() { 4 | if (!getenv("R_HOME")) setenv("R_HOME", "/Library/Frameworks/R.framework/Resources", 1); 5 | char *argv[] = { "swiftR", "--gui=none", "--no-save", "--silent", "--vanilla", "--slave", "--no-readline", "" }; 6 | Rf_initEmbeddedR(7, argv); 7 | } 8 | 9 | void printFromR(const char *message) { 10 | Rprintf(message); 11 | } 12 | 13 | inline SEXP RPROTECT(SEXP x) { 14 | return(PROTECT(x)); 15 | } 16 | 17 | inline void RUNPROTECT(int x) { 18 | UNPROTECT(x); 19 | } 20 | 21 | inline const char *CHAR_Rf_asChar(SEXP x) { 22 | return(CHAR(Rf_asChar(x))); 23 | } 24 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/swift-r-bridge.h: -------------------------------------------------------------------------------- 1 | #ifndef swift_r_bridge_h 2 | #define swift_r_bridge_h 3 | 4 | #define USE_RINTERNALS 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void initEmbeddedR(void); 12 | void printFromR(const char *message); 13 | SEXP RPROTECT(SEXP x); 14 | void RUNPROTECT(int x); 15 | const char *CHAR_Rf_asChar(SEXP x); 16 | 17 | #endif /* swift_r_bridge_h */ 18 | -------------------------------------------------------------------------------- /ephemerids-2/ephemerids/utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Date { 4 | 5 | init(fromISODate: String) { 6 | self = Date.iso8601Formatter.date(from: fromISODate)! 7 | } 8 | 9 | var ISODate: String { 10 | Date.iso8601Formatter.string(from: self) 11 | } 12 | 13 | static let iso8601Formatter: ISO8601DateFormatter = { 14 | let fmt = ISO8601DateFormatter() 15 | fmt.formatOptions = [ .withFullDate, .withDashSeparatorInDate ] 16 | return(fmt) 17 | }() 18 | 19 | } 20 | 21 | extension Double { 22 | 23 | var whole: Self { rounded(.toNearestOrAwayFromZero) } 24 | var fraction: Self { truncatingRemainder(dividingBy: 1) } 25 | 26 | var decimalTimeToHM: String { 27 | return(String(format: "%02d:%02d", Int(whole), Int(fraction*60))) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "swift-argument-parser", 6 | "repositoryURL": "https://github.com/apple/swift-argument-parser", 7 | "state": { 8 | "branch": null, 9 | "revision": "92646c0cdbaca076c8d3d0207891785b3379cbff", 10 | "version": "0.3.1" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids.xcodeproj/xcshareddata/xcschemes/ephemerids.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 44 | 46 | 52 | 53 | 54 | 55 | 61 | 63 | 69 | 70 | 71 | 72 | 74 | 75 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | is.rud.ephemerids 7 | CFBundleName 8 | ephemerids 9 | CFBundleShortVersionString 10 | 1 11 | 12 | 13 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/ephemerids-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #include "swift-r-bridge.h" 6 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/ephemerids.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.disable-library-validation 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ArgumentParser 3 | 4 | struct Sunriset: ParsableCommand { 5 | 6 | static var configuration = CommandConfiguration( 7 | abstract: "Outputs sunrise/sunset times for a given longitude/latitude for today or a speficied date. ", 8 | discussion: "Use -- before positional parameters if any are negative.\ne.g. ephemerids -- -70.8636 43.2683" 9 | ) 10 | 11 | @Option(help: "A ISO3601 date. — e.g. 2021-01-01") var date: String = Date().ISODate // default --date= to today 12 | 13 | @Argument(help: "Longitude, decimal — e.g. -70.8636. ") 14 | var lng: Double = -70.8636 // provide a default 15 | 16 | @Argument(help: "Latitude, decimal — e.g. 43.2683. Use -- before positional parameters if any are negative.") 17 | var lat: Double = 43.2683 // provide a default 18 | 19 | mutating func run() throws { 20 | doSunRiseSet(date: Date(fromISODate: date), lng: lng, lat: lat) 21 | } 22 | 23 | } 24 | 25 | initEmbeddedR() 26 | 27 | Sunriset.main() 28 | 29 | Rf_endEmbeddedR(0) 30 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/r-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let PROTECT = Rf_protect 4 | let UNPROTECT = Rf_unprotect 5 | 6 | // we only have one R error exception type for the moment. 7 | enum RError: Error { 8 | case tryEvalError(String) 9 | } 10 | 11 | // Swift helper to require(package) 12 | func require(_ pkg: String) throws -> Bool { 13 | 14 | var rErr: CInt = 0 // holds our error return value from evaluation 15 | 16 | let requireCall: SEXP = RPROTECT(Rf_allocVector(SEXPTYPE(LANGSXP), 3+1))! 17 | defer { RUNPROTECT(1) } 18 | SETCAR(requireCall, Rf_install("require")) 19 | 20 | var reqParams: SEXP = CDR(requireCall); 21 | SETCAR(reqParams, RPROTECT(Rf_mkString(pkg))) 22 | defer { RUNPROTECT(1) } 23 | SET_TAG(reqParams, Rf_install("package")) 24 | 25 | reqParams = CDR(reqParams) 26 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 27 | defer { RUNPROTECT(1) } 28 | SET_TAG(reqParams, Rf_install("warn.conflicts")) 29 | 30 | reqParams = CDR(reqParams) 31 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 32 | defer { RUNPROTECT(1) } 33 | SET_TAG(reqParams, Rf_install("quietly")) 34 | 35 | // requireCall is PROTECTed above but Rf_install() may allocate which could kill it so make sure it is safe 36 | let suppressWarningsCall = RPROTECT(Rf_lang2(Rf_install("suppressWarnings"), RPROTECT(requireCall))) 37 | defer { RUNPROTECT(2) } 38 | 39 | let res: SEXP = RPROTECT(R_tryEvalSilent(suppressWarningsCall, R_GlobalEnv, &rErr))! 40 | defer { RUNPROTECT(1) } 41 | 42 | if (rErr == 0) { 43 | if (SEXPTYPE(TYPEOF(res)) == LGLSXP) { 44 | return(Rf_asLogical(res) == 1) 45 | } else { 46 | throw RError.tryEvalError( 47 | "R evaluation error attempting to load. Expected TRUE/FALSE, but found '\(String(cString: Rf_type2char(SEXPTYPE(TYPEOF(res)))))" 48 | ) 49 | } 50 | } else { 51 | throw RError.tryEvalError("R evaluation error attempting to load \(pkg): \(String(cString: R_curErrorBuf()))") 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/sexp-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | init?(_ sexp: SEXP) { 5 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 6 | switch (TYPEOF(sexp)) { 7 | case STRSXP: self = String(cString: CHAR_Rf_asChar(sexp)) 8 | default: return(nil) 9 | } 10 | } else { 11 | return(nil) 12 | } 13 | } 14 | var SEXP: SEXP { return(Rf_mkString(self)) } 15 | var protectedSEXP: SEXP { return(RPROTECT(Rf_mkString(self))) } 16 | } 17 | 18 | extension Int { 19 | init?(_ sexp: SEXP) { 20 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 21 | switch (TYPEOF(sexp)) { 22 | case INTSXP: self = Int(Rf_asInteger(sexp)) 23 | default: return(nil) 24 | } 25 | } else { 26 | return(nil) 27 | } 28 | } 29 | var SEXP: SEXP { return(Rf_ScalarInteger(CInt(self))) } 30 | var protectedSEXP: SEXP { return(RPROTECT(Rf_ScalarInteger(CInt(self)))) } 31 | } 32 | 33 | extension Double { 34 | 35 | init?(_ sexp: SEXP) { 36 | 37 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 38 | switch (TYPEOF(sexp)) { 39 | case REALSXP: self = Rf_asReal(sexp) 40 | case INTSXP: self = Double(Int(sexp)!) // a tad more dangerous 41 | case STRSXP: self = Double(String(sexp)!)! // a tad more dangerous 42 | default: return(nil) 43 | } 44 | } else { 45 | return(nil) 46 | } 47 | 48 | } 49 | 50 | var SEXP: SEXP { return(Rf_ScalarReal(self)) } 51 | var protectedSEXP: SEXP { return(RPROTECT(Rf_ScalarReal(self))) } 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/sunriseset.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct sunriset { 4 | let rise: Double 5 | let set: Double 6 | } 7 | 8 | // sun_rise_set("2021-01-02", lon = -70.8636, lat = 43.2683) 9 | // ## $rise 10 | // ## [1] 12.25761 11 | // ## 12 | // ## $set 13 | // ## [1] 21.28906 14 | func sunRiseSet(date: Date, lng: Double, lat: Double) -> sunriset { 15 | 16 | var err: CInt = 0 17 | 18 | let call_ƒ = RPROTECT( 19 | Rf_lang4( 20 | Rf_install("sun_rise_set"), 21 | date.ISODate.protectedSEXP, 22 | lng.protectedSEXP, 23 | lat.protectedSEXP 24 | ) 25 | ) 26 | defer { RUNPROTECT(4) } 27 | 28 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, R_GlobalEnv, &err)) ?? R_NilValue 29 | defer { RUNPROTECT(1) } 30 | 31 | return(sunriset( 32 | rise: Double(VECTOR_ELT(res, 0)) ?? -1, 33 | set: Double(VECTOR_ELT(res, 1)) ?? -1 34 | )) 35 | 36 | } 37 | 38 | func doSunRiseSet(date: Date, lng: Double, lat: Double) { 39 | 40 | do { 41 | if try require("daybreak") { 42 | let res = sunRiseSet(date: date, lng: lng, lat: lat) 43 | print("Sunrise 🌅: \(res.rise.decimalTimeToHM) GMT\n Sunset 🌇: \(res.set.decimalTimeToHM) GMT") 44 | } else { 45 | print("The {daybreak} package is not available. Please remotes::install_github('hrbrmstr/daybreak')") 46 | } 47 | } catch { 48 | print("Error: \(error)") 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/swift-r-bridge.c: -------------------------------------------------------------------------------- 1 | #include "swift-r-bridge.h" 2 | 3 | void initEmbeddedR() { 4 | if (!getenv("R_HOME")) setenv("R_HOME", "/Library/Frameworks/R.framework/Resources", 1); 5 | char *argv[] = { "swiftR", "--gui=none", "--no-save", "--silent", "--vanilla", "--slave", "--no-readline", "" }; 6 | Rf_initEmbeddedR(7, argv); 7 | } 8 | 9 | void printFromR(const char *message) { 10 | Rprintf(message); 11 | } 12 | 13 | inline SEXP RPROTECT(SEXP x) { 14 | return(PROTECT(x)); 15 | } 16 | 17 | inline void RUNPROTECT(int x) { 18 | UNPROTECT(x); 19 | } 20 | 21 | inline const char *CHAR_Rf_asChar(SEXP x) { 22 | return(CHAR(Rf_asChar(x))); 23 | } 24 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/swift-r-bridge.h: -------------------------------------------------------------------------------- 1 | #ifndef swift_r_bridge_h 2 | #define swift_r_bridge_h 3 | 4 | #define USE_RINTERNALS 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void initEmbeddedR(void); 12 | void printFromR(const char *message); 13 | SEXP RPROTECT(SEXP x); 14 | void RUNPROTECT(int x); 15 | const char *CHAR_Rf_asChar(SEXP x); 16 | 17 | #endif /* swift_r_bridge_h */ 18 | -------------------------------------------------------------------------------- /ephemerids-3/ephemerids/utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Date { 4 | 5 | init(fromISODate: String) { 6 | self = Date.iso8601Formatter.date(from: fromISODate)! 7 | } 8 | 9 | var ISODate: String { 10 | Date.iso8601Formatter.string(from: self) 11 | } 12 | 13 | static let iso8601Formatter: ISO8601DateFormatter = { 14 | let fmt = ISO8601DateFormatter() 15 | fmt.formatOptions = [ .withFullDate, .withDashSeparatorInDate ] 16 | return(fmt) 17 | }() 18 | 19 | } 20 | 21 | extension Double { 22 | 23 | var whole: Self { rounded(.toNearestOrAwayFromZero) } 24 | var fraction: Self { truncatingRemainder(dividingBy: 1) } 25 | 26 | var decimalTimeToHM: String { 27 | return(String(format: "%02d:%02d", Int(whole), Int(fraction*60))) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /ephemerids/ephemerids.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ephemerids/ephemerids.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids/ephemerids.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids/ephemerids.xcodeproj/xcshareddata/xcschemes/ephemerids.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 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/ephemerids-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #include "swift-r-bridge.h" 6 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/ephemerids.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.disable-library-validation 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | initEmbeddedR() 4 | 5 | do { 6 | if try require("daybreak") { 7 | let res = sunRiseSet(date: Date(fromISODate: "2021-01-09"), lng: 43.2683, lat: -70.8636) 8 | print("Sunrise: \(res.rise.decimalTimeToHM) GMT\n Sunset: \(res.set.decimalTimeToHM) GMT") 9 | } else { 10 | print("The {daybreak} package is not available. Please remotes::install_github('hrbrmstr/daybreak')") 11 | } 12 | } catch { 13 | print("Error: \(error)") 14 | } 15 | 16 | Rf_endEmbeddedR(0) 17 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/r-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let PROTECT = Rf_protect 4 | let UNPROTECT = Rf_unprotect 5 | 6 | // we only have one R error exception type for the moment. 7 | enum RError: Error { 8 | case tryEvalError(String) 9 | } 10 | 11 | // Swift helper to require(package) 12 | func require(_ pkg: String) throws -> Bool { 13 | 14 | var rErr: CInt = 0 // holds our error return value from evaluation 15 | 16 | let requireCall: SEXP = RPROTECT(Rf_allocVector(SEXPTYPE(LANGSXP), 3+1))! 17 | defer { RUNPROTECT(1) } 18 | SETCAR(requireCall, Rf_install("require")) 19 | 20 | var reqParams: SEXP = CDR(requireCall); 21 | SETCAR(reqParams, RPROTECT(Rf_mkString(pkg))) 22 | defer { RUNPROTECT(1) } 23 | SET_TAG(reqParams, Rf_install("package")) 24 | 25 | reqParams = CDR(reqParams) 26 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 27 | defer { RUNPROTECT(1) } 28 | SET_TAG(reqParams, Rf_install("warn.conflicts")) 29 | 30 | reqParams = CDR(reqParams) 31 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 32 | defer { RUNPROTECT(1) } 33 | SET_TAG(reqParams, Rf_install("quietly")) 34 | 35 | // requireCall is PROTECTed above but Rf_install() may allocate which could kill it so make sure it is safe 36 | let suppressWarningsCall = RPROTECT(Rf_lang2(Rf_install("suppressWarnings"), RPROTECT(requireCall))) 37 | defer { RUNPROTECT(2) } 38 | 39 | let res: SEXP = RPROTECT(R_tryEvalSilent(suppressWarningsCall, R_GlobalEnv, &rErr))! 40 | defer { RUNPROTECT(1) } 41 | 42 | if (rErr == 0) { 43 | if (SEXPTYPE(TYPEOF(res)) == LGLSXP) { 44 | return(Rf_asLogical(res) == 1) 45 | } else { 46 | throw RError.tryEvalError( 47 | "R evaluation error attempting to load. Expected TRUE/FALSE, but found '\(String(cString: Rf_type2char(SEXPTYPE(TYPEOF(res)))))" 48 | ) 49 | } 50 | } else { 51 | throw RError.tryEvalError("R evaluation error attempting to load \(pkg): \(String(cString: R_curErrorBuf()))") 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/sexp-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | init?(_ sexp: SEXP) { 5 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 6 | switch (TYPEOF(sexp)) { 7 | case STRSXP: self = String(cString: CHAR_Rf_asChar(sexp)) 8 | default: return(nil) 9 | } 10 | } else { 11 | return(nil) 12 | } 13 | } 14 | var SEXP: SEXP { return(Rf_mkString(self)) } 15 | var protectedSEXP: SEXP { return(RPROTECT(Rf_mkString(self))) } 16 | } 17 | 18 | extension Int { 19 | init?(_ sexp: SEXP) { 20 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 21 | switch (TYPEOF(sexp)) { 22 | case INTSXP: self = Int(Rf_asInteger(sexp)) 23 | default: return(nil) 24 | } 25 | } else { 26 | return(nil) 27 | } 28 | } 29 | var SEXP: SEXP { return(Rf_ScalarInteger(CInt(self))) } 30 | var protectedSEXP: SEXP { return(RPROTECT(Rf_ScalarInteger(CInt(self)))) } 31 | } 32 | 33 | extension Double { 34 | 35 | init?(_ sexp: SEXP) { 36 | 37 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 38 | switch (TYPEOF(sexp)) { 39 | case REALSXP: self = Rf_asReal(sexp) 40 | case INTSXP: self = Double(Int(sexp)!) // a tad more dangerous 41 | case STRSXP: self = Double(String(sexp)!)! // a tad more dangerous 42 | default: return(nil) 43 | } 44 | } else { 45 | return(nil) 46 | } 47 | 48 | } 49 | 50 | var SEXP: SEXP { return(Rf_ScalarReal(self)) } 51 | var protectedSEXP: SEXP { return(RPROTECT(Rf_ScalarReal(self))) } 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/sunriseset.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct sunriset { 4 | let rise: Double 5 | let set: Double 6 | } 7 | 8 | // sun_rise_set("2021-01-02", -70.8636, 43.2683) 9 | // ## $rise 10 | // ## [1] 12.25761 11 | // ## 12 | // ## $set 13 | // ## [1] 21.28906 14 | func sunRiseSet(date: Date, lng: Double, lat: Double) -> sunriset { 15 | 16 | var err: CInt = 0 17 | 18 | let call_ƒ = RPROTECT( 19 | Rf_lang4( 20 | Rf_install("sun_rise_set"), 21 | date.ISODate.protectedSEXP, 22 | lat.protectedSEXP, 23 | lng.protectedSEXP 24 | ) 25 | ) 26 | defer { RUNPROTECT(4) } 27 | 28 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, R_GlobalEnv, &err)) ?? R_NilValue 29 | defer { RUNPROTECT(1) } 30 | 31 | return(sunriset( 32 | rise: Double(VECTOR_ELT(res, 0)) ?? -1, 33 | set: Double(VECTOR_ELT(res, 1)) ?? -1 34 | )) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/swift-r-bridge.c: -------------------------------------------------------------------------------- 1 | #include "swift-r-bridge.h" 2 | 3 | void initEmbeddedR() { 4 | if (!getenv("R_HOME")) setenv("R_HOME", "/Library/Frameworks/R.framework/Resources", 1); 5 | char *argv[] = { "swiftR", "--gui=none", "--no-save", "--silent", "--vanilla", "--slave", "--no-readline", "" }; 6 | Rf_initEmbeddedR(7, argv); 7 | } 8 | 9 | void printFromR(const char *message) { 10 | Rprintf(message); 11 | } 12 | 13 | inline SEXP RPROTECT(SEXP x) { 14 | return(PROTECT(x)); 15 | } 16 | 17 | inline void RUNPROTECT(int x) { 18 | UNPROTECT(x); 19 | } 20 | 21 | inline const char *CHAR_Rf_asChar(SEXP x) { 22 | return(CHAR(Rf_asChar(x))); 23 | } 24 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/swift-r-bridge.h: -------------------------------------------------------------------------------- 1 | #ifndef swift_r_bridge_h 2 | #define swift_r_bridge_h 3 | 4 | #define USE_RINTERNALS 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void initEmbeddedR(void); 12 | void printFromR(const char *message); 13 | SEXP RPROTECT(SEXP x); 14 | void RUNPROTECT(int x); 15 | const char *CHAR_Rf_asChar(SEXP x); 16 | 17 | #endif /* swift_r_bridge_h */ 18 | -------------------------------------------------------------------------------- /ephemerids/ephemerids/utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Date { 4 | 5 | init(fromISODate: String) { 6 | self = Date.iso8601Formatter.date(from: fromISODate)! 7 | } 8 | 9 | var ISODate: String { 10 | Date.iso8601Formatter.string(from: self) 11 | } 12 | 13 | static let iso8601Formatter: ISO8601DateFormatter = { 14 | let fmt = ISO8601DateFormatter() 15 | fmt.formatOptions = [ .withFullDate, .withDashSeparatorInDate ] 16 | return(fmt) 17 | }() 18 | 19 | } 20 | 21 | extension Double { 22 | 23 | var whole: Self { rounded(.toNearestOrAwayFromZero) } 24 | var fraction: Self { truncatingRemainder(dividingBy: 1) } 25 | 26 | var decimalTimeToHM: String { 27 | return(String(format: "%02d:%02d", Int(whole), Int(fraction*60))) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /random/random.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /random/random.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /random/random/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 | -------------------------------------------------------------------------------- /random/random/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /random/random/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /random/random/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | 5 | @State var randomSeed: String = "20210120" 6 | @State var sampleRangeStart: String = "1" 7 | @State var sampleRangeEnd: String = "1000" 8 | @State var sampledValues: String = "" 9 | 10 | @ObservedObject var randomApp = RandomModel() 11 | 12 | var body: some View { 13 | HStack { 14 | VStack(alignment: .trailing) { 15 | 16 | HStack { 17 | Text("Random Seed:") 18 | TextField("", text: $randomSeed) 19 | .multilineTextAlignment(.trailing) 20 | .frame(width: 100) 21 | } 22 | 23 | HStack { 24 | Text("Range Start:") 25 | TextField("", text: $sampleRangeStart) 26 | .multilineTextAlignment(.trailing) 27 | .frame(width: 100) 28 | } 29 | 30 | HStack { 31 | Text("Range End:") 32 | TextField("", text: $sampleRangeEnd) 33 | .multilineTextAlignment(.trailing) 34 | .frame(width: 100) 35 | } 36 | 37 | Button("Generate Values", action: { 38 | sampledValues = randomApp.sample( 39 | rangeStart: Int(sampleRangeStart) ?? 1, 40 | rangeEnd: Int(sampleRangeEnd) ?? 100, 41 | size: 20, 42 | seed : Int(randomSeed) ?? 20210120 43 | ) 44 | }) 45 | 46 | } 47 | .padding() 48 | 49 | VStack { 50 | TextEditor(text: $sampledValues) 51 | .padding(4) 52 | .border(Color.primary) 53 | .padding() 54 | } 55 | }.frame(width: 500, height: 200) 56 | } 57 | } 58 | 59 | struct ContentView_Previews: PreviewProvider { 60 | static var previews: some View { 61 | ContentView() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /random/random/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSMinimumSystemVersion 22 | $(MACOSX_DEPLOYMENT_TARGET) 23 | 24 | 25 | -------------------------------------------------------------------------------- /random/random/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /random/random/RandomModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class RandomModel : ObservableObject { 4 | 5 | init() { 6 | initEmbeddedR("/Library/Frameworks/R.framework/Resources") 7 | } 8 | 9 | /// Set R's random seed 10 | /// 11 | /// Calls `set.seed()` with the provided seed 12 | /// 13 | /// - Parameter seed: a single value, interpreted as an integer 14 | func setSeed(_ seed: Int) { 15 | do { 16 | _ = try Rlang2(Rf_install("set.seed"), x: seed.protectedSEXP) 17 | RUNPROTECT(1) 18 | } catch { 19 | } 20 | } 21 | 22 | /// Random samples 23 | /// 24 | /// Takes a sample of the specified `size` from `rangeStart`:`rangeEnd` with replacement. 25 | /// 26 | /// The random seed is first set with `set.seed(seed = seed)` and then `sample()` is called 27 | /// to generate the values which are then turned into a comma-separated string with `toString()` 28 | /// (all in R). 29 | /// 30 | /// - Parameters: 31 | /// - rangeStart: start of the range to sample from 32 | /// - rangeEnd: end of the range to sample from 33 | /// - size: a non-negative integer giving the number of items to choose. 34 | /// - seed: the random seed to use 35 | func sample(rangeStart: Int, rangeEnd: Int, size: Int, seed: Int) -> String { 36 | setSeed(seed) 37 | let rCode = "toString(sample(x = \(rangeStart):\(rangeEnd), size = \(size), replace = TRUE))" 38 | let res = safeSilentEvalParseString(rCode) 39 | R_gc() 40 | return(String(res[0]) ?? "") 41 | } 42 | 43 | deinit { Rf_endEmbeddedR(0) } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /random/random/random-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #include "swift-r-bridge.h" 2 | -------------------------------------------------------------------------------- /random/random/random.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /random/random/randomApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // randomApp.swift 3 | // random 4 | // 5 | // Created by boB Rudis on 1/16/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct randomApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /scriptr-2/scriptr.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /scriptr-2/scriptr.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scriptr-2/scriptr.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "swift-argument-parser", 6 | "repositoryURL": "https://github.com/apple/swift-argument-parser", 7 | "state": { 8 | "branch": null, 9 | "revision": "92646c0cdbaca076c8d3d0207891785b3379cbff", 10 | "version": "0.3.1" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /scriptr-2/scriptr/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | is.rud.scriptr 7 | CFBundleName 8 | scriptr 9 | CFBundleShortVersionString 10 | 1 11 | 12 | 13 | -------------------------------------------------------------------------------- /scriptr-2/scriptr/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ArgumentParser 3 | 4 | initEmbeddedR() 5 | 6 | let calculateMeanPerPatient = safeSilentEvalParseString(""" 7 | # https://swcarpentry.github.io/r-novice-inflammation/05-cmdline/index.html 8 | calculate_mean_per_patient <- function(filename) { 9 | 10 | if (file.exists(filename)) { 11 | 12 | dat <- read.csv(file = filename, header = FALSE) 13 | mean_per_patient <- apply(dat, 1, mean) 14 | out <- jsonlite::toJSON(mean_per_patient, pretty = TRUE) 15 | 16 | cat(out, "\n", sep = "") 17 | 18 | return(0L) 19 | 20 | } else { 21 | cat("[]\n") 22 | return(1L) 23 | } 24 | 25 | } 26 | """) 27 | 28 | var ret: CInt = 0 29 | 30 | struct MeanR: ParsableCommand { 31 | 32 | @Argument(help: "CSV file to analyze") 33 | var csvFile: String = "~/books/swiftr/repo/data/inflammation-01.csv" 34 | 35 | mutating func run() throws { 36 | let res = try Rlang2(calculateMeanPerPatient[0], x: csvFile.protectedSEXP) 37 | defer { RUNPROTECT(1) } 38 | if (res.isINTEGER) { 39 | ret = CInt(Int(res)!) 40 | } 41 | } 42 | 43 | } 44 | 45 | MeanR.main() 46 | 47 | Rf_endEmbeddedR(0) 48 | 49 | exit(ret) 50 | 51 | 52 | // MARK: Uncomment this section to get the bare metal R stats app 53 | 54 | //import Foundation 55 | // 56 | //initEmbeddedR() 57 | // 58 | //var ret: CInt = 0 59 | // 60 | //let res = safeSilentEvalParseString(""" 61 | //# https://swcarpentry.github.io/r-novice-inflammation/05-cmdline/index.html 62 | // 63 | //filename <- "\(CommandLine.arguments[1])" 64 | // 65 | //if (file.exists(filename)) { 66 | // 67 | // dat <- read.csv(file = filename, header = FALSE) 68 | // mean_per_patient <- apply(dat, 1, mean) 69 | // out <- jsonlite::toJSON(mean_per_patient, pretty = TRUE) 70 | // 71 | // cat(out, "\n", sep = "") 72 | // 73 | // 0L 74 | // 75 | //} else { 76 | // cat("[]\n") 77 | // 1L 78 | //} 79 | //""") 80 | // 81 | //ret = ((res.count == 1) && (res[0].isINTEGER)) ? CInt(Int(res[0])!) : 1 82 | // 83 | //Rf_endEmbeddedR(0) 84 | // 85 | //exit(ret) 86 | 87 | 88 | // MARK: Uncomment this section to get the Shiny app 89 | 90 | //import Foundation 91 | // 92 | //initEmbeddedR() 93 | // 94 | //let _ = safeSilentEvalParseString(""" 95 | //library(shiny) 96 | // 97 | //runApp(list( 98 | // ui = bootstrapPage( 99 | // numericInput('n', 'Number of obs', 100), 100 | // plotOutput('plot') 101 | // ), 102 | // server = function(input, output) { 103 | // output$plot <- renderPlot({ hist(runif(input$n)) }) 104 | // } 105 | //)) 106 | //""") 107 | // 108 | //Rf_endEmbeddedR(0) 109 | // 110 | -------------------------------------------------------------------------------- /scriptr-2/scriptr/r-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let PROTECT = Rf_protect 4 | let UNPROTECT = Rf_unprotect 5 | 6 | // we only have one R error exception type for the moment. 7 | enum RError: Error { 8 | case tryEvalError(String) 9 | } 10 | 11 | // Swift helper to require(package) 12 | func require(_ pkg: String) throws -> Bool { 13 | 14 | var rErr: CInt = 0 // holds our error return value from evaluation 15 | 16 | let requireCall: SEXP = RPROTECT(Rf_allocVector(SEXPTYPE(LANGSXP), 3+1))! 17 | defer { RUNPROTECT(1) } 18 | SETCAR(requireCall, Rf_install("require")) 19 | 20 | var reqParams: SEXP = CDR(requireCall); 21 | SETCAR(reqParams, RPROTECT(Rf_mkString(pkg))) 22 | defer { RUNPROTECT(1) } 23 | SET_TAG(reqParams, Rf_install("package")) 24 | 25 | reqParams = CDR(reqParams) 26 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 27 | defer { RUNPROTECT(1) } 28 | SET_TAG(reqParams, Rf_install("warn.conflicts")) 29 | 30 | reqParams = CDR(reqParams) 31 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 32 | defer { RUNPROTECT(1) } 33 | SET_TAG(reqParams, Rf_install("quietly")) 34 | 35 | // requireCall is PROTECTed above but Rf_install() may allocate which could kill it so make sure it is safe 36 | let suppressWarningsCall = RPROTECT(Rf_lang2(Rf_install("suppressWarnings"), RPROTECT(requireCall))) 37 | defer { RUNPROTECT(2) } 38 | 39 | let res: SEXP = RPROTECT(R_tryEvalSilent(suppressWarningsCall, R_GlobalEnv, &rErr))! 40 | defer { RUNPROTECT(1) } 41 | 42 | if (rErr == 0) { 43 | if (SEXPTYPE(TYPEOF(res)) == LGLSXP) { 44 | return(Rf_asLogical(res) == 1) 45 | } else { 46 | throw RError.tryEvalError( 47 | "R evaluation error attempting to load. Expected TRUE/FALSE, but found '\(String(cString: Rf_type2char(SEXPTYPE(TYPEOF(res)))))" 48 | ) 49 | } 50 | } else { 51 | throw RError.tryEvalError("R evaluation error attempting to load \(pkg): \(String(cString: R_curErrorBuf()))") 52 | } 53 | 54 | } 55 | 56 | func safeSilentEvalParse(_ vec: [String]) -> [SEXP] { 57 | 58 | let vectorToParse = vec.protectedSEXP 59 | defer { RUNPROTECT(1)} 60 | var status: ParseStatus = PARSE_OK 61 | let parsed = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 62 | defer { RUNPROTECT(1)} 63 | 64 | if (status == PARSE_OK) { 65 | var rErr: CInt = 0 66 | var res: [SEXP] = [SEXP]() 67 | res.reserveCapacity(Int(Rf_length(parsed))) 68 | for idx in 0.. [SEXP] { 86 | 87 | let vectorToParse = [ string ].protectedSEXP 88 | defer { RUNPROTECT(1)} 89 | var status: ParseStatus = PARSE_OK 90 | let parsed = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 91 | defer { RUNPROTECT(1)} 92 | 93 | if (status == PARSE_OK) { 94 | var rErr: CInt = 0 95 | var res: [SEXP] = [SEXP]() 96 | res.reserveCapacity(Int(Rf_length(parsed))) 97 | for idx in 0.. SEXP { 117 | 118 | var err: CInt = 0 119 | 120 | let call_ƒ = RPROTECT(Rf_lang1(call.protectedSEXP)) 121 | defer { RUNPROTECT(2) } 122 | 123 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 124 | defer { RUNPROTECT(1) } 125 | 126 | if (err != 0) { 127 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 128 | } 129 | 130 | return(res) 131 | 132 | } 133 | 134 | 135 | func Rlang2(_ call: SEXP, x: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 136 | 137 | var err: CInt = 0 138 | 139 | let call_ƒ = RPROTECT( 140 | Rf_lang2( 141 | call.protectedSEXP, 142 | x.protectedSEXP 143 | ) 144 | ) 145 | defer { RUNPROTECT(3) } 146 | 147 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 148 | defer { RUNPROTECT(1) } 149 | 150 | if (err != 0) { 151 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 152 | } 153 | 154 | return(res) 155 | 156 | } 157 | 158 | func Rlang3(_ call: SEXP, x: SEXP, y: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 159 | 160 | var err: CInt = 0 161 | 162 | let call_ƒ = RPROTECT( 163 | Rf_lang3( 164 | call.protectedSEXP, 165 | x.protectedSEXP, 166 | y.protectedSEXP 167 | ) 168 | ) 169 | defer { RUNPROTECT(4) } 170 | 171 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 172 | defer { RUNPROTECT(1) } 173 | 174 | if (err != 0) { 175 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 176 | } 177 | 178 | return(res) 179 | 180 | } 181 | 182 | func Rlang4(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 183 | 184 | var err: CInt = 0 185 | 186 | let call_ƒ = RPROTECT( 187 | Rf_lang4( 188 | call.protectedSEXP, 189 | x.protectedSEXP, 190 | y.protectedSEXP, 191 | z.protectedSEXP 192 | ) 193 | ) 194 | defer { RUNPROTECT(5) } 195 | 196 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 197 | defer { RUNPROTECT(1) } 198 | 199 | if (err != 0) { 200 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 201 | } 202 | 203 | return(res) 204 | 205 | } 206 | 207 | func Rlang5(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, a: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 208 | 209 | var err: CInt = 0 210 | 211 | let call_ƒ = RPROTECT( 212 | Rf_lang5( 213 | call.protectedSEXP, 214 | x.protectedSEXP, 215 | y.protectedSEXP, 216 | z.protectedSEXP, 217 | a.protectedSEXP 218 | ) 219 | ) 220 | defer { RUNPROTECT(6) } 221 | 222 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 223 | defer { RUNPROTECT(1) } 224 | 225 | if (err != 0) { 226 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 227 | } 228 | 229 | return(res) 230 | 231 | } 232 | 233 | func Rlang6(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, a: SEXP, b: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 234 | 235 | var err: CInt = 0 236 | 237 | let call_ƒ = RPROTECT( 238 | Rf_lang6( 239 | call.protectedSEXP, 240 | x.protectedSEXP, 241 | y.protectedSEXP, 242 | z.protectedSEXP, 243 | a.protectedSEXP, 244 | b.protectedSEXP 245 | ) 246 | ) 247 | defer { RUNPROTECT(7) } 248 | 249 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 250 | defer { RUNPROTECT(1) } 251 | 252 | if (err != 0) { 253 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 254 | } 255 | 256 | return(res) 257 | 258 | } 259 | -------------------------------------------------------------------------------- /scriptr-2/scriptr/scriptr-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #include "swift-r-bridge.h" 6 | -------------------------------------------------------------------------------- /scriptr-2/scriptr/scriptr.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.disable-library-validation 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scriptr-2/scriptr/sexp-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | init?(_ sexp: SEXP) { 5 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 6 | switch (TYPEOF(sexp)) { 7 | case STRSXP: self = String(cString: CHAR_Rf_asChar(sexp)) 8 | default: return(nil) 9 | } 10 | } else { 11 | return(nil) 12 | } 13 | } 14 | var SEXP: SEXP { return(Rf_mkString(self)) } 15 | var protectedSEXP: SEXP { 16 | return(SEXP.protectedSEXP) 17 | } 18 | } 19 | 20 | extension Bool { 21 | init?(_ sexp: SEXP) { 22 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 23 | switch (TYPEOF(sexp)) { 24 | case LGLSXP: self = (Rf_asLogical(sexp) == 1) 25 | default: return(nil) 26 | } 27 | } else { 28 | return(nil) 29 | } 30 | } 31 | var SEXP: SEXP { return(Rf_ScalarLogical(self ? 1 : 0)) } 32 | var protectedSEXP: SEXP { 33 | return(SEXP.protectedSEXP) 34 | } 35 | } 36 | 37 | extension Int { 38 | init?(_ sexp: SEXP) { 39 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 40 | switch (TYPEOF(sexp)) { 41 | case INTSXP: self = Int(Rf_asInteger(sexp)) 42 | default: return(nil) 43 | } 44 | } else { 45 | return(nil) 46 | } 47 | } 48 | var SEXP: SEXP { return(Rf_ScalarInteger(CInt(self))) } 49 | var protectedSEXP: SEXP { 50 | return(SEXP.protectedSEXP) 51 | } 52 | } 53 | 54 | extension Double { 55 | 56 | init?(_ sexp: SEXP) { 57 | 58 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 59 | switch (TYPEOF(sexp)) { 60 | case REALSXP: self = Rf_asReal(sexp) 61 | case INTSXP: self = Double(Int(sexp)!) // a tad more dangerous 62 | case STRSXP: self = Double(String(sexp)!)! // a tad more dangerous 63 | default: return(nil) 64 | } 65 | } else { 66 | return(nil) 67 | } 68 | 69 | } 70 | 71 | var SEXP: SEXP { return(Rf_ScalarReal(self)) } 72 | var protectedSEXP: SEXP { 73 | return(SEXP.protectedSEXP) 74 | } 75 | } 76 | 77 | extension Array where Element == Bool { 78 | init?(_ sexp: SEXP) { 79 | if (sexp.isLOGICAL) { 80 | var val : [Bool] = [Bool]() 81 | val.reserveCapacity(Int(sexp.count)) 82 | let LOGICALVEC = LOGICAL(sexp) 83 | for idx in 0.. 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void initEmbeddedR(void); 13 | void printFromR(const char *message); 14 | SEXP RPROTECT(SEXP x); 15 | void RUNPROTECT(int x); 16 | const char *CHAR_Rf_asChar(SEXP x); 17 | const char *RVecElToCstr(SEXP x, R_xlen_t i); 18 | 19 | #endif /* swift_r_bridge_h */ 20 | -------------------------------------------------------------------------------- /scriptr-2/scriptr/utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Date { 4 | 5 | init(fromISODate: String) { 6 | self = Date.iso8601Formatter.date(from: fromISODate)! 7 | } 8 | 9 | var ISODate: String { 10 | Date.iso8601Formatter.string(from: self) 11 | } 12 | 13 | static let iso8601Formatter: ISO8601DateFormatter = { 14 | let fmt = ISO8601DateFormatter() 15 | fmt.formatOptions = [ .withFullDate, .withDashSeparatorInDate ] 16 | return(fmt) 17 | }() 18 | 19 | } 20 | 21 | extension Double { 22 | 23 | var whole: Self { rounded(.toNearestOrAwayFromZero) } 24 | var fraction: Self { truncatingRemainder(dividingBy: 1) } 25 | 26 | var decimalTimeToHM: String { 27 | return(String(format: "%02d:%02d", Int(whole), Int(fraction*60))) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /scriptr/scriptr.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /scriptr/scriptr.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scriptr/scriptr/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | is.rud.scriptr 7 | CFBundleName 8 | scriptr 9 | CFBundleShortVersionString 10 | 1 11 | 12 | 13 | -------------------------------------------------------------------------------- /scriptr/scriptr/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | initEmbeddedR() 4 | 5 | // if we care about the returned SEXP use a variable instead of _ 6 | let _ = R_ParseEvalString(""" 7 | cat("Hello, World!\n") 8 | """, R_GlobalEnv) 9 | 10 | let vectorToParse = [""" 11 | cat("Hello, again, World!\n") 12 | """].protectedSEXP 13 | defer { RUNPROTECT(1) } 14 | 15 | var status: ParseStatus = PARSE_OK 16 | let parsed = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 17 | defer { RUNPROTECT(1)} 18 | 19 | if (status == PARSE_OK) { 20 | var rErr: CInt = 0 21 | for idx in 0.. \(String(cString: CHAR_Rf_asChar(sexp)))") 54 | } else { 55 | print("Evaluated result: [] \([String](sexp)!)") 56 | } 57 | } else if (sexp.isREAL) { 58 | if (sexp.count == 1) { 59 | print("Evaluated result: \(Double(sexp)!)") 60 | } else { 61 | print("Evaluated result: [] \([Double](sexp)!)") 62 | } 63 | } else if (sexp.isINTEGER) { 64 | if (sexp.count == 1) { 65 | print("Evaluated result: \(Int(sexp)!)") 66 | } else { 67 | print("Evaluated result: [] \([Int](sexp)!)") 68 | } 69 | } else if (sexp.isLOGICAL) { 70 | if (sexp.count == 1) { 71 | print("Evaluated result: \(Bool(sexp)!)") 72 | } else { 73 | print("Evaluated result: [] \([Bool](sexp)!)") 74 | } 75 | } else { 76 | print("Evaluted result is of type <\(sexp.type)> which we do not handle yet") 77 | } 78 | } 79 | } 80 | 81 | parsedProtectedSEXPs.UNPROTECT() 82 | 83 | 84 | let res = safeSilentEvalParseString("mtcars") 85 | defer { res.UNPROTECT() } 86 | 87 | if (res.count == 1) { 88 | let mtcars = res[0] 89 | 90 | print(mtcars.classAttrib!) 91 | print(mtcars.namesAttrib!) 92 | print(mtcars.mode!) 93 | print(mtcars.anyNA!) 94 | } 95 | 96 | Rf_endEmbeddedR(0) 97 | 98 | // 99 | //let app = [""" 100 | //library(shiny) 101 | // 102 | //runApp(list( 103 | // ui = bootstrapPage( 104 | // numericInput('n', 'Number of obs', 100), 105 | // plotOutput('plot') 106 | // ), 107 | // server = function(input, output) { 108 | // output$plot <- renderPlot({ hist(runif(input$n)) }) 109 | // } 110 | // )) 111 | //"""] 112 | // 113 | //let appRes = safeSilentEvalParse(app) 114 | //appRes.UNPROTECT() 115 | 116 | -------------------------------------------------------------------------------- /scriptr/scriptr/r-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let PROTECT = Rf_protect 4 | let UNPROTECT = Rf_unprotect 5 | 6 | // we only have one R error exception type for the moment. 7 | enum RError: Error { 8 | case tryEvalError(String) 9 | } 10 | 11 | // Swift helper to require(package) 12 | func require(_ pkg: String) throws -> Bool { 13 | 14 | var rErr: CInt = 0 // holds our error return value from evaluation 15 | 16 | let requireCall: SEXP = RPROTECT(Rf_allocVector(SEXPTYPE(LANGSXP), 3+1))! 17 | defer { RUNPROTECT(1) } 18 | SETCAR(requireCall, Rf_install("require")) 19 | 20 | var reqParams: SEXP = CDR(requireCall); 21 | SETCAR(reqParams, RPROTECT(Rf_mkString(pkg))) 22 | defer { RUNPROTECT(1) } 23 | SET_TAG(reqParams, Rf_install("package")) 24 | 25 | reqParams = CDR(reqParams) 26 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 27 | defer { RUNPROTECT(1) } 28 | SET_TAG(reqParams, Rf_install("warn.conflicts")) 29 | 30 | reqParams = CDR(reqParams) 31 | SETCAR(reqParams, RPROTECT(Rf_ScalarLogical(1))) 32 | defer { RUNPROTECT(1) } 33 | SET_TAG(reqParams, Rf_install("quietly")) 34 | 35 | // requireCall is PROTECTed above but Rf_install() may allocate which could kill it so make sure it is safe 36 | let suppressWarningsCall = RPROTECT(Rf_lang2(Rf_install("suppressWarnings"), RPROTECT(requireCall))) 37 | defer { RUNPROTECT(2) } 38 | 39 | let res: SEXP = RPROTECT(R_tryEvalSilent(suppressWarningsCall, R_GlobalEnv, &rErr))! 40 | defer { RUNPROTECT(1) } 41 | 42 | if (rErr == 0) { 43 | if (SEXPTYPE(TYPEOF(res)) == LGLSXP) { 44 | return(Rf_asLogical(res) == 1) 45 | } else { 46 | throw RError.tryEvalError( 47 | "R evaluation error attempting to load. Expected TRUE/FALSE, but found '\(String(cString: Rf_type2char(SEXPTYPE(TYPEOF(res)))))" 48 | ) 49 | } 50 | } else { 51 | throw RError.tryEvalError("R evaluation error attempting to load \(pkg): \(String(cString: R_curErrorBuf()))") 52 | } 53 | 54 | } 55 | 56 | func safeSilentEvalParse(_ vec: [String]) -> [SEXP] { 57 | 58 | let vectorToParse = vec.protectedSEXP 59 | defer { RUNPROTECT(1)} 60 | var status: ParseStatus = PARSE_OK 61 | let parsed = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 62 | defer { RUNPROTECT(1)} 63 | 64 | if (status == PARSE_OK) { 65 | var rErr: CInt = 0 66 | var res: [SEXP] = [SEXP]() 67 | res.reserveCapacity(Int(Rf_length(parsed))) 68 | for idx in 0.. [SEXP] { 86 | 87 | let vectorToParse = [ string ].protectedSEXP 88 | defer { RUNPROTECT(1)} 89 | var status: ParseStatus = PARSE_OK 90 | let parsed = RPROTECT(R_ParseVector(vectorToParse, -1, &status, R_NilValue)) 91 | defer { RUNPROTECT(1)} 92 | 93 | if (status == PARSE_OK) { 94 | var rErr: CInt = 0 95 | var res: [SEXP] = [SEXP]() 96 | res.reserveCapacity(Int(Rf_length(parsed))) 97 | for idx in 0.. SEXP { 117 | 118 | var err: CInt = 0 119 | 120 | let call_ƒ = RPROTECT(Rf_lang1(call.protectedSEXP)) 121 | defer { RUNPROTECT(2) } 122 | 123 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 124 | defer { RUNPROTECT(1) } 125 | 126 | if (err != 0) { 127 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 128 | } 129 | 130 | return(res) 131 | 132 | } 133 | 134 | 135 | func Rlang2(_ call: SEXP, x: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 136 | 137 | var err: CInt = 0 138 | 139 | let call_ƒ = RPROTECT( 140 | Rf_lang2( 141 | call.protectedSEXP, 142 | x.protectedSEXP 143 | ) 144 | ) 145 | defer { RUNPROTECT(3) } 146 | 147 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 148 | defer { RUNPROTECT(1) } 149 | 150 | if (err != 0) { 151 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 152 | } 153 | 154 | return(res) 155 | 156 | } 157 | 158 | func Rlang3(_ call: SEXP, x: SEXP, y: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 159 | 160 | var err: CInt = 0 161 | 162 | let call_ƒ = RPROTECT( 163 | Rf_lang3( 164 | call.protectedSEXP, 165 | x.protectedSEXP, 166 | y.protectedSEXP 167 | ) 168 | ) 169 | defer { RUNPROTECT(4) } 170 | 171 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 172 | defer { RUNPROTECT(1) } 173 | 174 | if (err != 0) { 175 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 176 | } 177 | 178 | return(res) 179 | 180 | } 181 | 182 | func Rlang4(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 183 | 184 | var err: CInt = 0 185 | 186 | let call_ƒ = RPROTECT( 187 | Rf_lang4( 188 | call.protectedSEXP, 189 | x.protectedSEXP, 190 | y.protectedSEXP, 191 | z.protectedSEXP 192 | ) 193 | ) 194 | defer { RUNPROTECT(5) } 195 | 196 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 197 | defer { RUNPROTECT(1) } 198 | 199 | if (err != 0) { 200 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 201 | } 202 | 203 | return(res) 204 | 205 | } 206 | 207 | func Rlang5(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, a: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 208 | 209 | var err: CInt = 0 210 | 211 | let call_ƒ = RPROTECT( 212 | Rf_lang5( 213 | call.protectedSEXP, 214 | x.protectedSEXP, 215 | y.protectedSEXP, 216 | z.protectedSEXP, 217 | a.protectedSEXP 218 | ) 219 | ) 220 | defer { RUNPROTECT(6) } 221 | 222 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 223 | defer { RUNPROTECT(1) } 224 | 225 | if (err != 0) { 226 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 227 | } 228 | 229 | return(res) 230 | 231 | } 232 | 233 | func Rlang6(_ call: SEXP, x: SEXP, y: SEXP, z: SEXP, a: SEXP, b: SEXP, env: SEXP = R_GlobalEnv) throws -> SEXP { 234 | 235 | var err: CInt = 0 236 | 237 | let call_ƒ = RPROTECT( 238 | Rf_lang6( 239 | call.protectedSEXP, 240 | x.protectedSEXP, 241 | y.protectedSEXP, 242 | z.protectedSEXP, 243 | a.protectedSEXP, 244 | b.protectedSEXP 245 | ) 246 | ) 247 | defer { RUNPROTECT(7) } 248 | 249 | let res: SEXP = RPROTECT(R_tryEvalSilent(call_ƒ, env, &err)) ?? R_NilValue 250 | defer { RUNPROTECT(1) } 251 | 252 | if (err != 0) { 253 | throw RError.tryEvalError(String(cString: R_curErrorBuf())) 254 | } 255 | 256 | return(res) 257 | 258 | } 259 | -------------------------------------------------------------------------------- /scriptr/scriptr/scriptr-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #include "swift-r-bridge.h" 6 | -------------------------------------------------------------------------------- /scriptr/scriptr/scriptr.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.disable-library-validation 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scriptr/scriptr/sexp-utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | init?(_ sexp: SEXP) { 5 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 6 | switch (TYPEOF(sexp)) { 7 | case STRSXP: self = String(cString: CHAR_Rf_asChar(sexp)) 8 | default: return(nil) 9 | } 10 | } else { 11 | return(nil) 12 | } 13 | } 14 | var SEXP: SEXP { return(Rf_mkString(self)) } 15 | var protectedSEXP: SEXP { 16 | return(SEXP.protectedSEXP) 17 | } 18 | } 19 | 20 | extension Bool { 21 | init?(_ sexp: SEXP) { 22 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 23 | switch (TYPEOF(sexp)) { 24 | case LGLSXP: self = (Rf_asLogical(sexp) == 1) 25 | default: return(nil) 26 | } 27 | } else { 28 | return(nil) 29 | } 30 | } 31 | var SEXP: SEXP { return(Rf_ScalarLogical(self ? 1 : 0)) } 32 | var protectedSEXP: SEXP { 33 | return(SEXP.protectedSEXP) 34 | } 35 | } 36 | 37 | extension Int { 38 | init?(_ sexp: SEXP) { 39 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 40 | switch (TYPEOF(sexp)) { 41 | case INTSXP: self = Int(Rf_asInteger(sexp)) 42 | default: return(nil) 43 | } 44 | } else { 45 | return(nil) 46 | } 47 | } 48 | var SEXP: SEXP { return(Rf_ScalarInteger(CInt(self))) } 49 | var protectedSEXP: SEXP { 50 | return(SEXP.protectedSEXP) 51 | } 52 | } 53 | 54 | extension Double { 55 | 56 | init?(_ sexp: SEXP) { 57 | 58 | if ((sexp != R_NilValue) && (Rf_length(sexp) == 1)) { 59 | switch (TYPEOF(sexp)) { 60 | case REALSXP: self = Rf_asReal(sexp) 61 | case INTSXP: self = Double(Int(sexp)!) // a tad more dangerous 62 | case STRSXP: self = Double(String(sexp)!)! // a tad more dangerous 63 | default: return(nil) 64 | } 65 | } else { 66 | return(nil) 67 | } 68 | 69 | } 70 | 71 | var SEXP: SEXP { return(Rf_ScalarReal(self)) } 72 | var protectedSEXP: SEXP { 73 | return(SEXP.protectedSEXP) 74 | } 75 | } 76 | 77 | extension Array where Element == Bool { 78 | init?(_ sexp: SEXP) { 79 | if (sexp.isLOGICAL) { 80 | var val : [Bool] = [Bool]() 81 | val.reserveCapacity(Int(sexp.count)) 82 | let LOGICALVEC = LOGICAL(sexp) 83 | for idx in 0.. 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void initEmbeddedR(void); 13 | void printFromR(const char *message); 14 | SEXP RPROTECT(SEXP x); 15 | void RUNPROTECT(int x); 16 | const char *CHAR_Rf_asChar(SEXP x); 17 | const char *RVecElToCstr(SEXP x, R_xlen_t i); 18 | 19 | #endif /* swift_r_bridge_h */ 20 | -------------------------------------------------------------------------------- /scriptr/scriptr/utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Date { 4 | 5 | init(fromISODate: String) { 6 | self = Date.iso8601Formatter.date(from: fromISODate)! 7 | } 8 | 9 | var ISODate: String { 10 | Date.iso8601Formatter.string(from: self) 11 | } 12 | 13 | static let iso8601Formatter: ISO8601DateFormatter = { 14 | let fmt = ISO8601DateFormatter() 15 | fmt.formatOptions = [ .withFullDate, .withDashSeparatorInDate ] 16 | return(fmt) 17 | }() 18 | 19 | } 20 | 21 | extension Double { 22 | 23 | var whole: Self { rounded(.toNearestOrAwayFromZero) } 24 | var fraction: Self { truncatingRemainder(dividingBy: 1) } 25 | 26 | var decimalTimeToHM: String { 27 | return(String(format: "%02d:%02d", Int(whole), Int(fraction*60))) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /secondr/secondr.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 01872DBF259FA39000E9CE7B /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01872DBE259FA39000E9CE7B /* main.swift */; }; 11 | 01872DC9259FA4FE00E9CE7B /* swift-r-bridge.c in Sources */ = {isa = PBXBuildFile; fileRef = 01872DC8259FA4FE00E9CE7B /* swift-r-bridge.c */; }; 12 | 01872DCD259FA7C900E9CE7B /* R.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01872DCC259FA7C900E9CE7B /* R.framework */; }; 13 | 01872DD3259FAAC600E9CE7B /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 01872DD2259FAAC600E9CE7B /* libc++.tbd */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | 01872DB9259FA38F00E9CE7B /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = /usr/share/man/man1/; 21 | dstSubfolderSpec = 0; 22 | files = ( 23 | ); 24 | runOnlyForDeploymentPostprocessing = 1; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 01872DBB259FA38F00E9CE7B /* secondr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = secondr; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 01872DBE259FA39000E9CE7B /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 31 | 01872DC6259FA4FE00E9CE7B /* secondr-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "secondr-Bridging-Header.h"; sourceTree = ""; }; 32 | 01872DC7259FA4FE00E9CE7B /* swift-r-bridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "swift-r-bridge.h"; sourceTree = ""; }; 33 | 01872DC8259FA4FE00E9CE7B /* swift-r-bridge.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "swift-r-bridge.c"; sourceTree = ""; }; 34 | 01872DCC259FA7C900E9CE7B /* R.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = R.framework; path = ../../../../Library/Frameworks/R.framework; sourceTree = ""; }; 35 | 01872DD2259FAAC600E9CE7B /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; 36 | /* End PBXFileReference section */ 37 | 38 | /* Begin PBXFrameworksBuildPhase section */ 39 | 01872DB8259FA38F00E9CE7B /* Frameworks */ = { 40 | isa = PBXFrameworksBuildPhase; 41 | buildActionMask = 2147483647; 42 | files = ( 43 | 01872DD3259FAAC600E9CE7B /* libc++.tbd in Frameworks */, 44 | 01872DCD259FA7C900E9CE7B /* R.framework in Frameworks */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | 01872DB2259FA38F00E9CE7B = { 52 | isa = PBXGroup; 53 | children = ( 54 | 01872DBD259FA39000E9CE7B /* secondr */, 55 | 01872DBC259FA38F00E9CE7B /* Products */, 56 | 01872DCB259FA7C900E9CE7B /* Frameworks */, 57 | ); 58 | sourceTree = ""; 59 | }; 60 | 01872DBC259FA38F00E9CE7B /* Products */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 01872DBB259FA38F00E9CE7B /* secondr */, 64 | ); 65 | name = Products; 66 | sourceTree = ""; 67 | }; 68 | 01872DBD259FA39000E9CE7B /* secondr */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 01872DBE259FA39000E9CE7B /* main.swift */, 72 | 01872DC7259FA4FE00E9CE7B /* swift-r-bridge.h */, 73 | 01872DC8259FA4FE00E9CE7B /* swift-r-bridge.c */, 74 | 01872DC6259FA4FE00E9CE7B /* secondr-Bridging-Header.h */, 75 | ); 76 | path = secondr; 77 | sourceTree = ""; 78 | }; 79 | 01872DCB259FA7C900E9CE7B /* Frameworks */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 01872DD2259FAAC600E9CE7B /* libc++.tbd */, 83 | 01872DCC259FA7C900E9CE7B /* R.framework */, 84 | ); 85 | name = Frameworks; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | 01872DBA259FA38F00E9CE7B /* secondr */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = 01872DC2259FA39000E9CE7B /* Build configuration list for PBXNativeTarget "secondr" */; 94 | buildPhases = ( 95 | 01872DB7259FA38F00E9CE7B /* Sources */, 96 | 01872DB8259FA38F00E9CE7B /* Frameworks */, 97 | 01872DB9259FA38F00E9CE7B /* CopyFiles */, 98 | ); 99 | buildRules = ( 100 | ); 101 | dependencies = ( 102 | ); 103 | name = secondr; 104 | productName = secondr; 105 | productReference = 01872DBB259FA38F00E9CE7B /* secondr */; 106 | productType = "com.apple.product-type.tool"; 107 | }; 108 | /* End PBXNativeTarget section */ 109 | 110 | /* Begin PBXProject section */ 111 | 01872DB3259FA38F00E9CE7B /* Project object */ = { 112 | isa = PBXProject; 113 | attributes = { 114 | LastSwiftUpdateCheck = 1230; 115 | LastUpgradeCheck = 1230; 116 | TargetAttributes = { 117 | 01872DBA259FA38F00E9CE7B = { 118 | CreatedOnToolsVersion = 12.3; 119 | LastSwiftMigration = 1230; 120 | }; 121 | }; 122 | }; 123 | buildConfigurationList = 01872DB6259FA38F00E9CE7B /* Build configuration list for PBXProject "secondr" */; 124 | compatibilityVersion = "Xcode 9.3"; 125 | developmentRegion = en; 126 | hasScannedForEncodings = 0; 127 | knownRegions = ( 128 | en, 129 | Base, 130 | ); 131 | mainGroup = 01872DB2259FA38F00E9CE7B; 132 | productRefGroup = 01872DBC259FA38F00E9CE7B /* Products */; 133 | projectDirPath = ""; 134 | projectRoot = ""; 135 | targets = ( 136 | 01872DBA259FA38F00E9CE7B /* secondr */, 137 | ); 138 | }; 139 | /* End PBXProject section */ 140 | 141 | /* Begin PBXSourcesBuildPhase section */ 142 | 01872DB7259FA38F00E9CE7B /* Sources */ = { 143 | isa = PBXSourcesBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | 01872DBF259FA39000E9CE7B /* main.swift in Sources */, 147 | 01872DC9259FA4FE00E9CE7B /* swift-r-bridge.c in Sources */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | /* End PBXSourcesBuildPhase section */ 152 | 153 | /* Begin XCBuildConfiguration section */ 154 | 01872DC0259FA39000E9CE7B /* Debug */ = { 155 | isa = XCBuildConfiguration; 156 | buildSettings = { 157 | ALWAYS_SEARCH_USER_PATHS = NO; 158 | CLANG_ANALYZER_NONNULL = YES; 159 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 160 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 161 | CLANG_CXX_LIBRARY = "libc++"; 162 | CLANG_ENABLE_MODULES = YES; 163 | CLANG_ENABLE_OBJC_ARC = YES; 164 | CLANG_ENABLE_OBJC_WEAK = YES; 165 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 166 | CLANG_WARN_BOOL_CONVERSION = YES; 167 | CLANG_WARN_COMMA = YES; 168 | CLANG_WARN_CONSTANT_CONVERSION = YES; 169 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 170 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 171 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 172 | CLANG_WARN_EMPTY_BODY = YES; 173 | CLANG_WARN_ENUM_CONVERSION = YES; 174 | CLANG_WARN_INFINITE_RECURSION = YES; 175 | CLANG_WARN_INT_CONVERSION = YES; 176 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 177 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 178 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 179 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 180 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 181 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 182 | CLANG_WARN_STRICT_PROTOTYPES = YES; 183 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 184 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 185 | CLANG_WARN_UNREACHABLE_CODE = YES; 186 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 187 | COPY_PHASE_STRIP = NO; 188 | DEBUG_INFORMATION_FORMAT = dwarf; 189 | ENABLE_STRICT_OBJC_MSGSEND = YES; 190 | ENABLE_TESTABILITY = YES; 191 | GCC_C_LANGUAGE_STANDARD = gnu11; 192 | GCC_DYNAMIC_NO_PIC = NO; 193 | GCC_NO_COMMON_BLOCKS = YES; 194 | GCC_OPTIMIZATION_LEVEL = 0; 195 | GCC_PREPROCESSOR_DEFINITIONS = ( 196 | "DEBUG=1", 197 | "$(inherited)", 198 | ); 199 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 200 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 201 | GCC_WARN_UNDECLARED_SELECTOR = YES; 202 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 203 | GCC_WARN_UNUSED_FUNCTION = YES; 204 | GCC_WARN_UNUSED_VARIABLE = YES; 205 | MACOSX_DEPLOYMENT_TARGET = 11.1; 206 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 207 | MTL_FAST_MATH = YES; 208 | ONLY_ACTIVE_ARCH = YES; 209 | SDKROOT = macosx; 210 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 211 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 212 | }; 213 | name = Debug; 214 | }; 215 | 01872DC1259FA39000E9CE7B /* Release */ = { 216 | isa = XCBuildConfiguration; 217 | buildSettings = { 218 | ALWAYS_SEARCH_USER_PATHS = NO; 219 | CLANG_ANALYZER_NONNULL = YES; 220 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 221 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 222 | CLANG_CXX_LIBRARY = "libc++"; 223 | CLANG_ENABLE_MODULES = YES; 224 | CLANG_ENABLE_OBJC_ARC = YES; 225 | CLANG_ENABLE_OBJC_WEAK = YES; 226 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 227 | CLANG_WARN_BOOL_CONVERSION = YES; 228 | CLANG_WARN_COMMA = YES; 229 | CLANG_WARN_CONSTANT_CONVERSION = YES; 230 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 231 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 232 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 233 | CLANG_WARN_EMPTY_BODY = YES; 234 | CLANG_WARN_ENUM_CONVERSION = YES; 235 | CLANG_WARN_INFINITE_RECURSION = YES; 236 | CLANG_WARN_INT_CONVERSION = YES; 237 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 238 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 239 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 240 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 241 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 242 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 243 | CLANG_WARN_STRICT_PROTOTYPES = YES; 244 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 245 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 246 | CLANG_WARN_UNREACHABLE_CODE = YES; 247 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 248 | COPY_PHASE_STRIP = NO; 249 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 250 | ENABLE_NS_ASSERTIONS = NO; 251 | ENABLE_STRICT_OBJC_MSGSEND = YES; 252 | GCC_C_LANGUAGE_STANDARD = gnu11; 253 | GCC_NO_COMMON_BLOCKS = YES; 254 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 255 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 256 | GCC_WARN_UNDECLARED_SELECTOR = YES; 257 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 258 | GCC_WARN_UNUSED_FUNCTION = YES; 259 | GCC_WARN_UNUSED_VARIABLE = YES; 260 | MACOSX_DEPLOYMENT_TARGET = 11.1; 261 | MTL_ENABLE_DEBUG_INFO = NO; 262 | MTL_FAST_MATH = YES; 263 | SDKROOT = macosx; 264 | SWIFT_COMPILATION_MODE = wholemodule; 265 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 266 | }; 267 | name = Release; 268 | }; 269 | 01872DC3259FA39000E9CE7B /* Debug */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | CLANG_ENABLE_MODULES = YES; 273 | CODE_SIGN_STYLE = Automatic; 274 | DEVELOPMENT_TEAM = CBY22P58G8; 275 | ENABLE_HARDENED_RUNTIME = YES; 276 | FRAMEWORK_SEARCH_PATHS = ( 277 | "$(inherited)", 278 | "$(LOCAL_LIBRARY_DIR)/Frameworks", 279 | ); 280 | LD_RUNPATH_SEARCH_PATHS = ( 281 | "$(inherited)", 282 | "@executable_path/../Frameworks", 283 | "@loader_path/../Frameworks", 284 | ); 285 | PRODUCT_NAME = "$(TARGET_NAME)"; 286 | SWIFT_OBJC_BRIDGING_HEADER = "secondr/secondr-Bridging-Header.h"; 287 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 288 | SWIFT_VERSION = 5.0; 289 | SYSTEM_HEADER_SEARCH_PATHS = "/Library/Frameworks/R.framework/Headers/**"; 290 | }; 291 | name = Debug; 292 | }; 293 | 01872DC4259FA39000E9CE7B /* Release */ = { 294 | isa = XCBuildConfiguration; 295 | buildSettings = { 296 | CLANG_ENABLE_MODULES = YES; 297 | CODE_SIGN_STYLE = Automatic; 298 | DEVELOPMENT_TEAM = CBY22P58G8; 299 | ENABLE_HARDENED_RUNTIME = YES; 300 | FRAMEWORK_SEARCH_PATHS = ( 301 | "$(inherited)", 302 | "$(LOCAL_LIBRARY_DIR)/Frameworks", 303 | ); 304 | LD_RUNPATH_SEARCH_PATHS = ( 305 | "$(inherited)", 306 | "@executable_path/../Frameworks", 307 | "@loader_path/../Frameworks", 308 | ); 309 | PRODUCT_NAME = "$(TARGET_NAME)"; 310 | SWIFT_OBJC_BRIDGING_HEADER = "secondr/secondr-Bridging-Header.h"; 311 | SWIFT_VERSION = 5.0; 312 | SYSTEM_HEADER_SEARCH_PATHS = "/Library/Frameworks/R.framework/Headers/**"; 313 | }; 314 | name = Release; 315 | }; 316 | /* End XCBuildConfiguration section */ 317 | 318 | /* Begin XCConfigurationList section */ 319 | 01872DB6259FA38F00E9CE7B /* Build configuration list for PBXProject "secondr" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | 01872DC0259FA39000E9CE7B /* Debug */, 323 | 01872DC1259FA39000E9CE7B /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | 01872DC2259FA39000E9CE7B /* Build configuration list for PBXNativeTarget "secondr" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | 01872DC3259FA39000E9CE7B /* Debug */, 332 | 01872DC4259FA39000E9CE7B /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | /* End XCConfigurationList section */ 338 | }; 339 | rootObject = 01872DB3259FA38F00E9CE7B /* Project object */; 340 | } 341 | -------------------------------------------------------------------------------- /secondr/secondr.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /secondr/secondr.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /secondr/secondr/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | initEmbeddedR() 4 | 5 | //printFromR("Hello, World! (from R)\n") 6 | 7 | debugPrint("Hello") 8 | debugPrint(12.25761.decimalTimeToHM) 9 | 10 | Rf_endEmbeddedR(0) 11 | -------------------------------------------------------------------------------- /secondr/secondr/secondr-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #include "swift-r-bridge.h" 6 | -------------------------------------------------------------------------------- /secondr/secondr/swift-r-bridge.c: -------------------------------------------------------------------------------- 1 | #include "swift-r-bridge.h" 2 | 3 | void initEmbeddedR() { 4 | if (!getenv("R_HOME")) setenv("R_HOME", "/Library/Frameworks/R.framework/Resources", 1); 5 | char *argv[] = { "swiftR", "--gui=none", "--no-save", "--silent", "--vanilla", "--slave", "--no-readline", "" }; 6 | Rf_initEmbeddedR(7, argv); 7 | R_ReplDLLinit(); 8 | } 9 | 10 | void printFromR(const char *message) { 11 | Rprintf(message); 12 | } 13 | -------------------------------------------------------------------------------- /secondr/secondr/swift-r-bridge.h: -------------------------------------------------------------------------------- 1 | #ifndef swift_r_bridge_h 2 | #define swift_r_bridge_h 3 | 4 | #define USE_RINTERNALS 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void initEmbeddedR(void); 12 | void printFromR(const char *message); 13 | 14 | #endif /* swift_r_bridge_h */ 15 | -------------------------------------------------------------------------------- /shiny/shiny.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /shiny/shiny.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /shiny/shiny.xcodeproj/xcshareddata/xcschemes/shiny.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 | -------------------------------------------------------------------------------- /shiny/shiny/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 | -------------------------------------------------------------------------------- /shiny/shiny/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /shiny/shiny/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /shiny/shiny/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | 5 | @StateObject var webViewStore = WebViewStore() 6 | 7 | var body: some View { 8 | WebView(webView: webViewStore.webView) 9 | .frame(width: 900, height: 500, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) 10 | .onReceive(NotificationCenter.default.publisher(for: NSApplication.willTerminateNotification)) { _ in 11 | webViewStore.killit() 12 | } 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /shiny/shiny/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSMinimumSystemVersion 22 | $(MACOSX_DEPLOYMENT_TARGET) 23 | NSSupportsSuddenTermination 24 | 25 | NSSupportsAutomaticTermination 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSTemporaryExceptionAllowsInsecureHTTPSLoads 34 | 35 | NSIncludesSubdomains 36 | 37 | NSTemporaryExceptionAllowsInsecureHTTPLoads 38 | 39 | NSTemporaryExceptionMinimumTLSVersion 40 | 1.0 41 | NSTemporaryExceptionRequiresForwardSecrecy 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /shiny/shiny/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /shiny/shiny/ShinyModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class RandomModel : ObservableObject { 4 | 5 | init() { 6 | initEmbeddedR("/Library/Frameworks/R.framework/Resources") 7 | } 8 | 9 | /// Set R's random seed 10 | /// 11 | /// Calls `set.seed()` with the provided seed 12 | /// 13 | /// - Parameter seed: a single value, interpreted as an integer 14 | func setSeed(_ seed: Int) { 15 | do { 16 | _ = try Rlang2(Rf_install("set.seed"), x: seed.protectedSEXP) 17 | RUNPROTECT(1) 18 | } catch { 19 | } 20 | } 21 | 22 | /// Random samples 23 | /// 24 | /// Takes a sample of the specified `size` from `rangeStart`:`rangeEnd` with replacement. 25 | /// 26 | /// The random seed is first set with `set.seed(seed = seed)` and then `sample()` is called 27 | /// to generate the values which are then turned into a comma-separated string with `toString()` 28 | /// (all in R). 29 | /// 30 | /// - Parameters: 31 | /// - rangeStart: start of the range to sample from 32 | /// - rangeEnd: end of the range to sample from 33 | /// - size: a non-negative integer giving the number of items to choose. 34 | /// - seed: the random seed to use 35 | func sample(rangeStart: Int, rangeEnd: Int, size: Int, seed: Int) -> String { 36 | setSeed(seed) 37 | let rCode = "toString(sample(x = \(rangeStart):\(rangeEnd), size = \(size), replace = TRUE))" 38 | let res = safeSilentEvalParseString(rCode) 39 | R_gc() 40 | return(String(res[0]) ?? "") 41 | } 42 | 43 | deinit { Rf_endEmbeddedR(0) } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /shiny/shiny/WebView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import Combine 3 | import WebKit 4 | 5 | func runApp(_ appPath: String) -> Process { 6 | 7 | let task = Process() 8 | task.environment = ["RAPP_HOME": Bundle.main.bundlePath.appending("/Contents/Frameworks/R.framework/Resources") ] 9 | task.launchPath = Bundle.main.bundlePath.appending("/Contents/Frameworks/R.framework/Resources/bin/R") 10 | task.arguments = [ 11 | "--vanilla", 12 | "--silent", 13 | "--no-echo", 14 | "--file=\(appPath)" 15 | ] 16 | task.terminationHandler = { _ in 17 | print("Shiny process terminating") 18 | } 19 | task.launch() 20 | return(task) 21 | 22 | } 23 | 24 | @dynamicMemberLookup 25 | public class WebViewStore: ObservableObject { 26 | 27 | @Published public var webView: WKWebView { didSet { setupObservers() } } 28 | 29 | private var shinyApp: Process 30 | 31 | init(webView: WKWebView = WKWebView()) { 32 | 33 | self.webView = webView 34 | 35 | let shinyAppPath = Bundle.main.path(forResource: "shiny-app", ofType: "rscript")! 36 | shinyApp = runApp(shinyAppPath) 37 | 38 | self.webView.allowsBackForwardNavigationGestures = false 39 | 40 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) { 41 | self.webView.load(URLRequest(url: URL(string: "http://127.0.0.1:11111")!)) 42 | } 43 | 44 | setupObservers() 45 | 46 | } 47 | 48 | func killit() { 49 | shinyApp.terminate() 50 | } 51 | 52 | deinit { 53 | shinyApp.terminate() 54 | } 55 | 56 | private func setupObservers() { 57 | func subscriber(for keyPath: KeyPath) -> NSKeyValueObservation { 58 | return webView.observe(keyPath, options: [.prior]) { _, change in 59 | if change.isPrior { 60 | self.objectWillChange.send() 61 | } 62 | } 63 | } 64 | // Setup observers for all KVO compliant properties 65 | observers = [ 66 | subscriber(for: \.title), 67 | subscriber(for: \.url), 68 | subscriber(for: \.isLoading), 69 | subscriber(for: \.estimatedProgress), 70 | subscriber(for: \.hasOnlySecureContent), 71 | subscriber(for: \.serverTrust), 72 | subscriber(for: \.canGoBack), 73 | subscriber(for: \.canGoForward) 74 | ] 75 | } 76 | 77 | private var observers: [NSKeyValueObservation] = [] 78 | 79 | public subscript(dynamicMember keyPath: KeyPath) -> T { 80 | webView[keyPath: keyPath] 81 | } 82 | 83 | } 84 | 85 | /// A container for using a WKWebView in SwiftUI 86 | public struct WebView: View, NSViewRepresentable { 87 | /// The WKWebView to display 88 | public let webView: WKWebView 89 | 90 | public init(webView: WKWebView) { 91 | self.webView = webView 92 | } 93 | 94 | public func makeNSView(context: NSViewRepresentableContext) -> WKWebView { 95 | webView 96 | } 97 | 98 | public func updateNSView(_ uiView: WKWebView, context: NSViewRepresentableContext) { 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /shiny/shiny/process.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Errors that can occur when executing a command. 4 | public enum ProcessError: Error { 5 | 6 | /// Occurs when a process exits with a non-zero termination status. 7 | case nonZeroReturn(Int32) 8 | 9 | /// Occurs if the command returns output that was not encoded in UTF8. 10 | case unableToDecodeOutput 11 | } 12 | 13 | public extension Process { 14 | 15 | /// Executes a basic command and returns that command's output. 16 | /// - parameter args: The command (and arguments) to execute. 17 | /// - returns: The standard output of the command. 18 | /// - throws: A `ProcessError` describing what went wrong. 19 | static func exec(_ args: [String]) throws -> String { 20 | let process = Process() 21 | return try process.exec(args) 22 | } 23 | 24 | /// Executes a basic command and returns the command's output. 25 | /// - parameter args: The command (and arguments) to execute. 26 | /// - returns: The standard output of the command. 27 | /// - throws: A `ProcessError` describing what went wrong. 28 | func exec(_ args: [String]) throws -> String { 29 | launchPath = "/usr/bin/env" 30 | arguments = args 31 | 32 | let stdout = Pipe() 33 | standardOutput = stdout 34 | launch() 35 | waitUntilExit() 36 | 37 | if terminationStatus != 0 { 38 | throw ProcessError.nonZeroReturn(Int32(terminationStatus)) 39 | } 40 | 41 | let data = stdout.fileHandleForReading.readDataToEndOfFile() 42 | if let str = String(data: data, encoding: .utf8) { 43 | return str 44 | } else { 45 | throw ProcessError.unableToDecodeOutput 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /shiny/shiny/shiny-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #include "swift-r-bridge.h" 2 | -------------------------------------------------------------------------------- /shiny/shiny/shiny-app.rscript: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | 3 | vars <- setdiff(names(iris), "Species") 4 | 5 | runApp( 6 | appDir = list( 7 | ui = pageWithSidebar( 8 | headerPanel('Iris k-means clustering'), 9 | sidebarPanel( 10 | selectInput('xcol', 'X Variable', vars), 11 | selectInput('ycol', 'Y Variable', vars, selected = vars[[2]]), 12 | numericInput('clusters', 'Cluster count', 3, min = 1, max = 9) 13 | ), 14 | mainPanel( 15 | plotOutput('plot1') 16 | ) 17 | ), 18 | server = function(input, output, session) { 19 | 20 | # Combine the selected variables into a new data frame 21 | selectedData <- reactive({ 22 | iris[, c(input$xcol, input$ycol)] 23 | }) 24 | 25 | clusters <- reactive({ 26 | kmeans(selectedData(), input$clusters) 27 | }) 28 | 29 | output$plot1 <- renderPlot({ 30 | palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", 31 | "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999")) 32 | 33 | par(mar = c(5.1, 4.1, 0, 1)) 34 | plot(selectedData(), 35 | col = clusters()$cluster, 36 | pch = 20, cex = 3) 37 | points(clusters()$centers, pch = 4, cex = 4, lwd = 4) 38 | }) 39 | 40 | } 41 | ), 42 | host = "127.0.0.1", 43 | port = 11111, 44 | launch.browser = FALSE, 45 | quiet = FALSE 46 | ) 47 | 48 | -------------------------------------------------------------------------------- /shiny/shiny/shiny.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /shiny/shiny/shinyApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct randomApp: App { 5 | var body: some Scene { 6 | WindowGroup { 7 | ContentView() 8 | } 9 | } 10 | } 11 | --------------------------------------------------------------------------------