├── Gemfile ├── docs └── scheme-selection.png ├── AdditionalAssets.xcassets ├── Contents.json ├── CustomLoopIcon_orig.appiconset │ ├── Icon.png │ ├── icon_20pt.png │ ├── icon_29pt.png │ ├── icon_40pt.png │ ├── icon_76pt.png │ ├── icon_20pt@2x.png │ ├── icon_20pt@3x.png │ ├── icon_29pt@2x.png │ ├── icon_29pt@3x.png │ ├── icon_40pt@2x.png │ ├── icon_40pt@3x.png │ ├── icon_60pt@2x.png │ ├── icon_60pt@3x.png │ ├── icon_76pt@2x.png │ ├── icon_83.5@2x.png │ ├── icon_20pt@2x-1.png │ ├── icon_29pt@2x-1.png │ ├── icon_40pt@2x-1.png │ └── Contents.json └── CustomLoopIcon.appiconset │ ├── ipad_app76x76.png │ ├── ipad_app76x76@2x.png │ ├── ipad_settings29x29.png │ ├── ipad_spotlight40x40.png │ ├── iphone_app60x60@2x.png │ ├── iphone_app60x60@3x.png │ ├── ios_marketing1024x1024.png │ ├── ipad_notification20x20.png │ ├── ipad_settings29x29@2x.png │ ├── ipad_spotlight40x40@2x.png │ ├── ipad_pro_app83.5x83.5@2x.png │ ├── iphone_settings29x29@2x.png │ ├── iphone_settings29x29@3x.png │ ├── iphone_spotlight40x40@2x.png │ ├── iphone_spotlight40x40@3x.png │ ├── ipad_notification20x20@2x.png │ ├── iphone_notification20x20@2x.png │ ├── iphone_notification20x20@3x.png │ └── Contents.json ├── AdditionalWatchAssets.xcassets ├── Contents.json ├── CustomLoopIcon_orig.appiconset │ ├── Icon.png │ ├── icon_24pt@2x.png │ ├── icon_29pt@2x.png │ ├── icon_29pt@3x.png │ ├── icon_40pt@2x.png │ ├── icon_44pt@2x.png │ ├── icon_50pt@2x.png │ ├── icon_86pt@2x.png │ ├── icon_98pt@2x.png │ ├── icon_108pt@2x.png │ ├── icon_27.5pt@2x.png │ └── Contents.json └── CustomLoopIcon.appiconset │ ├── watch_home40x40@2x.png │ ├── watch_home44x44@2x.png │ ├── watch_home50x50@2x.png │ ├── watch_appstore1024x1024.png │ ├── watch_short_look86x86@2x.png │ ├── watch_short_look98x98@2x.png │ ├── watch_short_look108x108@2x.png │ ├── watch_companion_settings29x29@2x.png │ ├── watch_companion_settings29x29@3x.png │ ├── watch_notification_center24x24@2x.png │ ├── watch_notification_center27.5x27.5@2x.png │ └── Contents.json ├── fastlane ├── swift │ ├── FastlaneSwiftRunner │ │ ├── FastlaneSwiftRunner.xcodeproj │ │ │ ├── project.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata │ │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ ├── xcshareddata │ │ │ │ └── xcschemes │ │ │ │ │ └── FastlaneRunner.xcscheme │ │ │ └── project.pbxproj │ │ └── README.txt │ ├── Fastfile.swift │ ├── Actions.swift │ ├── Plugins.swift │ ├── RunnerArgument.swift │ ├── Gymfile.swift │ ├── Scanfile.swift │ ├── Matchfile.swift │ ├── Deliverfile.swift │ ├── Precheckfile.swift │ ├── Snapshotfile.swift │ ├── SocketClientDelegateProtocol.swift │ ├── upgrade_manifest.json │ ├── Screengrabfile.swift │ ├── Appfile.swift │ ├── RubyCommandable.swift │ ├── PrecheckfileProtocol.swift │ ├── main.swift │ ├── ControlCommand.swift │ ├── SocketResponse.swift │ ├── ArgumentProcessor.swift │ ├── ScreengrabfileProtocol.swift │ ├── LaneFileProtocol.swift │ ├── RubyCommand.swift │ ├── MatchfileProtocol.swift │ ├── SnapshotfileProtocol.swift │ ├── GymfileProtocol.swift │ ├── Runner.swift │ ├── DeliverfileProtocol.swift │ ├── ScanfileProtocol.swift │ └── SocketClient.swift ├── Appfile.swift └── Fastfile.swift ├── Loop.xcworkspace ├── xcshareddata │ ├── IDEWorkspaceChecks.plist │ ├── WorkspaceSettings.xcsettings │ └── xcschemes │ │ ├── Learn (Workspace).xcscheme │ │ └── Loop (Workspace).xcscheme └── contents.xcworkspacedata ├── .travis.yml ├── README.md ├── LoopConfigOverride.xcconfig ├── .gitignore └── .gitmodules /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | -------------------------------------------------------------------------------- /docs/scheme-selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/docs/scheme-selection.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/Icon.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_app76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_app76x76.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_20pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_20pt.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_76pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_76pt.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/Icon.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_app76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_app76x76@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_20pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_20pt@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_20pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_20pt@3x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@3x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt@3x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_60pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_60pt@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_60pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_60pt@3x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_76pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_76pt@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_83.5@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_settings29x29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_settings29x29.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_spotlight40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_spotlight40x40.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_app60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_app60x60@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_app60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_app60x60@3x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_20pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_20pt@2x-1.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@2x-1.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt@2x-1.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ios_marketing1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ios_marketing1024x1024.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_notification20x20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_notification20x20.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_settings29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_settings29x29@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_spotlight40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_spotlight40x40@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_24pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_24pt@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_29pt@3x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_40pt@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_44pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_44pt@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_50pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_50pt@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_86pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_86pt@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_98pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_98pt@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_pro_app83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_pro_app83.5x83.5@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_settings29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_settings29x29@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_settings29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_settings29x29@3x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_spotlight40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_spotlight40x40@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_spotlight40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_spotlight40x40@3x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_home40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_home40x40@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_home44x44@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_home44x44@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_home50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_home50x50@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_108pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_108pt@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_27.5pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/icon_27.5pt@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_notification20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/ipad_notification20x20@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_notification20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_notification20x20@2x.png -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_notification20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalAssets.xcassets/CustomLoopIcon.appiconset/iphone_notification20x20@3x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_appstore1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_appstore1024x1024.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_short_look86x86@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_short_look86x86@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_short_look98x98@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_short_look98x98@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_short_look108x108@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_short_look108x108@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_companion_settings29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_companion_settings29x29@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_companion_settings29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_companion_settings29x29@3x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_notification_center24x24@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_notification_center24x24@2x.png -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_notification_center27.5x27.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivalkou/LoopWorkspace/HEAD/AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/watch_notification_center27.5x27.5@2x.png -------------------------------------------------------------------------------- /fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Loop.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Loop.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | PreviewsEnabled 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /fastlane/swift/FastlaneSwiftRunner/README.txt: -------------------------------------------------------------------------------- 1 | Don't modify the structure of this group including but not limited to: 2 | - renaming this group 3 | - adding sub groups 4 | - removing sub groups 5 | - adding new files 6 | - removing files 7 | 8 | If you modify anything in this folder, future fastlane upgrades may not be able to be applied automatically. 9 | 10 | If you need to add new groups, please add them at the root of the "Fastlane Runner" group. 11 | -------------------------------------------------------------------------------- /fastlane/Appfile.swift: -------------------------------------------------------------------------------- 1 | var appIdentifier: String { return "com.BA7ZHP4963.loopkit.Loop" } // The bundle identifier of your app 2 | var appleID: String { return "ivalkou@gmail.com" } // Your Apple email address 3 | 4 | var itcTeam: String? { return "2082062" } // App Store Connect Team ID 5 | var teamID: String { return "BA7ZHP4963" } // Apple Developer Portal Team ID 6 | 7 | 8 | // For more information about the Appfile, see: 9 | // https://docs.fastlane.tools/advanced/#appfile 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | osx_image: xcode10.2 4 | 5 | script: 6 | - set -o pipefail && time xcodebuild -workspace Loop.xcworkspace -scheme 'Loop (Workspace)' -destination 'name=iPhone SE' build | xcpretty 7 | - set -o pipefail && time xcodebuild -workspace Loop.xcworkspace -scheme 'Learn (Workspace)' -destination 'name=iPhone SE' build | xcpretty 8 | - set -o pipefail && time xcodebuild -workspace Loop.xcworkspace -scheme 'Loop (Workspace)' -destination 'name=iPhone SE' test | xcpretty 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Attention please! 2 | 3 | Dear friends! The FreeAPS project is frozen. All the forces of our team are aimed at developing a new project [FreeAPS X](https://github.com/ivalkou/freeaps) based on OpenAPS reference design. If you would like to continue developing FreeAPS/Loop project, please fork. 4 | 5 | # Loop and learn fork 6 | 7 | [Loop and Learn](https://www.loopandlearn.org) community continued to develop FreeAPS/Loop. [We recommend using their fork](https://github.com/loopnlearn/LoopWorkspace). 8 | -------------------------------------------------------------------------------- /LoopConfigOverride.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | 3 | // Override this if you don't want the default com.${DEVELOPMENT_TEAM}.loopkit that loop uses 4 | // MAIN_APP_BUNDLE_IDENTIFIER = com.myname.loop 5 | 6 | // Customize this to change the app name displayed 7 | MAIN_APP_DISPLAY_NAME = FreeAPS 8 | 9 | MAIN_APP_PRODUCT_NAME = FreeAPS 10 | 11 | // Features 12 | // SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) FEATURE_OVERRIDES_DISABLED 13 | 14 | // Assets 15 | INCLUDED_SOURCE_FILE_NAMES = AdditionalAssets.xcassets 16 | 17 | APPICON_NAME = CustomLoopIcon 18 | -------------------------------------------------------------------------------- /fastlane/swift/Fastfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | // If you have a custom Fastfile.swift, this file will be replaced by it 3 | // Don't modify this file unless you are familiar with how fastlane's swift code generation works 4 | // *** This file will be overwritten or replaced during build time *** 5 | 6 | import Foundation 7 | 8 | class Fastfile: LaneFile { } 9 | 10 | // Please don't remove the lines below 11 | // They are used to detect outdated files 12 | // FastlaneRunnerAPIVersion [0.9.1] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Build 2 | DerivedData/ 3 | 4 | ## Settings 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata/ 14 | 15 | ## Other 16 | *.moved-aside 17 | *.xccheckout 18 | *.xcscmblueprint 19 | *.xcuserstate 20 | .DS_Store 21 | 22 | ## Obj-C/Swift specific 23 | *.hmap 24 | *.ipa 25 | 26 | ## Playgrounds 27 | *.playground 28 | playground.xcworkspace 29 | timeline.xctimeline 30 | 31 | ## fastlane temporary profiling data 32 | fastlane/report.xml 33 | -------------------------------------------------------------------------------- /fastlane/swift/Actions.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced when running "fastlane generate_swift" 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | import Foundation 12 | 13 | // Please don't remove the lines below 14 | // They are used to detect outdated files 15 | // FastlaneRunnerAPIVersion [0.9.56] 16 | -------------------------------------------------------------------------------- /fastlane/swift/Plugins.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced when installing/updating plugins or running "fastlane generate_swift" 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | import Foundation 12 | 13 | // Please don't remove the lines below 14 | // They are used to detect outdated files 15 | // FastlaneRunnerAPIVersion [0.9.56] 16 | -------------------------------------------------------------------------------- /fastlane/swift/RunnerArgument.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RunnerArgument.swift 3 | // FastlaneSwiftRunner 4 | // 5 | // Created by Joshua Liebowitz on 9/1/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | struct RunnerArgument { 18 | let name: String 19 | let value: String 20 | } 21 | 22 | // Please don't remove the lines below 23 | // They are used to detect outdated files 24 | // FastlaneRunnerAPIVersion [0.9.2] 25 | -------------------------------------------------------------------------------- /fastlane/Fastfile.swift: -------------------------------------------------------------------------------- 1 | // This file contains the fastlane.tools configuration 2 | // You can find the documentation at https://docs.fastlane.tools 3 | // 4 | // For a list of all available actions, check out 5 | // 6 | // https://docs.fastlane.tools/actions 7 | // 8 | 9 | import Foundation 10 | 11 | class Fastfile: LaneFile { 12 | func archiveLane() { 13 | desc("Archive") 14 | 15 | let project = "Loop/Loop.xcodeproj" 16 | let scheme = "Loop (Workspace)" 17 | let version = "2.1" 18 | let buildNumber = numberOfCommits() 19 | 20 | incrementVersionNumber(versionNumber: version, xcodeproj: project) 21 | incrementBuildNumber(buildNumber: buildNumber, xcodeproj: project) 22 | buildApp(scheme: scheme, clean: true, skipPackageIpa: true) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fastlane/swift/Gymfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced during build time, or when you initialize `gym` 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | class Gymfile: GymfileProtocol { 12 | // If you want to enable `gym`, run `fastlane gym init` 13 | // After, this file will be replaced with a custom implementation that contains values you supplied 14 | // during the `init` process, and you won't see this message 15 | } 16 | 17 | 18 | 19 | 20 | 21 | // Generated with fastlane 2.135.2 22 | -------------------------------------------------------------------------------- /fastlane/swift/Scanfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced during build time, or when you initialize `scan` 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | class Scanfile: ScanfileProtocol { 12 | // If you want to enable `scan`, run `fastlane scan init` 13 | // After, this file will be replaced with a custom implementation that contains values you supplied 14 | // during the `init` process, and you won't see this message 15 | } 16 | 17 | 18 | 19 | 20 | 21 | // Generated with fastlane 2.135.2 22 | -------------------------------------------------------------------------------- /fastlane/swift/Matchfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced during build time, or when you initialize `match` 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | class Matchfile: MatchfileProtocol { 12 | // If you want to enable `match`, run `fastlane match init` 13 | // After, this file will be replaced with a custom implementation that contains values you supplied 14 | // during the `init` process, and you won't see this message 15 | } 16 | 17 | 18 | 19 | 20 | 21 | // Generated with fastlane 2.135.2 22 | -------------------------------------------------------------------------------- /fastlane/swift/Deliverfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced during build time, or when you initialize `deliver` 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | class Deliverfile: DeliverfileProtocol { 12 | // If you want to enable `deliver`, run `fastlane deliver init` 13 | // After, this file will be replaced with a custom implementation that contains values you supplied 14 | // during the `init` process, and you won't see this message 15 | } 16 | 17 | 18 | 19 | 20 | 21 | // Generated with fastlane 2.135.2 22 | -------------------------------------------------------------------------------- /fastlane/swift/Precheckfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced during build time, or when you initialize `precheck` 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | class Precheckfile: PrecheckfileProtocol { 12 | // If you want to enable `precheck`, run `fastlane precheck init` 13 | // After, this file will be replaced with a custom implementation that contains values you supplied 14 | // during the `init` process, and you won't see this message 15 | } 16 | 17 | 18 | 19 | 20 | 21 | // Generated with fastlane 2.135.2 22 | -------------------------------------------------------------------------------- /fastlane/swift/Snapshotfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced during build time, or when you initialize `snapshot` 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | class Snapshotfile: SnapshotfileProtocol { 12 | // If you want to enable `snapshot`, run `fastlane snapshot init` 13 | // After, this file will be replaced with a custom implementation that contains values you supplied 14 | // during the `init` process, and you won't see this message 15 | } 16 | 17 | 18 | 19 | 20 | 21 | // Generated with fastlane 2.135.2 22 | -------------------------------------------------------------------------------- /fastlane/swift/SocketClientDelegateProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SocketClientDelegateProtocol.swift 3 | // FastlaneSwiftRunner 4 | // 5 | // Created by Joshua Liebowitz on 8/12/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | protocol SocketClientDelegateProtocol: class { 18 | func connectionsOpened() 19 | func connectionsClosed() 20 | func commandExecuted(serverResponse: SocketClientResponse, completion: (SocketClient) -> Void) 21 | } 22 | 23 | // Please don't remove the lines below 24 | // They are used to detect outdated files 25 | // FastlaneRunnerAPIVersion [0.9.2] 26 | -------------------------------------------------------------------------------- /fastlane/swift/upgrade_manifest.json: -------------------------------------------------------------------------------- 1 | {"Actions.swift":"Autogenerated API","Fastlane.swift":"Autogenerated API","DeliverfileProtocol.swift":"Autogenerated API","GymfileProtocol.swift":"Autogenerated API","MatchfileProtocol.swift":"Autogenerated API","Plugins.swift":"Autogenerated API","PrecheckfileProtocol.swift":"Autogenerated API","ScanfileProtocol.swift":"Autogenerated API","ScreengrabfileProtocol.swift":"Autogenerated API","SnapshotfileProtocol.swift":"Autogenerated API","LaneFileProtocol.swift":"Fastfile Components","ControlCommand.swift":"Networking","RubyCommand.swift":"Networking","RubyCommandable.swift":"Networking","Runner.swift":"Networking","SocketClient.swift":"Networking","SocketClientDelegateProtocol.swift":"Networking","SocketResponse.swift":"Networking","ArgumentProcessor.swift":"Runner Code","main.swift":"Runner Code","RunnerArgument.swift":"Runner Code"} -------------------------------------------------------------------------------- /fastlane/swift/Screengrabfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | 3 | // This autogenerated file will be overwritten or replaced during build time, or when you initialize `screengrab` 4 | // 5 | // ** NOTE ** 6 | // This file is provided by fastlane and WILL be overwritten in future updates 7 | // If you want to add extra functionality to this project, create a new file in a 8 | // new group so that it won't be marked for upgrade 9 | // 10 | 11 | class Screengrabfile: ScreengrabfileProtocol { 12 | // If you want to enable `screengrab`, run `fastlane screengrab init` 13 | // After, this file will be replaced with a custom implementation that contains values you supplied 14 | // during the `init` process, and you won't see this message 15 | } 16 | 17 | 18 | 19 | 20 | 21 | // Generated with fastlane 2.135.2 22 | -------------------------------------------------------------------------------- /fastlane/swift/Appfile.swift: -------------------------------------------------------------------------------- 1 | // This class is automatically included in FastlaneRunner during build 2 | // If you have a custom Appfile.swift, this file will be replaced by it 3 | // Don't modify this file unless you are familiar with how fastlane's swift code generation works 4 | // *** This file will be overwritten or replaced during build time *** 5 | 6 | var appIdentifier: String { return "" } // The bundle identifier of your app 7 | var appleID: String { return "" } // Your Apple email address 8 | 9 | var teamID: String { return "" } // Developer Portal Team ID 10 | var itcTeam: String? { return nil } // App Store Connect Team ID (may be nil if no team) 11 | 12 | // you can even provide different app identifiers, Apple IDs and team names per lane: 13 | // More information: https://docs.fastlane.tools/advanced/#appfile 14 | 15 | // Please don't remove the lines below 16 | // They are used to detect outdated files 17 | // FastlaneRunnerAPIVersion [0.9.1] 18 | -------------------------------------------------------------------------------- /fastlane/swift/RubyCommandable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RubyCommandable.swift 3 | // FastlaneRunner 4 | // 5 | // Created by Joshua Liebowitz on 1/4/18. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | enum CommandType { 18 | case action 19 | case control 20 | 21 | var token: String { 22 | switch self { 23 | case .action: 24 | return "action" 25 | case .control: 26 | return "control" 27 | } 28 | } 29 | } 30 | 31 | protocol RubyCommandable { 32 | var type: CommandType { get } 33 | var commandJson: String { get } 34 | var id: String { get } 35 | } 36 | 37 | extension RubyCommandable { 38 | var json: String { 39 | return """ 40 | { "commandType": "\(type.token)", "command": \(commandJson) } 41 | """ 42 | } 43 | } 44 | 45 | // Please don't remove the lines below 46 | // They are used to detect outdated files 47 | // FastlaneRunnerAPIVersion [0.9.2] 48 | -------------------------------------------------------------------------------- /fastlane/swift/PrecheckfileProtocol.swift: -------------------------------------------------------------------------------- 1 | protocol PrecheckfileProtocol: class { 2 | /// The bundle identifier of your app 3 | var appIdentifier: String { get } 4 | 5 | /// Your Apple ID Username 6 | var username: String { get } 7 | 8 | /// The ID of your App Store Connect team if you're in multiple teams 9 | var teamId: String? { get } 10 | 11 | /// The name of your App Store Connect team if you're in multiple teams 12 | var teamName: String? { get } 13 | 14 | /// The default rule level unless otherwise configured 15 | var defaultRuleLevel: String { get } 16 | 17 | /// Should check in-app purchases? 18 | var includeInAppPurchases: Bool { get } 19 | 20 | /// using text indicating that your IAP is free 21 | var freeStuffInIap: String? { get } 22 | } 23 | 24 | extension PrecheckfileProtocol { 25 | var appIdentifier: String { return "" } 26 | var username: String { return "" } 27 | var teamId: String? { return nil } 28 | var teamName: String? { return nil } 29 | var defaultRuleLevel: String { return "error" } 30 | var includeInAppPurchases: Bool { return true } 31 | var freeStuffInIap: String? { return nil } 32 | } 33 | 34 | // Please don't remove the lines below 35 | // They are used to detect outdated files 36 | // FastlaneRunnerAPIVersion [0.9.21] 37 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Loop"] 2 | path = Loop 3 | url=https://github.com/ivalkou/Loop.git 4 | branch = freeaps 5 | [submodule "LoopKit"] 6 | path = LoopKit 7 | url = https://github.com/LoopKit/LoopKit.git 8 | branch = dev 9 | [submodule "CGMBLEKit"] 10 | path = CGMBLEKit 11 | url = https://github.com/LoopKit/CGMBLEKit.git 12 | branch = dev 13 | [submodule "SwiftCharts"] 14 | path = SwiftCharts 15 | url = https://github.com/i-schuetz/SwiftCharts.git 16 | [submodule "dexcom-share-client-swift"] 17 | path = dexcom-share-client-swift 18 | url = https://github.com/LoopKit/dexcom-share-client-swift.git 19 | branch = dev 20 | [submodule "G4ShareSpy"] 21 | path = G4ShareSpy 22 | url = https://github.com/LoopKit/G4ShareSpy.git 23 | branch = dev 24 | [submodule "rileylink_ios"] 25 | path = rileylink_ios 26 | url = https://github.com/ps2/rileylink_ios.git 27 | branch = carthage-pin 28 | [submodule "Amplitude-iOS"] 29 | path = Amplitude-iOS 30 | url = https://github.com/LoopKit/Amplitude-iOS.git 31 | branch = decreepify 32 | [submodule "MKRingProgressView"] 33 | path = MKRingProgressView 34 | url = https://github.com/LoopKit/MKRingProgressView.git 35 | [submodule "NightscoutAPIClient"] 36 | path = NightscoutAPIClient 37 | url = https://github.com/ivalkou/NightscoutAPIClient.git 38 | [submodule "bubble-client-swift"] 39 | path = bubble-client-swift 40 | url = https://github.com/bubbledevteam/bubble-client-swift 41 | -------------------------------------------------------------------------------- /Loop.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | 25 | 27 | 28 | 30 | 31 | 33 | 34 | 36 | 37 | 39 | 40 | 42 | 43 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /fastlane/swift/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // FastlaneSwiftRunner 4 | // 5 | // Created by Joshua Liebowitz on 8/26/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | let argumentProcessor = ArgumentProcessor(args: CommandLine.arguments) 18 | let timeout = argumentProcessor.commandTimeout 19 | 20 | class MainProcess { 21 | var doneRunningLane = false 22 | var thread: Thread! 23 | 24 | @objc func connectToFastlaneAndRunLane() { 25 | runner.startSocketThread(port: argumentProcessor.port) 26 | 27 | let completedRun = Fastfile.runLane(named: argumentProcessor.currentLane, parameters: argumentProcessor.laneParameters()) 28 | if completedRun { 29 | runner.disconnectFromFastlaneProcess() 30 | } 31 | 32 | doneRunningLane = true 33 | } 34 | 35 | func startFastlaneThread() { 36 | thread = Thread(target: self, selector: #selector(connectToFastlaneAndRunLane), object: nil) 37 | thread.name = "worker thread" 38 | thread.start() 39 | } 40 | } 41 | 42 | let process: MainProcess = MainProcess() 43 | process.startFastlaneThread() 44 | 45 | while !process.doneRunningLane, RunLoop.current.run(mode: RunLoopMode.defaultRunLoopMode, before: Date(timeIntervalSinceNow: 2)) { 46 | // no op 47 | } 48 | 49 | // Please don't remove the lines below 50 | // They are used to detect outdated files 51 | // FastlaneRunnerAPIVersion [0.9.2] 52 | -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon_orig.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "filename" : "icon_24pt@2x.png", 7 | "scale" : "2x", 8 | "role" : "notificationCenter", 9 | "subtype" : "38mm" 10 | }, 11 | { 12 | "size" : "27.5x27.5", 13 | "idiom" : "watch", 14 | "filename" : "icon_27.5pt@2x.png", 15 | "scale" : "2x", 16 | "role" : "notificationCenter", 17 | "subtype" : "42mm" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "watch", 22 | "filename" : "icon_29pt@2x.png", 23 | "role" : "companionSettings", 24 | "scale" : "2x" 25 | }, 26 | { 27 | "size" : "29x29", 28 | "idiom" : "watch", 29 | "filename" : "icon_29pt@3x.png", 30 | "role" : "companionSettings", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "watch", 36 | "filename" : "icon_40pt@2x.png", 37 | "scale" : "2x", 38 | "role" : "appLauncher", 39 | "subtype" : "38mm" 40 | }, 41 | { 42 | "size" : "44x44", 43 | "idiom" : "watch", 44 | "filename" : "icon_44pt@2x.png", 45 | "scale" : "2x", 46 | "role" : "appLauncher", 47 | "subtype" : "40mm" 48 | }, 49 | { 50 | "size" : "50x50", 51 | "idiom" : "watch", 52 | "filename" : "icon_50pt@2x.png", 53 | "scale" : "2x", 54 | "role" : "appLauncher", 55 | "subtype" : "44mm" 56 | }, 57 | { 58 | "size" : "86x86", 59 | "idiom" : "watch", 60 | "filename" : "icon_86pt@2x.png", 61 | "scale" : "2x", 62 | "role" : "quickLook", 63 | "subtype" : "38mm" 64 | }, 65 | { 66 | "size" : "98x98", 67 | "idiom" : "watch", 68 | "filename" : "icon_98pt@2x.png", 69 | "scale" : "2x", 70 | "role" : "quickLook", 71 | "subtype" : "42mm" 72 | }, 73 | { 74 | "size" : "108x108", 75 | "idiom" : "watch", 76 | "filename" : "icon_108pt@2x.png", 77 | "scale" : "2x", 78 | "role" : "quickLook", 79 | "subtype" : "44mm" 80 | }, 81 | { 82 | "size" : "1024x1024", 83 | "idiom" : "watch-marketing", 84 | "filename" : "Icon.png", 85 | "scale" : "1x" 86 | } 87 | ], 88 | "info" : { 89 | "version" : 1, 90 | "author" : "xcode" 91 | } 92 | } -------------------------------------------------------------------------------- /AdditionalWatchAssets.xcassets/CustomLoopIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "filename" : "watch_notification_center24x24@2x.png", 7 | "scale" : "2x", 8 | "role" : "notificationCenter", 9 | "subtype" : "38mm" 10 | }, 11 | { 12 | "size" : "27.5x27.5", 13 | "idiom" : "watch", 14 | "filename" : "watch_notification_center27.5x27.5@2x.png", 15 | "scale" : "2x", 16 | "role" : "notificationCenter", 17 | "subtype" : "42mm" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "watch", 22 | "filename" : "watch_companion_settings29x29@2x.png", 23 | "role" : "companionSettings", 24 | "scale" : "2x" 25 | }, 26 | { 27 | "size" : "29x29", 28 | "idiom" : "watch", 29 | "filename" : "watch_companion_settings29x29@3x.png", 30 | "role" : "companionSettings", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "watch", 36 | "filename" : "watch_home40x40@2x.png", 37 | "scale" : "2x", 38 | "role" : "appLauncher", 39 | "subtype" : "38mm" 40 | }, 41 | { 42 | "size" : "44x44", 43 | "idiom" : "watch", 44 | "filename" : "watch_home44x44@2x.png", 45 | "scale" : "2x", 46 | "role" : "appLauncher", 47 | "subtype" : "40mm" 48 | }, 49 | { 50 | "size" : "50x50", 51 | "idiom" : "watch", 52 | "filename" : "watch_home50x50@2x.png", 53 | "scale" : "2x", 54 | "role" : "appLauncher", 55 | "subtype" : "44mm" 56 | }, 57 | { 58 | "size" : "86x86", 59 | "idiom" : "watch", 60 | "filename" : "watch_short_look86x86@2x.png", 61 | "scale" : "2x", 62 | "role" : "quickLook", 63 | "subtype" : "38mm" 64 | }, 65 | { 66 | "size" : "98x98", 67 | "idiom" : "watch", 68 | "filename" : "watch_short_look98x98@2x.png", 69 | "scale" : "2x", 70 | "role" : "quickLook", 71 | "subtype" : "42mm" 72 | }, 73 | { 74 | "size" : "108x108", 75 | "idiom" : "watch", 76 | "filename" : "watch_short_look108x108@2x.png", 77 | "scale" : "2x", 78 | "role" : "quickLook", 79 | "subtype" : "44mm" 80 | }, 81 | { 82 | "size" : "1024x1024", 83 | "idiom" : "watch-marketing", 84 | "filename" : "watch_appstore1024x1024.png", 85 | "scale" : "1x" 86 | } 87 | ], 88 | "info" : { 89 | "version" : 1, 90 | "author" : "xcode" 91 | } 92 | } -------------------------------------------------------------------------------- /fastlane/swift/ControlCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ControlCommand.swift 3 | // FastlaneRunner 4 | // 5 | // Created by Joshua Liebowitz on 1/3/18. 6 | 7 | // 8 | // ** NOTE ** 9 | // This file is provided by fastlane and WILL be overwritten in future updates 10 | // If you want to add extra functionality to this project, create a new file in a 11 | // new group so that it won't be marked for upgrade 12 | // 13 | 14 | import Foundation 15 | 16 | struct ControlCommand: RubyCommandable { 17 | static let commandKey = "command" 18 | var type: CommandType { return .control } 19 | 20 | enum ShutdownCommandType { 21 | static let userMessageKey: String = "userMessage" 22 | 23 | enum CancelReason { 24 | static let reasonKey: String = "reason" 25 | case clientError 26 | case serverError 27 | 28 | var reasonText: String { 29 | switch self { 30 | case .clientError: 31 | return "clientError" 32 | case .serverError: 33 | return "serverError" 34 | } 35 | } 36 | } 37 | 38 | case done 39 | case cancel(cancelReason: CancelReason) 40 | 41 | var token: String { 42 | switch self { 43 | case .done: 44 | return "done" 45 | case .cancel: 46 | return "cancelFastlaneRun" 47 | } 48 | } 49 | } 50 | 51 | let message: String? 52 | let id: String = UUID().uuidString 53 | let shutdownCommandType: ShutdownCommandType 54 | var commandJson: String { 55 | var jsonDictionary: [String: Any] = [ControlCommand.commandKey: shutdownCommandType.token] 56 | 57 | if let message = message { 58 | jsonDictionary[ShutdownCommandType.userMessageKey] = message 59 | } 60 | if case let .cancel(reason) = shutdownCommandType { 61 | jsonDictionary[ShutdownCommandType.CancelReason.reasonKey] = reason.reasonText 62 | } 63 | 64 | let jsonData = try! JSONSerialization.data(withJSONObject: jsonDictionary, options: []) 65 | let jsonString = String(data: jsonData, encoding: .utf8)! 66 | return jsonString 67 | } 68 | 69 | init(commandType: ShutdownCommandType, message: String? = nil) { 70 | shutdownCommandType = commandType 71 | self.message = message 72 | } 73 | } 74 | 75 | // Please don't remove the lines below 76 | // They are used to detect outdated files 77 | // FastlaneRunnerAPIVersion [0.9.2] 78 | -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon_orig.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "icon_20pt@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "icon_20pt@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon_29pt@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "icon_29pt@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "icon_40pt@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "icon_40pt@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon_60pt@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "icon_60pt@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "icon_20pt.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "icon_20pt@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "icon_29pt.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "icon_29pt@2x-1.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "icon_40pt.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "icon_40pt@2x-1.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "icon_76pt.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "icon_76pt@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "icon_83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "Icon.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /AdditionalAssets.xcassets/CustomLoopIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "iphone_notification20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "iphone_notification20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "iphone_settings29x29@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "iphone_settings29x29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "iphone_spotlight40x40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "iphone_spotlight40x40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "iphone_app60x60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "iphone_app60x60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "ipad_notification20x20.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "ipad_notification20x20@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "ipad_settings29x29.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "ipad_settings29x29@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "ipad_spotlight40x40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "ipad_spotlight40x40@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "ipad_app76x76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "ipad_app76x76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "ipad_pro_app83.5x83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "ios_marketing1024x1024.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /fastlane/swift/SocketResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SocketResponse.swift 3 | // FastlaneSwiftRunner 4 | // 5 | // Created by Joshua Liebowitz on 7/30/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | struct SocketResponse { 18 | enum ResponseType { 19 | case parseFailure(failureInformation: [String]) 20 | case failure(failureInformation: [String]) 21 | case readyForNext(returnedObject: String?, closureArgumentValue: String?) 22 | case clientInitiatedCancel 23 | 24 | init(statusDictionary: [String: Any]) { 25 | guard let status = statusDictionary["status"] as? String else { 26 | self = .parseFailure(failureInformation: ["Message failed to parse from Ruby server"]) 27 | return 28 | } 29 | 30 | if status == "ready_for_next" { 31 | verbose(message: "ready for next") 32 | let returnedObject = statusDictionary["return_object"] as? String 33 | let closureArgumentValue = statusDictionary["closure_argument_value"] as? String 34 | self = .readyForNext(returnedObject: returnedObject, closureArgumentValue: closureArgumentValue) 35 | return 36 | 37 | } else if status == "cancelled" { 38 | self = .clientInitiatedCancel 39 | return 40 | 41 | } else if status == "failure" { 42 | guard let failureInformation = statusDictionary["failure_information"] as? [String] else { 43 | self = .parseFailure(failureInformation: ["Ruby server indicated failure but Swift couldn't receive it"]) 44 | return 45 | } 46 | 47 | self = .failure(failureInformation: failureInformation) 48 | return 49 | } 50 | self = .parseFailure(failureInformation: ["Message status: \(status) not a supported status"]) 51 | } 52 | } 53 | 54 | let responseType: ResponseType 55 | 56 | init(payload: String) { 57 | guard let data = SocketResponse.convertToDictionary(text: payload) else { 58 | responseType = .parseFailure(failureInformation: ["Unable to parse message from Ruby server"]) 59 | return 60 | } 61 | 62 | guard case let statusDictionary? = data["payload"] as? [String: Any] else { 63 | responseType = .parseFailure(failureInformation: ["Payload missing from Ruby server response"]) 64 | return 65 | } 66 | 67 | responseType = ResponseType(statusDictionary: statusDictionary) 68 | } 69 | } 70 | 71 | extension SocketResponse { 72 | static func convertToDictionary(text: String) -> [String: Any]? { 73 | if let data = text.data(using: .utf8) { 74 | do { 75 | return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] 76 | } catch { 77 | log(message: error.localizedDescription) 78 | } 79 | } 80 | return nil 81 | } 82 | } 83 | 84 | // Please don't remove the lines below 85 | // They are used to detect outdated files 86 | // FastlaneRunnerAPIVersion [0.9.2] 87 | -------------------------------------------------------------------------------- /fastlane/swift/ArgumentProcessor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArgumentProcessor.swift 3 | // FastlaneRunner 4 | // 5 | // Created by Joshua Liebowitz on 9/28/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | struct ArgumentProcessor { 18 | let args: [RunnerArgument] 19 | let currentLane: String 20 | let commandTimeout: Int 21 | let port: UInt32 22 | 23 | init(args: [String]) { 24 | // Dump the first arg which is the program name 25 | let fastlaneArgs = stride(from: 1, to: args.count - 1, by: 2).map { 26 | RunnerArgument(name: args[$0], value: args[$0 + 1]) 27 | } 28 | self.args = fastlaneArgs 29 | 30 | let fastlaneArgsMinusLanes = fastlaneArgs.filter { arg in 31 | arg.name.lowercased() != "lane" 32 | } 33 | 34 | let potentialLogMode = fastlaneArgsMinusLanes.filter { arg in 35 | arg.name.lowercased() == "logmode" 36 | } 37 | 38 | port = UInt32(fastlaneArgsMinusLanes.first(where: { $0.name == "swiftServerPort" })?.value ?? "") ?? 2000 39 | 40 | // Configure logMode since we might need to use it before we finish parsing 41 | if let logModeArg = potentialLogMode.first { 42 | let logModeString = logModeArg.value 43 | Logger.logMode = Logger.LogMode(logMode: logModeString) 44 | } 45 | 46 | let lanes = self.args.filter { arg in 47 | arg.name.lowercased() == "lane" 48 | } 49 | verbose(message: lanes.description) 50 | 51 | guard lanes.count == 1 else { 52 | let message = "You must have exactly one lane specified as an arg, here's what I got: \(lanes)" 53 | log(message: message) 54 | fatalError(message) 55 | } 56 | 57 | let lane = lanes.first! 58 | currentLane = lane.value 59 | 60 | // User might have configured a timeout for the socket connection 61 | let potentialTimeout = fastlaneArgsMinusLanes.filter { arg in 62 | arg.name.lowercased() == "timeoutseconds" 63 | } 64 | 65 | if let logModeArg = potentialLogMode.first { 66 | let logModeString = logModeArg.value 67 | Logger.logMode = Logger.LogMode(logMode: logModeString) 68 | } 69 | 70 | if let timeoutArg = potentialTimeout.first { 71 | let timeoutString = timeoutArg.value 72 | commandTimeout = (timeoutString as NSString).integerValue 73 | } else { 74 | commandTimeout = SocketClient.defaultCommandTimeoutSeconds 75 | } 76 | } 77 | 78 | func laneParameters() -> [String: String] { 79 | let laneParametersArgs = args.filter { arg in 80 | let lowercasedName = arg.name.lowercased() 81 | return lowercasedName != "timeoutseconds" && lowercasedName != "lane" && lowercasedName != "logmode" 82 | } 83 | var laneParameters = [String: String]() 84 | for arg in laneParametersArgs { 85 | laneParameters[arg.name] = arg.value 86 | } 87 | return laneParameters 88 | } 89 | } 90 | 91 | // Please don't remove the lines below 92 | // They are used to detect outdated files 93 | // FastlaneRunnerAPIVersion [0.9.2] 94 | -------------------------------------------------------------------------------- /fastlane/swift/ScreengrabfileProtocol.swift: -------------------------------------------------------------------------------- 1 | protocol ScreengrabfileProtocol: class { 2 | /// Path to the root of your Android SDK installation, e.g. ~/tools/android-sdk-macosx 3 | var androidHome: String? { get } 4 | 5 | /// The Android build tools version to use, e.g. '23.0.2' 6 | var buildToolsVersion: String? { get } 7 | 8 | /// A list of locales which should be used 9 | var locales: [String] { get } 10 | 11 | /// Enabling this option will automatically clear previously generated screenshots before running screengrab 12 | var clearPreviousScreenshots: Bool { get } 13 | 14 | /// The directory where to store the screenshots 15 | var outputDirectory: String { get } 16 | 17 | /// Don't open the summary after running _screengrab_ 18 | var skipOpenSummary: Bool { get } 19 | 20 | /// The package name of the app under test (e.g. com.yourcompany.yourapp) 21 | var appPackageName: String { get } 22 | 23 | /// The package name of the tests bundle (e.g. com.yourcompany.yourapp.test) 24 | var testsPackageName: String? { get } 25 | 26 | /// Only run tests in these Java packages 27 | var useTestsInPackages: [String]? { get } 28 | 29 | /// Only run tests in these Java classes 30 | var useTestsInClasses: [String]? { get } 31 | 32 | /// Additional launch arguments 33 | var launchArguments: [String]? { get } 34 | 35 | /// The fully qualified class name of your test instrumentation runner 36 | var testInstrumentationRunner: String { get } 37 | 38 | /// Return the device to this locale after running tests 39 | var endingLocale: String { get } 40 | 41 | /// Restarts the adb daemon using `adb root` to allow access to screenshots directories on device. Use if getting 'Permission denied' errors 42 | var useAdbRoot: Bool { get } 43 | 44 | /// The path to the APK for the app under test 45 | var appApkPath: String? { get } 46 | 47 | /// The path to the APK for the the tests bundle 48 | var testsApkPath: String? { get } 49 | 50 | /// Use the device or emulator with the given serial number or qualifier 51 | var specificDevice: String? { get } 52 | 53 | /// Type of device used for screenshots. Matches Google Play Types (phone, sevenInch, tenInch, tv, wear) 54 | var deviceType: String { get } 55 | 56 | /// Whether or not to exit Screengrab on test failure. Exiting on failure will not copy sceenshots to local machine nor open sceenshots summary 57 | var exitOnTestFailure: Bool { get } 58 | 59 | /// Enabling this option will automatically uninstall the application before running it 60 | var reinstallApp: Bool { get } 61 | 62 | /// Add timestamp suffix to screenshot filename 63 | var useTimestampSuffix: Bool { get } 64 | 65 | /// Configure the host used by adb to connect, allows running on remote devices farm 66 | var adbHost: String? { get } 67 | } 68 | 69 | extension ScreengrabfileProtocol { 70 | var androidHome: String? { return nil } 71 | var buildToolsVersion: String? { return nil } 72 | var locales: [String] { return ["en-US"] } 73 | var clearPreviousScreenshots: Bool { return false } 74 | var outputDirectory: String { return "fastlane/metadata/android" } 75 | var skipOpenSummary: Bool { return false } 76 | var appPackageName: String { return "" } 77 | var testsPackageName: String? { return nil } 78 | var useTestsInPackages: [String]? { return nil } 79 | var useTestsInClasses: [String]? { return nil } 80 | var launchArguments: [String]? { return nil } 81 | var testInstrumentationRunner: String { return "androidx.test.runner.AndroidJUnitRunner" } 82 | var endingLocale: String { return "en-US" } 83 | var useAdbRoot: Bool { return false } 84 | var appApkPath: String? { return nil } 85 | var testsApkPath: String? { return nil } 86 | var specificDevice: String? { return nil } 87 | var deviceType: String { return "phone" } 88 | var exitOnTestFailure: Bool { return true } 89 | var reinstallApp: Bool { return false } 90 | var useTimestampSuffix: Bool { return true } 91 | var adbHost: String? { return nil } 92 | } 93 | 94 | // Please don't remove the lines below 95 | // They are used to detect outdated files 96 | // FastlaneRunnerAPIVersion [0.9.23] 97 | -------------------------------------------------------------------------------- /fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/xcshareddata/xcschemes/FastlaneRunner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | 79 | 85 | 87 | 93 | 94 | 95 | 96 | 98 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /fastlane/swift/LaneFileProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaneFileProtocol.swift 3 | // FastlaneSwiftRunner 4 | // 5 | // Created by Joshua Liebowitz on 8/4/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | public protocol LaneFileProtocol: class { 18 | var fastlaneVersion: String { get } 19 | static func runLane(named: String, parameters: [String: String]) -> Bool 20 | 21 | func recordLaneDescriptions() 22 | func beforeAll() 23 | func afterAll(currentLane: String) 24 | func onError(currentLane: String, errorInfo: String) 25 | } 26 | 27 | public extension LaneFileProtocol { 28 | var fastlaneVersion: String { return "" } // default "" because that means any is fine 29 | func beforeAll() {} // no op by default 30 | func afterAll(currentLane _: String) {} // no op by default 31 | func onError(currentLane _: String, errorInfo _: String) {} // no op by default 32 | func recordLaneDescriptions() {} // no op by default 33 | } 34 | 35 | @objcMembers 36 | public class LaneFile: NSObject, LaneFileProtocol { 37 | private(set) static var fastfileInstance: Fastfile? 38 | 39 | // Called before any lane is executed. 40 | private func setupAllTheThings() { 41 | LaneFile.fastfileInstance!.beforeAll() 42 | } 43 | 44 | private static func trimLaneFromName(laneName: String) -> String { 45 | return String(laneName.prefix(laneName.count - 4)) 46 | } 47 | 48 | private static func trimLaneWithOptionsFromName(laneName: String) -> String { 49 | return String(laneName.prefix(laneName.count - 12)) 50 | } 51 | 52 | private static var laneFunctionNames: [String] { 53 | var lanes: [String] = [] 54 | var methodCount: UInt32 = 0 55 | let methodList = class_copyMethodList(self, &methodCount) 56 | for i in 0 ..< Int(methodCount) { 57 | let selName = sel_getName(method_getName(methodList![i])) 58 | let name = String(cString: selName) 59 | let lowercasedName = name.lowercased() 60 | if lowercasedName.hasSuffix("lane") || lowercasedName.hasSuffix("lanewithoptions:") { 61 | lanes.append(name) 62 | } 63 | } 64 | return lanes 65 | } 66 | 67 | public static var lanes: [String: String] { 68 | var laneToMethodName: [String: String] = [:] 69 | laneFunctionNames.forEach { name in 70 | let lowercasedName = name.lowercased() 71 | if lowercasedName.hasSuffix("lane") { 72 | laneToMethodName[lowercasedName] = name 73 | let lowercasedNameNoLane = trimLaneFromName(laneName: lowercasedName) 74 | laneToMethodName[lowercasedNameNoLane] = name 75 | } else if lowercasedName.hasSuffix("lanewithoptions:") { 76 | let lowercasedNameNoOptions = trimLaneWithOptionsFromName(laneName: lowercasedName) 77 | laneToMethodName[lowercasedNameNoOptions] = name 78 | let lowercasedNameNoLane = trimLaneFromName(laneName: lowercasedNameNoOptions) 79 | laneToMethodName[lowercasedNameNoLane] = name 80 | } 81 | } 82 | 83 | return laneToMethodName 84 | } 85 | 86 | public static func loadFastfile() { 87 | if fastfileInstance == nil { 88 | let fastfileType: AnyObject.Type = NSClassFromString(className())! 89 | let fastfileAsNSObjectType: NSObject.Type = fastfileType as! NSObject.Type 90 | let currentFastfileInstance: Fastfile? = fastfileAsNSObjectType.init() as? Fastfile 91 | fastfileInstance = currentFastfileInstance 92 | } 93 | } 94 | 95 | public static func runLane(named: String, parameters: [String: String]) -> Bool { 96 | log(message: "Running lane: \(named)") 97 | loadFastfile() 98 | 99 | guard let fastfileInstance: Fastfile = self.fastfileInstance else { 100 | let message = "Unable to instantiate class named: \(className())" 101 | log(message: message) 102 | fatalError(message) 103 | } 104 | 105 | let currentLanes = lanes 106 | let lowerCasedLaneRequested = named.lowercased() 107 | 108 | guard let laneMethod = currentLanes[lowerCasedLaneRequested] else { 109 | let laneNames = laneFunctionNames.map { laneFuctionName in 110 | if laneFuctionName.hasSuffix("lanewithoptions:") { 111 | return trimLaneWithOptionsFromName(laneName: laneFuctionName) 112 | } else { 113 | return trimLaneFromName(laneName: laneFuctionName) 114 | } 115 | }.joined(separator: ", ") 116 | 117 | let message = "[!] Could not find lane '\(named)'. Available lanes: \(laneNames)" 118 | log(message: message) 119 | 120 | let shutdownCommand = ControlCommand(commandType: .cancel(cancelReason: .clientError), message: message) 121 | _ = runner.executeCommand(shutdownCommand) 122 | return false 123 | } 124 | 125 | // call all methods that need to be called before we start calling lanes 126 | fastfileInstance.setupAllTheThings() 127 | 128 | // We need to catch all possible errors here and display a nice message 129 | _ = fastfileInstance.perform(NSSelectorFromString(laneMethod), with: parameters) 130 | 131 | // only call on success 132 | fastfileInstance.afterAll(currentLane: named) 133 | log(message: "Done running lane: \(named) 🚀") 134 | return true 135 | } 136 | } 137 | 138 | // Please don't remove the lines below 139 | // They are used to detect outdated files 140 | // FastlaneRunnerAPIVersion [0.9.2] 141 | -------------------------------------------------------------------------------- /fastlane/swift/RubyCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RubyCommand.swift 3 | // FastlaneSwiftRunner 4 | // 5 | // Created by Joshua Liebowitz on 8/4/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | struct RubyCommand: RubyCommandable { 18 | var type: CommandType { return .action } 19 | 20 | struct Argument { 21 | enum ArgType { 22 | case stringClosure 23 | 24 | var typeString: String { 25 | switch self { 26 | case .stringClosure: 27 | return "string_closure" // this should match when is in ruby's SocketServerActionCommandExecutor 28 | } 29 | } 30 | } 31 | 32 | let name: String 33 | let value: Any? 34 | let type: ArgType? 35 | 36 | init(name: String, value: Any?, type: ArgType? = nil) { 37 | self.name = name 38 | self.value = value 39 | self.type = type 40 | } 41 | 42 | var hasValue: Bool { 43 | return value != nil 44 | } 45 | 46 | var json: String { 47 | if let someValue = value { 48 | let typeJson: String 49 | if let type = type { 50 | typeJson = ", \"value_type\" : \"\(type.typeString)\"" 51 | } else { 52 | typeJson = "" 53 | } 54 | 55 | if type == .stringClosure { 56 | return "{\"name\" : \"\(name)\", \"value\" : \"ignored_for_closure\"\(typeJson)}" 57 | } else if let array = someValue as? [String] { 58 | return "{\"name\" : \"\(name)\", \"value\" : \"\(array.joined(separator: ","))\"\(typeJson)}" 59 | } else if let hash = someValue as? [String: Any] { 60 | let jsonData = try! JSONSerialization.data(withJSONObject: hash, options: []) 61 | let jsonString = String(data: jsonData, encoding: .utf8)! 62 | return "{\"name\" : \"\(name)\", \"value\" : \(jsonString)\(typeJson)}" 63 | } else { 64 | let dictionary = [ 65 | "name": name, 66 | "value": someValue, 67 | ] 68 | let jsonData = try! JSONSerialization.data(withJSONObject: dictionary, options: []) 69 | let jsonString = String(data: jsonData, encoding: .utf8)! 70 | return jsonString 71 | } 72 | } else { 73 | // Just exclude this arg if it doesn't have a value 74 | return "" 75 | } 76 | } 77 | } 78 | 79 | let commandID: String 80 | let methodName: String 81 | let className: String? 82 | let args: [Argument] 83 | let id: String = UUID().uuidString 84 | 85 | var closure: ((String) -> Void)? { 86 | let callbacks = args.filter { ($0.type != nil) && $0.type == .stringClosure } 87 | guard let callback = callbacks.first else { 88 | return nil 89 | } 90 | 91 | guard let callbackArgValue = callback.value else { 92 | return nil 93 | } 94 | 95 | guard let callbackClosure = callbackArgValue as? ((String) -> Void) else { 96 | return nil 97 | } 98 | return callbackClosure 99 | } 100 | 101 | func callbackClosure(_ callbackArg: String) -> ((String) -> Void)? { 102 | // WARNING: This will perform the first callback it receives 103 | let callbacks = args.filter { ($0.type != nil) && $0.type == .stringClosure } 104 | guard let callback = callbacks.first else { 105 | verbose(message: "received call to performCallback with \(callbackArg), but no callback available to perform") 106 | return nil 107 | } 108 | 109 | guard let callbackArgValue = callback.value else { 110 | verbose(message: "received call to performCallback with \(callbackArg), but callback is nil") 111 | return nil 112 | } 113 | 114 | guard let callbackClosure = callbackArgValue as? ((String) -> Void) else { 115 | verbose(message: "received call to performCallback with \(callbackArg), but callback type is unknown \(callbackArgValue.self)") 116 | return nil 117 | } 118 | return callbackClosure 119 | } 120 | 121 | func performCallback(callbackArg: String, socket: SocketClient, completion: @escaping () -> Void) { 122 | verbose(message: "Performing callback with: \(callbackArg)") 123 | socket.leave() 124 | callbackClosure(callbackArg)?(callbackArg) 125 | completion() 126 | } 127 | 128 | var commandJson: String { 129 | let argsArrayJson = args 130 | .map { $0.json } 131 | .filter { $0 != "" } 132 | 133 | let argsJson: String? 134 | if !argsArrayJson.isEmpty { 135 | argsJson = "\"args\" : [\(argsArrayJson.joined(separator: ","))]" 136 | } else { 137 | argsJson = nil 138 | } 139 | 140 | let commandIDJson = "\"commandID\" : \"\(commandID)\"" 141 | let methodNameJson = "\"methodName\" : \"\(methodName)\"" 142 | 143 | var jsonParts = [commandIDJson, methodNameJson] 144 | if let argsJson = argsJson { 145 | jsonParts.append(argsJson) 146 | } 147 | 148 | if let className = className { 149 | let classNameJson = "\"className\" : \"\(className)\"" 150 | jsonParts.append(classNameJson) 151 | } 152 | 153 | let commandJsonString = "{\(jsonParts.joined(separator: ","))}" 154 | 155 | return commandJsonString 156 | } 157 | } 158 | 159 | // Please don't remove the lines below 160 | // They are used to detect outdated files 161 | // FastlaneRunnerAPIVersion [0.9.2] 162 | -------------------------------------------------------------------------------- /Loop.xcworkspace/xcshareddata/xcschemes/Learn (Workspace).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 57 | 63 | 64 | 65 | 71 | 77 | 78 | 79 | 85 | 91 | 92 | 93 | 94 | 95 | 100 | 101 | 107 | 108 | 109 | 110 | 111 | 112 | 122 | 124 | 130 | 131 | 132 | 133 | 139 | 140 | 146 | 147 | 148 | 149 | 151 | 152 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /fastlane/swift/MatchfileProtocol.swift: -------------------------------------------------------------------------------- 1 | protocol MatchfileProtocol: class { 2 | /// Define the profile type, can be appstore, adhoc, development, enterprise, developer_id 3 | var type: String { get } 4 | 5 | /// Create additional cert types needed for macOS installers (valid values: mac_installer_distribution, developer_id_installer) 6 | var additionalCertTypes: [String]? { get } 7 | 8 | /// Only fetch existing certificates and profiles, don't generate new ones 9 | var readonly: Bool { get } 10 | 11 | /// Create a certificate type for Xcode 11 and later (Apple Development or Apple Distribution) 12 | var generateAppleCerts: Bool { get } 13 | 14 | /// Skip syncing provisioning profiles 15 | var skipProvisioningProfiles: Bool { get } 16 | 17 | /// The bundle identifier(s) of your app (comma-separated) 18 | var appIdentifier: [String] { get } 19 | 20 | /// Your Apple ID Username 21 | var username: String { get } 22 | 23 | /// The ID of your Developer Portal team if you're in multiple teams 24 | var teamId: String? { get } 25 | 26 | /// The name of your Developer Portal team if you're in multiple teams 27 | var teamName: String? { get } 28 | 29 | /// Define where you want to store your certificates 30 | var storageMode: String { get } 31 | 32 | /// URL to the git repo containing all the certificates 33 | var gitUrl: String { get } 34 | 35 | /// Specific git branch to use 36 | var gitBranch: String { get } 37 | 38 | /// git user full name to commit 39 | var gitFullName: String? { get } 40 | 41 | /// git user email to commit 42 | var gitUserEmail: String? { get } 43 | 44 | /// Make a shallow clone of the repository (truncate the history to 1 revision) 45 | var shallowClone: Bool { get } 46 | 47 | /// Clone just the branch specified, instead of the whole repo. This requires that the branch already exists. Otherwise the command will fail 48 | var cloneBranchDirectly: Bool { get } 49 | 50 | /// Use a basic authorization header to access the git repo (e.g.: access via HTTPS, GitHub Actions, etc), usually a string in Base64 51 | var gitBasicAuthorization: String? { get } 52 | 53 | /// Use a bearer authorization header to access the git repo (e.g.: access to an Azure Devops repository), usually a string in Base64 54 | var gitBearerAuthorization: String? { get } 55 | 56 | /// Name of the Google Cloud Storage bucket to use 57 | var googleCloudBucketName: String? { get } 58 | 59 | /// Path to the gc_keys.json file 60 | var googleCloudKeysFile: String? { get } 61 | 62 | /// ID of the Google Cloud project to use for authentication 63 | var googleCloudProjectId: String? { get } 64 | 65 | /// Name of the S3 region 66 | var s3Region: String? { get } 67 | 68 | /// S3 access key 69 | var s3AccessKey: String? { get } 70 | 71 | /// S3 secret access key 72 | var s3SecretAccessKey: String? { get } 73 | 74 | /// Name of the S3 bucket 75 | var s3Bucket: String? { get } 76 | 77 | /// Prefix to be used on all objects uploaded to S3 78 | var s3ObjectPrefix: String? { get } 79 | 80 | /// Keychain the items should be imported to 81 | var keychainName: String { get } 82 | 83 | /// This might be required the first time you access certificates on a new mac. For the login/default keychain this is your account password 84 | var keychainPassword: String? { get } 85 | 86 | /// Renew the provisioning profiles every time you run match 87 | var force: Bool { get } 88 | 89 | /// Renew the provisioning profiles if the device count on the developer portal has changed. Ignored for profile type 'appstore' 90 | var forceForNewDevices: Bool { get } 91 | 92 | /// Disables confirmation prompts during nuke, answering them with yes 93 | var skipConfirmation: Bool { get } 94 | 95 | /// Skip generation of a README.md for the created git repository 96 | var skipDocs: Bool { get } 97 | 98 | /// Set the provisioning profile's platform to work with (i.e. ios, tvos, macos) 99 | var platform: String { get } 100 | 101 | /// The name of provisioning profile template. If the developer account has provisioning profile templates (aka: custom entitlements), the template name can be found by inspecting the Entitlements drop-down while creating/editing a provisioning profile (e.g. "Apple Pay Pass Suppression Development") 102 | var templateName: String? { get } 103 | 104 | /// A custom name for the provisioning profile. This will replace the default provisioning profile name if specified 105 | var profileName: String? { get } 106 | 107 | /// Should the command fail if it was about to create a duplicate of an existing provisioning profile. It can happen due to issues on Apple Developer Portal, when profile to be recreated was not properly deleted first 108 | var failOnNameTaken: Bool { get } 109 | 110 | /// Path in which to export certificates, key and profile 111 | var outputPath: String? { get } 112 | 113 | /// Print out extra information and all commands 114 | var verbose: Bool { get } 115 | } 116 | 117 | extension MatchfileProtocol { 118 | var type: String { return "development" } 119 | var additionalCertTypes: [String]? { return nil } 120 | var readonly: Bool { return false } 121 | var generateAppleCerts: Bool { return true } 122 | var skipProvisioningProfiles: Bool { return false } 123 | var appIdentifier: [String] { return [] } 124 | var username: String { return "" } 125 | var teamId: String? { return nil } 126 | var teamName: String? { return nil } 127 | var storageMode: String { return "git" } 128 | var gitUrl: String { return "" } 129 | var gitBranch: String { return "master" } 130 | var gitFullName: String? { return nil } 131 | var gitUserEmail: String? { return nil } 132 | var shallowClone: Bool { return false } 133 | var cloneBranchDirectly: Bool { return false } 134 | var gitBasicAuthorization: String? { return nil } 135 | var gitBearerAuthorization: String? { return nil } 136 | var googleCloudBucketName: String? { return nil } 137 | var googleCloudKeysFile: String? { return nil } 138 | var googleCloudProjectId: String? { return nil } 139 | var s3Region: String? { return nil } 140 | var s3AccessKey: String? { return nil } 141 | var s3SecretAccessKey: String? { return nil } 142 | var s3Bucket: String? { return nil } 143 | var s3ObjectPrefix: String? { return nil } 144 | var keychainName: String { return "login.keychain" } 145 | var keychainPassword: String? { return nil } 146 | var force: Bool { return false } 147 | var forceForNewDevices: Bool { return false } 148 | var skipConfirmation: Bool { return false } 149 | var skipDocs: Bool { return false } 150 | var platform: String { return "ios" } 151 | var templateName: String? { return nil } 152 | var profileName: String? { return nil } 153 | var failOnNameTaken: Bool { return false } 154 | var outputPath: String? { return nil } 155 | var verbose: Bool { return false } 156 | } 157 | 158 | // Please don't remove the lines below 159 | // They are used to detect outdated files 160 | // FastlaneRunnerAPIVersion [0.9.22] 161 | -------------------------------------------------------------------------------- /fastlane/swift/SnapshotfileProtocol.swift: -------------------------------------------------------------------------------- 1 | protocol SnapshotfileProtocol: class { 2 | /// Path the workspace file 3 | var workspace: String? { get } 4 | 5 | /// Path the project file 6 | var project: String? { get } 7 | 8 | /// Pass additional arguments to xcodebuild for the test phase. Be sure to quote the setting names and values e.g. OTHER_LDFLAGS="-ObjC -lstdc++" 9 | var xcargs: String? { get } 10 | 11 | /// Use an extra XCCONFIG file to build your app 12 | var xcconfig: String? { get } 13 | 14 | /// A list of devices you want to take the screenshots from 15 | var devices: [String]? { get } 16 | 17 | /// A list of languages which should be used 18 | var languages: [String] { get } 19 | 20 | /// A list of launch arguments which should be used 21 | var launchArguments: [String] { get } 22 | 23 | /// The directory where to store the screenshots 24 | var outputDirectory: String { get } 25 | 26 | /// If the logs generated by the app (e.g. using NSLog, perror, etc.) in the Simulator should be written to the output_directory 27 | var outputSimulatorLogs: Bool { get } 28 | 29 | /// By default, the latest version should be used automatically. If you want to change it, do it here 30 | var iosVersion: String? { get } 31 | 32 | /// Don't open the HTML summary after running _snapshot_ 33 | var skipOpenSummary: Bool { get } 34 | 35 | /// Do not check for most recent SnapshotHelper code 36 | var skipHelperVersionCheck: Bool { get } 37 | 38 | /// Enabling this option will automatically clear previously generated screenshots before running snapshot 39 | var clearPreviousScreenshots: Bool { get } 40 | 41 | /// Enabling this option will automatically uninstall the application before running it 42 | var reinstallApp: Bool { get } 43 | 44 | /// Enabling this option will automatically erase the simulator before running the application 45 | var eraseSimulator: Bool { get } 46 | 47 | /// Enabling this option wil automatically override the status bar to show 9:41 AM, full battery, and full reception 48 | var overrideStatusBar: Bool { get } 49 | 50 | /// Enabling this option will configure the Simulator's system language 51 | var localizeSimulator: Bool { get } 52 | 53 | /// Enabling this option will configure the Simulator to be in dark mode (false for light, true for dark) 54 | var darkMode: Bool? { get } 55 | 56 | /// The bundle identifier of the app to uninstall (only needed when enabling reinstall_app) 57 | var appIdentifier: String? { get } 58 | 59 | /// A list of photos that should be added to the simulator before running the application 60 | var addPhotos: [String]? { get } 61 | 62 | /// A list of videos that should be added to the simulator before running the application 63 | var addVideos: [String]? { get } 64 | 65 | /// A path to screenshots.html template 66 | var htmlTemplate: String? { get } 67 | 68 | /// The directory where to store the build log 69 | var buildlogPath: String { get } 70 | 71 | /// Should the project be cleaned before building it? 72 | var clean: Bool { get } 73 | 74 | /// Test without building, requires a derived data path 75 | var testWithoutBuilding: Bool? { get } 76 | 77 | /// The configuration to use when building the app. Defaults to 'Release' 78 | var configuration: String? { get } 79 | 80 | /// Additional xcpretty arguments 81 | var xcprettyArgs: String? { get } 82 | 83 | /// The SDK that should be used for building the application 84 | var sdk: String? { get } 85 | 86 | /// The scheme you want to use, this must be the scheme for the UI Tests 87 | var scheme: String? { get } 88 | 89 | /// The number of times a test can fail before snapshot should stop retrying 90 | var numberOfRetries: Int { get } 91 | 92 | /// Should snapshot stop immediately after the tests completely failed on one device? 93 | var stopAfterFirstError: Bool { get } 94 | 95 | /// The directory where build products and other derived data will go 96 | var derivedDataPath: String? { get } 97 | 98 | /// Should an Xcode result bundle be generated in the output directory 99 | var resultBundle: Bool { get } 100 | 101 | /// The name of the target you want to test (if you desire to override the Target Application from Xcode) 102 | var testTargetName: String? { get } 103 | 104 | /// Separate the log files per device and per language 105 | var namespaceLogFiles: String? { get } 106 | 107 | /// Take snapshots on multiple simulators concurrently. Note: This option is only applicable when running against Xcode 9 108 | var concurrentSimulators: Bool { get } 109 | 110 | /// Disable the simulator from showing the 'Slide to type' prompt 111 | var disableSlideToType: Bool { get } 112 | 113 | /// Sets a custom path for Swift Package Manager dependencies 114 | var clonedSourcePackagesPath: String? { get } 115 | 116 | /// The testplan associated with the scheme that should be used for testing 117 | var testplan: String? { get } 118 | 119 | /// Array of strings matching Test Bundle/Test Suite/Test Cases to run 120 | var onlyTesting: String? { get } 121 | 122 | /// Array of strings matching Test Bundle/Test Suite/Test Cases to skip 123 | var skipTesting: String? { get } 124 | 125 | /// Disable xcpretty formatting of build 126 | var disableXcpretty: Bool? { get } 127 | } 128 | 129 | extension SnapshotfileProtocol { 130 | var workspace: String? { return nil } 131 | var project: String? { return nil } 132 | var xcargs: String? { return nil } 133 | var xcconfig: String? { return nil } 134 | var devices: [String]? { return nil } 135 | var languages: [String] { return ["en-US"] } 136 | var launchArguments: [String] { return [""] } 137 | var outputDirectory: String { return "screenshots" } 138 | var outputSimulatorLogs: Bool { return false } 139 | var iosVersion: String? { return nil } 140 | var skipOpenSummary: Bool { return false } 141 | var skipHelperVersionCheck: Bool { return false } 142 | var clearPreviousScreenshots: Bool { return false } 143 | var reinstallApp: Bool { return false } 144 | var eraseSimulator: Bool { return false } 145 | var overrideStatusBar: Bool { return false } 146 | var localizeSimulator: Bool { return false } 147 | var darkMode: Bool? { return nil } 148 | var appIdentifier: String? { return nil } 149 | var addPhotos: [String]? { return nil } 150 | var addVideos: [String]? { return nil } 151 | var htmlTemplate: String? { return nil } 152 | var buildlogPath: String { return "~/Library/Logs/snapshot" } 153 | var clean: Bool { return false } 154 | var testWithoutBuilding: Bool? { return nil } 155 | var configuration: String? { return nil } 156 | var xcprettyArgs: String? { return nil } 157 | var sdk: String? { return nil } 158 | var scheme: String? { return nil } 159 | var numberOfRetries: Int { return 1 } 160 | var stopAfterFirstError: Bool { return false } 161 | var derivedDataPath: String? { return nil } 162 | var resultBundle: Bool { return false } 163 | var testTargetName: String? { return nil } 164 | var namespaceLogFiles: String? { return nil } 165 | var concurrentSimulators: Bool { return true } 166 | var disableSlideToType: Bool { return false } 167 | var clonedSourcePackagesPath: String? { return nil } 168 | var testplan: String? { return nil } 169 | var onlyTesting: String? { return nil } 170 | var skipTesting: String? { return nil } 171 | var disableXcpretty: Bool? { return nil } 172 | } 173 | 174 | // Please don't remove the lines below 175 | // They are used to detect outdated files 176 | // FastlaneRunnerAPIVersion [0.9.17] 177 | -------------------------------------------------------------------------------- /fastlane/swift/GymfileProtocol.swift: -------------------------------------------------------------------------------- 1 | protocol GymfileProtocol: class { 2 | /// Path to the workspace file 3 | var workspace: String? { get } 4 | 5 | /// Path to the project file 6 | var project: String? { get } 7 | 8 | /// The project's scheme. Make sure it's marked as `Shared` 9 | var scheme: String? { get } 10 | 11 | /// Should the project be cleaned before building it? 12 | var clean: Bool { get } 13 | 14 | /// The directory in which the ipa file should be stored in 15 | var outputDirectory: String { get } 16 | 17 | /// The name of the resulting ipa file 18 | var outputName: String? { get } 19 | 20 | /// The configuration to use when building the app. Defaults to 'Release' 21 | var configuration: String? { get } 22 | 23 | /// Hide all information that's not necessary while building 24 | var silent: Bool { get } 25 | 26 | /// The name of the code signing identity to use. It has to match the name exactly. e.g. 'iPhone Distribution: SunApps GmbH' 27 | var codesigningIdentity: String? { get } 28 | 29 | /// Should we skip packaging the ipa? 30 | var skipPackageIpa: Bool { get } 31 | 32 | /// Should we skip packaging the pkg? 33 | var skipPackagePkg: Bool { get } 34 | 35 | /// Should the ipa file include symbols? 36 | var includeSymbols: Bool? { get } 37 | 38 | /// Should the ipa file include bitcode? 39 | var includeBitcode: Bool? { get } 40 | 41 | /// Method used to export the archive. Valid values are: app-store, ad-hoc, package, enterprise, development, developer-id 42 | var exportMethod: String? { get } 43 | 44 | /// Path to an export options plist or a hash with export options. Use 'xcodebuild -help' to print the full set of available options 45 | var exportOptions: [String: Any]? { get } 46 | 47 | /// Pass additional arguments to xcodebuild for the package phase. Be sure to quote the setting names and values e.g. OTHER_LDFLAGS="-ObjC -lstdc++" 48 | var exportXcargs: String? { get } 49 | 50 | /// Export ipa from previously built xcarchive. Uses archive_path as source 51 | var skipBuildArchive: Bool? { get } 52 | 53 | /// After building, don't archive, effectively not including -archivePath param 54 | var skipArchive: Bool? { get } 55 | 56 | /// Build without codesigning 57 | var skipCodesigning: Bool? { get } 58 | 59 | /// Platform to build when using a Catalyst enabled app. Valid values are: ios, macos 60 | var catalystPlatform: String? { get } 61 | 62 | /// Full name of 3rd Party Mac Developer Installer or Developer ID Installer certificate. Example: `3rd Party Mac Developer Installer: Your Company (ABC1234XWYZ)` 63 | var installerCertName: String? { get } 64 | 65 | /// The directory in which the archive should be stored in 66 | var buildPath: String? { get } 67 | 68 | /// The path to the created archive 69 | var archivePath: String? { get } 70 | 71 | /// The directory where built products and other derived data will go 72 | var derivedDataPath: String? { get } 73 | 74 | /// Should an Xcode result bundle be generated in the output directory 75 | var resultBundle: Bool { get } 76 | 77 | /// Path to the result bundle directory to create. Ignored if `result_bundle` if false 78 | var resultBundlePath: String? { get } 79 | 80 | /// The directory where to store the build log 81 | var buildlogPath: String { get } 82 | 83 | /// The SDK that should be used for building the application 84 | var sdk: String? { get } 85 | 86 | /// The toolchain that should be used for building the application (e.g. com.apple.dt.toolchain.Swift_2_3, org.swift.30p620160816a) 87 | var toolchain: String? { get } 88 | 89 | /// Use a custom destination for building the app 90 | var destination: String? { get } 91 | 92 | /// Optional: Sometimes you need to specify a team id when exporting the ipa file 93 | var exportTeamId: String? { get } 94 | 95 | /// Pass additional arguments to xcodebuild for the build phase. Be sure to quote the setting names and values e.g. OTHER_LDFLAGS="-ObjC -lstdc++" 96 | var xcargs: String? { get } 97 | 98 | /// Use an extra XCCONFIG file to build your app 99 | var xcconfig: String? { get } 100 | 101 | /// Suppress the output of xcodebuild to stdout. Output is still saved in buildlog_path 102 | var suppressXcodeOutput: Bool? { get } 103 | 104 | /// Disable xcpretty formatting of build output 105 | var disableXcpretty: Bool? { get } 106 | 107 | /// Use the test (RSpec style) format for build output 108 | var xcprettyTestFormat: Bool? { get } 109 | 110 | /// A custom xcpretty formatter to use 111 | var xcprettyFormatter: String? { get } 112 | 113 | /// Have xcpretty create a JUnit-style XML report at the provided path 114 | var xcprettyReportJunit: String? { get } 115 | 116 | /// Have xcpretty create a simple HTML report at the provided path 117 | var xcprettyReportHtml: String? { get } 118 | 119 | /// Have xcpretty create a JSON compilation database at the provided path 120 | var xcprettyReportJson: String? { get } 121 | 122 | /// Analyze the project build time and store the output in 'culprits.txt' file 123 | var analyzeBuildTime: Bool? { get } 124 | 125 | /// Have xcpretty use unicode encoding when reporting builds 126 | var xcprettyUtf: Bool? { get } 127 | 128 | /// Do not try to build a profile mapping from the xcodeproj. Match or a manually provided mapping should be used 129 | var skipProfileDetection: Bool { get } 130 | 131 | /// Sets a custom path for Swift Package Manager dependencies 132 | var clonedSourcePackagesPath: String? { get } 133 | } 134 | 135 | extension GymfileProtocol { 136 | var workspace: String? { return nil } 137 | var project: String? { return nil } 138 | var scheme: String? { return nil } 139 | var clean: Bool { return false } 140 | var outputDirectory: String { return "." } 141 | var outputName: String? { return nil } 142 | var configuration: String? { return nil } 143 | var silent: Bool { return false } 144 | var codesigningIdentity: String? { return nil } 145 | var skipPackageIpa: Bool { return false } 146 | var skipPackagePkg: Bool { return false } 147 | var includeSymbols: Bool? { return nil } 148 | var includeBitcode: Bool? { return nil } 149 | var exportMethod: String? { return nil } 150 | var exportOptions: [String: Any]? { return nil } 151 | var exportXcargs: String? { return nil } 152 | var skipBuildArchive: Bool? { return nil } 153 | var skipArchive: Bool? { return nil } 154 | var skipCodesigning: Bool? { return nil } 155 | var catalystPlatform: String? { return nil } 156 | var installerCertName: String? { return nil } 157 | var buildPath: String? { return nil } 158 | var archivePath: String? { return nil } 159 | var derivedDataPath: String? { return nil } 160 | var resultBundle: Bool { return false } 161 | var resultBundlePath: String? { return nil } 162 | var buildlogPath: String { return "~/Library/Logs/gym" } 163 | var sdk: String? { return nil } 164 | var toolchain: String? { return nil } 165 | var destination: String? { return nil } 166 | var exportTeamId: String? { return nil } 167 | var xcargs: String? { return nil } 168 | var xcconfig: String? { return nil } 169 | var suppressXcodeOutput: Bool? { return nil } 170 | var disableXcpretty: Bool? { return nil } 171 | var xcprettyTestFormat: Bool? { return nil } 172 | var xcprettyFormatter: String? { return nil } 173 | var xcprettyReportJunit: String? { return nil } 174 | var xcprettyReportHtml: String? { return nil } 175 | var xcprettyReportJson: String? { return nil } 176 | var analyzeBuildTime: Bool? { return nil } 177 | var xcprettyUtf: Bool? { return nil } 178 | var skipProfileDetection: Bool { return false } 179 | var clonedSourcePackagesPath: String? { return nil } 180 | } 181 | 182 | // Please don't remove the lines below 183 | // They are used to detect outdated files 184 | // FastlaneRunnerAPIVersion [0.9.28] 185 | -------------------------------------------------------------------------------- /fastlane/swift/Runner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Runner.swift 3 | // FastlaneSwiftRunner 4 | // 5 | // Created by Joshua Liebowitz on 8/26/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Foundation 16 | 17 | let logger: Logger = { 18 | Logger() 19 | }() 20 | 21 | let runner: Runner = { 22 | Runner() 23 | }() 24 | 25 | func desc(_: String) { 26 | // no-op, this is handled in fastlane/lane_list.rb 27 | } 28 | 29 | class Runner { 30 | private var thread: Thread! 31 | private var socketClient: SocketClient! 32 | private let dispatchGroup: DispatchGroup = DispatchGroup() 33 | private var returnValue: String? // lol, so safe 34 | private var currentlyExecutingCommand: RubyCommandable? 35 | private var shouldLeaveDispatchGroupDuringDisconnect = false 36 | private var executeNext: [String: Bool] = [:] 37 | 38 | func executeCommand(_ command: RubyCommandable) -> String { 39 | dispatchGroup.enter() 40 | currentlyExecutingCommand = command 41 | socketClient.send(rubyCommand: command) 42 | 43 | let secondsToWait = DispatchTimeInterval.seconds(SocketClient.defaultCommandTimeoutSeconds) 44 | // swiftlint:disable next 45 | let timeoutResult = waitWithPolling(self.executeNext[command.id], toEventually: { $0 == true }, timeout: SocketClient.defaultCommandTimeoutSeconds) 46 | executeNext.removeValue(forKey: command.id) 47 | let failureMessage = "command didn't execute in: \(SocketClient.defaultCommandTimeoutSeconds) seconds" 48 | let success = testDispatchTimeoutResult(timeoutResult, failureMessage: failureMessage, timeToWait: secondsToWait) 49 | guard success else { 50 | log(message: "command timeout") 51 | preconditionFailure() 52 | } 53 | 54 | if let _returnValue = returnValue { 55 | return _returnValue 56 | } else { 57 | return "" 58 | } 59 | } 60 | 61 | private func waitWithPolling(_ expression: @autoclosure @escaping () throws -> T, toEventually predicate: @escaping (T) -> Bool, timeout: Int, pollingInterval: DispatchTimeInterval = .milliseconds(4)) -> DispatchTimeoutResult { 62 | func memoizedClosure(_ closure: @escaping () throws -> T) -> (Bool) throws -> T { 63 | var cache: T? 64 | return { withoutCaching in 65 | if withoutCaching || cache == nil { 66 | cache = try closure() 67 | } 68 | guard let cache = cache else { 69 | preconditionFailure() 70 | } 71 | 72 | return cache 73 | } 74 | } 75 | 76 | let runLoop = RunLoop.current 77 | let timeoutDate = Date(timeInterval: TimeInterval(timeout), since: Date()) 78 | var fulfilled: Bool = false 79 | let _expression = memoizedClosure(expression) 80 | repeat { 81 | do { 82 | let exp = try _expression(true) 83 | fulfilled = predicate(exp) 84 | } catch { 85 | fatalError("Error raised \(error.localizedDescription)") 86 | } 87 | if !fulfilled { 88 | runLoop.run(until: Date(timeIntervalSinceNow: pollingInterval.timeInterval)) 89 | } else { 90 | break 91 | } 92 | } while Date().compare(timeoutDate) == .orderedAscending 93 | 94 | if fulfilled { 95 | return .success 96 | } else { 97 | return .timedOut 98 | } 99 | } 100 | } 101 | 102 | // Handle threading stuff 103 | extension Runner { 104 | func startSocketThread(port: UInt32) { 105 | let secondsToWait = DispatchTimeInterval.seconds(SocketClient.connectTimeoutSeconds) 106 | 107 | dispatchGroup.enter() 108 | 109 | socketClient = SocketClient(port: port, commandTimeoutSeconds: timeout, socketDelegate: self) 110 | thread = Thread(target: self, selector: #selector(startSocketComs), object: nil) 111 | guard let thread = thread else { 112 | preconditionFailure("Thread did not instantiate correctly") 113 | } 114 | 115 | thread.name = "socket thread" 116 | thread.start() 117 | 118 | let connectTimeout = DispatchTime.now() + secondsToWait 119 | let timeoutResult = dispatchGroup.wait(timeout: connectTimeout) 120 | 121 | let failureMessage = "couldn't start socket thread in: \(SocketClient.connectTimeoutSeconds) seconds" 122 | let success = testDispatchTimeoutResult(timeoutResult, failureMessage: failureMessage, timeToWait: secondsToWait) 123 | guard success else { 124 | log(message: "socket thread timeout") 125 | preconditionFailure() 126 | } 127 | } 128 | 129 | func disconnectFromFastlaneProcess() { 130 | shouldLeaveDispatchGroupDuringDisconnect = true 131 | dispatchGroup.enter() 132 | socketClient.sendComplete() 133 | 134 | let connectTimeout = DispatchTime.now() + 2 135 | _ = dispatchGroup.wait(timeout: connectTimeout) 136 | } 137 | 138 | @objc func startSocketComs() { 139 | guard let socketClient = self.socketClient else { 140 | return 141 | } 142 | 143 | socketClient.connectAndOpenStreams() 144 | dispatchGroup.leave() 145 | } 146 | 147 | fileprivate func testDispatchTimeoutResult(_ timeoutResult: DispatchTimeoutResult, failureMessage: String, timeToWait _: DispatchTimeInterval) -> Bool { 148 | switch timeoutResult { 149 | case .success: 150 | return true 151 | case .timedOut: 152 | log(message: "timeout: \(failureMessage)") 153 | return false 154 | } 155 | } 156 | } 157 | 158 | extension Runner: SocketClientDelegateProtocol { 159 | func commandExecuted(serverResponse: SocketClientResponse, completion: (SocketClient) -> Void) { 160 | switch serverResponse { 161 | case let .success(returnedObject, closureArgumentValue): 162 | verbose(message: "command executed") 163 | returnValue = returnedObject 164 | if let command = currentlyExecutingCommand as? RubyCommand { 165 | if let closureArgumentValue = closureArgumentValue, !closureArgumentValue.isEmpty { 166 | command.performCallback(callbackArg: closureArgumentValue, socket: socketClient) { 167 | self.executeNext[command.id] = true 168 | } 169 | } else { 170 | executeNext[command.id] = true 171 | } 172 | } 173 | dispatchGroup.leave() 174 | completion(socketClient) 175 | case .clientInitiatedCancelAcknowledged: 176 | verbose(message: "server acknowledged a cancel request") 177 | dispatchGroup.leave() 178 | if let command = currentlyExecutingCommand as? RubyCommand { 179 | executeNext[command.id] = true 180 | } 181 | completion(socketClient) 182 | case .alreadyClosedSockets, .connectionFailure, .malformedRequest, .malformedResponse, .serverError: 183 | log(message: "error encountered while executing command:\n\(serverResponse)") 184 | dispatchGroup.leave() 185 | if let command = currentlyExecutingCommand as? RubyCommand { 186 | executeNext[command.id] = true 187 | } 188 | completion(socketClient) 189 | case let .commandTimeout(timeout): 190 | log(message: "Runner timed out after \(timeout) second(s)") 191 | } 192 | } 193 | 194 | func connectionsOpened() { 195 | DispatchQueue.main.async { 196 | verbose(message: "connected!") 197 | } 198 | } 199 | 200 | func connectionsClosed() { 201 | DispatchQueue.main.async { 202 | if let thread = self.thread { 203 | thread.cancel() 204 | } 205 | self.thread = nil 206 | self.socketClient.closeSession() 207 | self.socketClient = nil 208 | verbose(message: "connection closed!") 209 | if self.shouldLeaveDispatchGroupDuringDisconnect { 210 | self.dispatchGroup.leave() 211 | } 212 | exit(0) 213 | } 214 | } 215 | } 216 | 217 | class Logger { 218 | enum LogMode { 219 | init(logMode: String) { 220 | switch logMode { 221 | case "normal", "default": 222 | self = .normal 223 | case "verbose": 224 | self = .verbose 225 | default: 226 | logger.log(message: "unrecognized log mode: \(logMode), defaulting to 'normal'") 227 | self = .normal 228 | } 229 | } 230 | 231 | case normal 232 | case verbose 233 | } 234 | 235 | public static var logMode: LogMode = .normal 236 | 237 | func log(message: String) { 238 | let timestamp = NSDate().timeIntervalSince1970 239 | print("[\(timestamp)]: \(message)") 240 | } 241 | 242 | func verbose(message: String) { 243 | if Logger.logMode == .verbose { 244 | let timestamp = NSDate().timeIntervalSince1970 245 | print("[\(timestamp)]: \(message)") 246 | } 247 | } 248 | } 249 | 250 | func log(message: String) { 251 | logger.log(message: message) 252 | } 253 | 254 | func verbose(message: String) { 255 | logger.verbose(message: message) 256 | } 257 | 258 | private extension DispatchTimeInterval { 259 | var timeInterval: TimeInterval { 260 | var result: TimeInterval = 0 261 | switch self { 262 | case let .seconds(value): 263 | result = TimeInterval(value) 264 | case let .milliseconds(value): 265 | result = TimeInterval(value) * 0.001 266 | case let .microseconds(value): 267 | result = TimeInterval(value) * 0.000_001 268 | case let .nanoseconds(value): 269 | result = TimeInterval(value) * 0.000_000_001 270 | case .never: 271 | fatalError() 272 | } 273 | return result 274 | } 275 | } 276 | 277 | // Please don't remove the lines below 278 | // They are used to detect outdated files 279 | // FastlaneRunnerAPIVersion [0.9.2] 280 | -------------------------------------------------------------------------------- /fastlane/swift/DeliverfileProtocol.swift: -------------------------------------------------------------------------------- 1 | protocol DeliverfileProtocol: class { 2 | /// Your Apple ID Username 3 | var username: String { get } 4 | 5 | /// The bundle identifier of your app 6 | var appIdentifier: String? { get } 7 | 8 | /// The version that should be edited or created 9 | var appVersion: String? { get } 10 | 11 | /// Path to your ipa file 12 | var ipa: String? { get } 13 | 14 | /// Path to your pkg file 15 | var pkg: String? { get } 16 | 17 | /// If set the given build number (already uploaded to iTC) will be used instead of the current built one 18 | var buildNumber: String? { get } 19 | 20 | /// The platform to use (optional) 21 | var platform: String { get } 22 | 23 | /// Modify live metadata, this option disables ipa upload and screenshot upload 24 | var editLive: Bool { get } 25 | 26 | /// Force usage of live version rather than edit version 27 | var useLiveVersion: Bool { get } 28 | 29 | /// Path to the folder containing the metadata files 30 | var metadataPath: String? { get } 31 | 32 | /// Path to the folder containing the screenshots 33 | var screenshotsPath: String? { get } 34 | 35 | /// Skip uploading an ipa or pkg to App Store Connect 36 | var skipBinaryUpload: Bool { get } 37 | 38 | /// Don't upload the screenshots 39 | var skipScreenshots: Bool { get } 40 | 41 | /// Don't upload the metadata (e.g. title, description). This will still upload screenshots 42 | var skipMetadata: Bool { get } 43 | 44 | /// Don't update app version for submission 45 | var skipAppVersionUpdate: Bool { get } 46 | 47 | /// Skip the HTML report file verification 48 | var force: Bool { get } 49 | 50 | /// Clear all previously uploaded screenshots before uploading the new ones 51 | var overwriteScreenshots: Bool { get } 52 | 53 | /// Submit the new version for Review after uploading everything 54 | var submitForReview: Bool { get } 55 | 56 | /// Rejects the previously submitted build if it's in a state where it's possible 57 | var rejectIfPossible: Bool { get } 58 | 59 | /// Should the app be automatically released once it's approved? (Can not be used together with `auto_release_date`) 60 | var automaticRelease: Bool { get } 61 | 62 | /// Date in milliseconds for automatically releasing on pending approval (Can not be used together with `automatic_release`) 63 | var autoReleaseDate: Int? { get } 64 | 65 | /// Enable the phased release feature of iTC 66 | var phasedRelease: Bool { get } 67 | 68 | /// Reset the summary rating when you release a new version of the application 69 | var resetRatings: Bool { get } 70 | 71 | /// The price tier of this application 72 | var priceTier: String? { get } 73 | 74 | /// Path to the app rating's config 75 | var appRatingConfigPath: String? { get } 76 | 77 | /// Extra information for the submission (e.g. compliance specifications, IDFA settings) 78 | var submissionInformation: [String: Any]? { get } 79 | 80 | /// The ID of your App Store Connect team if you're in multiple teams 81 | var teamId: String? { get } 82 | 83 | /// The name of your App Store Connect team if you're in multiple teams 84 | var teamName: String? { get } 85 | 86 | /// The short ID of your Developer Portal team, if you're in multiple teams. Different from your iTC team ID! 87 | var devPortalTeamId: String? { get } 88 | 89 | /// The name of your Developer Portal team if you're in multiple teams 90 | var devPortalTeamName: String? { get } 91 | 92 | /// The provider short name to be used with the iTMSTransporter to identify your team. This value will override the automatically detected provider short name. To get provider short name run `pathToXcode.app/Contents/Applications/Application\ Loader.app/Contents/itms/bin/iTMSTransporter -m provider -u 'USERNAME' -p 'PASSWORD' -account_type itunes_connect -v off`. The short names of providers should be listed in the second column 93 | var itcProvider: String? { get } 94 | 95 | /// Run precheck before submitting to app review 96 | var runPrecheckBeforeSubmit: Bool { get } 97 | 98 | /// The default precheck rule level unless otherwise configured 99 | var precheckDefaultRuleLevel: String { get } 100 | 101 | /// **DEPRECATED!** Removed after the migration to the new App Store Connect API in June 2020 - An array of localized metadata items to upload individually by language so that errors can be identified. E.g. ['name', 'keywords', 'description']. Note: slow 102 | var individualMetadataItems: [String]? { get } 103 | 104 | /// **DEPRECATED!** Removed after the migration to the new App Store Connect API in June 2020 - Metadata: The path to the app icon 105 | var appIcon: String? { get } 106 | 107 | /// **DEPRECATED!** Removed after the migration to the new App Store Connect API in June 2020 - Metadata: The path to the Apple Watch app icon 108 | var appleWatchAppIcon: String? { get } 109 | 110 | /// Metadata: The copyright notice 111 | var copyright: String? { get } 112 | 113 | /// Metadata: The english name of the primary category (e.g. `Business`, `Books`) 114 | var primaryCategory: String? { get } 115 | 116 | /// Metadata: The english name of the secondary category (e.g. `Business`, `Books`) 117 | var secondaryCategory: String? { get } 118 | 119 | /// Metadata: The english name of the primary first sub category (e.g. `Educational`, `Puzzle`) 120 | var primaryFirstSubCategory: String? { get } 121 | 122 | /// Metadata: The english name of the primary second sub category (e.g. `Educational`, `Puzzle`) 123 | var primarySecondSubCategory: String? { get } 124 | 125 | /// Metadata: The english name of the secondary first sub category (e.g. `Educational`, `Puzzle`) 126 | var secondaryFirstSubCategory: String? { get } 127 | 128 | /// Metadata: The english name of the secondary second sub category (e.g. `Educational`, `Puzzle`) 129 | var secondarySecondSubCategory: String? { get } 130 | 131 | /// Metadata: A hash containing the trade representative contact information 132 | var tradeRepresentativeContactInformation: [String: Any]? { get } 133 | 134 | /// Metadata: A hash containing the review information 135 | var appReviewInformation: [String: Any]? { get } 136 | 137 | /// Metadata: Path to the app review attachment file 138 | var appReviewAttachmentFile: String? { get } 139 | 140 | /// Metadata: The localised app description 141 | var description: String? { get } 142 | 143 | /// Metadata: The localised app name 144 | var name: String? { get } 145 | 146 | /// Metadata: The localised app subtitle 147 | var subtitle: [String: Any]? { get } 148 | 149 | /// Metadata: An array of localised keywords 150 | var keywords: [String: Any]? { get } 151 | 152 | /// Metadata: An array of localised promotional texts 153 | var promotionalText: [String: Any]? { get } 154 | 155 | /// Metadata: Localised release notes for this version 156 | var releaseNotes: String? { get } 157 | 158 | /// Metadata: Localised privacy url 159 | var privacyUrl: String? { get } 160 | 161 | /// Metadata: Localised Apple TV privacy policy text 162 | var appleTvPrivacyPolicy: String? { get } 163 | 164 | /// Metadata: Localised support url 165 | var supportUrl: String? { get } 166 | 167 | /// Metadata: Localised marketing url 168 | var marketingUrl: String? { get } 169 | 170 | /// Metadata: List of languages to activate 171 | var languages: [String]? { get } 172 | 173 | /// Ignore errors when invalid languages are found in metadata and screenshot directories 174 | var ignoreLanguageDirectoryValidation: Bool { get } 175 | 176 | /// Should precheck check in-app purchases? 177 | var precheckIncludeInAppPurchases: Bool { get } 178 | 179 | /// The (spaceship) app ID of the app you want to use/modify 180 | var app: String { get } 181 | } 182 | 183 | extension DeliverfileProtocol { 184 | var username: String { return "" } 185 | var appIdentifier: String? { return nil } 186 | var appVersion: String? { return nil } 187 | var ipa: String? { return nil } 188 | var pkg: String? { return nil } 189 | var buildNumber: String? { return nil } 190 | var platform: String { return "ios" } 191 | var editLive: Bool { return false } 192 | var useLiveVersion: Bool { return false } 193 | var metadataPath: String? { return nil } 194 | var screenshotsPath: String? { return nil } 195 | var skipBinaryUpload: Bool { return false } 196 | var skipScreenshots: Bool { return false } 197 | var skipMetadata: Bool { return false } 198 | var skipAppVersionUpdate: Bool { return false } 199 | var force: Bool { return false } 200 | var overwriteScreenshots: Bool { return false } 201 | var submitForReview: Bool { return false } 202 | var rejectIfPossible: Bool { return false } 203 | var automaticRelease: Bool { return false } 204 | var autoReleaseDate: Int? { return nil } 205 | var phasedRelease: Bool { return false } 206 | var resetRatings: Bool { return false } 207 | var priceTier: String? { return nil } 208 | var appRatingConfigPath: String? { return nil } 209 | var submissionInformation: [String: Any]? { return nil } 210 | var teamId: String? { return nil } 211 | var teamName: String? { return nil } 212 | var devPortalTeamId: String? { return nil } 213 | var devPortalTeamName: String? { return nil } 214 | var itcProvider: String? { return nil } 215 | var runPrecheckBeforeSubmit: Bool { return true } 216 | var precheckDefaultRuleLevel: String { return "warn" } 217 | var individualMetadataItems: [String]? { return nil } 218 | var appIcon: String? { return nil } 219 | var appleWatchAppIcon: String? { return nil } 220 | var copyright: String? { return nil } 221 | var primaryCategory: String? { return nil } 222 | var secondaryCategory: String? { return nil } 223 | var primaryFirstSubCategory: String? { return nil } 224 | var primarySecondSubCategory: String? { return nil } 225 | var secondaryFirstSubCategory: String? { return nil } 226 | var secondarySecondSubCategory: String? { return nil } 227 | var tradeRepresentativeContactInformation: [String: Any]? { return nil } 228 | var appReviewInformation: [String: Any]? { return nil } 229 | var appReviewAttachmentFile: String? { return nil } 230 | var description: String? { return nil } 231 | var name: String? { return nil } 232 | var subtitle: [String: Any]? { return nil } 233 | var keywords: [String: Any]? { return nil } 234 | var promotionalText: [String: Any]? { return nil } 235 | var releaseNotes: String? { return nil } 236 | var privacyUrl: String? { return nil } 237 | var appleTvPrivacyPolicy: String? { return nil } 238 | var supportUrl: String? { return nil } 239 | var marketingUrl: String? { return nil } 240 | var languages: [String]? { return nil } 241 | var ignoreLanguageDirectoryValidation: Bool { return false } 242 | var precheckIncludeInAppPurchases: Bool { return true } 243 | var app: String { return "" } 244 | } 245 | 246 | // Please don't remove the lines below 247 | // They are used to detect outdated files 248 | // FastlaneRunnerAPIVersion [0.9.25] 249 | -------------------------------------------------------------------------------- /fastlane/swift/ScanfileProtocol.swift: -------------------------------------------------------------------------------- 1 | protocol ScanfileProtocol: class { 2 | /// Path to the workspace file 3 | var workspace: String? { get } 4 | 5 | /// Path to the project file 6 | var project: String? { get } 7 | 8 | /// The project's scheme. Make sure it's marked as `Shared` 9 | var scheme: String? { get } 10 | 11 | /// The name of the simulator type you want to run tests on (e.g. 'iPhone 6') 12 | var device: String? { get } 13 | 14 | /// Array of devices to run the tests on (e.g. ['iPhone 6', 'iPad Air']) 15 | var devices: [String]? { get } 16 | 17 | /// Should skip auto detecting of devices if none were specified 18 | var skipDetectDevices: Bool { get } 19 | 20 | /// Enabling this option will automatically killall Simulator processes before the run 21 | var forceQuitSimulator: Bool { get } 22 | 23 | /// Enabling this option will automatically erase the simulator before running the application 24 | var resetSimulator: Bool { get } 25 | 26 | /// Enabling this option will disable the simulator from showing the 'Slide to type' prompt 27 | var disableSlideToType: Bool { get } 28 | 29 | /// Enabling this option will launch the first simulator prior to calling any xcodebuild command 30 | var prelaunchSimulator: Bool? { get } 31 | 32 | /// Enabling this option will automatically uninstall the application before running it 33 | var reinstallApp: Bool { get } 34 | 35 | /// The bundle identifier of the app to uninstall (only needed when enabling reinstall_app) 36 | var appIdentifier: String? { get } 37 | 38 | /// Array of strings matching Test Bundle/Test Suite/Test Cases to run 39 | var onlyTesting: String? { get } 40 | 41 | /// Array of strings matching Test Bundle/Test Suite/Test Cases to skip 42 | var skipTesting: String? { get } 43 | 44 | /// The testplan associated with the scheme that should be used for testing 45 | var testplan: String? { get } 46 | 47 | /// Array of strings matching test plan configurations to run 48 | var onlyTestConfigurations: String? { get } 49 | 50 | /// Array of strings matching test plan configurations to skip 51 | var skipTestConfigurations: String? { get } 52 | 53 | /// Run tests using the provided `.xctestrun` file 54 | var xctestrun: String? { get } 55 | 56 | /// The toolchain that should be used for building the application (e.g. `com.apple.dt.toolchain.Swift_2_3, org.swift.30p620160816a`) 57 | var toolchain: String? { get } 58 | 59 | /// Should the project be cleaned before building it? 60 | var clean: Bool { get } 61 | 62 | /// Should code coverage be generated? (Xcode 7 and up) 63 | var codeCoverage: Bool? { get } 64 | 65 | /// Should the address sanitizer be turned on? 66 | var addressSanitizer: Bool? { get } 67 | 68 | /// Should the thread sanitizer be turned on? 69 | var threadSanitizer: Bool? { get } 70 | 71 | /// Should the HTML report be opened when tests are completed? 72 | var openReport: Bool { get } 73 | 74 | /// Disable xcpretty formatting of build, similar to `output_style='raw'` but this will also skip the test results table 75 | var disableXcpretty: Bool? { get } 76 | 77 | /// The directory in which all reports will be stored 78 | var outputDirectory: String { get } 79 | 80 | /// Define how the output should look like. Valid values are: standard, basic, rspec, or raw (disables xcpretty during xcodebuild) 81 | var outputStyle: String? { get } 82 | 83 | /// Comma separated list of the output types (e.g. html, junit, json-compilation-database) 84 | var outputTypes: String { get } 85 | 86 | /// Comma separated list of the output files, corresponding to the types provided by :output_types (order should match). If specifying an output type of json-compilation-database with :use_clang_report_name enabled, that option will take precedence 87 | var outputFiles: String? { get } 88 | 89 | /// The directory where to store the raw log 90 | var buildlogPath: String { get } 91 | 92 | /// If the logs generated by the app (e.g. using NSLog, perror, etc.) in the Simulator should be written to the output_directory 93 | var includeSimulatorLogs: Bool { get } 94 | 95 | /// Suppress the output of xcodebuild to stdout. Output is still saved in buildlog_path 96 | var suppressXcodeOutput: Bool? { get } 97 | 98 | /// A custom xcpretty formatter to use 99 | var formatter: String? { get } 100 | 101 | /// Pass in xcpretty additional command line arguments (e.g. '--test --no-color' or '--tap --no-utf') 102 | var xcprettyArgs: String? { get } 103 | 104 | /// The directory where build products and other derived data will go 105 | var derivedDataPath: String? { get } 106 | 107 | /// Should zip the derived data build products and place in output path? 108 | var shouldZipBuildProducts: Bool { get } 109 | 110 | /// Should an Xcode result bundle be generated in the output directory 111 | var resultBundle: Bool { get } 112 | 113 | /// Generate the json compilation database with clang naming convention (compile_commands.json) 114 | var useClangReportName: Bool { get } 115 | 116 | /// Specify the exact number of test runners that will be spawned during parallel testing. Equivalent to -parallel-testing-worker-count 117 | var concurrentWorkers: Int? { get } 118 | 119 | /// Constrain the number of simulator devices on which to test concurrently. Equivalent to -maximum-concurrent-test-simulator-destinations 120 | var maxConcurrentSimulators: Int? { get } 121 | 122 | /// Do not run test bundles in parallel on the specified destinations. Testing will occur on each destination serially. Equivalent to -disable-concurrent-testing 123 | var disableConcurrentTesting: Bool { get } 124 | 125 | /// Should debug build be skipped before test build? 126 | var skipBuild: Bool { get } 127 | 128 | /// Test without building, requires a derived data path 129 | var testWithoutBuilding: Bool? { get } 130 | 131 | /// Build for testing only, does not run tests 132 | var buildForTesting: Bool? { get } 133 | 134 | /// The SDK that should be used for building the application 135 | var sdk: String? { get } 136 | 137 | /// The configuration to use when building the app. Defaults to 'Release' 138 | var configuration: String? { get } 139 | 140 | /// Pass additional arguments to xcodebuild. Be sure to quote the setting names and values e.g. OTHER_LDFLAGS="-ObjC -lstdc++" 141 | var xcargs: String? { get } 142 | 143 | /// Use an extra XCCONFIG file to build your app 144 | var xcconfig: String? { get } 145 | 146 | /// App name to use in slack message and logfile name 147 | var appName: String? { get } 148 | 149 | /// Target version of the app being build or tested. Used to filter out simulator version 150 | var deploymentTargetVersion: String? { get } 151 | 152 | /// Create an Incoming WebHook for your Slack group to post results there 153 | var slackUrl: String? { get } 154 | 155 | /// #channel or @username 156 | var slackChannel: String? { get } 157 | 158 | /// The message included with each message posted to slack 159 | var slackMessage: String? { get } 160 | 161 | /// Use webhook's default username and icon settings? (true/false) 162 | var slackUseWebhookConfiguredUsernameAndIcon: Bool { get } 163 | 164 | /// Overrides the webhook's username property if slack_use_webhook_configured_username_and_icon is false 165 | var slackUsername: String { get } 166 | 167 | /// Overrides the webhook's image property if slack_use_webhook_configured_username_and_icon is false 168 | var slackIconUrl: String { get } 169 | 170 | /// Don't publish to slack, even when an URL is given 171 | var skipSlack: Bool { get } 172 | 173 | /// Only post on Slack if the tests fail 174 | var slackOnlyOnFailure: Bool { get } 175 | 176 | /// Use only if you're a pro, use the other options instead 177 | var destination: String? { get } 178 | 179 | /// **DEPRECATED!** Use `--output_files` instead - Sets custom full report file name when generating a single report 180 | var customReportFileName: String? { get } 181 | 182 | /// Allows for override of the default `xcodebuild` command 183 | var xcodebuildCommand: String { get } 184 | 185 | /// Sets a custom path for Swift Package Manager dependencies 186 | var clonedSourcePackagesPath: String? { get } 187 | 188 | /// Should this step stop the build if the tests fail? Set this to false if you're using trainer 189 | var failBuild: Bool { get } 190 | } 191 | 192 | extension ScanfileProtocol { 193 | var workspace: String? { return nil } 194 | var project: String? { return nil } 195 | var scheme: String? { return nil } 196 | var device: String? { return nil } 197 | var devices: [String]? { return nil } 198 | var skipDetectDevices: Bool { return false } 199 | var forceQuitSimulator: Bool { return false } 200 | var resetSimulator: Bool { return false } 201 | var disableSlideToType: Bool { return true } 202 | var prelaunchSimulator: Bool? { return nil } 203 | var reinstallApp: Bool { return false } 204 | var appIdentifier: String? { return nil } 205 | var onlyTesting: String? { return nil } 206 | var skipTesting: String? { return nil } 207 | var testplan: String? { return nil } 208 | var onlyTestConfigurations: String? { return nil } 209 | var skipTestConfigurations: String? { return nil } 210 | var xctestrun: String? { return nil } 211 | var toolchain: String? { return nil } 212 | var clean: Bool { return false } 213 | var codeCoverage: Bool? { return nil } 214 | var addressSanitizer: Bool? { return nil } 215 | var threadSanitizer: Bool? { return nil } 216 | var openReport: Bool { return false } 217 | var disableXcpretty: Bool? { return nil } 218 | var outputDirectory: String { return "./test_output" } 219 | var outputStyle: String? { return nil } 220 | var outputTypes: String { return "html,junit" } 221 | var outputFiles: String? { return nil } 222 | var buildlogPath: String { return "~/Library/Logs/scan" } 223 | var includeSimulatorLogs: Bool { return false } 224 | var suppressXcodeOutput: Bool? { return nil } 225 | var formatter: String? { return nil } 226 | var xcprettyArgs: String? { return nil } 227 | var derivedDataPath: String? { return nil } 228 | var shouldZipBuildProducts: Bool { return false } 229 | var resultBundle: Bool { return false } 230 | var useClangReportName: Bool { return false } 231 | var concurrentWorkers: Int? { return nil } 232 | var maxConcurrentSimulators: Int? { return nil } 233 | var disableConcurrentTesting: Bool { return false } 234 | var skipBuild: Bool { return false } 235 | var testWithoutBuilding: Bool? { return nil } 236 | var buildForTesting: Bool? { return nil } 237 | var sdk: String? { return nil } 238 | var configuration: String? { return nil } 239 | var xcargs: String? { return nil } 240 | var xcconfig: String? { return nil } 241 | var appName: String? { return nil } 242 | var deploymentTargetVersion: String? { return nil } 243 | var slackUrl: String? { return nil } 244 | var slackChannel: String? { return nil } 245 | var slackMessage: String? { return nil } 246 | var slackUseWebhookConfiguredUsernameAndIcon: Bool { return false } 247 | var slackUsername: String { return "fastlane" } 248 | var slackIconUrl: String { return "https://fastlane.tools/assets/img/fastlane_icon.png" } 249 | var skipSlack: Bool { return false } 250 | var slackOnlyOnFailure: Bool { return false } 251 | var destination: String? { return nil } 252 | var customReportFileName: String? { return nil } 253 | var xcodebuildCommand: String { return "env NSUnbufferedIO=YES xcodebuild" } 254 | var clonedSourcePackagesPath: String? { return nil } 255 | var failBuild: Bool { return true } 256 | } 257 | 258 | // Please don't remove the lines below 259 | // They are used to detect outdated files 260 | // FastlaneRunnerAPIVersion [0.9.33] 261 | -------------------------------------------------------------------------------- /fastlane/swift/SocketClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SocketClient.swift 3 | // FastlaneSwiftRunner 4 | // 5 | // Created by Joshua Liebowitz on 7/30/17. 6 | // 7 | 8 | // 9 | // ** NOTE ** 10 | // This file is provided by fastlane and WILL be overwritten in future updates 11 | // If you want to add extra functionality to this project, create a new file in a 12 | // new group so that it won't be marked for upgrade 13 | // 14 | 15 | import Dispatch 16 | import Foundation 17 | 18 | public enum SocketClientResponse: Error { 19 | case alreadyClosedSockets 20 | case malformedRequest 21 | case malformedResponse 22 | case serverError 23 | case clientInitiatedCancelAcknowledged 24 | case commandTimeout(seconds: Int) 25 | case connectionFailure 26 | case success(returnedObject: String?, closureArgumentValue: String?) 27 | } 28 | 29 | class SocketClient: NSObject { 30 | enum SocketStatus { 31 | case ready 32 | case closed 33 | } 34 | 35 | static let connectTimeoutSeconds = 2 36 | static let defaultCommandTimeoutSeconds = 10_800 // 3 hours 37 | static let doneToken = "done" // TODO: remove these 38 | static let cancelToken = "cancelFastlaneRun" 39 | 40 | fileprivate var inputStream: InputStream! 41 | fileprivate var outputStream: OutputStream! 42 | fileprivate var cleaningUpAfterDone = false 43 | fileprivate let dispatchGroup: DispatchGroup = DispatchGroup() 44 | fileprivate let readSemaphore = DispatchSemaphore(value: 1) 45 | fileprivate let writeSemaphore = DispatchSemaphore(value: 1) 46 | fileprivate let commandTimeoutSeconds: Int 47 | 48 | private let writeQueue: DispatchQueue 49 | private let readQueue: DispatchQueue 50 | private let streamQueue: DispatchQueue 51 | private let host: String 52 | private let port: UInt32 53 | 54 | let maxReadLength = 65536 // max for ipc on 10.12 is kern.ipc.maxsockbuf: 8388608 ($sysctl kern.ipc.maxsockbuf) 55 | 56 | private(set) weak var socketDelegate: SocketClientDelegateProtocol? 57 | 58 | public private(set) var socketStatus: SocketStatus 59 | 60 | // localhost only, this prevents other computers from connecting 61 | init(host: String = "localhost", port: UInt32 = 2000, commandTimeoutSeconds: Int = defaultCommandTimeoutSeconds, socketDelegate: SocketClientDelegateProtocol) { 62 | self.host = host 63 | self.port = port 64 | self.commandTimeoutSeconds = commandTimeoutSeconds 65 | readQueue = DispatchQueue(label: "readQueue", qos: .background, attributes: .concurrent) 66 | writeQueue = DispatchQueue(label: "writeQueue", qos: .background, attributes: .concurrent) 67 | streamQueue = DispatchQueue.global(qos: .background) 68 | socketStatus = .closed 69 | self.socketDelegate = socketDelegate 70 | super.init() 71 | } 72 | 73 | func connectAndOpenStreams() { 74 | var readStream: Unmanaged? 75 | var writeStream: Unmanaged? 76 | 77 | streamQueue.sync { 78 | CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, self.host as CFString, self.port, &readStream, &writeStream) 79 | 80 | self.inputStream = readStream!.takeRetainedValue() 81 | self.outputStream = writeStream!.takeRetainedValue() 82 | 83 | self.inputStream.delegate = self 84 | self.outputStream.delegate = self 85 | 86 | self.inputStream.schedule(in: .main, forMode: .defaultRunLoopMode) 87 | self.outputStream.schedule(in: .main, forMode: .defaultRunLoopMode) 88 | } 89 | 90 | dispatchGroup.enter() 91 | readQueue.sync { 92 | self.inputStream.open() 93 | } 94 | 95 | dispatchGroup.enter() 96 | writeQueue.sync { 97 | self.outputStream.open() 98 | } 99 | 100 | let secondsToWait = DispatchTimeInterval.seconds(SocketClient.connectTimeoutSeconds) 101 | let connectTimeout = DispatchTime.now() + secondsToWait 102 | 103 | let timeoutResult = dispatchGroup.wait(timeout: connectTimeout) 104 | let failureMessage = "Couldn't connect to ruby process within: \(SocketClient.connectTimeoutSeconds) seconds" 105 | 106 | let success = testDispatchTimeoutResult(timeoutResult, failureMessage: failureMessage, timeToWait: secondsToWait) 107 | 108 | guard success else { 109 | socketDelegate?.commandExecuted(serverResponse: .connectionFailure) { _ in } 110 | return 111 | } 112 | 113 | socketStatus = .ready 114 | socketDelegate?.connectionsOpened() 115 | } 116 | 117 | public func send(rubyCommand: RubyCommandable) { 118 | verbose(message: "sending: \(rubyCommand.json)") 119 | send(string: rubyCommand.json) 120 | writeSemaphore.signal() 121 | } 122 | 123 | public func sendComplete() { 124 | closeSession(sendAbort: true) 125 | } 126 | 127 | private func testDispatchTimeoutResult(_ timeoutResult: DispatchTimeoutResult, failureMessage: String, timeToWait: DispatchTimeInterval) -> Bool { 128 | switch timeoutResult { 129 | case .success: 130 | return true 131 | case .timedOut: 132 | log(message: "Timeout: \(failureMessage)") 133 | 134 | if case let .seconds(seconds) = timeToWait { 135 | socketDelegate?.commandExecuted(serverResponse: .commandTimeout(seconds: seconds)) { _ in } 136 | } 137 | return false 138 | } 139 | } 140 | 141 | private func stopInputSession() { 142 | inputStream.close() 143 | } 144 | 145 | private func stopOutputSession() { 146 | outputStream.close() 147 | } 148 | 149 | private func sendThroughQueue(string: String) { 150 | let data = string.data(using: .utf8)! 151 | _ = data.withUnsafeBytes { self.outputStream.write($0, maxLength: data.count) } 152 | } 153 | 154 | private func privateSend(string: String) { 155 | writeQueue.sync { 156 | writeSemaphore.wait() 157 | self.sendThroughQueue(string: string) 158 | writeSemaphore.signal() 159 | let timeoutSeconds = self.cleaningUpAfterDone ? 1 : self.commandTimeoutSeconds 160 | let timeToWait = DispatchTimeInterval.seconds(timeoutSeconds) 161 | let commandTimeout = DispatchTime.now() + timeToWait 162 | let timeoutResult = writeSemaphore.wait(timeout: commandTimeout) 163 | 164 | _ = self.testDispatchTimeoutResult(timeoutResult, failureMessage: "Ruby process didn't return after: \(SocketClient.connectTimeoutSeconds) seconds", timeToWait: timeToWait) 165 | } 166 | } 167 | 168 | private func send(string: String) { 169 | guard !cleaningUpAfterDone else { 170 | // This will happen after we abort if there are commands waiting to be executed 171 | // Need to check state of SocketClient in command runner to make sure we can accept `send` 172 | socketDelegate?.commandExecuted(serverResponse: .alreadyClosedSockets) { _ in } 173 | return 174 | } 175 | 176 | if string == SocketClient.doneToken { 177 | cleaningUpAfterDone = true 178 | } 179 | 180 | privateSend(string: string) 181 | } 182 | 183 | func closeSession(sendAbort: Bool = true) { 184 | socketStatus = .closed 185 | 186 | stopInputSession() 187 | 188 | if sendAbort { 189 | send(rubyCommand: ControlCommand(commandType: .done)) 190 | } 191 | 192 | stopOutputSession() 193 | socketDelegate?.connectionsClosed() 194 | } 195 | 196 | public func enter() { 197 | dispatchGroup.enter() 198 | } 199 | 200 | public func leave() { 201 | readSemaphore.signal() 202 | writeSemaphore.signal() 203 | } 204 | } 205 | 206 | extension SocketClient: StreamDelegate { 207 | func stream(_ aStream: Stream, handle eventCode: Stream.Event) { 208 | guard !cleaningUpAfterDone else { 209 | // Still getting response from server eventhough we are done. 210 | // No big deal, we're closing the streams anyway. 211 | // That being said, we need to balance out the dispatchGroups 212 | dispatchGroup.leave() 213 | return 214 | } 215 | 216 | if aStream === inputStream { 217 | switch eventCode { 218 | case Stream.Event.openCompleted: 219 | dispatchGroup.leave() 220 | 221 | case Stream.Event.errorOccurred: 222 | verbose(message: "input stream error occurred") 223 | closeSession(sendAbort: true) 224 | 225 | case Stream.Event.hasBytesAvailable: 226 | read() 227 | 228 | case Stream.Event.endEncountered: 229 | // nothing special here 230 | break 231 | 232 | case Stream.Event.hasSpaceAvailable: 233 | // we don't care about this 234 | break 235 | 236 | default: 237 | verbose(message: "input stream caused unrecognized event: \(eventCode)") 238 | } 239 | 240 | } else if aStream === outputStream { 241 | switch eventCode { 242 | case Stream.Event.openCompleted: 243 | dispatchGroup.leave() 244 | 245 | case Stream.Event.errorOccurred: 246 | // probably safe to close all the things because Ruby already disconnected 247 | verbose(message: "output stream recevied error") 248 | 249 | case Stream.Event.endEncountered: 250 | // nothing special here 251 | break 252 | 253 | case Stream.Event.hasSpaceAvailable: 254 | // we don't care about this 255 | break 256 | 257 | default: 258 | verbose(message: "output stream caused unrecognized event: \(eventCode)") 259 | } 260 | } 261 | } 262 | 263 | func read() { 264 | readQueue.sync { 265 | self.readSemaphore.wait() 266 | var buffer = [UInt8](repeating: 0, count: maxReadLength) 267 | var output = "" 268 | while self.inputStream!.hasBytesAvailable { 269 | let bytesRead: Int = inputStream!.read(&buffer, maxLength: buffer.count) 270 | if bytesRead >= 0 { 271 | guard let read = String(bytes: buffer[.. 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 57 | 63 | 64 | 65 | 71 | 77 | 78 | 79 | 85 | 91 | 92 | 93 | 99 | 105 | 106 | 107 | 113 | 119 | 120 | 121 | 127 | 133 | 134 | 135 | 141 | 147 | 148 | 149 | 155 | 161 | 162 | 163 | 169 | 175 | 176 | 177 | 183 | 189 | 190 | 191 | 197 | 203 | 204 | 205 | 211 | 217 | 218 | 219 | 225 | 231 | 232 | 233 | 239 | 245 | 246 | 247 | 253 | 259 | 260 | 261 | 267 | 273 | 274 | 275 | 281 | 287 | 288 | 289 | 295 | 301 | 302 | 303 | 309 | 315 | 316 | 317 | 323 | 329 | 330 | 331 | 337 | 343 | 344 | 345 | 351 | 357 | 358 | 359 | 365 | 371 | 372 | 373 | 379 | 385 | 386 | 387 | 388 | 389 | 394 | 395 | 401 | 402 | 403 | 404 | 406 | 412 | 413 | 414 | 416 | 422 | 423 | 424 | 426 | 432 | 433 | 434 | 436 | 442 | 443 | 444 | 446 | 452 | 453 | 454 | 456 | 462 | 463 | 464 | 466 | 472 | 473 | 474 | 476 | 482 | 483 | 484 | 486 | 492 | 493 | 494 | 496 | 502 | 503 | 504 | 506 | 512 | 513 | 514 | 515 | 516 | 526 | 528 | 534 | 535 | 536 | 537 | 543 | 544 | 550 | 551 | 552 | 553 | 555 | 556 | 559 | 560 | 561 | -------------------------------------------------------------------------------- /fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0311E387230AC1B20060BB5C /* Plugins.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0311E386230AC1B20060BB5C /* Plugins.swift */; }; 11 | 0311E38B230AC9490060BB5C /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0311E38A230AC9490060BB5C /* Actions.swift */; }; 12 | B302067B1F5E3E9000DE6EBD /* SnapshotfileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B30206741F5E3E9000DE6EBD /* SnapshotfileProtocol.swift */; }; 13 | B302067C1F5E3E9000DE6EBD /* GymfileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B30206751F5E3E9000DE6EBD /* GymfileProtocol.swift */; }; 14 | B302067D1F5E3E9000DE6EBD /* MatchfileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B30206761F5E3E9000DE6EBD /* MatchfileProtocol.swift */; }; 15 | B302067E1F5E3E9000DE6EBD /* PrecheckfileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B30206771F5E3E9000DE6EBD /* PrecheckfileProtocol.swift */; }; 16 | B302067F1F5E3E9000DE6EBD /* ScanfileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B30206781F5E3E9000DE6EBD /* ScanfileProtocol.swift */; }; 17 | B30206801F5E3E9000DE6EBD /* ScreengrabfileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B30206791F5E3E9000DE6EBD /* ScreengrabfileProtocol.swift */; }; 18 | B30206811F5E3E9000DE6EBD /* DeliverfileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B302067A1F5E3E9000DE6EBD /* DeliverfileProtocol.swift */; }; 19 | B3BA65A61F5A269100B34850 /* Fastlane.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA659D1F5A269100B34850 /* Fastlane.swift */; }; 20 | B3BA65A71F5A269100B34850 /* LaneFileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA659E1F5A269100B34850 /* LaneFileProtocol.swift */; }; 21 | B3BA65A81F5A269100B34850 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA659F1F5A269100B34850 /* main.swift */; }; 22 | B3BA65A91F5A269100B34850 /* RubyCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA65A01F5A269100B34850 /* RubyCommand.swift */; }; 23 | B3BA65AA1F5A269100B34850 /* Runner.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA65A11F5A269100B34850 /* Runner.swift */; }; 24 | B3BA65AB1F5A269100B34850 /* SocketClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA65A21F5A269100B34850 /* SocketClient.swift */; }; 25 | B3BA65AC1F5A269100B34850 /* SocketClientDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA65A31F5A269100B34850 /* SocketClientDelegateProtocol.swift */; }; 26 | B3BA65AD1F5A269100B34850 /* SocketResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA65A41F5A269100B34850 /* SocketResponse.swift */; }; 27 | B3BA65AF1F5A2D5C00B34850 /* RunnerArgument.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA65AE1F5A2D5C00B34850 /* RunnerArgument.swift */; }; 28 | D55B28C31F6C588300DC42C5 /* Deliverfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B28BC1F6C588300DC42C5 /* Deliverfile.swift */; }; 29 | D55B28C41F6C588300DC42C5 /* Gymfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B28BD1F6C588300DC42C5 /* Gymfile.swift */; }; 30 | D55B28C51F6C588300DC42C5 /* Matchfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B28BE1F6C588300DC42C5 /* Matchfile.swift */; }; 31 | D55B28C61F6C588300DC42C5 /* Precheckfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B28BF1F6C588300DC42C5 /* Precheckfile.swift */; }; 32 | D55B28C71F6C588300DC42C5 /* Scanfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B28C01F6C588300DC42C5 /* Scanfile.swift */; }; 33 | D55B28C81F6C588300DC42C5 /* Screengrabfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B28C11F6C588300DC42C5 /* Screengrabfile.swift */; }; 34 | D55B28C91F6C588300DC42C5 /* Snapshotfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B28C21F6C588300DC42C5 /* Snapshotfile.swift */; }; 35 | D5A7C48F1F7C4DAF00A91DE6 /* Appfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A7C48D1F7C4DAF00A91DE6 /* Appfile.swift */; }; 36 | D5A7C4901F7C4DAF00A91DE6 /* Fastfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A7C48E1F7C4DAF00A91DE6 /* Fastfile.swift */; }; 37 | D5B8A5B31FFDC49E00536B24 /* ControlCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B8A5B21FFDC49D00536B24 /* ControlCommand.swift */; }; 38 | D5BAFD121F7DAAFC0030B324 /* ArgumentProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BAFD111F7DAAFC0030B324 /* ArgumentProcessor.swift */; }; 39 | D5D1DE991FFEE8EA00502A00 /* RubyCommandable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D1DE981FFEE8E900502A00 /* RubyCommandable.swift */; }; 40 | /* End PBXBuildFile section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 0311E386230AC1B20060BB5C /* Plugins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Plugins.swift; path = ../Plugins.swift; sourceTree = ""; }; 44 | 0311E38A230AC9490060BB5C /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Actions.swift; path = ../Actions.swift; sourceTree = ""; }; 45 | B30206741F5E3E9000DE6EBD /* SnapshotfileProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotfileProtocol.swift; path = ../SnapshotfileProtocol.swift; sourceTree = ""; }; 46 | B30206751F5E3E9000DE6EBD /* GymfileProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GymfileProtocol.swift; path = ../GymfileProtocol.swift; sourceTree = ""; }; 47 | B30206761F5E3E9000DE6EBD /* MatchfileProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MatchfileProtocol.swift; path = ../MatchfileProtocol.swift; sourceTree = ""; }; 48 | B30206771F5E3E9000DE6EBD /* PrecheckfileProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PrecheckfileProtocol.swift; path = ../PrecheckfileProtocol.swift; sourceTree = ""; }; 49 | B30206781F5E3E9000DE6EBD /* ScanfileProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ScanfileProtocol.swift; path = ../ScanfileProtocol.swift; sourceTree = ""; }; 50 | B30206791F5E3E9000DE6EBD /* ScreengrabfileProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ScreengrabfileProtocol.swift; path = ../ScreengrabfileProtocol.swift; sourceTree = ""; }; 51 | B302067A1F5E3E9000DE6EBD /* DeliverfileProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeliverfileProtocol.swift; path = ../DeliverfileProtocol.swift; sourceTree = ""; }; 52 | B3144C072005533400470AFE /* README.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.txt; sourceTree = ""; }; 53 | B3144C08200553C800470AFE /* README.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.txt; sourceTree = ""; }; 54 | B3144C09200553D400470AFE /* README.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.txt; sourceTree = ""; }; 55 | B3144C0A200553DC00470AFE /* README.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.txt; sourceTree = ""; }; 56 | B3BA659D1F5A269100B34850 /* Fastlane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Fastlane.swift; path = ../Fastlane.swift; sourceTree = ""; }; 57 | B3BA659E1F5A269100B34850 /* LaneFileProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LaneFileProtocol.swift; path = ../LaneFileProtocol.swift; sourceTree = ""; }; 58 | B3BA659F1F5A269100B34850 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = main.swift; path = ../main.swift; sourceTree = ""; }; 59 | B3BA65A01F5A269100B34850 /* RubyCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RubyCommand.swift; path = ../RubyCommand.swift; sourceTree = ""; }; 60 | B3BA65A11F5A269100B34850 /* Runner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Runner.swift; path = ../Runner.swift; sourceTree = ""; }; 61 | B3BA65A21F5A269100B34850 /* SocketClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketClient.swift; path = ../SocketClient.swift; sourceTree = ""; }; 62 | B3BA65A31F5A269100B34850 /* SocketClientDelegateProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketClientDelegateProtocol.swift; path = ../SocketClientDelegateProtocol.swift; sourceTree = ""; }; 63 | B3BA65A41F5A269100B34850 /* SocketResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketResponse.swift; path = ../SocketResponse.swift; sourceTree = ""; }; 64 | B3BA65AE1F5A2D5C00B34850 /* RunnerArgument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RunnerArgument.swift; path = ../RunnerArgument.swift; sourceTree = ""; }; 65 | D556D6A91F6A08F5003108E3 /* FastlaneRunner */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = FastlaneRunner; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | D55B28BC1F6C588300DC42C5 /* Deliverfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Deliverfile.swift; path = ../Deliverfile.swift; sourceTree = ""; }; 67 | D55B28BD1F6C588300DC42C5 /* Gymfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Gymfile.swift; path = ../Gymfile.swift; sourceTree = ""; }; 68 | D55B28BE1F6C588300DC42C5 /* Matchfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Matchfile.swift; path = ../Matchfile.swift; sourceTree = ""; }; 69 | D55B28BF1F6C588300DC42C5 /* Precheckfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Precheckfile.swift; path = ../Precheckfile.swift; sourceTree = ""; }; 70 | D55B28C01F6C588300DC42C5 /* Scanfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Scanfile.swift; path = ../Scanfile.swift; sourceTree = ""; }; 71 | D55B28C11F6C588300DC42C5 /* Screengrabfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Screengrabfile.swift; path = ../Screengrabfile.swift; sourceTree = ""; }; 72 | D55B28C21F6C588300DC42C5 /* Snapshotfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Snapshotfile.swift; path = ../Snapshotfile.swift; sourceTree = ""; }; 73 | D5A7C48D1F7C4DAF00A91DE6 /* Appfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Appfile.swift; path = ../../Appfile.swift; sourceTree = ""; }; 74 | D5A7C48E1F7C4DAF00A91DE6 /* Fastfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Fastfile.swift; path = ../../Fastfile.swift; sourceTree = ""; }; 75 | D5B8A5B21FFDC49D00536B24 /* ControlCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ControlCommand.swift; path = ../ControlCommand.swift; sourceTree = ""; }; 76 | D5BAFD111F7DAAFC0030B324 /* ArgumentProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ArgumentProcessor.swift; path = ../ArgumentProcessor.swift; sourceTree = ""; }; 77 | D5D1DE981FFEE8E900502A00 /* RubyCommandable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RubyCommandable.swift; path = ../RubyCommandable.swift; sourceTree = ""; }; 78 | /* End PBXFileReference section */ 79 | 80 | /* Begin PBXFrameworksBuildPhase section */ 81 | B33BAF541F51F8D90001A751 /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | B33BAF4E1F51F8D90001A751 = { 92 | isa = PBXGroup; 93 | children = ( 94 | B3BA65B01F5A324A00B34850 /* Fastlane Runner */, 95 | D556D6A91F6A08F5003108E3 /* FastlaneRunner */, 96 | ); 97 | sourceTree = ""; 98 | }; 99 | B3BA65B01F5A324A00B34850 /* Fastlane Runner */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | B3BA65B21F5A327B00B34850 /* Autogenerated API */, 103 | B3BA65B31F5A329800B34850 /* Fastfile Components */, 104 | B3BA65B11F5A325E00B34850 /* Networking */, 105 | D512BA011F7C7F40000D2137 /* Runner Code */, 106 | D5A7C48D1F7C4DAF00A91DE6 /* Appfile.swift */, 107 | D55B28BC1F6C588300DC42C5 /* Deliverfile.swift */, 108 | D5A7C48E1F7C4DAF00A91DE6 /* Fastfile.swift */, 109 | D55B28BD1F6C588300DC42C5 /* Gymfile.swift */, 110 | D55B28BE1F6C588300DC42C5 /* Matchfile.swift */, 111 | D55B28BF1F6C588300DC42C5 /* Precheckfile.swift */, 112 | D55B28C01F6C588300DC42C5 /* Scanfile.swift */, 113 | D55B28C11F6C588300DC42C5 /* Screengrabfile.swift */, 114 | D55B28C21F6C588300DC42C5 /* Snapshotfile.swift */, 115 | ); 116 | name = "Fastlane Runner"; 117 | sourceTree = ""; 118 | }; 119 | B3BA65B11F5A325E00B34850 /* Networking */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | B3144C072005533400470AFE /* README.txt */, 123 | D5B8A5B21FFDC49D00536B24 /* ControlCommand.swift */, 124 | B3BA65A01F5A269100B34850 /* RubyCommand.swift */, 125 | D5D1DE981FFEE8E900502A00 /* RubyCommandable.swift */, 126 | B3BA65A11F5A269100B34850 /* Runner.swift */, 127 | B3BA65A21F5A269100B34850 /* SocketClient.swift */, 128 | B3BA65A31F5A269100B34850 /* SocketClientDelegateProtocol.swift */, 129 | B3BA65A41F5A269100B34850 /* SocketResponse.swift */, 130 | ); 131 | name = Networking; 132 | sourceTree = ""; 133 | }; 134 | B3BA65B21F5A327B00B34850 /* Autogenerated API */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | B3144C09200553D400470AFE /* README.txt */, 138 | 0311E38A230AC9490060BB5C /* Actions.swift */, 139 | B3BA659D1F5A269100B34850 /* Fastlane.swift */, 140 | B302067A1F5E3E9000DE6EBD /* DeliverfileProtocol.swift */, 141 | B30206751F5E3E9000DE6EBD /* GymfileProtocol.swift */, 142 | B30206761F5E3E9000DE6EBD /* MatchfileProtocol.swift */, 143 | 0311E386230AC1B20060BB5C /* Plugins.swift */, 144 | B30206771F5E3E9000DE6EBD /* PrecheckfileProtocol.swift */, 145 | B30206781F5E3E9000DE6EBD /* ScanfileProtocol.swift */, 146 | B30206791F5E3E9000DE6EBD /* ScreengrabfileProtocol.swift */, 147 | B30206741F5E3E9000DE6EBD /* SnapshotfileProtocol.swift */, 148 | ); 149 | name = "Autogenerated API"; 150 | sourceTree = ""; 151 | }; 152 | B3BA65B31F5A329800B34850 /* Fastfile Components */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | B3144C08200553C800470AFE /* README.txt */, 156 | B3BA659E1F5A269100B34850 /* LaneFileProtocol.swift */, 157 | ); 158 | name = "Fastfile Components"; 159 | sourceTree = ""; 160 | }; 161 | D512BA011F7C7F40000D2137 /* Runner Code */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | B3144C0A200553DC00470AFE /* README.txt */, 165 | D5BAFD111F7DAAFC0030B324 /* ArgumentProcessor.swift */, 166 | B3BA659F1F5A269100B34850 /* main.swift */, 167 | B3BA65AE1F5A2D5C00B34850 /* RunnerArgument.swift */, 168 | ); 169 | name = "Runner Code"; 170 | sourceTree = ""; 171 | }; 172 | /* End PBXGroup section */ 173 | 174 | /* Begin PBXNativeTarget section */ 175 | B33BAF561F51F8D90001A751 /* FastlaneRunner */ = { 176 | isa = PBXNativeTarget; 177 | buildConfigurationList = B33BAF5E1F51F8D90001A751 /* Build configuration list for PBXNativeTarget "FastlaneRunner" */; 178 | buildPhases = ( 179 | B33BAF531F51F8D90001A751 /* Sources */, 180 | B33BAF541F51F8D90001A751 /* Frameworks */, 181 | D529C72B1F68BB1C0036536D /* ShellScript */, 182 | ); 183 | buildRules = ( 184 | ); 185 | dependencies = ( 186 | ); 187 | name = FastlaneRunner; 188 | productName = FastlaneSwiftRunner; 189 | productReference = D556D6A91F6A08F5003108E3 /* FastlaneRunner */; 190 | productType = "com.apple.product-type.tool"; 191 | }; 192 | /* End PBXNativeTarget section */ 193 | 194 | /* Begin PBXProject section */ 195 | B33BAF4F1F51F8D90001A751 /* Project object */ = { 196 | isa = PBXProject; 197 | attributes = { 198 | LastSwiftUpdateCheck = 0830; 199 | LastUpgradeCheck = 0900; 200 | ORGANIZATIONNAME = "Joshua Liebowitz"; 201 | TargetAttributes = { 202 | B33BAF561F51F8D90001A751 = { 203 | CreatedOnToolsVersion = 8.3.3; 204 | LastSwiftMigration = 0900; 205 | ProvisioningStyle = Automatic; 206 | }; 207 | }; 208 | }; 209 | buildConfigurationList = B33BAF521F51F8D90001A751 /* Build configuration list for PBXProject "FastlaneSwiftRunner" */; 210 | compatibilityVersion = "Xcode 3.2"; 211 | developmentRegion = English; 212 | hasScannedForEncodings = 0; 213 | knownRegions = ( 214 | English, 215 | en, 216 | ); 217 | mainGroup = B33BAF4E1F51F8D90001A751; 218 | productRefGroup = B33BAF4E1F51F8D90001A751; 219 | projectDirPath = ""; 220 | projectRoot = ""; 221 | targets = ( 222 | B33BAF561F51F8D90001A751 /* FastlaneRunner */, 223 | ); 224 | }; 225 | /* End PBXProject section */ 226 | 227 | /* Begin PBXShellScriptBuildPhase section */ 228 | D529C72B1F68BB1C0036536D /* ShellScript */ = { 229 | isa = PBXShellScriptBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | ); 233 | inputPaths = ( 234 | ); 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "cd \"${SRCROOT}\"\ncd ../..\ncp \"${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}\" ."; 240 | }; 241 | /* End PBXShellScriptBuildPhase section */ 242 | 243 | /* Begin PBXSourcesBuildPhase section */ 244 | B33BAF531F51F8D90001A751 /* Sources */ = { 245 | isa = PBXSourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | B3BA65A91F5A269100B34850 /* RubyCommand.swift in Sources */, 249 | D5D1DE991FFEE8EA00502A00 /* RubyCommandable.swift in Sources */, 250 | D55B28C41F6C588300DC42C5 /* Gymfile.swift in Sources */, 251 | B302067D1F5E3E9000DE6EBD /* MatchfileProtocol.swift in Sources */, 252 | B3BA65AC1F5A269100B34850 /* SocketClientDelegateProtocol.swift in Sources */, 253 | B3BA65A71F5A269100B34850 /* LaneFileProtocol.swift in Sources */, 254 | D55B28C61F6C588300DC42C5 /* Precheckfile.swift in Sources */, 255 | B302067F1F5E3E9000DE6EBD /* ScanfileProtocol.swift in Sources */, 256 | D55B28C51F6C588300DC42C5 /* Matchfile.swift in Sources */, 257 | B30206801F5E3E9000DE6EBD /* ScreengrabfileProtocol.swift in Sources */, 258 | D5BAFD121F7DAAFC0030B324 /* ArgumentProcessor.swift in Sources */, 259 | B302067C1F5E3E9000DE6EBD /* GymfileProtocol.swift in Sources */, 260 | B302067B1F5E3E9000DE6EBD /* SnapshotfileProtocol.swift in Sources */, 261 | D55B28C31F6C588300DC42C5 /* Deliverfile.swift in Sources */, 262 | D5A7C4901F7C4DAF00A91DE6 /* Fastfile.swift in Sources */, 263 | 0311E38B230AC9490060BB5C /* Actions.swift in Sources */, 264 | D5A7C48F1F7C4DAF00A91DE6 /* Appfile.swift in Sources */, 265 | B3BA65AB1F5A269100B34850 /* SocketClient.swift in Sources */, 266 | B30206811F5E3E9000DE6EBD /* DeliverfileProtocol.swift in Sources */, 267 | B3BA65AA1F5A269100B34850 /* Runner.swift in Sources */, 268 | B3BA65AF1F5A2D5C00B34850 /* RunnerArgument.swift in Sources */, 269 | D5B8A5B31FFDC49E00536B24 /* ControlCommand.swift in Sources */, 270 | B302067E1F5E3E9000DE6EBD /* PrecheckfileProtocol.swift in Sources */, 271 | B3BA65AD1F5A269100B34850 /* SocketResponse.swift in Sources */, 272 | B3BA65A81F5A269100B34850 /* main.swift in Sources */, 273 | D55B28C71F6C588300DC42C5 /* Scanfile.swift in Sources */, 274 | 0311E387230AC1B20060BB5C /* Plugins.swift in Sources */, 275 | D55B28C91F6C588300DC42C5 /* Snapshotfile.swift in Sources */, 276 | B3BA65A61F5A269100B34850 /* Fastlane.swift in Sources */, 277 | D55B28C81F6C588300DC42C5 /* Screengrabfile.swift in Sources */, 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | /* End PBXSourcesBuildPhase section */ 282 | 283 | /* Begin XCBuildConfiguration section */ 284 | B33BAF5C1F51F8D90001A751 /* Debug */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ALWAYS_SEARCH_USER_PATHS = NO; 288 | CLANG_ANALYZER_NONNULL = YES; 289 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 291 | CLANG_CXX_LIBRARY = "libc++"; 292 | CLANG_ENABLE_MODULES = YES; 293 | CLANG_ENABLE_OBJC_ARC = YES; 294 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 295 | CLANG_WARN_BOOL_CONVERSION = YES; 296 | CLANG_WARN_COMMA = YES; 297 | CLANG_WARN_CONSTANT_CONVERSION = YES; 298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 299 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 300 | CLANG_WARN_EMPTY_BODY = YES; 301 | CLANG_WARN_ENUM_CONVERSION = YES; 302 | CLANG_WARN_INFINITE_RECURSION = YES; 303 | CLANG_WARN_INT_CONVERSION = YES; 304 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 305 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 306 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 307 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 308 | CLANG_WARN_STRICT_PROTOTYPES = YES; 309 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 310 | CLANG_WARN_UNREACHABLE_CODE = YES; 311 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 312 | CODE_SIGN_IDENTITY = "-"; 313 | COPY_PHASE_STRIP = NO; 314 | DEBUG_INFORMATION_FORMAT = dwarf; 315 | ENABLE_STRICT_OBJC_MSGSEND = YES; 316 | ENABLE_TESTABILITY = YES; 317 | GCC_C_LANGUAGE_STANDARD = gnu99; 318 | GCC_DYNAMIC_NO_PIC = NO; 319 | GCC_NO_COMMON_BLOCKS = YES; 320 | GCC_OPTIMIZATION_LEVEL = 0; 321 | GCC_PREPROCESSOR_DEFINITIONS = ( 322 | "DEBUG=1", 323 | "$(inherited)", 324 | ); 325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 327 | GCC_WARN_UNDECLARED_SELECTOR = YES; 328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 329 | GCC_WARN_UNUSED_FUNCTION = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | MACOSX_DEPLOYMENT_TARGET = 10.12; 332 | MTL_ENABLE_DEBUG_INFO = YES; 333 | ONLY_ACTIVE_ARCH = YES; 334 | SDKROOT = macosx; 335 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 336 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 337 | }; 338 | name = Debug; 339 | }; 340 | B33BAF5D1F51F8D90001A751 /* Release */ = { 341 | isa = XCBuildConfiguration; 342 | buildSettings = { 343 | ALWAYS_SEARCH_USER_PATHS = NO; 344 | CLANG_ANALYZER_NONNULL = YES; 345 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 346 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 347 | CLANG_CXX_LIBRARY = "libc++"; 348 | CLANG_ENABLE_MODULES = YES; 349 | CLANG_ENABLE_OBJC_ARC = YES; 350 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 351 | CLANG_WARN_BOOL_CONVERSION = YES; 352 | CLANG_WARN_COMMA = YES; 353 | CLANG_WARN_CONSTANT_CONVERSION = YES; 354 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 355 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 356 | CLANG_WARN_EMPTY_BODY = YES; 357 | CLANG_WARN_ENUM_CONVERSION = YES; 358 | CLANG_WARN_INFINITE_RECURSION = YES; 359 | CLANG_WARN_INT_CONVERSION = YES; 360 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 361 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 362 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 363 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 364 | CLANG_WARN_STRICT_PROTOTYPES = YES; 365 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 366 | CLANG_WARN_UNREACHABLE_CODE = YES; 367 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 368 | CODE_SIGN_IDENTITY = "-"; 369 | COPY_PHASE_STRIP = NO; 370 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 371 | ENABLE_NS_ASSERTIONS = NO; 372 | ENABLE_STRICT_OBJC_MSGSEND = YES; 373 | GCC_C_LANGUAGE_STANDARD = gnu99; 374 | GCC_NO_COMMON_BLOCKS = YES; 375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 377 | GCC_WARN_UNDECLARED_SELECTOR = YES; 378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 379 | GCC_WARN_UNUSED_FUNCTION = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | MACOSX_DEPLOYMENT_TARGET = 10.12; 382 | MTL_ENABLE_DEBUG_INFO = NO; 383 | SDKROOT = macosx; 384 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 385 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 386 | }; 387 | name = Release; 388 | }; 389 | B33BAF5F1F51F8D90001A751 /* Debug */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | CLANG_ENABLE_MODULES = YES; 393 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 394 | PRODUCT_NAME = "$(TARGET_NAME)"; 395 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 396 | SWIFT_VERSION = 4.0; 397 | }; 398 | name = Debug; 399 | }; 400 | B33BAF601F51F8D90001A751 /* Release */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | CLANG_ENABLE_MODULES = YES; 404 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 405 | PRODUCT_NAME = "$(TARGET_NAME)"; 406 | SWIFT_VERSION = 4.0; 407 | }; 408 | name = Release; 409 | }; 410 | /* End XCBuildConfiguration section */ 411 | 412 | /* Begin XCConfigurationList section */ 413 | B33BAF521F51F8D90001A751 /* Build configuration list for PBXProject "FastlaneSwiftRunner" */ = { 414 | isa = XCConfigurationList; 415 | buildConfigurations = ( 416 | B33BAF5C1F51F8D90001A751 /* Debug */, 417 | B33BAF5D1F51F8D90001A751 /* Release */, 418 | ); 419 | defaultConfigurationIsVisible = 0; 420 | defaultConfigurationName = Release; 421 | }; 422 | B33BAF5E1F51F8D90001A751 /* Build configuration list for PBXNativeTarget "FastlaneRunner" */ = { 423 | isa = XCConfigurationList; 424 | buildConfigurations = ( 425 | B33BAF5F1F51F8D90001A751 /* Debug */, 426 | B33BAF601F51F8D90001A751 /* Release */, 427 | ); 428 | defaultConfigurationIsVisible = 0; 429 | defaultConfigurationName = Release; 430 | }; 431 | /* End XCConfigurationList section */ 432 | }; 433 | rootObject = B33BAF4F1F51F8D90001A751 /* Project object */; 434 | } 435 | --------------------------------------------------------------------------------