├── .gitignore ├── .gitmodules ├── AppStoreConnect.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── antoinevanderlee.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── AppStoreConnect.xcscheme └── xcuserdata │ └── antoinevanderlee.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── AppStoreConnect.xcworkspace └── contents.xcworkspacedata ├── AppStoreConnect ├── AppDelegate.swift ├── AppDetail │ ├── AppDetailCoordinator.swift │ ├── AppDetailViewController.swift │ └── TestFlight │ │ ├── BetaTestersViewController │ │ ├── BetaTestersViewController.swift │ │ ├── BetaTestersViewController.xib │ │ └── BetaTestersViewModel.swift │ │ ├── Menu │ │ ├── TestFlightMenuViewController.swift │ │ └── TestFlightMenuViewModel.swift │ │ ├── New User │ │ ├── AddTestFlightUserViewController.swift │ │ └── AddTestFlightUserViewController.xib │ │ ├── TestFlightCoordinator.swift │ │ └── TestFlightSplitViewController.swift ├── AppStoreConnect.entitlements ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon-1024.png │ │ ├── icon-1024@128w.png │ │ ├── icon-1024@16w.png │ │ ├── icon-1024@256w-1.png │ │ ├── icon-1024@256w.png │ │ ├── icon-1024@32w-1.png │ │ ├── icon-1024@32w.png │ │ ├── icon-1024@512w-1.png │ │ ├── icon-1024@512w.png │ │ └── icon-1024@64w.png │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── Info.plist ├── Menu │ ├── AppMenuTableCellView.swift │ ├── MenuCoordinator.swift │ └── MenuViewController.swift ├── SplitViewController │ └── MainSplitViewController.swift └── TransparentWindowController.swift ├── AppStoreConnectTests ├── AppStoreConnectTests.swift └── Info.plist ├── Assets └── appimage.png ├── Design └── AppIcon.sketch ├── LICENSE ├── Podfile ├── Podfile.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | Secrets/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/.gitmodules -------------------------------------------------------------------------------- /AppStoreConnect.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 13E0C2641DA88C924AC63329 /* Pods_AppStoreConnect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A82639D21BF68509540DD3BC /* Pods_AppStoreConnect.framework */; }; 11 | 50AD15D921BEADD10049E1FE /* AddTestFlightUserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AD15D721BEADD10049E1FE /* AddTestFlightUserViewController.swift */; }; 12 | 50AD15DA21BEADD10049E1FE /* AddTestFlightUserViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50AD15D821BEADD10049E1FE /* AddTestFlightUserViewController.xib */; }; 13 | 50EACD5921B17C7700AE60CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD5821B17C7700AE60CB /* AppDelegate.swift */; }; 14 | 50EACD5D21B17C7900AE60CB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50EACD5C21B17C7900AE60CB /* Assets.xcassets */; }; 15 | 50EACD6021B17C7900AE60CB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50EACD5E21B17C7900AE60CB /* Main.storyboard */; }; 16 | 50EACD6C21B17C7900AE60CB /* AppStoreConnectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD6B21B17C7900AE60CB /* AppStoreConnectTests.swift */; }; 17 | 50EACD7821B1938A00AE60CB /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD7721B1938A00AE60CB /* Keys.swift */; }; 18 | 50EACD7B21B1958A00AE60CB /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD7A21B1958A00AE60CB /* MenuViewController.swift */; }; 19 | 50EACD7D21B195D100AE60CB /* AppMenuTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD7C21B195D100AE60CB /* AppMenuTableCellView.swift */; }; 20 | 50EACD7F21B1A0AA00AE60CB /* TransparentWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD7E21B1A0AA00AE60CB /* TransparentWindowController.swift */; }; 21 | 50EACD8121B1AF0200AE60CB /* MenuCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD8021B1AF0200AE60CB /* MenuCoordinator.swift */; }; 22 | 50EACD8421B1B18300AE60CB /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD8321B1B18300AE60CB /* MainSplitViewController.swift */; }; 23 | 50EACD8721B1B3D600AE60CB /* AppDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD8621B1B3D600AE60CB /* AppDetailViewController.swift */; }; 24 | 50EACD9121B1EDDB00AE60CB /* AppDetailCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD9021B1EDDB00AE60CB /* AppDetailCoordinator.swift */; }; 25 | 50EACD9321B1EF3200AE60CB /* TestFlightCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD9221B1EF3200AE60CB /* TestFlightCoordinator.swift */; }; 26 | 50EACD9721B1F02500AE60CB /* TestFlightSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACD9621B1F02500AE60CB /* TestFlightSplitViewController.swift */; }; 27 | 50EACDA721B2FD9A00AE60CB /* BetaTestersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACDA121B2FD9900AE60CB /* BetaTestersViewController.swift */; }; 28 | 50EACDA821B2FD9A00AE60CB /* BetaTestersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACDA221B2FD9900AE60CB /* BetaTestersViewModel.swift */; }; 29 | 50EACDA921B2FD9A00AE60CB /* BetaTestersViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50EACDA321B2FD9900AE60CB /* BetaTestersViewController.xib */; }; 30 | 50EACDAA21B2FD9A00AE60CB /* TestFlightMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACDA521B2FD9900AE60CB /* TestFlightMenuViewController.swift */; }; 31 | 50EACDAB21B2FD9A00AE60CB /* TestFlightMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EACDA621B2FD9900AE60CB /* TestFlightMenuViewModel.swift */; }; 32 | B9186B7FBE5290F3C5E20758 /* Pods_AppStoreConnectTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AEA1A57F3E5DC8B71F446989 /* Pods_AppStoreConnectTests.framework */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | 50EACD6821B17C7900AE60CB /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 50EACD4D21B17C7600AE60CB /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 50EACD5421B17C7700AE60CB; 41 | remoteInfo = AppStoreConnect; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 1CD293AB5831DA40ABF49A0A /* Pods-AppStoreConnect.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppStoreConnect.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AppStoreConnect/Pods-AppStoreConnect.debug.xcconfig"; sourceTree = ""; }; 47 | 1FFCD194595EC034229699CE /* Pods-AppStoreConnect.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppStoreConnect.release.xcconfig"; path = "Pods/Target Support Files/Pods-AppStoreConnect/Pods-AppStoreConnect.release.xcconfig"; sourceTree = ""; }; 48 | 34A89FCDE0E6FC9F5A96A110 /* Pods-AppStoreConnectTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppStoreConnectTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AppStoreConnectTests/Pods-AppStoreConnectTests.debug.xcconfig"; sourceTree = ""; }; 49 | 3C2FAC4A432C140E6F920BC1 /* Pods-AppStoreConnectTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppStoreConnectTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AppStoreConnectTests/Pods-AppStoreConnectTests.release.xcconfig"; sourceTree = ""; }; 50 | 50AD15D721BEADD10049E1FE /* AddTestFlightUserViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTestFlightUserViewController.swift; sourceTree = ""; }; 51 | 50AD15D821BEADD10049E1FE /* AddTestFlightUserViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AddTestFlightUserViewController.xib; sourceTree = ""; }; 52 | 50EACD5521B17C7700AE60CB /* AppStoreConnect.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppStoreConnect.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 50EACD5821B17C7700AE60CB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 54 | 50EACD5C21B17C7900AE60CB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 55 | 50EACD5F21B17C7900AE60CB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 56 | 50EACD6121B17C7900AE60CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | 50EACD6221B17C7900AE60CB /* AppStoreConnect.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppStoreConnect.entitlements; sourceTree = ""; }; 58 | 50EACD6721B17C7900AE60CB /* AppStoreConnectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppStoreConnectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 50EACD6B21B17C7900AE60CB /* AppStoreConnectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStoreConnectTests.swift; sourceTree = ""; }; 60 | 50EACD6D21B17C7900AE60CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | 50EACD7721B1938A00AE60CB /* Keys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = ""; }; 62 | 50EACD7A21B1958A00AE60CB /* MenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; 63 | 50EACD7C21B195D100AE60CB /* AppMenuTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMenuTableCellView.swift; sourceTree = ""; }; 64 | 50EACD7E21B1A0AA00AE60CB /* TransparentWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransparentWindowController.swift; sourceTree = ""; }; 65 | 50EACD8021B1AF0200AE60CB /* MenuCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuCoordinator.swift; sourceTree = ""; }; 66 | 50EACD8321B1B18300AE60CB /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = ""; }; 67 | 50EACD8621B1B3D600AE60CB /* AppDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetailViewController.swift; sourceTree = ""; }; 68 | 50EACD9021B1EDDB00AE60CB /* AppDetailCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetailCoordinator.swift; sourceTree = ""; }; 69 | 50EACD9221B1EF3200AE60CB /* TestFlightCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestFlightCoordinator.swift; sourceTree = ""; }; 70 | 50EACD9621B1F02500AE60CB /* TestFlightSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestFlightSplitViewController.swift; sourceTree = ""; }; 71 | 50EACDA121B2FD9900AE60CB /* BetaTestersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BetaTestersViewController.swift; sourceTree = ""; }; 72 | 50EACDA221B2FD9900AE60CB /* BetaTestersViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BetaTestersViewModel.swift; sourceTree = ""; }; 73 | 50EACDA321B2FD9900AE60CB /* BetaTestersViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BetaTestersViewController.xib; sourceTree = ""; }; 74 | 50EACDA521B2FD9900AE60CB /* TestFlightMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestFlightMenuViewController.swift; sourceTree = ""; }; 75 | 50EACDA621B2FD9900AE60CB /* TestFlightMenuViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestFlightMenuViewModel.swift; sourceTree = ""; }; 76 | A82639D21BF68509540DD3BC /* Pods_AppStoreConnect.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppStoreConnect.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77 | AEA1A57F3E5DC8B71F446989 /* Pods_AppStoreConnectTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppStoreConnectTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | /* End PBXFileReference section */ 79 | 80 | /* Begin PBXFrameworksBuildPhase section */ 81 | 50EACD5221B17C7700AE60CB /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | 13E0C2641DA88C924AC63329 /* Pods_AppStoreConnect.framework in Frameworks */, 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | 50EACD6421B17C7900AE60CB /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | B9186B7FBE5290F3C5E20758 /* Pods_AppStoreConnectTests.framework in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXFrameworksBuildPhase section */ 98 | 99 | /* Begin PBXGroup section */ 100 | 1D0D31A9ACBF11C1E6820068 /* Frameworks */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | A82639D21BF68509540DD3BC /* Pods_AppStoreConnect.framework */, 104 | AEA1A57F3E5DC8B71F446989 /* Pods_AppStoreConnectTests.framework */, 105 | ); 106 | name = Frameworks; 107 | sourceTree = ""; 108 | }; 109 | 50AD15D621BEADB60049E1FE /* New User */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 50AD15D721BEADD10049E1FE /* AddTestFlightUserViewController.swift */, 113 | 50AD15D821BEADD10049E1FE /* AddTestFlightUserViewController.xib */, 114 | ); 115 | path = "New User"; 116 | sourceTree = ""; 117 | }; 118 | 50EACD4C21B17C7600AE60CB = { 119 | isa = PBXGroup; 120 | children = ( 121 | 50EACD5721B17C7700AE60CB /* AppStoreConnect */, 122 | 50EACD6A21B17C7900AE60CB /* AppStoreConnectTests */, 123 | 50EACD5621B17C7700AE60CB /* Products */, 124 | 5F072C8F4BDEE146D39843DB /* Pods */, 125 | 1D0D31A9ACBF11C1E6820068 /* Frameworks */, 126 | ); 127 | sourceTree = ""; 128 | }; 129 | 50EACD5621B17C7700AE60CB /* Products */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 50EACD5521B17C7700AE60CB /* AppStoreConnect.app */, 133 | 50EACD6721B17C7900AE60CB /* AppStoreConnectTests.xctest */, 134 | ); 135 | name = Products; 136 | sourceTree = ""; 137 | }; 138 | 50EACD5721B17C7700AE60CB /* AppStoreConnect */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 50EACD8521B1B3C900AE60CB /* AppDetail */, 142 | 50EACD7921B1958A00AE60CB /* Menu */, 143 | 50EACD7621B1938A00AE60CB /* Secrets */, 144 | 50EACD8221B1B17400AE60CB /* SplitViewController */, 145 | 50EACD5821B17C7700AE60CB /* AppDelegate.swift */, 146 | 50EACD5C21B17C7900AE60CB /* Assets.xcassets */, 147 | 50EACD5E21B17C7900AE60CB /* Main.storyboard */, 148 | 50EACD6121B17C7900AE60CB /* Info.plist */, 149 | 50EACD6221B17C7900AE60CB /* AppStoreConnect.entitlements */, 150 | 50EACD7E21B1A0AA00AE60CB /* TransparentWindowController.swift */, 151 | ); 152 | path = AppStoreConnect; 153 | sourceTree = ""; 154 | }; 155 | 50EACD6A21B17C7900AE60CB /* AppStoreConnectTests */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 50EACD6B21B17C7900AE60CB /* AppStoreConnectTests.swift */, 159 | 50EACD6D21B17C7900AE60CB /* Info.plist */, 160 | ); 161 | path = AppStoreConnectTests; 162 | sourceTree = ""; 163 | }; 164 | 50EACD7621B1938A00AE60CB /* Secrets */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 50EACD7721B1938A00AE60CB /* Keys.swift */, 168 | ); 169 | path = Secrets; 170 | sourceTree = SOURCE_ROOT; 171 | }; 172 | 50EACD7921B1958A00AE60CB /* Menu */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 50EACD7A21B1958A00AE60CB /* MenuViewController.swift */, 176 | 50EACD7C21B195D100AE60CB /* AppMenuTableCellView.swift */, 177 | 50EACD8021B1AF0200AE60CB /* MenuCoordinator.swift */, 178 | ); 179 | path = Menu; 180 | sourceTree = ""; 181 | }; 182 | 50EACD8221B1B17400AE60CB /* SplitViewController */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 50EACD8321B1B18300AE60CB /* MainSplitViewController.swift */, 186 | ); 187 | path = SplitViewController; 188 | sourceTree = ""; 189 | }; 190 | 50EACD8521B1B3C900AE60CB /* AppDetail */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | 50EACD8D21B1EDCE00AE60CB /* TestFlight */, 194 | 50EACD8621B1B3D600AE60CB /* AppDetailViewController.swift */, 195 | 50EACD9021B1EDDB00AE60CB /* AppDetailCoordinator.swift */, 196 | ); 197 | path = AppDetail; 198 | sourceTree = ""; 199 | }; 200 | 50EACD8D21B1EDCE00AE60CB /* TestFlight */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | 50EACDA021B2FD9900AE60CB /* BetaTestersViewController */, 204 | 50EACDA421B2FD9900AE60CB /* Menu */, 205 | 50AD15D621BEADB60049E1FE /* New User */, 206 | 50EACD9221B1EF3200AE60CB /* TestFlightCoordinator.swift */, 207 | 50EACD9621B1F02500AE60CB /* TestFlightSplitViewController.swift */, 208 | ); 209 | path = TestFlight; 210 | sourceTree = ""; 211 | }; 212 | 50EACDA021B2FD9900AE60CB /* BetaTestersViewController */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | 50EACDA121B2FD9900AE60CB /* BetaTestersViewController.swift */, 216 | 50EACDA221B2FD9900AE60CB /* BetaTestersViewModel.swift */, 217 | 50EACDA321B2FD9900AE60CB /* BetaTestersViewController.xib */, 218 | ); 219 | path = BetaTestersViewController; 220 | sourceTree = ""; 221 | }; 222 | 50EACDA421B2FD9900AE60CB /* Menu */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | 50EACDA521B2FD9900AE60CB /* TestFlightMenuViewController.swift */, 226 | 50EACDA621B2FD9900AE60CB /* TestFlightMenuViewModel.swift */, 227 | ); 228 | path = Menu; 229 | sourceTree = ""; 230 | }; 231 | 5F072C8F4BDEE146D39843DB /* Pods */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | 1CD293AB5831DA40ABF49A0A /* Pods-AppStoreConnect.debug.xcconfig */, 235 | 1FFCD194595EC034229699CE /* Pods-AppStoreConnect.release.xcconfig */, 236 | 34A89FCDE0E6FC9F5A96A110 /* Pods-AppStoreConnectTests.debug.xcconfig */, 237 | 3C2FAC4A432C140E6F920BC1 /* Pods-AppStoreConnectTests.release.xcconfig */, 238 | ); 239 | name = Pods; 240 | sourceTree = ""; 241 | }; 242 | /* End PBXGroup section */ 243 | 244 | /* Begin PBXNativeTarget section */ 245 | 50EACD5421B17C7700AE60CB /* AppStoreConnect */ = { 246 | isa = PBXNativeTarget; 247 | buildConfigurationList = 50EACD7021B17C7900AE60CB /* Build configuration list for PBXNativeTarget "AppStoreConnect" */; 248 | buildPhases = ( 249 | 134C04607C2283479509B197 /* [CP] Check Pods Manifest.lock */, 250 | 50EACD5121B17C7700AE60CB /* Sources */, 251 | 50EACD5221B17C7700AE60CB /* Frameworks */, 252 | 50EACD5321B17C7700AE60CB /* Resources */, 253 | 50BD4CF921B307EA00DB754C /* Xcode 10 Build System Caching Fix */, 254 | E5240230696EB1456ED1E828 /* [CP] Embed Pods Frameworks */, 255 | ); 256 | buildRules = ( 257 | ); 258 | dependencies = ( 259 | ); 260 | name = AppStoreConnect; 261 | productName = AppStoreConnect; 262 | productReference = 50EACD5521B17C7700AE60CB /* AppStoreConnect.app */; 263 | productType = "com.apple.product-type.application"; 264 | }; 265 | 50EACD6621B17C7900AE60CB /* AppStoreConnectTests */ = { 266 | isa = PBXNativeTarget; 267 | buildConfigurationList = 50EACD7321B17C7900AE60CB /* Build configuration list for PBXNativeTarget "AppStoreConnectTests" */; 268 | buildPhases = ( 269 | C431224D6C26743B5B9DDD75 /* [CP] Check Pods Manifest.lock */, 270 | 50EACD6321B17C7900AE60CB /* Sources */, 271 | 50EACD6421B17C7900AE60CB /* Frameworks */, 272 | 50EACD6521B17C7900AE60CB /* Resources */, 273 | ); 274 | buildRules = ( 275 | ); 276 | dependencies = ( 277 | 50EACD6921B17C7900AE60CB /* PBXTargetDependency */, 278 | ); 279 | name = AppStoreConnectTests; 280 | productName = AppStoreConnectTests; 281 | productReference = 50EACD6721B17C7900AE60CB /* AppStoreConnectTests.xctest */; 282 | productType = "com.apple.product-type.bundle.unit-test"; 283 | }; 284 | /* End PBXNativeTarget section */ 285 | 286 | /* Begin PBXProject section */ 287 | 50EACD4D21B17C7600AE60CB /* Project object */ = { 288 | isa = PBXProject; 289 | attributes = { 290 | LastSwiftUpdateCheck = 1010; 291 | LastUpgradeCheck = 1010; 292 | ORGANIZATIONNAME = SwiftLee; 293 | TargetAttributes = { 294 | 50EACD5421B17C7700AE60CB = { 295 | CreatedOnToolsVersion = 10.1; 296 | }; 297 | 50EACD6621B17C7900AE60CB = { 298 | CreatedOnToolsVersion = 10.1; 299 | TestTargetID = 50EACD5421B17C7700AE60CB; 300 | }; 301 | }; 302 | }; 303 | buildConfigurationList = 50EACD5021B17C7600AE60CB /* Build configuration list for PBXProject "AppStoreConnect" */; 304 | compatibilityVersion = "Xcode 9.3"; 305 | developmentRegion = en; 306 | hasScannedForEncodings = 0; 307 | knownRegions = ( 308 | en, 309 | Base, 310 | ); 311 | mainGroup = 50EACD4C21B17C7600AE60CB; 312 | productRefGroup = 50EACD5621B17C7700AE60CB /* Products */; 313 | projectDirPath = ""; 314 | projectRoot = ""; 315 | targets = ( 316 | 50EACD5421B17C7700AE60CB /* AppStoreConnect */, 317 | 50EACD6621B17C7900AE60CB /* AppStoreConnectTests */, 318 | ); 319 | }; 320 | /* End PBXProject section */ 321 | 322 | /* Begin PBXResourcesBuildPhase section */ 323 | 50EACD5321B17C7700AE60CB /* Resources */ = { 324 | isa = PBXResourcesBuildPhase; 325 | buildActionMask = 2147483647; 326 | files = ( 327 | 50EACD5D21B17C7900AE60CB /* Assets.xcassets in Resources */, 328 | 50AD15DA21BEADD10049E1FE /* AddTestFlightUserViewController.xib in Resources */, 329 | 50EACD6021B17C7900AE60CB /* Main.storyboard in Resources */, 330 | 50EACDA921B2FD9A00AE60CB /* BetaTestersViewController.xib in Resources */, 331 | ); 332 | runOnlyForDeploymentPostprocessing = 0; 333 | }; 334 | 50EACD6521B17C7900AE60CB /* Resources */ = { 335 | isa = PBXResourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | /* End PBXResourcesBuildPhase section */ 342 | 343 | /* Begin PBXShellScriptBuildPhase section */ 344 | 134C04607C2283479509B197 /* [CP] Check Pods Manifest.lock */ = { 345 | isa = PBXShellScriptBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | ); 349 | inputFileListPaths = ( 350 | ); 351 | inputPaths = ( 352 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 353 | "${PODS_ROOT}/Manifest.lock", 354 | ); 355 | name = "[CP] Check Pods Manifest.lock"; 356 | outputFileListPaths = ( 357 | ); 358 | outputPaths = ( 359 | "$(DERIVED_FILE_DIR)/Pods-AppStoreConnect-checkManifestLockResult.txt", 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | shellPath = /bin/sh; 363 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 364 | showEnvVarsInLog = 0; 365 | }; 366 | 50BD4CF921B307EA00DB754C /* Xcode 10 Build System Caching Fix */ = { 367 | isa = PBXShellScriptBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | ); 371 | inputFileListPaths = ( 372 | ); 373 | inputPaths = ( 374 | ); 375 | name = "Xcode 10 Build System Caching Fix"; 376 | outputFileListPaths = ( 377 | ); 378 | outputPaths = ( 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | shellPath = /bin/sh; 382 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nfind \"${SRCROOT}/Pods\" -type f -name *frameworks.sh -exec bash -c \"touch \\\"{}\\\"\" \\;\n"; 383 | }; 384 | C431224D6C26743B5B9DDD75 /* [CP] Check Pods Manifest.lock */ = { 385 | isa = PBXShellScriptBuildPhase; 386 | buildActionMask = 2147483647; 387 | files = ( 388 | ); 389 | inputFileListPaths = ( 390 | ); 391 | inputPaths = ( 392 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 393 | "${PODS_ROOT}/Manifest.lock", 394 | ); 395 | name = "[CP] Check Pods Manifest.lock"; 396 | outputFileListPaths = ( 397 | ); 398 | outputPaths = ( 399 | "$(DERIVED_FILE_DIR)/Pods-AppStoreConnectTests-checkManifestLockResult.txt", 400 | ); 401 | runOnlyForDeploymentPostprocessing = 0; 402 | shellPath = /bin/sh; 403 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 404 | showEnvVarsInLog = 0; 405 | }; 406 | E5240230696EB1456ED1E828 /* [CP] Embed Pods Frameworks */ = { 407 | isa = PBXShellScriptBuildPhase; 408 | buildActionMask = 2147483647; 409 | files = ( 410 | ); 411 | inputFileListPaths = ( 412 | ); 413 | inputPaths = ( 414 | "${PODS_ROOT}/Target Support Files/Pods-AppStoreConnect/Pods-AppStoreConnect-frameworks.sh", 415 | "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", 416 | "${BUILT_PRODUCTS_DIR}/AppStoreConnect-Swift-SDK/AppStoreConnect_Swift_SDK.framework", 417 | ); 418 | name = "[CP] Embed Pods Frameworks"; 419 | outputFileListPaths = ( 420 | ); 421 | outputPaths = ( 422 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", 423 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppStoreConnect_Swift_SDK.framework", 424 | ); 425 | runOnlyForDeploymentPostprocessing = 0; 426 | shellPath = /bin/sh; 427 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppStoreConnect/Pods-AppStoreConnect-frameworks.sh\"\n"; 428 | showEnvVarsInLog = 0; 429 | }; 430 | /* End PBXShellScriptBuildPhase section */ 431 | 432 | /* Begin PBXSourcesBuildPhase section */ 433 | 50EACD5121B17C7700AE60CB /* Sources */ = { 434 | isa = PBXSourcesBuildPhase; 435 | buildActionMask = 2147483647; 436 | files = ( 437 | 50EACD7821B1938A00AE60CB /* Keys.swift in Sources */, 438 | 50EACD9121B1EDDB00AE60CB /* AppDetailCoordinator.swift in Sources */, 439 | 50EACD9721B1F02500AE60CB /* TestFlightSplitViewController.swift in Sources */, 440 | 50EACDA821B2FD9A00AE60CB /* BetaTestersViewModel.swift in Sources */, 441 | 50EACD9321B1EF3200AE60CB /* TestFlightCoordinator.swift in Sources */, 442 | 50AD15D921BEADD10049E1FE /* AddTestFlightUserViewController.swift in Sources */, 443 | 50EACDAA21B2FD9A00AE60CB /* TestFlightMenuViewController.swift in Sources */, 444 | 50EACD8121B1AF0200AE60CB /* MenuCoordinator.swift in Sources */, 445 | 50EACD5921B17C7700AE60CB /* AppDelegate.swift in Sources */, 446 | 50EACD8721B1B3D600AE60CB /* AppDetailViewController.swift in Sources */, 447 | 50EACD8421B1B18300AE60CB /* MainSplitViewController.swift in Sources */, 448 | 50EACD7D21B195D100AE60CB /* AppMenuTableCellView.swift in Sources */, 449 | 50EACD7F21B1A0AA00AE60CB /* TransparentWindowController.swift in Sources */, 450 | 50EACDAB21B2FD9A00AE60CB /* TestFlightMenuViewModel.swift in Sources */, 451 | 50EACD7B21B1958A00AE60CB /* MenuViewController.swift in Sources */, 452 | 50EACDA721B2FD9A00AE60CB /* BetaTestersViewController.swift in Sources */, 453 | ); 454 | runOnlyForDeploymentPostprocessing = 0; 455 | }; 456 | 50EACD6321B17C7900AE60CB /* Sources */ = { 457 | isa = PBXSourcesBuildPhase; 458 | buildActionMask = 2147483647; 459 | files = ( 460 | 50EACD6C21B17C7900AE60CB /* AppStoreConnectTests.swift in Sources */, 461 | ); 462 | runOnlyForDeploymentPostprocessing = 0; 463 | }; 464 | /* End PBXSourcesBuildPhase section */ 465 | 466 | /* Begin PBXTargetDependency section */ 467 | 50EACD6921B17C7900AE60CB /* PBXTargetDependency */ = { 468 | isa = PBXTargetDependency; 469 | target = 50EACD5421B17C7700AE60CB /* AppStoreConnect */; 470 | targetProxy = 50EACD6821B17C7900AE60CB /* PBXContainerItemProxy */; 471 | }; 472 | /* End PBXTargetDependency section */ 473 | 474 | /* Begin PBXVariantGroup section */ 475 | 50EACD5E21B17C7900AE60CB /* Main.storyboard */ = { 476 | isa = PBXVariantGroup; 477 | children = ( 478 | 50EACD5F21B17C7900AE60CB /* Base */, 479 | ); 480 | name = Main.storyboard; 481 | sourceTree = ""; 482 | }; 483 | /* End PBXVariantGroup section */ 484 | 485 | /* Begin XCBuildConfiguration section */ 486 | 50EACD6E21B17C7900AE60CB /* Debug */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | ALWAYS_SEARCH_USER_PATHS = NO; 490 | CLANG_ANALYZER_NONNULL = YES; 491 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 492 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 493 | CLANG_CXX_LIBRARY = "libc++"; 494 | CLANG_ENABLE_MODULES = YES; 495 | CLANG_ENABLE_OBJC_ARC = YES; 496 | CLANG_ENABLE_OBJC_WEAK = YES; 497 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 498 | CLANG_WARN_BOOL_CONVERSION = YES; 499 | CLANG_WARN_COMMA = YES; 500 | CLANG_WARN_CONSTANT_CONVERSION = YES; 501 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 502 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 503 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 504 | CLANG_WARN_EMPTY_BODY = YES; 505 | CLANG_WARN_ENUM_CONVERSION = YES; 506 | CLANG_WARN_INFINITE_RECURSION = YES; 507 | CLANG_WARN_INT_CONVERSION = YES; 508 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 509 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 510 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 511 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 512 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 513 | CLANG_WARN_STRICT_PROTOTYPES = YES; 514 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 515 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 516 | CLANG_WARN_UNREACHABLE_CODE = YES; 517 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 518 | CODE_SIGN_IDENTITY = "Mac Developer"; 519 | COPY_PHASE_STRIP = NO; 520 | DEBUG_INFORMATION_FORMAT = dwarf; 521 | ENABLE_STRICT_OBJC_MSGSEND = YES; 522 | ENABLE_TESTABILITY = YES; 523 | GCC_C_LANGUAGE_STANDARD = gnu11; 524 | GCC_DYNAMIC_NO_PIC = NO; 525 | GCC_NO_COMMON_BLOCKS = YES; 526 | GCC_OPTIMIZATION_LEVEL = 0; 527 | GCC_PREPROCESSOR_DEFINITIONS = ( 528 | "DEBUG=1", 529 | "$(inherited)", 530 | ); 531 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 532 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 533 | GCC_WARN_UNDECLARED_SELECTOR = YES; 534 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 535 | GCC_WARN_UNUSED_FUNCTION = YES; 536 | GCC_WARN_UNUSED_VARIABLE = YES; 537 | MACOSX_DEPLOYMENT_TARGET = 10.14; 538 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 539 | MTL_FAST_MATH = YES; 540 | ONLY_ACTIVE_ARCH = YES; 541 | SDKROOT = macosx; 542 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 543 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 544 | }; 545 | name = Debug; 546 | }; 547 | 50EACD6F21B17C7900AE60CB /* Release */ = { 548 | isa = XCBuildConfiguration; 549 | buildSettings = { 550 | ALWAYS_SEARCH_USER_PATHS = NO; 551 | CLANG_ANALYZER_NONNULL = YES; 552 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 553 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 554 | CLANG_CXX_LIBRARY = "libc++"; 555 | CLANG_ENABLE_MODULES = YES; 556 | CLANG_ENABLE_OBJC_ARC = YES; 557 | CLANG_ENABLE_OBJC_WEAK = YES; 558 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 559 | CLANG_WARN_BOOL_CONVERSION = YES; 560 | CLANG_WARN_COMMA = YES; 561 | CLANG_WARN_CONSTANT_CONVERSION = YES; 562 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 563 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 564 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 565 | CLANG_WARN_EMPTY_BODY = YES; 566 | CLANG_WARN_ENUM_CONVERSION = YES; 567 | CLANG_WARN_INFINITE_RECURSION = YES; 568 | CLANG_WARN_INT_CONVERSION = YES; 569 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 570 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 571 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 572 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 573 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 574 | CLANG_WARN_STRICT_PROTOTYPES = YES; 575 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 576 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 577 | CLANG_WARN_UNREACHABLE_CODE = YES; 578 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 579 | CODE_SIGN_IDENTITY = "Mac Developer"; 580 | COPY_PHASE_STRIP = NO; 581 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 582 | ENABLE_NS_ASSERTIONS = NO; 583 | ENABLE_STRICT_OBJC_MSGSEND = YES; 584 | GCC_C_LANGUAGE_STANDARD = gnu11; 585 | GCC_NO_COMMON_BLOCKS = YES; 586 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 587 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 588 | GCC_WARN_UNDECLARED_SELECTOR = YES; 589 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 590 | GCC_WARN_UNUSED_FUNCTION = YES; 591 | GCC_WARN_UNUSED_VARIABLE = YES; 592 | MACOSX_DEPLOYMENT_TARGET = 10.14; 593 | MTL_ENABLE_DEBUG_INFO = NO; 594 | MTL_FAST_MATH = YES; 595 | SDKROOT = macosx; 596 | SWIFT_COMPILATION_MODE = wholemodule; 597 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 598 | }; 599 | name = Release; 600 | }; 601 | 50EACD7121B17C7900AE60CB /* Debug */ = { 602 | isa = XCBuildConfiguration; 603 | baseConfigurationReference = 1CD293AB5831DA40ABF49A0A /* Pods-AppStoreConnect.debug.xcconfig */; 604 | buildSettings = { 605 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 606 | CODE_SIGN_ENTITLEMENTS = AppStoreConnect/AppStoreConnect.entitlements; 607 | CODE_SIGN_STYLE = Automatic; 608 | COMBINE_HIDPI_IMAGES = YES; 609 | DEVELOPMENT_TEAM = 4QMDKC8VLJ; 610 | INFOPLIST_FILE = AppStoreConnect/Info.plist; 611 | LD_RUNPATH_SEARCH_PATHS = ( 612 | "$(inherited)", 613 | "@executable_path/../Frameworks", 614 | ); 615 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftlee.AppStoreConnect; 616 | PRODUCT_NAME = "$(TARGET_NAME)"; 617 | SWIFT_VERSION = 4.2; 618 | }; 619 | name = Debug; 620 | }; 621 | 50EACD7221B17C7900AE60CB /* Release */ = { 622 | isa = XCBuildConfiguration; 623 | baseConfigurationReference = 1FFCD194595EC034229699CE /* Pods-AppStoreConnect.release.xcconfig */; 624 | buildSettings = { 625 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 626 | CODE_SIGN_ENTITLEMENTS = AppStoreConnect/AppStoreConnect.entitlements; 627 | CODE_SIGN_STYLE = Automatic; 628 | COMBINE_HIDPI_IMAGES = YES; 629 | DEVELOPMENT_TEAM = 4QMDKC8VLJ; 630 | INFOPLIST_FILE = AppStoreConnect/Info.plist; 631 | LD_RUNPATH_SEARCH_PATHS = ( 632 | "$(inherited)", 633 | "@executable_path/../Frameworks", 634 | ); 635 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftlee.AppStoreConnect; 636 | PRODUCT_NAME = "$(TARGET_NAME)"; 637 | SWIFT_VERSION = 4.2; 638 | }; 639 | name = Release; 640 | }; 641 | 50EACD7421B17C7900AE60CB /* Debug */ = { 642 | isa = XCBuildConfiguration; 643 | baseConfigurationReference = 34A89FCDE0E6FC9F5A96A110 /* Pods-AppStoreConnectTests.debug.xcconfig */; 644 | buildSettings = { 645 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 646 | BUNDLE_LOADER = "$(TEST_HOST)"; 647 | CODE_SIGN_STYLE = Automatic; 648 | COMBINE_HIDPI_IMAGES = YES; 649 | DEVELOPMENT_TEAM = 4QMDKC8VLJ; 650 | INFOPLIST_FILE = AppStoreConnectTests/Info.plist; 651 | LD_RUNPATH_SEARCH_PATHS = ( 652 | "$(inherited)", 653 | "@executable_path/../Frameworks", 654 | "@loader_path/../Frameworks", 655 | ); 656 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftlee.AppStoreConnectTests; 657 | PRODUCT_NAME = "$(TARGET_NAME)"; 658 | SWIFT_VERSION = 4.2; 659 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AppStoreConnect.app/Contents/MacOS/AppStoreConnect"; 660 | }; 661 | name = Debug; 662 | }; 663 | 50EACD7521B17C7900AE60CB /* Release */ = { 664 | isa = XCBuildConfiguration; 665 | baseConfigurationReference = 3C2FAC4A432C140E6F920BC1 /* Pods-AppStoreConnectTests.release.xcconfig */; 666 | buildSettings = { 667 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 668 | BUNDLE_LOADER = "$(TEST_HOST)"; 669 | CODE_SIGN_STYLE = Automatic; 670 | COMBINE_HIDPI_IMAGES = YES; 671 | DEVELOPMENT_TEAM = 4QMDKC8VLJ; 672 | INFOPLIST_FILE = AppStoreConnectTests/Info.plist; 673 | LD_RUNPATH_SEARCH_PATHS = ( 674 | "$(inherited)", 675 | "@executable_path/../Frameworks", 676 | "@loader_path/../Frameworks", 677 | ); 678 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftlee.AppStoreConnectTests; 679 | PRODUCT_NAME = "$(TARGET_NAME)"; 680 | SWIFT_VERSION = 4.2; 681 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AppStoreConnect.app/Contents/MacOS/AppStoreConnect"; 682 | }; 683 | name = Release; 684 | }; 685 | /* End XCBuildConfiguration section */ 686 | 687 | /* Begin XCConfigurationList section */ 688 | 50EACD5021B17C7600AE60CB /* Build configuration list for PBXProject "AppStoreConnect" */ = { 689 | isa = XCConfigurationList; 690 | buildConfigurations = ( 691 | 50EACD6E21B17C7900AE60CB /* Debug */, 692 | 50EACD6F21B17C7900AE60CB /* Release */, 693 | ); 694 | defaultConfigurationIsVisible = 0; 695 | defaultConfigurationName = Release; 696 | }; 697 | 50EACD7021B17C7900AE60CB /* Build configuration list for PBXNativeTarget "AppStoreConnect" */ = { 698 | isa = XCConfigurationList; 699 | buildConfigurations = ( 700 | 50EACD7121B17C7900AE60CB /* Debug */, 701 | 50EACD7221B17C7900AE60CB /* Release */, 702 | ); 703 | defaultConfigurationIsVisible = 0; 704 | defaultConfigurationName = Release; 705 | }; 706 | 50EACD7321B17C7900AE60CB /* Build configuration list for PBXNativeTarget "AppStoreConnectTests" */ = { 707 | isa = XCConfigurationList; 708 | buildConfigurations = ( 709 | 50EACD7421B17C7900AE60CB /* Debug */, 710 | 50EACD7521B17C7900AE60CB /* Release */, 711 | ); 712 | defaultConfigurationIsVisible = 0; 713 | defaultConfigurationName = Release; 714 | }; 715 | /* End XCConfigurationList section */ 716 | }; 717 | rootObject = 50EACD4D21B17C7600AE60CB /* Project object */; 718 | } 719 | -------------------------------------------------------------------------------- /AppStoreConnect.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AppStoreConnect.xcodeproj/project.xcworkspace/xcuserdata/antoinevanderlee.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect.xcodeproj/project.xcworkspace/xcuserdata/antoinevanderlee.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AppStoreConnect.xcodeproj/xcshareddata/xcschemes/AppStoreConnect.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /AppStoreConnect.xcodeproj/xcuserdata/antoinevanderlee.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AppStoreConnect.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 4 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 50EACD5421B17C7700AE60CB 16 | 17 | primary 18 | 19 | 20 | 50EACD6621B17C7900AE60CB 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /AppStoreConnect.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | 19 | } 20 | 21 | func applicationWillTerminate(_ aNotification: Notification) { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/AppDetailCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDetailCoordinator.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppStoreConnect_Swift_SDK 11 | import AppKit 12 | 13 | final class AppDetailCoordinator { 14 | 15 | private let app: App 16 | 17 | init(app: App) { 18 | self.app = app 19 | } 20 | 21 | func start(tabBarController: NSTabViewController) { 22 | let testFlightSplitViewController: TestFlightSplitViewController = tabBarController.children[0] as! TestFlightSplitViewController 23 | let coordinator = TestFlightCoordinator(app: app) 24 | coordinator.start(splitViewController: testFlightSplitViewController) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/AppDetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDetailViewController.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import AppStoreConnect_Swift_SDK 11 | 12 | struct AppDetailViewModel { 13 | let app: App 14 | 15 | var title: String? { 16 | return self.app.attributes?.name 17 | } 18 | } 19 | 20 | final class AppDetailViewController: NSViewController { 21 | 22 | @IBOutlet private weak var appTitleLabel: NSTextField! 23 | 24 | var viewModel: AppDetailViewModel? { 25 | didSet { 26 | appTitleLabel.stringValue = viewModel?.title ?? "" 27 | } 28 | } 29 | 30 | override func viewDidLoad() { 31 | super.viewDidLoad() 32 | // Do view setup here. 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/BetaTestersViewController/BetaTestersViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BetaTestersViewController.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 01/12/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class BetaTestersViewController: NSViewController { 12 | 13 | @IBOutlet weak var titleLabel: NSTextField! 14 | @IBOutlet weak var tableView: NSTableView! 15 | @IBOutlet weak var searchTextField: NSSearchField! 16 | @IBOutlet weak var addNewUserButton: NSButton! 17 | 18 | let viewModel: BetaTestersViewModel 19 | 20 | required init(viewModel: BetaTestersViewModel) { 21 | self.viewModel = viewModel 22 | 23 | super.init(nibName: "BetaTestersViewController", bundle: nil) 24 | } 25 | 26 | required init?(coder: NSCoder) { 27 | fatalError("init(coder:) has not been implemented") 28 | } 29 | 30 | override func viewDidLoad() { 31 | super.viewDidLoad() 32 | updateViews() 33 | } 34 | 35 | private func updateViews() { 36 | titleLabel.stringValue = viewModel.title 37 | addNewUserButton.isHidden = !viewModel.canAddNewUser 38 | tableView.reloadData() 39 | } 40 | 41 | @IBAction func addNewUser(_ sender: NSButton) { 42 | guard let betaGroup = viewModel.betaGroup else { return } 43 | let addUserViewController = AddTestFlightUserViewController(betaGroup: betaGroup) 44 | present(addUserViewController, asPopoverRelativeTo: sender.bounds, of: sender, preferredEdge: NSRectEdge.maxY, behavior: NSPopover.Behavior.semitransient) 45 | } 46 | } 47 | 48 | extension BetaTestersViewController: NSTableViewDataSource, NSTableViewDelegate { 49 | func numberOfRows(in tableView: NSTableView) -> Int { 50 | return viewModel.totalNumberOfTesters 51 | } 52 | 53 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 54 | guard let betaTester = viewModel.betaTester(for: row), let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "BetaTesterCell"), owner: nil) as? NSTableCellView else { return nil } 55 | 56 | if tableColumn == tableView.tableColumns[0] { 57 | cell.textField?.stringValue = betaTester.attributes?.email ?? "" 58 | } else if tableColumn == tableView.tableColumns[1] { 59 | let firstName = betaTester.attributes?.firstName ?? "" 60 | let lastName = betaTester.attributes?.lastName ?? "" 61 | cell.textField?.stringValue = [firstName, lastName].joined(separator: " ") 62 | } 63 | 64 | return cell 65 | } 66 | } 67 | 68 | extension BetaTestersViewController: NSTextFieldDelegate { 69 | func controlTextDidChange(_ obj: Notification) { 70 | viewModel.applyFilter(with: searchTextField.stringValue) { 71 | tableView.reloadData() 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/BetaTestersViewController/BetaTestersViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 136 | 140 | 141 | 142 | 143 | 144 | 145 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/BetaTestersViewController/BetaTestersViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BetaTestersViewModel.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 01/12/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppStoreConnect_Swift_SDK 11 | 12 | final class BetaTestersViewModel { 13 | 14 | let betaGroup: BetaGroup? 15 | private let allBetaTesters: [BetaTester] 16 | private var filteredBetaTesters: [BetaTester] 17 | 18 | var title: String { 19 | return "All testers (\(allBetaTesters.count))" 20 | } 21 | 22 | var totalNumberOfTesters: Int { 23 | return filteredBetaTesters.count 24 | } 25 | 26 | var canAddNewUser: Bool { 27 | return betaGroup != nil 28 | } 29 | 30 | init(betaGroup: BetaGroup?, betaTesters: [BetaTester]) { 31 | self.betaGroup = betaGroup 32 | self.allBetaTesters = betaTesters 33 | self.filteredBetaTesters = betaTesters 34 | } 35 | 36 | func betaTester(for row: Int) -> BetaTester? { 37 | guard row < filteredBetaTesters.count else { return nil } 38 | return filteredBetaTesters[row] 39 | } 40 | 41 | func applyFilter(with searchInput: String, then completion: () -> Void) { 42 | guard !searchInput.isEmpty else { 43 | filteredBetaTesters = allBetaTesters 44 | completion() 45 | return 46 | } 47 | 48 | filteredBetaTesters = allBetaTesters.filter({ (tester) -> Bool in 49 | return [tester.attributes?.email, tester.attributes?.firstName, tester.attributes?.lastName].contains(where: { $0?.contains(searchInput) == true }) == true 50 | }) 51 | completion() 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/Menu/TestFlightMenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestFlightMenuViewController.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import AppStoreConnect_Swift_SDK 11 | 12 | final class TestFlightMenuViewController: NSViewController { 13 | 14 | @IBOutlet private weak var tableView: NSTableView! 15 | 16 | var viewModel: TestFlightMenuViewModel? { 17 | didSet { 18 | viewModel?.update(using: self) 19 | } 20 | } 21 | 22 | var didSelectBetaGroup: ((_ betaGroup: BetaGroup?, _ betaTesters: [BetaTester]) -> Void)? 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | view.widthAnchor.constraint(equalToConstant: 220).isActive = true 28 | } 29 | 30 | } 31 | 32 | extension TestFlightMenuViewController: NSTableViewDataSource { 33 | func numberOfRows(in tableView: NSTableView) -> Int { 34 | return viewModel?.numberOfItems() ?? 0 35 | } 36 | } 37 | 38 | extension TestFlightMenuViewController: NSTableViewDelegate { 39 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 40 | guard let viewModel = viewModel else { return nil } 41 | var result: AppMenuTableCellView 42 | result = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) as! AppMenuTableCellView 43 | result.appNameLabel.stringValue = viewModel.titleOfItem(for: row) 44 | return result 45 | } 46 | 47 | func tableViewSelectionDidChange(_ notification: Notification) { 48 | guard let viewModel = viewModel else { return } 49 | if tableView.selectedRow == 0 { 50 | didSelectBetaGroup?(nil, viewModel.betaTesters) 51 | } else { 52 | let betaGroup = viewModel.betaGroups[tableView.selectedRow - 1] 53 | let betaTesters = viewModel.betaTesters.filter { betaTester in 54 | return betaGroup.relationships?.betaTesters?.data?.contains(where: { $0.id == betaTester.id }) == true 55 | } 56 | 57 | didSelectBetaGroup?(betaGroup, betaTesters) 58 | } 59 | } 60 | } 61 | 62 | extension TestFlightMenuViewController: TestFlightMenuViewModelDelegate { 63 | func didLoadAllGroups() { 64 | tableView.reloadData() 65 | 66 | if tableView.selectedRowIndexes.isEmpty { 67 | tableView.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) 68 | } 69 | } 70 | 71 | func didLoadAllUsers() { 72 | tableView.reloadData() 73 | 74 | if tableView.selectedRowIndexes.isEmpty { 75 | tableView.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/Menu/TestFlightMenuViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestFlightMenuViewModel.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppStoreConnect_Swift_SDK 11 | 12 | protocol TestFlightMenuViewModelDelegate: class { 13 | func didLoadAllUsers() 14 | func didLoadAllGroups() 15 | } 16 | 17 | final class TestFlightMenuViewModel { 18 | 19 | private let app: App 20 | private(set) var betaTesters: [BetaTester] = [] 21 | private(set) var betaGroups: [BetaGroup] = [] 22 | 23 | weak var delegate: TestFlightMenuViewModelDelegate? 24 | 25 | var totalNumberOfTesters: Int = 0 26 | 27 | init(app: App) { 28 | self.app = app 29 | } 30 | 31 | func update(using delegate: TestFlightMenuViewModelDelegate) { 32 | self.delegate = delegate 33 | fetchTesters(with: nil) 34 | } 35 | 36 | func numberOfItems() -> Int { 37 | guard !betaTesters.isEmpty && !betaGroups.isEmpty else { return 0 } 38 | return 1 + betaGroups.count 39 | } 40 | 41 | func titleOfItem(for row: Int) -> String { 42 | if row == 0 { 43 | return "All Testers (\(totalNumberOfTesters))" 44 | } else { 45 | return betaGroups[row - 1].attributes?.name ?? "-" 46 | } 47 | } 48 | } 49 | 50 | extension TestFlightMenuViewModel { 51 | private func fetchTesters(with next: PagedDocumentLinks?) { 52 | let filters = [ListBetaTesters.Filter.apps([app.id])] 53 | let limit = [ListBetaTesters.Limit.betaTesters(200)] 54 | let sort = [ListBetaTesters.Sort.emailAscending] 55 | 56 | APIProvider.shared.request(.betaTesters(filter: filters, limit: limit, sort: sort, next: next)) { [weak self] (result) in 57 | guard let betaTestersInfo = result.value else { return } 58 | self?.totalNumberOfTesters = betaTestersInfo.meta?.paging.total ?? 0 59 | self?.betaTesters.append(contentsOf: betaTestersInfo.data) 60 | 61 | if self?.betaTesters.count != self?.totalNumberOfTesters { 62 | self?.fetchTesters(with: betaTestersInfo.links) 63 | } else { 64 | DispatchQueue.main.async { 65 | self?.delegate?.didLoadAllUsers() 66 | } 67 | } 68 | } 69 | 70 | let groupRelationships = [ListBetaGroups.Include.betaTesters] 71 | let groupFilter = [ListBetaGroups.Filter.app([self.app.id])] 72 | APIProvider.shared.request(APIEndpoint.betaGroups(filter: groupFilter, include: groupRelationships)) { [weak self] (result) in 73 | guard let groups = result.value else { return } 74 | self?.betaGroups = groups.data 75 | 76 | DispatchQueue.main.async { 77 | self?.delegate?.didLoadAllGroups() 78 | } 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/New User/AddTestFlightUserViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddTestFlightUserViewController.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 10/12/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import AppStoreConnect_Swift_SDK 11 | 12 | final class AddTestFlightUserViewController: NSViewController { 13 | 14 | @IBOutlet weak var emailTextField: NSTextField! 15 | @IBOutlet weak var firstNameTextField: NSTextField! 16 | @IBOutlet weak var lastNameTextField: NSTextField! 17 | @IBOutlet weak var addButton: NSButton! 18 | 19 | private let betaGroup: BetaGroup 20 | 21 | required init(betaGroup: BetaGroup) { 22 | self.betaGroup = betaGroup 23 | 24 | super.init(nibName: "AddTestFlightUserViewController", bundle: nil) 25 | } 26 | 27 | required init?(coder: NSCoder) { 28 | fatalError("init(coder:) has not been implemented") 29 | } 30 | 31 | @IBAction func addButtonTapped(_ sender: NSButton) { 32 | dismiss(self) 33 | APIProvider.shared.request(APIEndpoint.create(betaTesterWithEmail: emailTextField.stringValue, firstName: firstNameTextField.stringValue, lastName: lastNameTextField.stringValue, betaGroupIds: [betaGroup.id])) { (result) in 34 | switch result { 35 | case .success(let result): 36 | print(result) 37 | case .failure(let error): 38 | print("Adding failed with \(error)") 39 | } 40 | } 41 | } 42 | } 43 | 44 | extension AddTestFlightUserViewController: NSTextFieldDelegate { 45 | 46 | func controlTextDidChange(_ obj: Notification) { 47 | addButton.isEnabled = emailTextField.stringValue.isValidEmail() 48 | } 49 | } 50 | 51 | extension String { 52 | func isValidEmail() -> Bool { 53 | let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" 54 | let emailValidation = NSPredicate(format:"SELF MATCHES %@", emailRegEx) 55 | return emailValidation.evaluate(with: self) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/New User/AddTestFlightUserViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/TestFlightCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestFlightCoordinator.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | import AppStoreConnect_Swift_SDK 12 | 13 | final class TestFlightCoordinator { 14 | 15 | private let app: App 16 | 17 | init(app: App) { 18 | self.app = app 19 | } 20 | 21 | func start(splitViewController: NSSplitViewController) { 22 | let testFlightMenuViewController = splitViewController.children.first as! TestFlightMenuViewController 23 | let viewModel = TestFlightMenuViewModel(app: app) 24 | testFlightMenuViewController.viewModel = viewModel 25 | 26 | testFlightMenuViewController.didSelectBetaGroup = { betaGroup, betaTesters in 27 | splitViewController.children.remove(at: 1) 28 | let viewModel = BetaTestersViewModel(betaGroup: betaGroup, betaTesters: betaTesters) 29 | let betaTestersViewController = BetaTestersViewController(viewModel: viewModel) 30 | splitViewController.addChild(betaTestersViewController) 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /AppStoreConnect/AppDetail/TestFlight/TestFlightSplitViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestFlightSplitViewController.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class TestFlightSplitViewController: NSSplitViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | } 16 | 17 | override func splitView(_ splitView: NSSplitView, effectiveRect proposedEffectiveRect: NSRect, forDrawnRect drawnRect: NSRect, ofDividerAt dividerIndex: Int) -> NSRect { 18 | return NSZeroRect 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /AppStoreConnect/AppStoreConnect.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon-1024@16w.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon-1024@32w-1.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon-1024@32w.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon-1024@64w.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon-1024@128w.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon-1024@256w.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon-1024@256w-1.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon-1024@512w.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon-1024@512w-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon-1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@128w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@128w.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@16w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@16w.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@256w-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@256w-1.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@256w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@256w.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@32w-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@32w-1.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@32w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@32w.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@512w-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@512w-1.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@512w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@512w.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@64w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/AppStoreConnect/Assets.xcassets/AppIcon.appiconset/icon-1024@64w.png -------------------------------------------------------------------------------- /AppStoreConnect/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /AppStoreConnect/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | Default 530 | 531 | 532 | 533 | 534 | 535 | 536 | Left to Right 537 | 538 | 539 | 540 | 541 | 542 | 543 | Right to Left 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | Default 555 | 556 | 557 | 558 | 559 | 560 | 561 | Left to Right 562 | 563 | 564 | 565 | 566 | 567 | 568 | Right to Left 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 797 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 941 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | -------------------------------------------------------------------------------- /AppStoreConnect/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018 SwiftLee. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /AppStoreConnect/Menu/AppMenuTableCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppMenuTableCellView.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import AppKit 11 | 12 | class AppMenuTableCellView: NSTableCellView { 13 | 14 | @IBOutlet weak var appNameLabel: NSTextField! 15 | 16 | } 17 | -------------------------------------------------------------------------------- /AppStoreConnect/Menu/MenuCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuCoordinator.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | final class MenuCoordinator { 13 | 14 | let menuViewController: MenuViewController 15 | 16 | init(menuViewController: MenuViewController) { 17 | self.menuViewController = menuViewController 18 | 19 | menuViewController.didSelectApp = { app in 20 | print("Did select app \(app)") 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /AppStoreConnect/Menu/MenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuViewController.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import AppStoreConnect_Swift_SDK 11 | 12 | extension APIProvider { 13 | static var shared: APIProvider = APIProvider(configuration: APIConfiguration.testConfiguration) 14 | } 15 | 16 | final class MenuViewController: NSViewController { 17 | 18 | @IBOutlet private weak var tableView: NSTableView! 19 | 20 | private var apps: [App]? { 21 | didSet { 22 | tableView.reloadData() 23 | } 24 | } 25 | 26 | var didSelectApp: ((_ app: App) -> Void)? 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | 31 | view.widthAnchor.constraint(equalToConstant: 220).isActive = true 32 | 33 | APIProvider.shared.request(.apps()) { [weak self] (result) in 34 | DispatchQueue.main.async { 35 | self?.apps = result.value?.data.sortByName() 36 | self?.tableView.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) 37 | } 38 | } 39 | } 40 | 41 | } 42 | 43 | extension MenuViewController: NSTableViewDataSource { 44 | func numberOfRows(in tableView: NSTableView) -> Int { 45 | return apps?.count ?? 0 46 | } 47 | } 48 | 49 | extension MenuViewController: NSTableViewDelegate { 50 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{ 51 | var result: AppMenuTableCellView 52 | result = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) as! AppMenuTableCellView 53 | result.appNameLabel.stringValue = apps?[row].attributes?.name ?? "Unknown name" 54 | return result 55 | } 56 | 57 | func tableViewSelectionDidChange(_ notification: Notification) { 58 | let table = notification.object as! NSTableView 59 | guard let app = apps?[table.selectedRow] else { return } 60 | didSelectApp?(app) 61 | } 62 | } 63 | 64 | extension Collection where Element == App { 65 | func sortByName() -> [Element] { 66 | return sorted(by: { (lhs, rhs) -> Bool in 67 | guard let lhsName = lhs.attributes?.name, let rhsName = rhs.attributes?.name else { return false } 68 | return lhsName < rhsName 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AppStoreConnect/SplitViewController/MainSplitViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainSplitViewController.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import AppKit 11 | 12 | final class MainSplitViewController: NSSplitViewController { 13 | 14 | private var menuViewController: MenuViewController! 15 | private var detailTabBarController: NSTabViewController! 16 | 17 | private var appDetailCoordinator: AppDetailCoordinator? 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | menuViewController = (children[0] as! MenuViewController) 22 | detailTabBarController = (children[1] as! NSTabViewController) 23 | 24 | menuViewController.didSelectApp = { [weak self] app in 25 | print("Did select app \(String(describing: app.attributes?.name))") 26 | self?.appDetailCoordinator = AppDetailCoordinator(app: app) 27 | self?.appDetailCoordinator?.start(tabBarController: self!.detailTabBarController) 28 | 29 | } 30 | } 31 | 32 | override func splitView(_ splitView: NSSplitView, effectiveRect proposedEffectiveRect: NSRect, forDrawnRect drawnRect: NSRect, ofDividerAt dividerIndex: Int) -> NSRect { 33 | return NSZeroRect 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /AppStoreConnect/TransparentWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransparentWindowController.swift 3 | // AppStoreConnect 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class TransparentWindowController: NSWindowController { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | 16 | window?.titlebarAppearsTransparent = true 17 | window?.titleVisibility = .hidden 18 | window?.styleMask = [.closable, .titled, .miniaturizable, .resizable, .fullSizeContentView] 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /AppStoreConnectTests/AppStoreConnectTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppStoreConnectTests.swift 3 | // AppStoreConnectTests 4 | // 5 | // Created by Antoine van der Lee on 30/11/2018. 6 | // Copyright © 2018 SwiftLee. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import AppStoreConnect 11 | 12 | class AppStoreConnectTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /AppStoreConnectTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Assets/appimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/Assets/appimage.png -------------------------------------------------------------------------------- /Design/AppIcon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvdLee/appstoreconnect-app/d880b60a0ae0187e4dcfc87e860fda0f1c42e83b/Design/AppIcon.sketch -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :osx, '10.14' 3 | 4 | target 'AppStoreConnect' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for AppStoreConnect 9 | pod 'AppStoreConnect-Swift-SDK', :git => 'https://github.com/AvdLee/appstoreconnect-swift-sdk.git' 10 | 11 | target 'AppStoreConnectTests' do 12 | inherit! :search_paths 13 | # Pods for testing 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.8.0) 3 | - AppStoreConnect-Swift-SDK (0.1.0): 4 | - Alamofire 5 | 6 | DEPENDENCIES: 7 | - AppStoreConnect-Swift-SDK (from `https://github.com/AvdLee/appstoreconnect-swift-sdk.git`) 8 | 9 | SPEC REPOS: 10 | https://github.com/cocoapods/specs.git: 11 | - Alamofire 12 | 13 | EXTERNAL SOURCES: 14 | AppStoreConnect-Swift-SDK: 15 | :git: https://github.com/AvdLee/appstoreconnect-swift-sdk.git 16 | 17 | CHECKOUT OPTIONS: 18 | AppStoreConnect-Swift-SDK: 19 | :commit: 0812ad260f283a557a55d55061b08249f633de52 20 | :git: https://github.com/AvdLee/appstoreconnect-swift-sdk.git 21 | 22 | SPEC CHECKSUMS: 23 | Alamofire: 3ec537f71edc9804815215393ae2b1a8ea33a844 24 | AppStoreConnect-Swift-SDK: 2519f6b2325bafbab8c1a6bee050fa709a9c418d 25 | 26 | PODFILE CHECKSUM: 55b8aa6d7a8baeac55bf467c9f5c453f10a9767d 27 | 28 | COCOAPODS: 1.6.0.beta.2 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # App Store Connect App 2 | A Mac App to control your App Store Connect account, build using the [App Store Connect Swift SDK](https://github.com/AvdLee/appstoreconnect-swift-sdk). 3 | 4 | This currently is work in progress, but hey, it's a start! 5 | 6 | ![Swift Version](https://img.shields.io/badge/Swift-4.2-F16D39.svg?style=flat) [![Twitter](https://img.shields.io/badge/twitter-@Twannl-blue.svg?style=flat)](https://twitter.com/twannl) 7 | 8 | ## Current features 9 | 10 | - [x] List all apps 11 | - [x] List all related TestFlight Groups 12 | - [x] List all TestFlight users (filtered by group) 13 | - [x] Add a new user to the selected group 14 | - [x] Search in the current selected TestFlight group 15 | - [ ] More to come! 16 | 17 | ![](Assets/appimage.png) 18 | 19 | ## Communication 20 | 21 | - If you **found a bug**, open an [issue](https://github.com/AvdLee/appstoreconnect-app/issues). 22 | - If you **have a feature request**, open an [issue](https://github.com/AvdLee/appstoreconnect-app/issues). 23 | - If you **want to contribute**, submit a [pull request](https://github.com/AvdLee/appstoreconnect-app/pulls). 24 | 25 | 26 | ## License 27 | 28 | **App Store Connect Swift SDK** is available under the GNU General Public license. See the [LICENSE](https://github.com/AvdLee/appstoreconnect-app/blob/master/LICENSE) file for more info. 29 | --------------------------------------------------------------------------------