├── .gitignore ├── .travis.yml ├── Cartfile ├── Cartfile.resolved ├── README.md ├── RepoAssets └── GitHub_OAuthSetup.png ├── SwiftFlowGitHubBrowser.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── SwiftFlowGitHubBrowser.xcscheme ├── SwiftFlowGitHubBrowser ├── APIRequests │ └── GitHubRepositories.swift ├── Actions │ ├── AuthenticationActions.swift │ ├── BookmarkActions.swift │ └── GitHubRepositoriesActions.swift ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── GitHubAuth │ └── Credentials.swift ├── Info.plist ├── Reducers │ ├── AppReducer.swift │ ├── AuthenticationReducer.swift │ ├── BookmarksReducer.swift │ └── GitHubAPIReducers.swift ├── Routes │ └── Routes.swift ├── Services │ ├── AuthenticationService.swift │ └── BookmarkService.swift ├── State │ ├── AuthenticationState.swift │ └── State.swift ├── UIKitExtensions │ └── NavigationController+CompletionBlock.swift ├── ViewControllers │ ├── BookmarkViewController.swift │ ├── LoginViewController.swift │ ├── MainViewController.swift │ └── RepositoryDetailViewController.swift └── Views │ ├── BookmarkTableViewCell.swift │ └── RepositoryTableViewCell.swift └── SwiftFlowGitHubBrowserTests ├── GithubAuthSpec └── GitHubAuthSpec.swift └── Info.plist /.gitignore: -------------------------------------------------------------------------------- 1 | Carthage 2 | 3 | 4 | ##Mac OS 5 | .DS_Store 6 | 7 | ## Build generated 8 | build/ 9 | DerivedData 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata 21 | 22 | ## Other 23 | *.xccheckout 24 | *.moved-aside 25 | *.xcuserstate 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | install: 4 | - gem install xcpretty 5 | 6 | env: 7 | global: 8 | - LC_CTYPE=en_US.UTF-8 9 | - LANG=en_US.UTF-8 10 | - APP_NAME="SwiftFlowGitHubBrowser" 11 | 12 | osx_image: xcode10.1 13 | 14 | matrix: 15 | include: 16 | - env: SDK="iphonesimulator" DESTINATION="OS=12.1,name=iPhone 8" 17 | 18 | before_install: 19 | - carthage update --no-use-binaries --platform iOS 20 | 21 | script: 22 | - set -o pipefail 23 | - xcodebuild -version 24 | - xcodebuild -showsdks 25 | - xcodebuild 26 | -project "$APP_NAME.xcodeproj" 27 | -scheme "$APP_NAME" 28 | -sdk "$SDK" 29 | -destination "$DESTINATION" 30 | -configuration Debug 31 | ONLY_ACTIVE_ARCH=YES 32 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES 33 | GCC_GENERATE_TEST_COVERAGE_FILES=YES 34 | test 35 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "nerdishbynature/octokit.swift" 2 | github "nerdishbynature/RequestKit" "2.3.0" 3 | github "Quick/Quick" "master" 4 | github "Quick/Nimble" "master" 5 | github "soffes/SSKeychain" "master" 6 | github "Ben-G/ListKit" 7 | github "ReSwift/ReSwift" "master" 8 | github "ReSwift/ReSwiftRouter" "master" 9 | github "ReSwift/ReSwift-Thunk" "1.1.0" 10 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Ben-G/ListKit" "2.0.0" 2 | github "Quick/Nimble" "d7a8451738e22e76f0465d72b9f3c054df16a56b" 3 | github "Quick/Quick" "5ba1cef76f4dbd24a2ce5a804571667ebfef71a3" 4 | github "ReSwift/ReSwift" "5b7a5c6cd05fa2b7d3f68ef7765acb2ba696d83c" 5 | github "ReSwift/ReSwift-Thunk" "1.1.0" 6 | github "ReSwift/ReSwiftRouter" "17773950b0d1031e185fe6c15cda705fe2b5c204" 7 | github "nerdishbynature/RequestKit" "2.3.0" 8 | github "nerdishbynature/octokit.swift" "0.9.0" 9 | github "soffes/SSKeychain" "d3d64f8c5fa14bf2adb2d51a18ee8b6639232ca5" 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Swift 4.2](https://img.shields.io/badge/Swift-4.2-blue.svg?style=flat) 2 | ![License](https://img.shields.io/github/license/ReSwift/GitHubBrowserExample.svg?style=flat) 3 | 4 | ---- 5 | 6 | Work-in-Progress Real World Example for Swift Flow. 7 | 8 | This project is currently driving some API changes, so it is using newer APIs than provided by the latest Swift Flow releases. 9 | 10 | Successful API changes will be merged into the Swift Flow projects. 11 | 12 | # Checking out Project & Dependencies 13 | 14 | In order to install the projects dependencies, run the following Carthage command in the root directory: 15 | 16 | ``` 17 | carthage update --no-use-binaries --platform iOS 18 | ``` 19 | 20 | # GitHub Configuration Instructions 21 | 22 | This demo requires you to create a GitHub API token that provides the app with access to content on GitHub. 23 | 24 | Create an OAuth application on GitHub and note the **Client ID** and the **Client Secret**; also make sure to set the **Authorization callback URL** to `swiftflowgithub://success` as shown here: 25 | 26 | ![](RepoAssets/GitHub_OAuthSetup.png) 27 | 28 | Open `Credentials.swift` and enter the **Client ID** and the **Client Secret** of your OAuth application: 29 | 30 | ```swift 31 | // In order to try out this demo you need to create an OAuth application on GitHub and provide 32 | // the credentials here. 33 | // 34 | // For more instructions take a look at the README.md in this git repository. 35 | // 36 | // Insert GitHub Token and Secret Here 37 | let gitHubClientId = "" 38 | let gitHubClientSecret = "" 39 | ``` 40 | -------------------------------------------------------------------------------- /RepoAssets/GitHub_OAuthSetup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReSwift/GitHubBrowserExample/252799e54624a983f60e0040dbc1fc7c86b0e11f/RepoAssets/GitHub_OAuthSetup.png -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 251A75DC1C94F92900EBEB32 /* BookmarkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251A75DB1C94F92900EBEB32 /* BookmarkViewController.swift */; }; 11 | 2532E8F61C8F8BFB003F5392 /* BookmarkActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2532E8F51C8F8BFB003F5392 /* BookmarkActions.swift */; }; 12 | 254B3B951D3ABBFF00B1E4F0 /* OctoKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 254B3B881D3ABBCE00B1E4F0 /* OctoKit.framework */; }; 13 | 254B3B961D3ABBFF00B1E4F0 /* OctoKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 254B3B881D3ABBCE00B1E4F0 /* OctoKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 254B3B991D3ABC0400B1E4F0 /* RequestKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 254B3B701D3ABBC400B1E4F0 /* RequestKit.framework */; }; 15 | 254B3B9A1D3ABC0400B1E4F0 /* RequestKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 254B3B701D3ABBC400B1E4F0 /* RequestKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | 254B3B9D1D3ABC0A00B1E4F0 /* ReSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 254B3B581D3ABBB400B1E4F0 /* ReSwift.framework */; }; 17 | 254B3BA11D3ABC1300B1E4F0 /* ReSwiftRouter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 254B3B401D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */; }; 18 | 254B3BA21D3ABC1300B1E4F0 /* ReSwiftRouter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 254B3B401D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 19 | 2565B86A1DE74F440016371D /* SAMKeychain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 253038F41D92D8B900AA49F4 /* SAMKeychain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20 | 2565B89F1DE7506C0016371D /* ReSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 254B3B581D3ABBB400B1E4F0 /* ReSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 21 | 2574204D1D4421410002D020 /* BookmarkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2574204C1D4421410002D020 /* BookmarkService.swift */; }; 22 | 257420841D442A7C0002D020 /* RepositoryTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257420831D442A7C0002D020 /* RepositoryTableViewCell.swift */; }; 23 | 257420861D442E020002D020 /* BookmarkTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257420851D442E020002D020 /* BookmarkTableViewCell.swift */; }; 24 | 257420891D44304C0002D020 /* NavigationController+CompletionBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257420881D44304C0002D020 /* NavigationController+CompletionBlock.swift */; }; 25 | 257420E21D443D990002D020 /* AppReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257420DE1D443D990002D020 /* AppReducer.swift */; }; 26 | 257420E31D443D990002D020 /* AuthenticationReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257420DF1D443D990002D020 /* AuthenticationReducer.swift */; }; 27 | 257420E41D443D990002D020 /* BookmarksReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257420E01D443D990002D020 /* BookmarksReducer.swift */; }; 28 | 257420E51D443D990002D020 /* GitHubAPIReducers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257420E11D443D990002D020 /* GitHubAPIReducers.swift */; }; 29 | 258184351C3CC710008E200A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 258184341C3CC710008E200A /* State.swift */; }; 30 | 2581843A1C3CC912008E200A /* Routes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 258184391C3CC912008E200A /* Routes.swift */; }; 31 | 25BB76211C3CB8CB008EA13B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25BB76201C3CB8CB008EA13B /* AppDelegate.swift */; }; 32 | 25BB76261C3CB8CB008EA13B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 25BB76241C3CB8CB008EA13B /* Main.storyboard */; }; 33 | 25BB76281C3CB8CB008EA13B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 25BB76271C3CB8CB008EA13B /* Assets.xcassets */; }; 34 | 25BB762B1C3CB8CB008EA13B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 25BB76291C3CB8CB008EA13B /* LaunchScreen.storyboard */; }; 35 | 25BE03E11C7C07E6005EBB09 /* AuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25BE03E01C7C07E6005EBB09 /* AuthenticationService.swift */; }; 36 | 25C00E991C3CD8E50074655A /* AuthenticationActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25C00E981C3CD8E50074655A /* AuthenticationActions.swift */; }; 37 | 25C00E9C1C3CDAAC0074655A /* AuthenticationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25C00E9B1C3CDAAC0074655A /* AuthenticationState.swift */; }; 38 | 25FCB37B1F81D93B008EBFD4 /* Credentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25FCB37A1F81D93B008EBFD4 /* Credentials.swift */; }; 39 | 62C935481C8CD17F00887A23 /* GitHubRepositories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C935471C8CD17F00887A23 /* GitHubRepositories.swift */; }; 40 | 62C9354A1C8CD2CC00887A23 /* GitHubRepositoriesActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C935491C8CD2CC00887A23 /* GitHubRepositoriesActions.swift */; }; 41 | 62C935791C8CD6E500887A23 /* ListKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62C935581C8CD6B400887A23 /* ListKit.framework */; }; 42 | 62C9357B1C8CDBDE00887A23 /* RepositoryDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C9357A1C8CDBDE00887A23 /* RepositoryDetailViewController.swift */; }; 43 | 62EF0A6E1C700ABB00D13711 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EF0A6D1C700ABB00D13711 /* LoginViewController.swift */; }; 44 | 62EF0A701C700AD500D13711 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EF0A6F1C700AD500D13711 /* MainViewController.swift */; }; 45 | 62EF0AB61C700D4900D13711 /* GitHubAuthSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EF0AB51C700D4900D13711 /* GitHubAuthSpec.swift */; }; 46 | 62EF0ABA1C700D9D00D13711 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62EF0AA81C700D1F00D13711 /* Nimble.framework */; }; 47 | 62EF0ABB1C700DA200D13711 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62EF0A8D1C700D1200D13711 /* Quick.framework */; }; 48 | 66C463FC22270A970006963A /* ReSwiftThunk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 66C463F922270A7C0006963A /* ReSwiftThunk.framework */; }; 49 | 66C463FD22270A970006963A /* ReSwiftThunk.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 66C463F922270A7C0006963A /* ReSwiftThunk.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 50 | 7A7A57191C91D78C00670D38 /* ListKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 62C935581C8CD6B400887A23 /* ListKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 51 | /* End PBXBuildFile section */ 52 | 53 | /* Begin PBXContainerItemProxy section */ 54 | 14423EA61ECB43EF0099AE91 /* PBXContainerItemProxy */ = { 55 | isa = PBXContainerItemProxy; 56 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 57 | proxyType = 2; 58 | remoteGlobalIDString = 3F13C0E81E7B3E4000D8442C; 59 | remoteInfo = SwiftLintIntegration; 60 | }; 61 | 253038F31D92D8B900AA49F4 /* PBXContainerItemProxy */ = { 62 | isa = PBXContainerItemProxy; 63 | containerPortal = 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */; 64 | proxyType = 2; 65 | remoteGlobalIDString = E8A666341A844CC400287CA3; 66 | remoteInfo = "SAMKeychain-iOS"; 67 | }; 68 | 253038F51D92D8B900AA49F4 /* PBXContainerItemProxy */ = { 69 | isa = PBXContainerItemProxy; 70 | containerPortal = 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */; 71 | proxyType = 2; 72 | remoteGlobalIDString = E8A666551A844D3A00287CA3; 73 | remoteInfo = "SAMKeychainTests-iOS"; 74 | }; 75 | 253038F71D92D8B900AA49F4 /* PBXContainerItemProxy */ = { 76 | isa = PBXContainerItemProxy; 77 | containerPortal = 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */; 78 | proxyType = 2; 79 | remoteGlobalIDString = E8A6666F1A844E4100287CA3; 80 | remoteInfo = "SAMKeychain-OSX"; 81 | }; 82 | 253038F91D92D8B900AA49F4 /* PBXContainerItemProxy */ = { 83 | isa = PBXContainerItemProxy; 84 | containerPortal = 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */; 85 | proxyType = 2; 86 | remoteGlobalIDString = E8A666791A844E4100287CA3; 87 | remoteInfo = "SAMKeychainTests-OSX"; 88 | }; 89 | 253038FB1D92D8B900AA49F4 /* PBXContainerItemProxy */ = { 90 | isa = PBXContainerItemProxy; 91 | containerPortal = 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */; 92 | proxyType = 2; 93 | remoteGlobalIDString = 21632D981C925A3C00C40D7D; 94 | remoteInfo = "SAMKeychain-tvOS"; 95 | }; 96 | 253038FD1D92D8B900AA49F4 /* PBXContainerItemProxy */ = { 97 | isa = PBXContainerItemProxy; 98 | containerPortal = 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */; 99 | proxyType = 2; 100 | remoteGlobalIDString = 21632DA11C925A3C00C40D7D; 101 | remoteInfo = "SAMKeychainTests-tvOS"; 102 | }; 103 | 253038FF1D92D8B900AA49F4 /* PBXContainerItemProxy */ = { 104 | isa = PBXContainerItemProxy; 105 | containerPortal = 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */; 106 | proxyType = 2; 107 | remoteGlobalIDString = 21632DBC1C925B1F00C40D7D; 108 | remoteInfo = "SAMKeychain-watchOS"; 109 | }; 110 | 254B3B3F1D3ABBAB00B1E4F0 /* PBXContainerItemProxy */ = { 111 | isa = PBXContainerItemProxy; 112 | containerPortal = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 113 | proxyType = 2; 114 | remoteGlobalIDString = 625E66CD1C1FFE280027C288; 115 | remoteInfo = "ReSwiftRouter-iOS"; 116 | }; 117 | 254B3B411D3ABBAB00B1E4F0 /* PBXContainerItemProxy */ = { 118 | isa = PBXContainerItemProxy; 119 | containerPortal = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 120 | proxyType = 2; 121 | remoteGlobalIDString = 625E66F11C2000EA0027C288; 122 | remoteInfo = "ReSwiftRouterTests-iOS"; 123 | }; 124 | 254B3B431D3ABBAB00B1E4F0 /* PBXContainerItemProxy */ = { 125 | isa = PBXContainerItemProxy; 126 | containerPortal = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 127 | proxyType = 2; 128 | remoteGlobalIDString = 6209C04B1C54256F004E6B66; 129 | remoteInfo = "ReSwiftRouter-watchOS"; 130 | }; 131 | 254B3B451D3ABBAB00B1E4F0 /* PBXContainerItemProxy */ = { 132 | isa = PBXContainerItemProxy; 133 | containerPortal = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 134 | proxyType = 2; 135 | remoteGlobalIDString = 6209C0611C5427E0004E6B66; 136 | remoteInfo = "ReSwiftRouter-tvOS"; 137 | }; 138 | 254B3B471D3ABBAB00B1E4F0 /* PBXContainerItemProxy */ = { 139 | isa = PBXContainerItemProxy; 140 | containerPortal = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 141 | proxyType = 2; 142 | remoteGlobalIDString = 6209C06A1C5427E0004E6B66; 143 | remoteInfo = "ReSwiftRouter-tvOSTests"; 144 | }; 145 | 254B3B491D3ABBAB00B1E4F0 /* PBXContainerItemProxy */ = { 146 | isa = PBXContainerItemProxy; 147 | containerPortal = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 148 | proxyType = 2; 149 | remoteGlobalIDString = 6209C0881C5428DE004E6B66; 150 | remoteInfo = "ReSwiftRouter-OSX"; 151 | }; 152 | 254B3B4B1D3ABBAB00B1E4F0 /* PBXContainerItemProxy */ = { 153 | isa = PBXContainerItemProxy; 154 | containerPortal = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 155 | proxyType = 2; 156 | remoteGlobalIDString = 6209C0911C5428DE004E6B66; 157 | remoteInfo = "ReSwiftRouter-OSXTests"; 158 | }; 159 | 254B3B571D3ABBB400B1E4F0 /* PBXContainerItemProxy */ = { 160 | isa = PBXContainerItemProxy; 161 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 162 | proxyType = 2; 163 | remoteGlobalIDString = 625E66831C1FF97E0027C288; 164 | remoteInfo = "ReSwift-iOS"; 165 | }; 166 | 254B3B591D3ABBB400B1E4F0 /* PBXContainerItemProxy */ = { 167 | isa = PBXContainerItemProxy; 168 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 169 | proxyType = 2; 170 | remoteGlobalIDString = 625E669A1C1FFA3C0027C288; 171 | remoteInfo = "ReSwift-iOSTests"; 172 | }; 173 | 254B3B5B1D3ABBB400B1E4F0 /* PBXContainerItemProxy */ = { 174 | isa = PBXContainerItemProxy; 175 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 176 | proxyType = 2; 177 | remoteGlobalIDString = 25DBCF7B1C30C4AA00D63A58; 178 | remoteInfo = "ReSwift-OSX"; 179 | }; 180 | 254B3B5D1D3ABBB400B1E4F0 /* PBXContainerItemProxy */ = { 181 | isa = PBXContainerItemProxy; 182 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 183 | proxyType = 2; 184 | remoteGlobalIDString = 25DBCF871C30C4DB00D63A58; 185 | remoteInfo = "ReSwift-OSXTests"; 186 | }; 187 | 254B3B5F1D3ABBB400B1E4F0 /* PBXContainerItemProxy */ = { 188 | isa = PBXContainerItemProxy; 189 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 190 | proxyType = 2; 191 | remoteGlobalIDString = 25DBCF4E1C30C18D00D63A58; 192 | remoteInfo = "ReSwift-tvOS"; 193 | }; 194 | 254B3B611D3ABBB400B1E4F0 /* PBXContainerItemProxy */ = { 195 | isa = PBXContainerItemProxy; 196 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 197 | proxyType = 2; 198 | remoteGlobalIDString = 25DBCF641C30C1AC00D63A58; 199 | remoteInfo = "ReSwift-tvOSTests"; 200 | }; 201 | 254B3B631D3ABBB400B1E4F0 /* PBXContainerItemProxy */ = { 202 | isa = PBXContainerItemProxy; 203 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 204 | proxyType = 2; 205 | remoteGlobalIDString = 25DBCF371C30BF2B00D63A58; 206 | remoteInfo = "ReSwift-watchOS"; 207 | }; 208 | 254B3B6F1D3ABBC400B1E4F0 /* PBXContainerItemProxy */ = { 209 | isa = PBXContainerItemProxy; 210 | containerPortal = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 211 | proxyType = 2; 212 | remoteGlobalIDString = 234F4C1C1BDDF64300A58EF7; 213 | remoteInfo = RequestKit; 214 | }; 215 | 254B3B711D3ABBC400B1E4F0 /* PBXContainerItemProxy */ = { 216 | isa = PBXContainerItemProxy; 217 | containerPortal = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 218 | proxyType = 2; 219 | remoteGlobalIDString = 234F4C261BDDF64300A58EF7; 220 | remoteInfo = RequestKitTests; 221 | }; 222 | 254B3B731D3ABBC400B1E4F0 /* PBXContainerItemProxy */ = { 223 | isa = PBXContainerItemProxy; 224 | containerPortal = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 225 | proxyType = 2; 226 | remoteGlobalIDString = 233743001C7981A800013492; 227 | remoteInfo = "RequestKit tvOS"; 228 | }; 229 | 254B3B751D3ABBC400B1E4F0 /* PBXContainerItemProxy */ = { 230 | isa = PBXContainerItemProxy; 231 | containerPortal = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 232 | proxyType = 2; 233 | remoteGlobalIDString = 2337434D1C79833A00013492; 234 | remoteInfo = "RequestKit tvTests"; 235 | }; 236 | 254B3B771D3ABBC400B1E4F0 /* PBXContainerItemProxy */ = { 237 | isa = PBXContainerItemProxy; 238 | containerPortal = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 239 | proxyType = 2; 240 | remoteGlobalIDString = 2337430D1C7981D700013492; 241 | remoteInfo = "RequestKit watchOS"; 242 | }; 243 | 254B3B791D3ABBC400B1E4F0 /* PBXContainerItemProxy */ = { 244 | isa = PBXContainerItemProxy; 245 | containerPortal = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 246 | proxyType = 2; 247 | remoteGlobalIDString = 2337431A1C7981E700013492; 248 | remoteInfo = "RequestKit Mac"; 249 | }; 250 | 254B3B7B1D3ABBC400B1E4F0 /* PBXContainerItemProxy */ = { 251 | isa = PBXContainerItemProxy; 252 | containerPortal = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 253 | proxyType = 2; 254 | remoteGlobalIDString = 2337433E1C79832700013492; 255 | remoteInfo = "RequestKit MacTests"; 256 | }; 257 | 254B3B871D3ABBCE00B1E4F0 /* PBXContainerItemProxy */ = { 258 | isa = PBXContainerItemProxy; 259 | containerPortal = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 260 | proxyType = 2; 261 | remoteGlobalIDString = 239BE7C91B8C47A100D2CE22; 262 | remoteInfo = OctoKit; 263 | }; 264 | 254B3B891D3ABBCE00B1E4F0 /* PBXContainerItemProxy */ = { 265 | isa = PBXContainerItemProxy; 266 | containerPortal = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 267 | proxyType = 2; 268 | remoteGlobalIDString = 234F4BA91BDDE31A00A58EF7; 269 | remoteInfo = OctoKitTests; 270 | }; 271 | 254B3B8B1D3ABBCE00B1E4F0 /* PBXContainerItemProxy */ = { 272 | isa = PBXContainerItemProxy; 273 | containerPortal = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 274 | proxyType = 2; 275 | remoteGlobalIDString = 23CAF2621C7AB5FB005011C4; 276 | remoteInfo = "OctoKit tvOS"; 277 | }; 278 | 254B3B8D1D3ABBCE00B1E4F0 /* PBXContainerItemProxy */ = { 279 | isa = PBXContainerItemProxy; 280 | containerPortal = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 281 | proxyType = 2; 282 | remoteGlobalIDString = 23CAF26B1C7AB5FC005011C4; 283 | remoteInfo = "OctoKit tvOSTests"; 284 | }; 285 | 254B3B8F1D3ABBCE00B1E4F0 /* PBXContainerItemProxy */ = { 286 | isa = PBXContainerItemProxy; 287 | containerPortal = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 288 | proxyType = 2; 289 | remoteGlobalIDString = 23CAF27E1C7AB60C005011C4; 290 | remoteInfo = "OctoKit Mac"; 291 | }; 292 | 254B3B911D3ABBCE00B1E4F0 /* PBXContainerItemProxy */ = { 293 | isa = PBXContainerItemProxy; 294 | containerPortal = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 295 | proxyType = 2; 296 | remoteGlobalIDString = 23CAF2871C7AB60D005011C4; 297 | remoteInfo = "OctoKit MacTests"; 298 | }; 299 | 254B3B931D3ABBCE00B1E4F0 /* PBXContainerItemProxy */ = { 300 | isa = PBXContainerItemProxy; 301 | containerPortal = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 302 | proxyType = 2; 303 | remoteGlobalIDString = 23CAF29A1C7AB619005011C4; 304 | remoteInfo = "OctoKit watchOS"; 305 | }; 306 | 254B3B971D3ABBFF00B1E4F0 /* PBXContainerItemProxy */ = { 307 | isa = PBXContainerItemProxy; 308 | containerPortal = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 309 | proxyType = 1; 310 | remoteGlobalIDString = 239BE7C81B8C47A100D2CE22; 311 | remoteInfo = OctoKit; 312 | }; 313 | 254B3B9B1D3ABC0400B1E4F0 /* PBXContainerItemProxy */ = { 314 | isa = PBXContainerItemProxy; 315 | containerPortal = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 316 | proxyType = 1; 317 | remoteGlobalIDString = 234F4C1B1BDDF64300A58EF7; 318 | remoteInfo = RequestKit; 319 | }; 320 | 254B3B9F1D3ABC0A00B1E4F0 /* PBXContainerItemProxy */ = { 321 | isa = PBXContainerItemProxy; 322 | containerPortal = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 323 | proxyType = 1; 324 | remoteGlobalIDString = 625E66821C1FF97E0027C288; 325 | remoteInfo = "ReSwift-iOS"; 326 | }; 327 | 254B3BA31D3ABC1300B1E4F0 /* PBXContainerItemProxy */ = { 328 | isa = PBXContainerItemProxy; 329 | containerPortal = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 330 | proxyType = 1; 331 | remoteGlobalIDString = 625E66CC1C1FFE280027C288; 332 | remoteInfo = "ReSwiftRouter-iOS"; 333 | }; 334 | 2565B8861DE74F440016371D /* PBXContainerItemProxy */ = { 335 | isa = PBXContainerItemProxy; 336 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 337 | proxyType = 2; 338 | remoteGlobalIDString = 64076CF51D6D7C2000E2B499; 339 | remoteInfo = "QuickAfterSuite-macOSTests"; 340 | }; 341 | 2565B8881DE74F440016371D /* PBXContainerItemProxy */ = { 342 | isa = PBXContainerItemProxy; 343 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 344 | proxyType = 2; 345 | remoteGlobalIDString = 64076D081D6D7CD600E2B499; 346 | remoteInfo = "QuickAfterSuite-iOSTests"; 347 | }; 348 | 2565B88A1DE74F440016371D /* PBXContainerItemProxy */ = { 349 | isa = PBXContainerItemProxy; 350 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 351 | proxyType = 2; 352 | remoteGlobalIDString = 64076D1A1D6D7CEA00E2B499; 353 | remoteInfo = "QuickAfterSuite-tvOSTests"; 354 | }; 355 | 62C935531C8CD6B400887A23 /* PBXContainerItemProxy */ = { 356 | isa = PBXContainerItemProxy; 357 | containerPortal = 62C9354C1C8CD6B300887A23 /* ListKit.xcodeproj */; 358 | proxyType = 2; 359 | remoteGlobalIDString = 626518121A96473700F1D760; 360 | remoteInfo = ListKitDemo; 361 | }; 362 | 62C935551C8CD6B400887A23 /* PBXContainerItemProxy */ = { 363 | isa = PBXContainerItemProxy; 364 | containerPortal = 62C9354C1C8CD6B300887A23 /* ListKit.xcodeproj */; 365 | proxyType = 2; 366 | remoteGlobalIDString = 626518271A96473700F1D760; 367 | remoteInfo = ListKitDemoTests; 368 | }; 369 | 62C935571C8CD6B400887A23 /* PBXContainerItemProxy */ = { 370 | isa = PBXContainerItemProxy; 371 | containerPortal = 62C9354C1C8CD6B300887A23 /* ListKit.xcodeproj */; 372 | proxyType = 2; 373 | remoteGlobalIDString = 6265183C1A96474900F1D760; 374 | remoteInfo = ListKit; 375 | }; 376 | 62C935591C8CD6B400887A23 /* PBXContainerItemProxy */ = { 377 | isa = PBXContainerItemProxy; 378 | containerPortal = 62C9354C1C8CD6B300887A23 /* ListKit.xcodeproj */; 379 | proxyType = 2; 380 | remoteGlobalIDString = 626518461A96474900F1D760; 381 | remoteInfo = ListKitTests; 382 | }; 383 | 62EF0A7A1C700C2300D13711 /* PBXContainerItemProxy */ = { 384 | isa = PBXContainerItemProxy; 385 | containerPortal = 25BB76151C3CB8CB008EA13B /* Project object */; 386 | proxyType = 1; 387 | remoteGlobalIDString = 25BB761C1C3CB8CB008EA13B; 388 | remoteInfo = SwiftFlowGitHubBrowser; 389 | }; 390 | 62EF0A8C1C700D1200D13711 /* PBXContainerItemProxy */ = { 391 | isa = PBXContainerItemProxy; 392 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 393 | proxyType = 2; 394 | remoteGlobalIDString = DAEB6B8E1943873100289F44; 395 | remoteInfo = "Quick-OSX"; 396 | }; 397 | 62EF0A8E1C700D1200D13711 /* PBXContainerItemProxy */ = { 398 | isa = PBXContainerItemProxy; 399 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 400 | proxyType = 2; 401 | remoteGlobalIDString = DAEB6B991943873100289F44; 402 | remoteInfo = "Quick-OSXTests"; 403 | }; 404 | 62EF0A901C700D1200D13711 /* PBXContainerItemProxy */ = { 405 | isa = PBXContainerItemProxy; 406 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 407 | proxyType = 2; 408 | remoteGlobalIDString = DA5663E81A4C8D8500193C88; 409 | remoteInfo = "QuickFocused-OSXTests"; 410 | }; 411 | 62EF0A921C700D1200D13711 /* PBXContainerItemProxy */ = { 412 | isa = PBXContainerItemProxy; 413 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 414 | proxyType = 2; 415 | remoteGlobalIDString = 5A5D117C19473F2100F6D13D; 416 | remoteInfo = "Quick-iOS"; 417 | }; 418 | 62EF0A941C700D1200D13711 /* PBXContainerItemProxy */ = { 419 | isa = PBXContainerItemProxy; 420 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 421 | proxyType = 2; 422 | remoteGlobalIDString = 5A5D118619473F2100F6D13D; 423 | remoteInfo = "Quick-iOSTests"; 424 | }; 425 | 62EF0A961C700D1200D13711 /* PBXContainerItemProxy */ = { 426 | isa = PBXContainerItemProxy; 427 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 428 | proxyType = 2; 429 | remoteGlobalIDString = DA9876B21A4C70EB0004AA17; 430 | remoteInfo = "QuickFocused-iOSTests"; 431 | }; 432 | 62EF0A981C700D1200D13711 /* PBXContainerItemProxy */ = { 433 | isa = PBXContainerItemProxy; 434 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 435 | proxyType = 2; 436 | remoteGlobalIDString = 1F118CD51BDCA4AB005013A2; 437 | remoteInfo = "Quick-tvOS"; 438 | }; 439 | 62EF0A9A1C700D1200D13711 /* PBXContainerItemProxy */ = { 440 | isa = PBXContainerItemProxy; 441 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 442 | proxyType = 2; 443 | remoteGlobalIDString = 1F118CDE1BDCA4AB005013A2; 444 | remoteInfo = "Quick-tvOSTests"; 445 | }; 446 | 62EF0A9C1C700D1200D13711 /* PBXContainerItemProxy */ = { 447 | isa = PBXContainerItemProxy; 448 | containerPortal = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 449 | proxyType = 2; 450 | remoteGlobalIDString = 1F118CF01BDCA4BB005013A2; 451 | remoteInfo = "QuickFocused-tvOSTests"; 452 | }; 453 | 62EF0AA71C700D1F00D13711 /* PBXContainerItemProxy */ = { 454 | isa = PBXContainerItemProxy; 455 | containerPortal = 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */; 456 | proxyType = 2; 457 | remoteGlobalIDString = 1F1A74291940169200FFFC47; 458 | remoteInfo = "Nimble-iOS"; 459 | }; 460 | 62EF0AA91C700D1F00D13711 /* PBXContainerItemProxy */ = { 461 | isa = PBXContainerItemProxy; 462 | containerPortal = 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */; 463 | proxyType = 2; 464 | remoteGlobalIDString = 1F1A74341940169200FFFC47; 465 | remoteInfo = "Nimble-iOSTests"; 466 | }; 467 | 62EF0AAB1C700D1F00D13711 /* PBXContainerItemProxy */ = { 468 | isa = PBXContainerItemProxy; 469 | containerPortal = 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */; 470 | proxyType = 2; 471 | remoteGlobalIDString = 1F925EAD195C0D6300ED456B; 472 | remoteInfo = "Nimble-OSX"; 473 | }; 474 | 62EF0AAD1C700D1F00D13711 /* PBXContainerItemProxy */ = { 475 | isa = PBXContainerItemProxy; 476 | containerPortal = 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */; 477 | proxyType = 2; 478 | remoteGlobalIDString = 1F925EB7195C0D6300ED456B; 479 | remoteInfo = "Nimble-OSXTests"; 480 | }; 481 | 62EF0AAF1C700D1F00D13711 /* PBXContainerItemProxy */ = { 482 | isa = PBXContainerItemProxy; 483 | containerPortal = 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */; 484 | proxyType = 2; 485 | remoteGlobalIDString = 1F5DF1551BDCA0CE00C3A531; 486 | remoteInfo = "Nimble-tvOS"; 487 | }; 488 | 62EF0AB11C700D1F00D13711 /* PBXContainerItemProxy */ = { 489 | isa = PBXContainerItemProxy; 490 | containerPortal = 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */; 491 | proxyType = 2; 492 | remoteGlobalIDString = 1F5DF15E1BDCA0CE00C3A531; 493 | remoteInfo = "Nimble-tvOSTests"; 494 | }; 495 | 66C463F822270A7C0006963A /* PBXContainerItemProxy */ = { 496 | isa = PBXContainerItemProxy; 497 | containerPortal = 66C463F322270A7C0006963A /* ReSwift-Thunk.xcodeproj */; 498 | proxyType = 2; 499 | remoteGlobalIDString = 65A3D6D5218B89A60075CB92; 500 | remoteInfo = "ReSwift-Thunk"; 501 | }; 502 | 66C463FA22270A7C0006963A /* PBXContainerItemProxy */ = { 503 | isa = PBXContainerItemProxy; 504 | containerPortal = 66C463F322270A7C0006963A /* ReSwift-Thunk.xcodeproj */; 505 | proxyType = 2; 506 | remoteGlobalIDString = 65A3D6DE218B89A60075CB92; 507 | remoteInfo = "ReSwift-Thunk-Tests"; 508 | }; 509 | 66C463FE22270A970006963A /* PBXContainerItemProxy */ = { 510 | isa = PBXContainerItemProxy; 511 | containerPortal = 66C463F322270A7C0006963A /* ReSwift-Thunk.xcodeproj */; 512 | proxyType = 1; 513 | remoteGlobalIDString = 65A3D6D4218B89A60075CB92; 514 | remoteInfo = "ReSwift-Thunk"; 515 | }; 516 | 7A7A571A1C91D78C00670D38 /* PBXContainerItemProxy */ = { 517 | isa = PBXContainerItemProxy; 518 | containerPortal = 62C9354C1C8CD6B300887A23 /* ListKit.xcodeproj */; 519 | proxyType = 1; 520 | remoteGlobalIDString = 6265183B1A96474900F1D760; 521 | remoteInfo = ListKit; 522 | }; 523 | /* End PBXContainerItemProxy section */ 524 | 525 | /* Begin PBXCopyFilesBuildPhase section */ 526 | 7A7A57071C91D73C00670D38 /* Embed Frameworks */ = { 527 | isa = PBXCopyFilesBuildPhase; 528 | buildActionMask = 2147483647; 529 | dstPath = ""; 530 | dstSubfolderSpec = 10; 531 | files = ( 532 | 254B3B961D3ABBFF00B1E4F0 /* OctoKit.framework in Embed Frameworks */, 533 | 254B3B9A1D3ABC0400B1E4F0 /* RequestKit.framework in Embed Frameworks */, 534 | 66C463FD22270A970006963A /* ReSwiftThunk.framework in Embed Frameworks */, 535 | 2565B89F1DE7506C0016371D /* ReSwift.framework in Embed Frameworks */, 536 | 7A7A57191C91D78C00670D38 /* ListKit.framework in Embed Frameworks */, 537 | 2565B86A1DE74F440016371D /* SAMKeychain.framework in Embed Frameworks */, 538 | 254B3BA21D3ABC1300B1E4F0 /* ReSwiftRouter.framework in Embed Frameworks */, 539 | ); 540 | name = "Embed Frameworks"; 541 | runOnlyForDeploymentPostprocessing = 0; 542 | }; 543 | /* End PBXCopyFilesBuildPhase section */ 544 | 545 | /* Begin PBXFileReference section */ 546 | 251A75DB1C94F92900EBEB32 /* BookmarkViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkViewController.swift; sourceTree = ""; }; 547 | 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SAMKeychain.xcodeproj; path = Carthage/Checkouts/SSKeychain/SAMKeychain.xcodeproj; sourceTree = SOURCE_ROOT; }; 548 | 2532E8F51C8F8BFB003F5392 /* BookmarkActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkActions.swift; sourceTree = ""; }; 549 | 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReSwiftRouter.xcodeproj; path = Carthage/Checkouts/ReSwiftRouter/ReSwiftRouter.xcodeproj; sourceTree = SOURCE_ROOT; }; 550 | 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReSwift.xcodeproj; path = Carthage/Checkouts/ReSwift/ReSwift.xcodeproj; sourceTree = SOURCE_ROOT; }; 551 | 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RequestKit.xcodeproj; path = Carthage/Checkouts/RequestKit/RequestKit.xcodeproj; sourceTree = SOURCE_ROOT; }; 552 | 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OctoKit.xcodeproj; path = Carthage/Checkouts/octokit.swift/OctoKit.xcodeproj; sourceTree = SOURCE_ROOT; }; 553 | 2574204C1D4421410002D020 /* BookmarkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkService.swift; sourceTree = ""; }; 554 | 257420831D442A7C0002D020 /* RepositoryTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepositoryTableViewCell.swift; sourceTree = ""; }; 555 | 257420851D442E020002D020 /* BookmarkTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkTableViewCell.swift; sourceTree = ""; }; 556 | 257420881D44304C0002D020 /* NavigationController+CompletionBlock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NavigationController+CompletionBlock.swift"; path = "UIKitExtensions/NavigationController+CompletionBlock.swift"; sourceTree = ""; }; 557 | 257420DE1D443D990002D020 /* AppReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppReducer.swift; path = Reducers/AppReducer.swift; sourceTree = ""; }; 558 | 257420DF1D443D990002D020 /* AuthenticationReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthenticationReducer.swift; path = Reducers/AuthenticationReducer.swift; sourceTree = ""; }; 559 | 257420E01D443D990002D020 /* BookmarksReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BookmarksReducer.swift; path = Reducers/BookmarksReducer.swift; sourceTree = ""; }; 560 | 257420E11D443D990002D020 /* GitHubAPIReducers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GitHubAPIReducers.swift; path = Reducers/GitHubAPIReducers.swift; sourceTree = ""; }; 561 | 258184341C3CC710008E200A /* State.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = ""; }; 562 | 258184391C3CC912008E200A /* Routes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Routes.swift; sourceTree = ""; }; 563 | 25BB761D1C3CB8CB008EA13B /* SwiftFlowGitHubBrowser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftFlowGitHubBrowser.app; sourceTree = BUILT_PRODUCTS_DIR; }; 564 | 25BB76201C3CB8CB008EA13B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 565 | 25BB76251C3CB8CB008EA13B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 566 | 25BB76271C3CB8CB008EA13B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 567 | 25BB762A1C3CB8CB008EA13B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 568 | 25BB762C1C3CB8CB008EA13B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 569 | 25BE03E01C7C07E6005EBB09 /* AuthenticationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = ""; }; 570 | 25C00E981C3CD8E50074655A /* AuthenticationActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationActions.swift; sourceTree = ""; }; 571 | 25C00E9B1C3CDAAC0074655A /* AuthenticationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationState.swift; sourceTree = ""; }; 572 | 25FCB37A1F81D93B008EBFD4 /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = ""; }; 573 | 62C935471C8CD17F00887A23 /* GitHubRepositories.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubRepositories.swift; sourceTree = ""; }; 574 | 62C935491C8CD2CC00887A23 /* GitHubRepositoriesActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubRepositoriesActions.swift; sourceTree = ""; }; 575 | 62C9354C1C8CD6B300887A23 /* ListKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ListKit.xcodeproj; path = Carthage/Checkouts/ListKit/ListKit.xcodeproj; sourceTree = SOURCE_ROOT; }; 576 | 62C9357A1C8CDBDE00887A23 /* RepositoryDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepositoryDetailViewController.swift; sourceTree = ""; }; 577 | 62EF0A6D1C700ABB00D13711 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; 578 | 62EF0A6F1C700AD500D13711 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 579 | 62EF0A751C700C2300D13711 /* SwiftFlowGitHubBrowserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftFlowGitHubBrowserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 580 | 62EF0A791C700C2300D13711 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 581 | 62EF0A801C700D1200D13711 /* Quick.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Quick.xcodeproj; path = Carthage/Checkouts/Quick/Quick.xcodeproj; sourceTree = SOURCE_ROOT; }; 582 | 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Nimble.xcodeproj; path = Carthage/Checkouts/Nimble/Nimble.xcodeproj; sourceTree = SOURCE_ROOT; }; 583 | 62EF0AB51C700D4900D13711 /* GitHubAuthSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubAuthSpec.swift; sourceTree = ""; }; 584 | 66C463F322270A7C0006963A /* ReSwift-Thunk.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "ReSwift-Thunk.xcodeproj"; path = "Carthage/Checkouts/ReSwift-Thunk/ReSwift-Thunk.xcodeproj"; sourceTree = SOURCE_ROOT; }; 585 | /* End PBXFileReference section */ 586 | 587 | /* Begin PBXFrameworksBuildPhase section */ 588 | 25BB761A1C3CB8CB008EA13B /* Frameworks */ = { 589 | isa = PBXFrameworksBuildPhase; 590 | buildActionMask = 2147483647; 591 | files = ( 592 | 254B3B951D3ABBFF00B1E4F0 /* OctoKit.framework in Frameworks */, 593 | 254B3B9D1D3ABC0A00B1E4F0 /* ReSwift.framework in Frameworks */, 594 | 66C463FC22270A970006963A /* ReSwiftThunk.framework in Frameworks */, 595 | 254B3B991D3ABC0400B1E4F0 /* RequestKit.framework in Frameworks */, 596 | 62C935791C8CD6E500887A23 /* ListKit.framework in Frameworks */, 597 | 254B3BA11D3ABC1300B1E4F0 /* ReSwiftRouter.framework in Frameworks */, 598 | ); 599 | runOnlyForDeploymentPostprocessing = 0; 600 | }; 601 | 62EF0A721C700C2300D13711 /* Frameworks */ = { 602 | isa = PBXFrameworksBuildPhase; 603 | buildActionMask = 2147483647; 604 | files = ( 605 | 62EF0ABA1C700D9D00D13711 /* Nimble.framework in Frameworks */, 606 | 62EF0ABB1C700DA200D13711 /* Quick.framework in Frameworks */, 607 | ); 608 | runOnlyForDeploymentPostprocessing = 0; 609 | }; 610 | /* End PBXFrameworksBuildPhase section */ 611 | 612 | /* Begin PBXGroup section */ 613 | 253038EA1D92D8B900AA49F4 /* Products */ = { 614 | isa = PBXGroup; 615 | children = ( 616 | 253038F41D92D8B900AA49F4 /* SAMKeychain.framework */, 617 | 253038F61D92D8B900AA49F4 /* SAMKeychainTests-iOS.xctest */, 618 | 253038F81D92D8B900AA49F4 /* SAMKeychain.framework */, 619 | 253038FA1D92D8B900AA49F4 /* SAMKeychainTests-macOS.xctest */, 620 | 253038FC1D92D8B900AA49F4 /* SAMKeychain.framework */, 621 | 253038FE1D92D8B900AA49F4 /* SAMKeychainTests.xctest */, 622 | 253039001D92D8B900AA49F4 /* SAMKeychain.framework */, 623 | ); 624 | name = Products; 625 | sourceTree = ""; 626 | }; 627 | 254B3B331D3ABBAB00B1E4F0 /* Products */ = { 628 | isa = PBXGroup; 629 | children = ( 630 | 254B3B401D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */, 631 | 254B3B421D3ABBAB00B1E4F0 /* ReSwiftRouterTests-iOS.xctest */, 632 | 254B3B441D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */, 633 | 254B3B461D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */, 634 | 254B3B481D3ABBAB00B1E4F0 /* ReSwiftRouter-tvOSTests.xctest */, 635 | 254B3B4A1D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */, 636 | 254B3B4C1D3ABBAB00B1E4F0 /* ReSwiftRouter.xctest */, 637 | ); 638 | name = Products; 639 | sourceTree = ""; 640 | }; 641 | 254B3B4E1D3ABBB400B1E4F0 /* Products */ = { 642 | isa = PBXGroup; 643 | children = ( 644 | 254B3B581D3ABBB400B1E4F0 /* ReSwift.framework */, 645 | 254B3B5A1D3ABBB400B1E4F0 /* ReSwift-iOSTests.xctest */, 646 | 254B3B5C1D3ABBB400B1E4F0 /* ReSwift.framework */, 647 | 254B3B5E1D3ABBB400B1E4F0 /* ReSwift-macOSTests.xctest */, 648 | 254B3B601D3ABBB400B1E4F0 /* ReSwift.framework */, 649 | 254B3B621D3ABBB400B1E4F0 /* ReSwift-tvOSTests.xctest */, 650 | 254B3B641D3ABBB400B1E4F0 /* ReSwift.framework */, 651 | 14423EA71ECB43EF0099AE91 /* SwiftLintIntegration.xctest */, 652 | ); 653 | name = Products; 654 | sourceTree = ""; 655 | }; 656 | 254B3B661D3ABBC400B1E4F0 /* Products */ = { 657 | isa = PBXGroup; 658 | children = ( 659 | 254B3B701D3ABBC400B1E4F0 /* RequestKit.framework */, 660 | 254B3B721D3ABBC400B1E4F0 /* RequestKitTests.xctest */, 661 | 254B3B741D3ABBC400B1E4F0 /* RequestKit.framework */, 662 | 254B3B761D3ABBC400B1E4F0 /* RequestKit tvTests.xctest */, 663 | 254B3B781D3ABBC400B1E4F0 /* RequestKit.framework */, 664 | 254B3B7A1D3ABBC400B1E4F0 /* RequestKit.framework */, 665 | 254B3B7C1D3ABBC400B1E4F0 /* RequestKit MacTests.xctest */, 666 | ); 667 | name = Products; 668 | sourceTree = ""; 669 | }; 670 | 254B3B7E1D3ABBCE00B1E4F0 /* Products */ = { 671 | isa = PBXGroup; 672 | children = ( 673 | 254B3B881D3ABBCE00B1E4F0 /* OctoKit.framework */, 674 | 254B3B8A1D3ABBCE00B1E4F0 /* OctoKitTests.xctest */, 675 | 254B3B8C1D3ABBCE00B1E4F0 /* OctoKit.framework */, 676 | 254B3B8E1D3ABBCE00B1E4F0 /* OctoKit tvOSTests.xctest */, 677 | 254B3B901D3ABBCE00B1E4F0 /* OctoKit.framework */, 678 | 254B3B921D3ABBCE00B1E4F0 /* OctoKit MacTests.xctest */, 679 | 254B3B941D3ABBCE00B1E4F0 /* OctoKit.framework */, 680 | ); 681 | name = Products; 682 | sourceTree = ""; 683 | }; 684 | 257420821D442A700002D020 /* Views */ = { 685 | isa = PBXGroup; 686 | children = ( 687 | 257420831D442A7C0002D020 /* RepositoryTableViewCell.swift */, 688 | 257420851D442E020002D020 /* BookmarkTableViewCell.swift */, 689 | ); 690 | path = Views; 691 | sourceTree = ""; 692 | }; 693 | 257420871D4430330002D020 /* UIKitExtensions */ = { 694 | isa = PBXGroup; 695 | children = ( 696 | 257420881D44304C0002D020 /* NavigationController+CompletionBlock.swift */, 697 | ); 698 | name = UIKitExtensions; 699 | sourceTree = ""; 700 | }; 701 | 258184331C3CC6F2008E200A /* State */ = { 702 | isa = PBXGroup; 703 | children = ( 704 | 258184341C3CC710008E200A /* State.swift */, 705 | 25C00E9B1C3CDAAC0074655A /* AuthenticationState.swift */, 706 | ); 707 | path = State; 708 | sourceTree = ""; 709 | }; 710 | 258184381C3CC8FA008E200A /* Routes */ = { 711 | isa = PBXGroup; 712 | children = ( 713 | 258184391C3CC912008E200A /* Routes.swift */, 714 | ); 715 | path = Routes; 716 | sourceTree = ""; 717 | }; 718 | 25BB76141C3CB8CB008EA13B = { 719 | isa = PBXGroup; 720 | children = ( 721 | 25BB761F1C3CB8CB008EA13B /* SwiftFlowGitHubBrowser */, 722 | 62EF0A761C700C2300D13711 /* SwiftFlowGitHubBrowserTests */, 723 | 25BB761E1C3CB8CB008EA13B /* Products */, 724 | ); 725 | sourceTree = ""; 726 | }; 727 | 25BB761E1C3CB8CB008EA13B /* Products */ = { 728 | isa = PBXGroup; 729 | children = ( 730 | 25BB761D1C3CB8CB008EA13B /* SwiftFlowGitHubBrowser.app */, 731 | 62EF0A751C700C2300D13711 /* SwiftFlowGitHubBrowserTests.xctest */, 732 | ); 733 | name = Products; 734 | sourceTree = ""; 735 | }; 736 | 25BB761F1C3CB8CB008EA13B /* SwiftFlowGitHubBrowser */ = { 737 | isa = PBXGroup; 738 | children = ( 739 | 25FCB3791F81D930008EBFD4 /* GitHubAuth */, 740 | 257420871D4430330002D020 /* UIKitExtensions */, 741 | 257420821D442A700002D020 /* Views */, 742 | 62C935461C8CD14F00887A23 /* APIRequests */, 743 | 25BE03BA1C7C079B005EBB09 /* Services */, 744 | 62EF0A6C1C700ABB00D13711 /* ViewControllers */, 745 | 25C00E9A1C3CDA340074655A /* Reducers */, 746 | 25C00E8E1C3CD8BE0074655A /* Actions */, 747 | 258184381C3CC8FA008E200A /* Routes */, 748 | 258184331C3CC6F2008E200A /* State */, 749 | 25BB76321C3CBA99008EA13B /* Dependencies */, 750 | 25BB76201C3CB8CB008EA13B /* AppDelegate.swift */, 751 | 25BB76241C3CB8CB008EA13B /* Main.storyboard */, 752 | 25BB76271C3CB8CB008EA13B /* Assets.xcassets */, 753 | 25BB76291C3CB8CB008EA13B /* LaunchScreen.storyboard */, 754 | 25BB762C1C3CB8CB008EA13B /* Info.plist */, 755 | ); 756 | path = SwiftFlowGitHubBrowser; 757 | sourceTree = ""; 758 | }; 759 | 25BB76321C3CBA99008EA13B /* Dependencies */ = { 760 | isa = PBXGroup; 761 | children = ( 762 | 66C463F322270A7C0006963A /* ReSwift-Thunk.xcodeproj */, 763 | 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */, 764 | 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */, 765 | 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */, 766 | 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */, 767 | 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */, 768 | 62C9354C1C8CD6B300887A23 /* ListKit.xcodeproj */, 769 | ); 770 | name = Dependencies; 771 | sourceTree = ""; 772 | }; 773 | 25BE03BA1C7C079B005EBB09 /* Services */ = { 774 | isa = PBXGroup; 775 | children = ( 776 | 25BE03E01C7C07E6005EBB09 /* AuthenticationService.swift */, 777 | 2574204C1D4421410002D020 /* BookmarkService.swift */, 778 | ); 779 | path = Services; 780 | sourceTree = ""; 781 | }; 782 | 25C00E8E1C3CD8BE0074655A /* Actions */ = { 783 | isa = PBXGroup; 784 | children = ( 785 | 25C00E981C3CD8E50074655A /* AuthenticationActions.swift */, 786 | 62C935491C8CD2CC00887A23 /* GitHubRepositoriesActions.swift */, 787 | 2532E8F51C8F8BFB003F5392 /* BookmarkActions.swift */, 788 | ); 789 | path = Actions; 790 | sourceTree = ""; 791 | }; 792 | 25C00E9A1C3CDA340074655A /* Reducers */ = { 793 | isa = PBXGroup; 794 | children = ( 795 | 257420DE1D443D990002D020 /* AppReducer.swift */, 796 | 257420DF1D443D990002D020 /* AuthenticationReducer.swift */, 797 | 257420E01D443D990002D020 /* BookmarksReducer.swift */, 798 | 257420E11D443D990002D020 /* GitHubAPIReducers.swift */, 799 | ); 800 | name = Reducers; 801 | sourceTree = ""; 802 | }; 803 | 25FCB3791F81D930008EBFD4 /* GitHubAuth */ = { 804 | isa = PBXGroup; 805 | children = ( 806 | 25FCB37A1F81D93B008EBFD4 /* Credentials.swift */, 807 | ); 808 | path = GitHubAuth; 809 | sourceTree = ""; 810 | }; 811 | 62C935461C8CD14F00887A23 /* APIRequests */ = { 812 | isa = PBXGroup; 813 | children = ( 814 | 62C935471C8CD17F00887A23 /* GitHubRepositories.swift */, 815 | ); 816 | path = APIRequests; 817 | sourceTree = ""; 818 | }; 819 | 62C9354B1C8CD41F00887A23 /* APIRequestSpec */ = { 820 | isa = PBXGroup; 821 | children = ( 822 | ); 823 | path = APIRequestSpec; 824 | sourceTree = ""; 825 | }; 826 | 62C9354D1C8CD6B300887A23 /* Products */ = { 827 | isa = PBXGroup; 828 | children = ( 829 | 62C935541C8CD6B400887A23 /* ListKitDemo.app */, 830 | 62C935561C8CD6B400887A23 /* ListKit.xctest */, 831 | 62C935581C8CD6B400887A23 /* ListKit.framework */, 832 | 62C9355A1C8CD6B400887A23 /* ListKitTests.xctest */, 833 | ); 834 | name = Products; 835 | sourceTree = ""; 836 | }; 837 | 62EF0A6C1C700ABB00D13711 /* ViewControllers */ = { 838 | isa = PBXGroup; 839 | children = ( 840 | 62EF0A6D1C700ABB00D13711 /* LoginViewController.swift */, 841 | 62EF0A6F1C700AD500D13711 /* MainViewController.swift */, 842 | 62C9357A1C8CDBDE00887A23 /* RepositoryDetailViewController.swift */, 843 | 251A75DB1C94F92900EBEB32 /* BookmarkViewController.swift */, 844 | ); 845 | path = ViewControllers; 846 | sourceTree = ""; 847 | }; 848 | 62EF0A761C700C2300D13711 /* SwiftFlowGitHubBrowserTests */ = { 849 | isa = PBXGroup; 850 | children = ( 851 | 62C9354B1C8CD41F00887A23 /* APIRequestSpec */, 852 | 62EF0AB31C700D3A00D13711 /* GithubAuthSpec */, 853 | 62EF0A7F1C700CFD00D13711 /* Dependencies */, 854 | 62EF0A791C700C2300D13711 /* Info.plist */, 855 | ); 856 | path = SwiftFlowGitHubBrowserTests; 857 | sourceTree = ""; 858 | }; 859 | 62EF0A7F1C700CFD00D13711 /* Dependencies */ = { 860 | isa = PBXGroup; 861 | children = ( 862 | 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */, 863 | 62EF0A801C700D1200D13711 /* Quick.xcodeproj */, 864 | ); 865 | name = Dependencies; 866 | sourceTree = ""; 867 | }; 868 | 62EF0A811C700D1200D13711 /* Products */ = { 869 | isa = PBXGroup; 870 | children = ( 871 | 62EF0A8D1C700D1200D13711 /* Quick.framework */, 872 | 62EF0A8F1C700D1200D13711 /* Quick - macOSTests.xctest */, 873 | 62EF0A911C700D1200D13711 /* QuickFocused - macOSTests.xctest */, 874 | 2565B8871DE74F440016371D /* QuickAfterSuite - macOSTests.xctest */, 875 | 62EF0A931C700D1200D13711 /* Quick.framework */, 876 | 62EF0A951C700D1200D13711 /* Quick - iOSTests.xctest */, 877 | 62EF0A971C700D1200D13711 /* QuickFocused - iOSTests.xctest */, 878 | 2565B8891DE74F440016371D /* QuickAfterSuite - iOSTests.xctest */, 879 | 62EF0A991C700D1200D13711 /* Quick.framework */, 880 | 62EF0A9B1C700D1200D13711 /* Quick - tvOSTests.xctest */, 881 | 62EF0A9D1C700D1200D13711 /* QuickFocused - tvOSTests.xctest */, 882 | 2565B88B1DE74F440016371D /* QuickAfterSuite - tvOSTests.xctest */, 883 | ); 884 | name = Products; 885 | sourceTree = ""; 886 | }; 887 | 62EF0A9F1C700D1F00D13711 /* Products */ = { 888 | isa = PBXGroup; 889 | children = ( 890 | 62EF0AAC1C700D1F00D13711 /* Nimble.framework */, 891 | 62EF0AAE1C700D1F00D13711 /* NimbleTests.xctest */, 892 | 62EF0AA81C700D1F00D13711 /* Nimble.framework */, 893 | 62EF0AAA1C700D1F00D13711 /* NimbleTests.xctest */, 894 | 62EF0AB01C700D1F00D13711 /* Nimble.framework */, 895 | 62EF0AB21C700D1F00D13711 /* NimbleTests.xctest */, 896 | ); 897 | name = Products; 898 | sourceTree = ""; 899 | }; 900 | 62EF0AB31C700D3A00D13711 /* GithubAuthSpec */ = { 901 | isa = PBXGroup; 902 | children = ( 903 | 62EF0AB51C700D4900D13711 /* GitHubAuthSpec.swift */, 904 | ); 905 | path = GithubAuthSpec; 906 | sourceTree = ""; 907 | }; 908 | 66C463F422270A7C0006963A /* Products */ = { 909 | isa = PBXGroup; 910 | children = ( 911 | 66C463F922270A7C0006963A /* ReSwiftThunk.framework */, 912 | 66C463FB22270A7C0006963A /* ReSwift-Thunk-Tests.xctest */, 913 | ); 914 | name = Products; 915 | sourceTree = ""; 916 | }; 917 | /* End PBXGroup section */ 918 | 919 | /* Begin PBXNativeTarget section */ 920 | 25BB761C1C3CB8CB008EA13B /* SwiftFlowGitHubBrowser */ = { 921 | isa = PBXNativeTarget; 922 | buildConfigurationList = 25BB762F1C3CB8CB008EA13B /* Build configuration list for PBXNativeTarget "SwiftFlowGitHubBrowser" */; 923 | buildPhases = ( 924 | 25BB76191C3CB8CB008EA13B /* Sources */, 925 | 25BB761A1C3CB8CB008EA13B /* Frameworks */, 926 | 25BB761B1C3CB8CB008EA13B /* Resources */, 927 | 7A7A57071C91D73C00670D38 /* Embed Frameworks */, 928 | 14423EB51ECB44250099AE91 /* ShellScript */, 929 | ); 930 | buildRules = ( 931 | ); 932 | dependencies = ( 933 | 7A7A571B1C91D78C00670D38 /* PBXTargetDependency */, 934 | 254B3B981D3ABBFF00B1E4F0 /* PBXTargetDependency */, 935 | 254B3B9C1D3ABC0400B1E4F0 /* PBXTargetDependency */, 936 | 254B3BA01D3ABC0A00B1E4F0 /* PBXTargetDependency */, 937 | 254B3BA41D3ABC1300B1E4F0 /* PBXTargetDependency */, 938 | 66C463FF22270A970006963A /* PBXTargetDependency */, 939 | ); 940 | name = SwiftFlowGitHubBrowser; 941 | productName = SwiftFlowGitHubBrowser; 942 | productReference = 25BB761D1C3CB8CB008EA13B /* SwiftFlowGitHubBrowser.app */; 943 | productType = "com.apple.product-type.application"; 944 | }; 945 | 62EF0A741C700C2300D13711 /* SwiftFlowGitHubBrowserTests */ = { 946 | isa = PBXNativeTarget; 947 | buildConfigurationList = 62EF0A7C1C700C2300D13711 /* Build configuration list for PBXNativeTarget "SwiftFlowGitHubBrowserTests" */; 948 | buildPhases = ( 949 | 62EF0A711C700C2300D13711 /* Sources */, 950 | 62EF0A721C700C2300D13711 /* Frameworks */, 951 | 62EF0A731C700C2300D13711 /* Resources */, 952 | ); 953 | buildRules = ( 954 | ); 955 | dependencies = ( 956 | 62EF0A7B1C700C2300D13711 /* PBXTargetDependency */, 957 | ); 958 | name = SwiftFlowGitHubBrowserTests; 959 | productName = SwiftFlowGitHubBrowserTests; 960 | productReference = 62EF0A751C700C2300D13711 /* SwiftFlowGitHubBrowserTests.xctest */; 961 | productType = "com.apple.product-type.bundle.unit-test"; 962 | }; 963 | /* End PBXNativeTarget section */ 964 | 965 | /* Begin PBXProject section */ 966 | 25BB76151C3CB8CB008EA13B /* Project object */ = { 967 | isa = PBXProject; 968 | attributes = { 969 | LastSwiftUpdateCheck = 0720; 970 | LastUpgradeCheck = 1010; 971 | ORGANIZATIONNAME = "Benji Encz"; 972 | TargetAttributes = { 973 | 25BB761C1C3CB8CB008EA13B = { 974 | CreatedOnToolsVersion = 7.2; 975 | LastSwiftMigration = 0800; 976 | }; 977 | 62EF0A741C700C2300D13711 = { 978 | CreatedOnToolsVersion = 7.2; 979 | LastSwiftMigration = 0800; 980 | TestTargetID = 25BB761C1C3CB8CB008EA13B; 981 | }; 982 | }; 983 | }; 984 | buildConfigurationList = 25BB76181C3CB8CB008EA13B /* Build configuration list for PBXProject "SwiftFlowGitHubBrowser" */; 985 | compatibilityVersion = "Xcode 3.2"; 986 | developmentRegion = English; 987 | hasScannedForEncodings = 0; 988 | knownRegions = ( 989 | en, 990 | Base, 991 | ); 992 | mainGroup = 25BB76141C3CB8CB008EA13B; 993 | productRefGroup = 25BB761E1C3CB8CB008EA13B /* Products */; 994 | projectDirPath = ""; 995 | projectReferences = ( 996 | { 997 | ProductGroup = 62C9354D1C8CD6B300887A23 /* Products */; 998 | ProjectRef = 62C9354C1C8CD6B300887A23 /* ListKit.xcodeproj */; 999 | }, 1000 | { 1001 | ProductGroup = 62EF0A9F1C700D1F00D13711 /* Products */; 1002 | ProjectRef = 62EF0A9E1C700D1F00D13711 /* Nimble.xcodeproj */; 1003 | }, 1004 | { 1005 | ProductGroup = 254B3B7E1D3ABBCE00B1E4F0 /* Products */; 1006 | ProjectRef = 254B3B7D1D3ABBCE00B1E4F0 /* OctoKit.xcodeproj */; 1007 | }, 1008 | { 1009 | ProductGroup = 62EF0A811C700D1200D13711 /* Products */; 1010 | ProjectRef = 62EF0A801C700D1200D13711 /* Quick.xcodeproj */; 1011 | }, 1012 | { 1013 | ProductGroup = 254B3B661D3ABBC400B1E4F0 /* Products */; 1014 | ProjectRef = 254B3B651D3ABBC400B1E4F0 /* RequestKit.xcodeproj */; 1015 | }, 1016 | { 1017 | ProductGroup = 66C463F422270A7C0006963A /* Products */; 1018 | ProjectRef = 66C463F322270A7C0006963A /* ReSwift-Thunk.xcodeproj */; 1019 | }, 1020 | { 1021 | ProductGroup = 254B3B4E1D3ABBB400B1E4F0 /* Products */; 1022 | ProjectRef = 254B3B4D1D3ABBB400B1E4F0 /* ReSwift.xcodeproj */; 1023 | }, 1024 | { 1025 | ProductGroup = 254B3B331D3ABBAB00B1E4F0 /* Products */; 1026 | ProjectRef = 254B3B321D3ABBAB00B1E4F0 /* ReSwiftRouter.xcodeproj */; 1027 | }, 1028 | { 1029 | ProductGroup = 253038EA1D92D8B900AA49F4 /* Products */; 1030 | ProjectRef = 253038E91D92D8B900AA49F4 /* SAMKeychain.xcodeproj */; 1031 | }, 1032 | ); 1033 | projectRoot = ""; 1034 | targets = ( 1035 | 25BB761C1C3CB8CB008EA13B /* SwiftFlowGitHubBrowser */, 1036 | 62EF0A741C700C2300D13711 /* SwiftFlowGitHubBrowserTests */, 1037 | ); 1038 | }; 1039 | /* End PBXProject section */ 1040 | 1041 | /* Begin PBXReferenceProxy section */ 1042 | 14423EA71ECB43EF0099AE91 /* SwiftLintIntegration.xctest */ = { 1043 | isa = PBXReferenceProxy; 1044 | fileType = wrapper.cfbundle; 1045 | path = SwiftLintIntegration.xctest; 1046 | remoteRef = 14423EA61ECB43EF0099AE91 /* PBXContainerItemProxy */; 1047 | sourceTree = BUILT_PRODUCTS_DIR; 1048 | }; 1049 | 253038F41D92D8B900AA49F4 /* SAMKeychain.framework */ = { 1050 | isa = PBXReferenceProxy; 1051 | fileType = wrapper.framework; 1052 | path = SAMKeychain.framework; 1053 | remoteRef = 253038F31D92D8B900AA49F4 /* PBXContainerItemProxy */; 1054 | sourceTree = BUILT_PRODUCTS_DIR; 1055 | }; 1056 | 253038F61D92D8B900AA49F4 /* SAMKeychainTests-iOS.xctest */ = { 1057 | isa = PBXReferenceProxy; 1058 | fileType = wrapper.cfbundle; 1059 | path = "SAMKeychainTests-iOS.xctest"; 1060 | remoteRef = 253038F51D92D8B900AA49F4 /* PBXContainerItemProxy */; 1061 | sourceTree = BUILT_PRODUCTS_DIR; 1062 | }; 1063 | 253038F81D92D8B900AA49F4 /* SAMKeychain.framework */ = { 1064 | isa = PBXReferenceProxy; 1065 | fileType = wrapper.framework; 1066 | path = SAMKeychain.framework; 1067 | remoteRef = 253038F71D92D8B900AA49F4 /* PBXContainerItemProxy */; 1068 | sourceTree = BUILT_PRODUCTS_DIR; 1069 | }; 1070 | 253038FA1D92D8B900AA49F4 /* SAMKeychainTests-macOS.xctest */ = { 1071 | isa = PBXReferenceProxy; 1072 | fileType = wrapper.cfbundle; 1073 | path = "SAMKeychainTests-macOS.xctest"; 1074 | remoteRef = 253038F91D92D8B900AA49F4 /* PBXContainerItemProxy */; 1075 | sourceTree = BUILT_PRODUCTS_DIR; 1076 | }; 1077 | 253038FC1D92D8B900AA49F4 /* SAMKeychain.framework */ = { 1078 | isa = PBXReferenceProxy; 1079 | fileType = wrapper.framework; 1080 | path = SAMKeychain.framework; 1081 | remoteRef = 253038FB1D92D8B900AA49F4 /* PBXContainerItemProxy */; 1082 | sourceTree = BUILT_PRODUCTS_DIR; 1083 | }; 1084 | 253038FE1D92D8B900AA49F4 /* SAMKeychainTests.xctest */ = { 1085 | isa = PBXReferenceProxy; 1086 | fileType = wrapper.cfbundle; 1087 | path = SAMKeychainTests.xctest; 1088 | remoteRef = 253038FD1D92D8B900AA49F4 /* PBXContainerItemProxy */; 1089 | sourceTree = BUILT_PRODUCTS_DIR; 1090 | }; 1091 | 253039001D92D8B900AA49F4 /* SAMKeychain.framework */ = { 1092 | isa = PBXReferenceProxy; 1093 | fileType = wrapper.framework; 1094 | path = SAMKeychain.framework; 1095 | remoteRef = 253038FF1D92D8B900AA49F4 /* PBXContainerItemProxy */; 1096 | sourceTree = BUILT_PRODUCTS_DIR; 1097 | }; 1098 | 254B3B401D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */ = { 1099 | isa = PBXReferenceProxy; 1100 | fileType = wrapper.framework; 1101 | path = ReSwiftRouter.framework; 1102 | remoteRef = 254B3B3F1D3ABBAB00B1E4F0 /* PBXContainerItemProxy */; 1103 | sourceTree = BUILT_PRODUCTS_DIR; 1104 | }; 1105 | 254B3B421D3ABBAB00B1E4F0 /* ReSwiftRouterTests-iOS.xctest */ = { 1106 | isa = PBXReferenceProxy; 1107 | fileType = wrapper.cfbundle; 1108 | path = "ReSwiftRouterTests-iOS.xctest"; 1109 | remoteRef = 254B3B411D3ABBAB00B1E4F0 /* PBXContainerItemProxy */; 1110 | sourceTree = BUILT_PRODUCTS_DIR; 1111 | }; 1112 | 254B3B441D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */ = { 1113 | isa = PBXReferenceProxy; 1114 | fileType = wrapper.framework; 1115 | path = ReSwiftRouter.framework; 1116 | remoteRef = 254B3B431D3ABBAB00B1E4F0 /* PBXContainerItemProxy */; 1117 | sourceTree = BUILT_PRODUCTS_DIR; 1118 | }; 1119 | 254B3B461D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */ = { 1120 | isa = PBXReferenceProxy; 1121 | fileType = wrapper.framework; 1122 | path = ReSwiftRouter.framework; 1123 | remoteRef = 254B3B451D3ABBAB00B1E4F0 /* PBXContainerItemProxy */; 1124 | sourceTree = BUILT_PRODUCTS_DIR; 1125 | }; 1126 | 254B3B481D3ABBAB00B1E4F0 /* ReSwiftRouter-tvOSTests.xctest */ = { 1127 | isa = PBXReferenceProxy; 1128 | fileType = wrapper.cfbundle; 1129 | path = "ReSwiftRouter-tvOSTests.xctest"; 1130 | remoteRef = 254B3B471D3ABBAB00B1E4F0 /* PBXContainerItemProxy */; 1131 | sourceTree = BUILT_PRODUCTS_DIR; 1132 | }; 1133 | 254B3B4A1D3ABBAB00B1E4F0 /* ReSwiftRouter.framework */ = { 1134 | isa = PBXReferenceProxy; 1135 | fileType = wrapper.framework; 1136 | path = ReSwiftRouter.framework; 1137 | remoteRef = 254B3B491D3ABBAB00B1E4F0 /* PBXContainerItemProxy */; 1138 | sourceTree = BUILT_PRODUCTS_DIR; 1139 | }; 1140 | 254B3B4C1D3ABBAB00B1E4F0 /* ReSwiftRouter.xctest */ = { 1141 | isa = PBXReferenceProxy; 1142 | fileType = wrapper.cfbundle; 1143 | path = ReSwiftRouter.xctest; 1144 | remoteRef = 254B3B4B1D3ABBAB00B1E4F0 /* PBXContainerItemProxy */; 1145 | sourceTree = BUILT_PRODUCTS_DIR; 1146 | }; 1147 | 254B3B581D3ABBB400B1E4F0 /* ReSwift.framework */ = { 1148 | isa = PBXReferenceProxy; 1149 | fileType = wrapper.framework; 1150 | path = ReSwift.framework; 1151 | remoteRef = 254B3B571D3ABBB400B1E4F0 /* PBXContainerItemProxy */; 1152 | sourceTree = BUILT_PRODUCTS_DIR; 1153 | }; 1154 | 254B3B5A1D3ABBB400B1E4F0 /* ReSwift-iOSTests.xctest */ = { 1155 | isa = PBXReferenceProxy; 1156 | fileType = wrapper.cfbundle; 1157 | path = "ReSwift-iOSTests.xctest"; 1158 | remoteRef = 254B3B591D3ABBB400B1E4F0 /* PBXContainerItemProxy */; 1159 | sourceTree = BUILT_PRODUCTS_DIR; 1160 | }; 1161 | 254B3B5C1D3ABBB400B1E4F0 /* ReSwift.framework */ = { 1162 | isa = PBXReferenceProxy; 1163 | fileType = wrapper.framework; 1164 | path = ReSwift.framework; 1165 | remoteRef = 254B3B5B1D3ABBB400B1E4F0 /* PBXContainerItemProxy */; 1166 | sourceTree = BUILT_PRODUCTS_DIR; 1167 | }; 1168 | 254B3B5E1D3ABBB400B1E4F0 /* ReSwift-macOSTests.xctest */ = { 1169 | isa = PBXReferenceProxy; 1170 | fileType = wrapper.cfbundle; 1171 | path = "ReSwift-macOSTests.xctest"; 1172 | remoteRef = 254B3B5D1D3ABBB400B1E4F0 /* PBXContainerItemProxy */; 1173 | sourceTree = BUILT_PRODUCTS_DIR; 1174 | }; 1175 | 254B3B601D3ABBB400B1E4F0 /* ReSwift.framework */ = { 1176 | isa = PBXReferenceProxy; 1177 | fileType = wrapper.framework; 1178 | path = ReSwift.framework; 1179 | remoteRef = 254B3B5F1D3ABBB400B1E4F0 /* PBXContainerItemProxy */; 1180 | sourceTree = BUILT_PRODUCTS_DIR; 1181 | }; 1182 | 254B3B621D3ABBB400B1E4F0 /* ReSwift-tvOSTests.xctest */ = { 1183 | isa = PBXReferenceProxy; 1184 | fileType = wrapper.cfbundle; 1185 | path = "ReSwift-tvOSTests.xctest"; 1186 | remoteRef = 254B3B611D3ABBB400B1E4F0 /* PBXContainerItemProxy */; 1187 | sourceTree = BUILT_PRODUCTS_DIR; 1188 | }; 1189 | 254B3B641D3ABBB400B1E4F0 /* ReSwift.framework */ = { 1190 | isa = PBXReferenceProxy; 1191 | fileType = wrapper.framework; 1192 | path = ReSwift.framework; 1193 | remoteRef = 254B3B631D3ABBB400B1E4F0 /* PBXContainerItemProxy */; 1194 | sourceTree = BUILT_PRODUCTS_DIR; 1195 | }; 1196 | 254B3B701D3ABBC400B1E4F0 /* RequestKit.framework */ = { 1197 | isa = PBXReferenceProxy; 1198 | fileType = wrapper.framework; 1199 | path = RequestKit.framework; 1200 | remoteRef = 254B3B6F1D3ABBC400B1E4F0 /* PBXContainerItemProxy */; 1201 | sourceTree = BUILT_PRODUCTS_DIR; 1202 | }; 1203 | 254B3B721D3ABBC400B1E4F0 /* RequestKitTests.xctest */ = { 1204 | isa = PBXReferenceProxy; 1205 | fileType = wrapper.cfbundle; 1206 | path = RequestKitTests.xctest; 1207 | remoteRef = 254B3B711D3ABBC400B1E4F0 /* PBXContainerItemProxy */; 1208 | sourceTree = BUILT_PRODUCTS_DIR; 1209 | }; 1210 | 254B3B741D3ABBC400B1E4F0 /* RequestKit.framework */ = { 1211 | isa = PBXReferenceProxy; 1212 | fileType = wrapper.framework; 1213 | path = RequestKit.framework; 1214 | remoteRef = 254B3B731D3ABBC400B1E4F0 /* PBXContainerItemProxy */; 1215 | sourceTree = BUILT_PRODUCTS_DIR; 1216 | }; 1217 | 254B3B761D3ABBC400B1E4F0 /* RequestKit tvTests.xctest */ = { 1218 | isa = PBXReferenceProxy; 1219 | fileType = wrapper.cfbundle; 1220 | path = "RequestKit tvTests.xctest"; 1221 | remoteRef = 254B3B751D3ABBC400B1E4F0 /* PBXContainerItemProxy */; 1222 | sourceTree = BUILT_PRODUCTS_DIR; 1223 | }; 1224 | 254B3B781D3ABBC400B1E4F0 /* RequestKit.framework */ = { 1225 | isa = PBXReferenceProxy; 1226 | fileType = wrapper.framework; 1227 | path = RequestKit.framework; 1228 | remoteRef = 254B3B771D3ABBC400B1E4F0 /* PBXContainerItemProxy */; 1229 | sourceTree = BUILT_PRODUCTS_DIR; 1230 | }; 1231 | 254B3B7A1D3ABBC400B1E4F0 /* RequestKit.framework */ = { 1232 | isa = PBXReferenceProxy; 1233 | fileType = wrapper.framework; 1234 | path = RequestKit.framework; 1235 | remoteRef = 254B3B791D3ABBC400B1E4F0 /* PBXContainerItemProxy */; 1236 | sourceTree = BUILT_PRODUCTS_DIR; 1237 | }; 1238 | 254B3B7C1D3ABBC400B1E4F0 /* RequestKit MacTests.xctest */ = { 1239 | isa = PBXReferenceProxy; 1240 | fileType = wrapper.cfbundle; 1241 | path = "RequestKit MacTests.xctest"; 1242 | remoteRef = 254B3B7B1D3ABBC400B1E4F0 /* PBXContainerItemProxy */; 1243 | sourceTree = BUILT_PRODUCTS_DIR; 1244 | }; 1245 | 254B3B881D3ABBCE00B1E4F0 /* OctoKit.framework */ = { 1246 | isa = PBXReferenceProxy; 1247 | fileType = wrapper.framework; 1248 | path = OctoKit.framework; 1249 | remoteRef = 254B3B871D3ABBCE00B1E4F0 /* PBXContainerItemProxy */; 1250 | sourceTree = BUILT_PRODUCTS_DIR; 1251 | }; 1252 | 254B3B8A1D3ABBCE00B1E4F0 /* OctoKitTests.xctest */ = { 1253 | isa = PBXReferenceProxy; 1254 | fileType = wrapper.cfbundle; 1255 | path = OctoKitTests.xctest; 1256 | remoteRef = 254B3B891D3ABBCE00B1E4F0 /* PBXContainerItemProxy */; 1257 | sourceTree = BUILT_PRODUCTS_DIR; 1258 | }; 1259 | 254B3B8C1D3ABBCE00B1E4F0 /* OctoKit.framework */ = { 1260 | isa = PBXReferenceProxy; 1261 | fileType = wrapper.framework; 1262 | path = OctoKit.framework; 1263 | remoteRef = 254B3B8B1D3ABBCE00B1E4F0 /* PBXContainerItemProxy */; 1264 | sourceTree = BUILT_PRODUCTS_DIR; 1265 | }; 1266 | 254B3B8E1D3ABBCE00B1E4F0 /* OctoKit tvOSTests.xctest */ = { 1267 | isa = PBXReferenceProxy; 1268 | fileType = wrapper.cfbundle; 1269 | path = "OctoKit tvOSTests.xctest"; 1270 | remoteRef = 254B3B8D1D3ABBCE00B1E4F0 /* PBXContainerItemProxy */; 1271 | sourceTree = BUILT_PRODUCTS_DIR; 1272 | }; 1273 | 254B3B901D3ABBCE00B1E4F0 /* OctoKit.framework */ = { 1274 | isa = PBXReferenceProxy; 1275 | fileType = wrapper.framework; 1276 | path = OctoKit.framework; 1277 | remoteRef = 254B3B8F1D3ABBCE00B1E4F0 /* PBXContainerItemProxy */; 1278 | sourceTree = BUILT_PRODUCTS_DIR; 1279 | }; 1280 | 254B3B921D3ABBCE00B1E4F0 /* OctoKit MacTests.xctest */ = { 1281 | isa = PBXReferenceProxy; 1282 | fileType = wrapper.cfbundle; 1283 | path = "OctoKit MacTests.xctest"; 1284 | remoteRef = 254B3B911D3ABBCE00B1E4F0 /* PBXContainerItemProxy */; 1285 | sourceTree = BUILT_PRODUCTS_DIR; 1286 | }; 1287 | 254B3B941D3ABBCE00B1E4F0 /* OctoKit.framework */ = { 1288 | isa = PBXReferenceProxy; 1289 | fileType = wrapper.framework; 1290 | path = OctoKit.framework; 1291 | remoteRef = 254B3B931D3ABBCE00B1E4F0 /* PBXContainerItemProxy */; 1292 | sourceTree = BUILT_PRODUCTS_DIR; 1293 | }; 1294 | 2565B8871DE74F440016371D /* QuickAfterSuite - macOSTests.xctest */ = { 1295 | isa = PBXReferenceProxy; 1296 | fileType = wrapper.cfbundle; 1297 | path = "QuickAfterSuite - macOSTests.xctest"; 1298 | remoteRef = 2565B8861DE74F440016371D /* PBXContainerItemProxy */; 1299 | sourceTree = BUILT_PRODUCTS_DIR; 1300 | }; 1301 | 2565B8891DE74F440016371D /* QuickAfterSuite - iOSTests.xctest */ = { 1302 | isa = PBXReferenceProxy; 1303 | fileType = wrapper.cfbundle; 1304 | path = "QuickAfterSuite - iOSTests.xctest"; 1305 | remoteRef = 2565B8881DE74F440016371D /* PBXContainerItemProxy */; 1306 | sourceTree = BUILT_PRODUCTS_DIR; 1307 | }; 1308 | 2565B88B1DE74F440016371D /* QuickAfterSuite - tvOSTests.xctest */ = { 1309 | isa = PBXReferenceProxy; 1310 | fileType = wrapper.cfbundle; 1311 | path = "QuickAfterSuite - tvOSTests.xctest"; 1312 | remoteRef = 2565B88A1DE74F440016371D /* PBXContainerItemProxy */; 1313 | sourceTree = BUILT_PRODUCTS_DIR; 1314 | }; 1315 | 62C935541C8CD6B400887A23 /* ListKitDemo.app */ = { 1316 | isa = PBXReferenceProxy; 1317 | fileType = wrapper.application; 1318 | path = ListKitDemo.app; 1319 | remoteRef = 62C935531C8CD6B400887A23 /* PBXContainerItemProxy */; 1320 | sourceTree = BUILT_PRODUCTS_DIR; 1321 | }; 1322 | 62C935561C8CD6B400887A23 /* ListKit.xctest */ = { 1323 | isa = PBXReferenceProxy; 1324 | fileType = wrapper.cfbundle; 1325 | path = ListKit.xctest; 1326 | remoteRef = 62C935551C8CD6B400887A23 /* PBXContainerItemProxy */; 1327 | sourceTree = BUILT_PRODUCTS_DIR; 1328 | }; 1329 | 62C935581C8CD6B400887A23 /* ListKit.framework */ = { 1330 | isa = PBXReferenceProxy; 1331 | fileType = wrapper.framework; 1332 | path = ListKit.framework; 1333 | remoteRef = 62C935571C8CD6B400887A23 /* PBXContainerItemProxy */; 1334 | sourceTree = BUILT_PRODUCTS_DIR; 1335 | }; 1336 | 62C9355A1C8CD6B400887A23 /* ListKitTests.xctest */ = { 1337 | isa = PBXReferenceProxy; 1338 | fileType = wrapper.cfbundle; 1339 | path = ListKitTests.xctest; 1340 | remoteRef = 62C935591C8CD6B400887A23 /* PBXContainerItemProxy */; 1341 | sourceTree = BUILT_PRODUCTS_DIR; 1342 | }; 1343 | 62EF0A8D1C700D1200D13711 /* Quick.framework */ = { 1344 | isa = PBXReferenceProxy; 1345 | fileType = wrapper.framework; 1346 | path = Quick.framework; 1347 | remoteRef = 62EF0A8C1C700D1200D13711 /* PBXContainerItemProxy */; 1348 | sourceTree = BUILT_PRODUCTS_DIR; 1349 | }; 1350 | 62EF0A8F1C700D1200D13711 /* Quick - macOSTests.xctest */ = { 1351 | isa = PBXReferenceProxy; 1352 | fileType = wrapper.cfbundle; 1353 | path = "Quick - macOSTests.xctest"; 1354 | remoteRef = 62EF0A8E1C700D1200D13711 /* PBXContainerItemProxy */; 1355 | sourceTree = BUILT_PRODUCTS_DIR; 1356 | }; 1357 | 62EF0A911C700D1200D13711 /* QuickFocused - macOSTests.xctest */ = { 1358 | isa = PBXReferenceProxy; 1359 | fileType = wrapper.cfbundle; 1360 | path = "QuickFocused - macOSTests.xctest"; 1361 | remoteRef = 62EF0A901C700D1200D13711 /* PBXContainerItemProxy */; 1362 | sourceTree = BUILT_PRODUCTS_DIR; 1363 | }; 1364 | 62EF0A931C700D1200D13711 /* Quick.framework */ = { 1365 | isa = PBXReferenceProxy; 1366 | fileType = wrapper.framework; 1367 | path = Quick.framework; 1368 | remoteRef = 62EF0A921C700D1200D13711 /* PBXContainerItemProxy */; 1369 | sourceTree = BUILT_PRODUCTS_DIR; 1370 | }; 1371 | 62EF0A951C700D1200D13711 /* Quick - iOSTests.xctest */ = { 1372 | isa = PBXReferenceProxy; 1373 | fileType = wrapper.cfbundle; 1374 | path = "Quick - iOSTests.xctest"; 1375 | remoteRef = 62EF0A941C700D1200D13711 /* PBXContainerItemProxy */; 1376 | sourceTree = BUILT_PRODUCTS_DIR; 1377 | }; 1378 | 62EF0A971C700D1200D13711 /* QuickFocused - iOSTests.xctest */ = { 1379 | isa = PBXReferenceProxy; 1380 | fileType = wrapper.cfbundle; 1381 | path = "QuickFocused - iOSTests.xctest"; 1382 | remoteRef = 62EF0A961C700D1200D13711 /* PBXContainerItemProxy */; 1383 | sourceTree = BUILT_PRODUCTS_DIR; 1384 | }; 1385 | 62EF0A991C700D1200D13711 /* Quick.framework */ = { 1386 | isa = PBXReferenceProxy; 1387 | fileType = wrapper.framework; 1388 | path = Quick.framework; 1389 | remoteRef = 62EF0A981C700D1200D13711 /* PBXContainerItemProxy */; 1390 | sourceTree = BUILT_PRODUCTS_DIR; 1391 | }; 1392 | 62EF0A9B1C700D1200D13711 /* Quick - tvOSTests.xctest */ = { 1393 | isa = PBXReferenceProxy; 1394 | fileType = wrapper.cfbundle; 1395 | path = "Quick - tvOSTests.xctest"; 1396 | remoteRef = 62EF0A9A1C700D1200D13711 /* PBXContainerItemProxy */; 1397 | sourceTree = BUILT_PRODUCTS_DIR; 1398 | }; 1399 | 62EF0A9D1C700D1200D13711 /* QuickFocused - tvOSTests.xctest */ = { 1400 | isa = PBXReferenceProxy; 1401 | fileType = wrapper.cfbundle; 1402 | path = "QuickFocused - tvOSTests.xctest"; 1403 | remoteRef = 62EF0A9C1C700D1200D13711 /* PBXContainerItemProxy */; 1404 | sourceTree = BUILT_PRODUCTS_DIR; 1405 | }; 1406 | 62EF0AA81C700D1F00D13711 /* Nimble.framework */ = { 1407 | isa = PBXReferenceProxy; 1408 | fileType = wrapper.framework; 1409 | path = Nimble.framework; 1410 | remoteRef = 62EF0AA71C700D1F00D13711 /* PBXContainerItemProxy */; 1411 | sourceTree = BUILT_PRODUCTS_DIR; 1412 | }; 1413 | 62EF0AAA1C700D1F00D13711 /* NimbleTests.xctest */ = { 1414 | isa = PBXReferenceProxy; 1415 | fileType = wrapper.cfbundle; 1416 | path = NimbleTests.xctest; 1417 | remoteRef = 62EF0AA91C700D1F00D13711 /* PBXContainerItemProxy */; 1418 | sourceTree = BUILT_PRODUCTS_DIR; 1419 | }; 1420 | 62EF0AAC1C700D1F00D13711 /* Nimble.framework */ = { 1421 | isa = PBXReferenceProxy; 1422 | fileType = wrapper.framework; 1423 | path = Nimble.framework; 1424 | remoteRef = 62EF0AAB1C700D1F00D13711 /* PBXContainerItemProxy */; 1425 | sourceTree = BUILT_PRODUCTS_DIR; 1426 | }; 1427 | 62EF0AAE1C700D1F00D13711 /* NimbleTests.xctest */ = { 1428 | isa = PBXReferenceProxy; 1429 | fileType = wrapper.cfbundle; 1430 | path = NimbleTests.xctest; 1431 | remoteRef = 62EF0AAD1C700D1F00D13711 /* PBXContainerItemProxy */; 1432 | sourceTree = BUILT_PRODUCTS_DIR; 1433 | }; 1434 | 62EF0AB01C700D1F00D13711 /* Nimble.framework */ = { 1435 | isa = PBXReferenceProxy; 1436 | fileType = wrapper.framework; 1437 | path = Nimble.framework; 1438 | remoteRef = 62EF0AAF1C700D1F00D13711 /* PBXContainerItemProxy */; 1439 | sourceTree = BUILT_PRODUCTS_DIR; 1440 | }; 1441 | 62EF0AB21C700D1F00D13711 /* NimbleTests.xctest */ = { 1442 | isa = PBXReferenceProxy; 1443 | fileType = wrapper.cfbundle; 1444 | path = NimbleTests.xctest; 1445 | remoteRef = 62EF0AB11C700D1F00D13711 /* PBXContainerItemProxy */; 1446 | sourceTree = BUILT_PRODUCTS_DIR; 1447 | }; 1448 | 66C463F922270A7C0006963A /* ReSwiftThunk.framework */ = { 1449 | isa = PBXReferenceProxy; 1450 | fileType = wrapper.framework; 1451 | path = ReSwiftThunk.framework; 1452 | remoteRef = 66C463F822270A7C0006963A /* PBXContainerItemProxy */; 1453 | sourceTree = BUILT_PRODUCTS_DIR; 1454 | }; 1455 | 66C463FB22270A7C0006963A /* ReSwift-Thunk-Tests.xctest */ = { 1456 | isa = PBXReferenceProxy; 1457 | fileType = wrapper.cfbundle; 1458 | path = "ReSwift-Thunk-Tests.xctest"; 1459 | remoteRef = 66C463FA22270A7C0006963A /* PBXContainerItemProxy */; 1460 | sourceTree = BUILT_PRODUCTS_DIR; 1461 | }; 1462 | /* End PBXReferenceProxy section */ 1463 | 1464 | /* Begin PBXResourcesBuildPhase section */ 1465 | 25BB761B1C3CB8CB008EA13B /* Resources */ = { 1466 | isa = PBXResourcesBuildPhase; 1467 | buildActionMask = 2147483647; 1468 | files = ( 1469 | 25BB762B1C3CB8CB008EA13B /* LaunchScreen.storyboard in Resources */, 1470 | 25BB76281C3CB8CB008EA13B /* Assets.xcassets in Resources */, 1471 | 25BB76261C3CB8CB008EA13B /* Main.storyboard in Resources */, 1472 | ); 1473 | runOnlyForDeploymentPostprocessing = 0; 1474 | }; 1475 | 62EF0A731C700C2300D13711 /* Resources */ = { 1476 | isa = PBXResourcesBuildPhase; 1477 | buildActionMask = 2147483647; 1478 | files = ( 1479 | ); 1480 | runOnlyForDeploymentPostprocessing = 0; 1481 | }; 1482 | /* End PBXResourcesBuildPhase section */ 1483 | 1484 | /* Begin PBXShellScriptBuildPhase section */ 1485 | 14423EB51ECB44250099AE91 /* ShellScript */ = { 1486 | isa = PBXShellScriptBuildPhase; 1487 | buildActionMask = 2147483647; 1488 | files = ( 1489 | ); 1490 | inputPaths = ( 1491 | ); 1492 | outputPaths = ( 1493 | ); 1494 | runOnlyForDeploymentPostprocessing = 0; 1495 | shellPath = /bin/sh; 1496 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 1497 | }; 1498 | /* End PBXShellScriptBuildPhase section */ 1499 | 1500 | /* Begin PBXSourcesBuildPhase section */ 1501 | 25BB76191C3CB8CB008EA13B /* Sources */ = { 1502 | isa = PBXSourcesBuildPhase; 1503 | buildActionMask = 2147483647; 1504 | files = ( 1505 | 62EF0A701C700AD500D13711 /* MainViewController.swift in Sources */, 1506 | 25BE03E11C7C07E6005EBB09 /* AuthenticationService.swift in Sources */, 1507 | 2574204D1D4421410002D020 /* BookmarkService.swift in Sources */, 1508 | 257420891D44304C0002D020 /* NavigationController+CompletionBlock.swift in Sources */, 1509 | 62C9357B1C8CDBDE00887A23 /* RepositoryDetailViewController.swift in Sources */, 1510 | 257420E51D443D990002D020 /* GitHubAPIReducers.swift in Sources */, 1511 | 25C00E9C1C3CDAAC0074655A /* AuthenticationState.swift in Sources */, 1512 | 257420E41D443D990002D020 /* BookmarksReducer.swift in Sources */, 1513 | 257420841D442A7C0002D020 /* RepositoryTableViewCell.swift in Sources */, 1514 | 257420E31D443D990002D020 /* AuthenticationReducer.swift in Sources */, 1515 | 2532E8F61C8F8BFB003F5392 /* BookmarkActions.swift in Sources */, 1516 | 62C9354A1C8CD2CC00887A23 /* GitHubRepositoriesActions.swift in Sources */, 1517 | 257420861D442E020002D020 /* BookmarkTableViewCell.swift in Sources */, 1518 | 25C00E991C3CD8E50074655A /* AuthenticationActions.swift in Sources */, 1519 | 25FCB37B1F81D93B008EBFD4 /* Credentials.swift in Sources */, 1520 | 2581843A1C3CC912008E200A /* Routes.swift in Sources */, 1521 | 62C935481C8CD17F00887A23 /* GitHubRepositories.swift in Sources */, 1522 | 25BB76211C3CB8CB008EA13B /* AppDelegate.swift in Sources */, 1523 | 62EF0A6E1C700ABB00D13711 /* LoginViewController.swift in Sources */, 1524 | 251A75DC1C94F92900EBEB32 /* BookmarkViewController.swift in Sources */, 1525 | 257420E21D443D990002D020 /* AppReducer.swift in Sources */, 1526 | 258184351C3CC710008E200A /* State.swift in Sources */, 1527 | ); 1528 | runOnlyForDeploymentPostprocessing = 0; 1529 | }; 1530 | 62EF0A711C700C2300D13711 /* Sources */ = { 1531 | isa = PBXSourcesBuildPhase; 1532 | buildActionMask = 2147483647; 1533 | files = ( 1534 | 62EF0AB61C700D4900D13711 /* GitHubAuthSpec.swift in Sources */, 1535 | ); 1536 | runOnlyForDeploymentPostprocessing = 0; 1537 | }; 1538 | /* End PBXSourcesBuildPhase section */ 1539 | 1540 | /* Begin PBXTargetDependency section */ 1541 | 254B3B981D3ABBFF00B1E4F0 /* PBXTargetDependency */ = { 1542 | isa = PBXTargetDependency; 1543 | name = OctoKit; 1544 | targetProxy = 254B3B971D3ABBFF00B1E4F0 /* PBXContainerItemProxy */; 1545 | }; 1546 | 254B3B9C1D3ABC0400B1E4F0 /* PBXTargetDependency */ = { 1547 | isa = PBXTargetDependency; 1548 | name = RequestKit; 1549 | targetProxy = 254B3B9B1D3ABC0400B1E4F0 /* PBXContainerItemProxy */; 1550 | }; 1551 | 254B3BA01D3ABC0A00B1E4F0 /* PBXTargetDependency */ = { 1552 | isa = PBXTargetDependency; 1553 | name = "ReSwift-iOS"; 1554 | targetProxy = 254B3B9F1D3ABC0A00B1E4F0 /* PBXContainerItemProxy */; 1555 | }; 1556 | 254B3BA41D3ABC1300B1E4F0 /* PBXTargetDependency */ = { 1557 | isa = PBXTargetDependency; 1558 | name = "ReSwiftRouter-iOS"; 1559 | targetProxy = 254B3BA31D3ABC1300B1E4F0 /* PBXContainerItemProxy */; 1560 | }; 1561 | 62EF0A7B1C700C2300D13711 /* PBXTargetDependency */ = { 1562 | isa = PBXTargetDependency; 1563 | target = 25BB761C1C3CB8CB008EA13B /* SwiftFlowGitHubBrowser */; 1564 | targetProxy = 62EF0A7A1C700C2300D13711 /* PBXContainerItemProxy */; 1565 | }; 1566 | 66C463FF22270A970006963A /* PBXTargetDependency */ = { 1567 | isa = PBXTargetDependency; 1568 | name = "ReSwift-Thunk"; 1569 | targetProxy = 66C463FE22270A970006963A /* PBXContainerItemProxy */; 1570 | }; 1571 | 7A7A571B1C91D78C00670D38 /* PBXTargetDependency */ = { 1572 | isa = PBXTargetDependency; 1573 | name = ListKit; 1574 | targetProxy = 7A7A571A1C91D78C00670D38 /* PBXContainerItemProxy */; 1575 | }; 1576 | /* End PBXTargetDependency section */ 1577 | 1578 | /* Begin PBXVariantGroup section */ 1579 | 25BB76241C3CB8CB008EA13B /* Main.storyboard */ = { 1580 | isa = PBXVariantGroup; 1581 | children = ( 1582 | 25BB76251C3CB8CB008EA13B /* Base */, 1583 | ); 1584 | name = Main.storyboard; 1585 | sourceTree = ""; 1586 | }; 1587 | 25BB76291C3CB8CB008EA13B /* LaunchScreen.storyboard */ = { 1588 | isa = PBXVariantGroup; 1589 | children = ( 1590 | 25BB762A1C3CB8CB008EA13B /* Base */, 1591 | ); 1592 | name = LaunchScreen.storyboard; 1593 | sourceTree = ""; 1594 | }; 1595 | /* End PBXVariantGroup section */ 1596 | 1597 | /* Begin XCBuildConfiguration section */ 1598 | 25BB762D1C3CB8CB008EA13B /* Debug */ = { 1599 | isa = XCBuildConfiguration; 1600 | buildSettings = { 1601 | ALWAYS_SEARCH_USER_PATHS = NO; 1602 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1603 | CLANG_CXX_LIBRARY = "libc++"; 1604 | CLANG_ENABLE_MODULES = YES; 1605 | CLANG_ENABLE_OBJC_ARC = YES; 1606 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 1607 | CLANG_WARN_BOOL_CONVERSION = YES; 1608 | CLANG_WARN_COMMA = YES; 1609 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1610 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 1611 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1612 | CLANG_WARN_EMPTY_BODY = YES; 1613 | CLANG_WARN_ENUM_CONVERSION = YES; 1614 | CLANG_WARN_INFINITE_RECURSION = YES; 1615 | CLANG_WARN_INT_CONVERSION = YES; 1616 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 1617 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 1618 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 1619 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1620 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 1621 | CLANG_WARN_STRICT_PROTOTYPES = YES; 1622 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1623 | CLANG_WARN_UNREACHABLE_CODE = YES; 1624 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1625 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 1626 | COPY_PHASE_STRIP = NO; 1627 | DEBUG_INFORMATION_FORMAT = dwarf; 1628 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1629 | ENABLE_TESTABILITY = YES; 1630 | GCC_C_LANGUAGE_STANDARD = gnu99; 1631 | GCC_DYNAMIC_NO_PIC = NO; 1632 | GCC_NO_COMMON_BLOCKS = YES; 1633 | GCC_OPTIMIZATION_LEVEL = 0; 1634 | GCC_PREPROCESSOR_DEFINITIONS = ( 1635 | "DEBUG=1", 1636 | "$(inherited)", 1637 | ); 1638 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1639 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1640 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1641 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1642 | GCC_WARN_UNUSED_FUNCTION = YES; 1643 | GCC_WARN_UNUSED_VARIABLE = YES; 1644 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 1645 | MTL_ENABLE_DEBUG_INFO = YES; 1646 | ONLY_ACTIVE_ARCH = YES; 1647 | SDKROOT = iphoneos; 1648 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 1649 | TARGETED_DEVICE_FAMILY = "1,2"; 1650 | }; 1651 | name = Debug; 1652 | }; 1653 | 25BB762E1C3CB8CB008EA13B /* Release */ = { 1654 | isa = XCBuildConfiguration; 1655 | buildSettings = { 1656 | ALWAYS_SEARCH_USER_PATHS = NO; 1657 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1658 | CLANG_CXX_LIBRARY = "libc++"; 1659 | CLANG_ENABLE_MODULES = YES; 1660 | CLANG_ENABLE_OBJC_ARC = YES; 1661 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 1662 | CLANG_WARN_BOOL_CONVERSION = YES; 1663 | CLANG_WARN_COMMA = YES; 1664 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1665 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 1666 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1667 | CLANG_WARN_EMPTY_BODY = YES; 1668 | CLANG_WARN_ENUM_CONVERSION = YES; 1669 | CLANG_WARN_INFINITE_RECURSION = YES; 1670 | CLANG_WARN_INT_CONVERSION = YES; 1671 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 1672 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 1673 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 1674 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1675 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 1676 | CLANG_WARN_STRICT_PROTOTYPES = YES; 1677 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1678 | CLANG_WARN_UNREACHABLE_CODE = YES; 1679 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1680 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 1681 | COPY_PHASE_STRIP = NO; 1682 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 1683 | ENABLE_NS_ASSERTIONS = NO; 1684 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1685 | GCC_C_LANGUAGE_STANDARD = gnu99; 1686 | GCC_NO_COMMON_BLOCKS = YES; 1687 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1688 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1689 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1690 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1691 | GCC_WARN_UNUSED_FUNCTION = YES; 1692 | GCC_WARN_UNUSED_VARIABLE = YES; 1693 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 1694 | MTL_ENABLE_DEBUG_INFO = NO; 1695 | SDKROOT = iphoneos; 1696 | SWIFT_COMPILATION_MODE = wholemodule; 1697 | TARGETED_DEVICE_FAMILY = "1,2"; 1698 | VALIDATE_PRODUCT = YES; 1699 | }; 1700 | name = Release; 1701 | }; 1702 | 25BB76301C3CB8CB008EA13B /* Debug */ = { 1703 | isa = XCBuildConfiguration; 1704 | buildSettings = { 1705 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1706 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 1707 | FRAMEWORK_SEARCH_PATHS = ( 1708 | "$(inherited)/**", 1709 | "$(SRCROOT)/Carthage/Build/iOS/**", 1710 | ); 1711 | INFOPLIST_FILE = SwiftFlowGitHubBrowser/Info.plist; 1712 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 1713 | PRODUCT_BUNDLE_IDENTIFIER = "Swift-Flow.SwiftFlowGitHubBrowser"; 1714 | PRODUCT_NAME = "$(TARGET_NAME)"; 1715 | SWIFT_VERSION = 4.2; 1716 | }; 1717 | name = Debug; 1718 | }; 1719 | 25BB76311C3CB8CB008EA13B /* Release */ = { 1720 | isa = XCBuildConfiguration; 1721 | buildSettings = { 1722 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1723 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 1724 | FRAMEWORK_SEARCH_PATHS = ( 1725 | "$(inherited)/**", 1726 | "$(SRCROOT)/Carthage/Build/iOS/**", 1727 | ); 1728 | INFOPLIST_FILE = SwiftFlowGitHubBrowser/Info.plist; 1729 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 1730 | PRODUCT_BUNDLE_IDENTIFIER = "Swift-Flow.SwiftFlowGitHubBrowser"; 1731 | PRODUCT_NAME = "$(TARGET_NAME)"; 1732 | SWIFT_VERSION = 4.2; 1733 | }; 1734 | name = Release; 1735 | }; 1736 | 62EF0A7D1C700C2300D13711 /* Debug */ = { 1737 | isa = XCBuildConfiguration; 1738 | buildSettings = { 1739 | BUNDLE_LOADER = "$(TEST_HOST)"; 1740 | CLANG_ENABLE_MODULES = YES; 1741 | FRAMEWORK_SEARCH_PATHS = ( 1742 | "$(inherited)", 1743 | "$(PROJECT_DIR)/ReSwiftDev/ReSwiftRouter/Carthage/Checkouts/Nimble/build/Debug-iphoneos", 1744 | ); 1745 | INFOPLIST_FILE = SwiftFlowGitHubBrowserTests/Info.plist; 1746 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1747 | PRODUCT_BUNDLE_IDENTIFIER = "de.benjamin-encz.SwiftFlowGitHubBrowserTests"; 1748 | PRODUCT_NAME = "$(TARGET_NAME)"; 1749 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 1750 | SWIFT_VERSION = 4.2; 1751 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftFlowGitHubBrowser.app/SwiftFlowGitHubBrowser"; 1752 | }; 1753 | name = Debug; 1754 | }; 1755 | 62EF0A7E1C700C2300D13711 /* Release */ = { 1756 | isa = XCBuildConfiguration; 1757 | buildSettings = { 1758 | BUNDLE_LOADER = "$(TEST_HOST)"; 1759 | CLANG_ENABLE_MODULES = YES; 1760 | FRAMEWORK_SEARCH_PATHS = ( 1761 | "$(inherited)", 1762 | "$(PROJECT_DIR)/ReSwiftDev/ReSwiftRouter/Carthage/Checkouts/Nimble/build/Debug-iphoneos", 1763 | ); 1764 | INFOPLIST_FILE = SwiftFlowGitHubBrowserTests/Info.plist; 1765 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1766 | PRODUCT_BUNDLE_IDENTIFIER = "de.benjamin-encz.SwiftFlowGitHubBrowserTests"; 1767 | PRODUCT_NAME = "$(TARGET_NAME)"; 1768 | SWIFT_VERSION = 4.2; 1769 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftFlowGitHubBrowser.app/SwiftFlowGitHubBrowser"; 1770 | }; 1771 | name = Release; 1772 | }; 1773 | /* End XCBuildConfiguration section */ 1774 | 1775 | /* Begin XCConfigurationList section */ 1776 | 25BB76181C3CB8CB008EA13B /* Build configuration list for PBXProject "SwiftFlowGitHubBrowser" */ = { 1777 | isa = XCConfigurationList; 1778 | buildConfigurations = ( 1779 | 25BB762D1C3CB8CB008EA13B /* Debug */, 1780 | 25BB762E1C3CB8CB008EA13B /* Release */, 1781 | ); 1782 | defaultConfigurationIsVisible = 0; 1783 | defaultConfigurationName = Release; 1784 | }; 1785 | 25BB762F1C3CB8CB008EA13B /* Build configuration list for PBXNativeTarget "SwiftFlowGitHubBrowser" */ = { 1786 | isa = XCConfigurationList; 1787 | buildConfigurations = ( 1788 | 25BB76301C3CB8CB008EA13B /* Debug */, 1789 | 25BB76311C3CB8CB008EA13B /* Release */, 1790 | ); 1791 | defaultConfigurationIsVisible = 0; 1792 | defaultConfigurationName = Release; 1793 | }; 1794 | 62EF0A7C1C700C2300D13711 /* Build configuration list for PBXNativeTarget "SwiftFlowGitHubBrowserTests" */ = { 1795 | isa = XCConfigurationList; 1796 | buildConfigurations = ( 1797 | 62EF0A7D1C700C2300D13711 /* Debug */, 1798 | 62EF0A7E1C700C2300D13711 /* Release */, 1799 | ); 1800 | defaultConfigurationIsVisible = 0; 1801 | defaultConfigurationName = Release; 1802 | }; 1803 | /* End XCConfigurationList section */ 1804 | }; 1805 | rootObject = 25BB76151C3CB8CB008EA13B /* Project object */; 1806 | } 1807 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser.xcodeproj/xcshareddata/xcschemes/SwiftFlowGitHubBrowser.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 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/APIRequests/GitHubRepositories.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubRepositories.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benjamin Encz on 3/6/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import OctoKit 11 | import ReSwift 12 | import ReSwiftThunk 13 | 14 | let fetchGitHubRepositories = Thunk { dispatch, getState in 15 | guard case let .loggedIn(configuration)? = getState()?.authenticationState.loggedInState else { return } 16 | 17 | _ = Octokit(configuration).repositories { response in 18 | DispatchQueue.main.async { 19 | dispatch(SetRepositories(repositories: response)) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Actions/AuthenticationActions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthenticationActions.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import ReSwift 10 | import ReSwiftRouter 11 | import ReSwiftThunk 12 | import OctoKit 13 | 14 | let authenticateUser = Thunk { dispatch, getStat in 15 | guard let config = getStat()?.authenticationState.oAuthConfig else { return } 16 | 17 | let url = config.authenticate() 18 | 19 | if let url = url { 20 | dispatch(SetOAuthURL(oAuthUrl: url)) 21 | dispatch(SetRouteAction([loginRoute, oAuthRoute])) 22 | } 23 | } 24 | 25 | func handleOpenURL(url: URL) -> Thunk { 26 | return Thunk { dispatch, getState in 27 | guard let config = getState()?.authenticationState.oAuthConfig else { return } 28 | 29 | config.handleOpenURL(openUrl: url) { (config: TokenConfiguration) in 30 | DispatchQueue.main.async { 31 | AuthenticationService().saveAuthenticationData(config) 32 | 33 | dispatch(UpdateLoggedInState(loggedInState: .loggedIn(config))) 34 | // Switch to the Main View Route 35 | dispatch(ReSwiftRouter.SetRouteAction([mainViewRoute])) 36 | } 37 | } 38 | } 39 | } 40 | 41 | struct SetOAuthURL: Action { 42 | let oAuthUrl: URL 43 | } 44 | 45 | struct UpdateLoggedInState: Action { 46 | let loggedInState: LoggedInState 47 | } 48 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Actions/BookmarkActions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BookmarkActions.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 3/8/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ReSwift 11 | import ReSwiftRouter 12 | 13 | struct CreateBookmark: Action { 14 | let route: [RouteElementIdentifier] 15 | let routeSpecificData: Any? 16 | } 17 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Actions/GitHubRepositoriesActions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubRepositoriesActions.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benjamin Encz on 3/6/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ReSwift 11 | import OctoKit 12 | import RequestKit 13 | 14 | struct SetRepositories: Action { 15 | let repositories: Response<[Repository]> 16 | } 17 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import OctoKit 11 | import ReSwift 12 | import ReSwiftRouter 13 | import ReSwiftThunk 14 | 15 | let thunkMiddleware: Middleware = createThunksMiddleware() 16 | var store = Store(reducer: appReducer, state: nil, middleware: [thunkMiddleware]) 17 | 18 | @UIApplicationMain 19 | class AppDelegate: UIResponder, UIApplicationDelegate { 20 | 21 | var window: UIWindow? 22 | var router: Router! 23 | 24 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 25 | 26 | window = UIWindow(frame: UIScreen.main.bounds) 27 | 28 | /* 29 | Set a dummy VC to satisfy UIKit 30 | Router will set correct VC throug async call which means 31 | window would not have rootVC at completion of this method 32 | which causes a crash. 33 | */ 34 | window?.rootViewController = UIViewController() 35 | 36 | let rootRoutable = RootRoutable(window: window!) 37 | 38 | router = Router(store: store, rootRoutable: rootRoutable) { state in 39 | state.select { $0.navigationState } 40 | } 41 | 42 | if case .loggedIn(_) = store.state.authenticationState.loggedInState { 43 | store.dispatch(ReSwiftRouter.SetRouteAction([mainViewRoute])) 44 | } else { 45 | store.dispatch(ReSwiftRouter.SetRouteAction([loginRoute])) 46 | } 47 | 48 | window?.makeKeyAndVisible() 49 | 50 | return true 51 | } 52 | 53 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool { 54 | store.dispatch(handleOpenURL(url: url)) 55 | 56 | return false 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/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 | 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 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/GitHubAuth/Credentials.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Credentials.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // Insert GitHub Token and Secret Here 12 | let gitHubClientId = "" 13 | let gitHubClientSecret = "" 14 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleURLName 25 | com.swiftflow.githubbrowser 26 | CFBundleURLSchemes 27 | 28 | swiftflowgithub 29 | 30 | 31 | 32 | CFBundleVersion 33 | 1 34 | LSRequiresIPhoneOS 35 | 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UISupportedInterfaceOrientations 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | UISupportedInterfaceOrientations~ipad 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationPortraitUpsideDown 52 | UIInterfaceOrientationLandscapeLeft 53 | UIInterfaceOrientationLandscapeRight 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Reducers/AppReducer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppReducer.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import ReSwift 10 | import ReSwiftRouter 11 | 12 | func appReducer(action: Action, state: State?) -> State { 13 | return State( 14 | navigationState: NavigationReducer.handleAction(action, state: state?.navigationState), 15 | authenticationState: authenticationReducer(state: state?.authenticationState, action: action), 16 | repositories: repositoriesReducer(state: state?.repositories, action: action), 17 | bookmarks: bookmarksReducer(state: state?.bookmarks, action: action) 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Reducers/AuthenticationReducer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthenticationReducer.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ReSwift 11 | import OctoKit 12 | 13 | func authenticationReducer(state: AuthenticationState?, action: Action) -> AuthenticationState { 14 | var state = state ?? initialAuthenticationState() 15 | 16 | switch action { 17 | case _ as ReSwiftInit: 18 | break 19 | case let action as SetOAuthURL: 20 | state.oAuthURL = action.oAuthUrl 21 | case let action as UpdateLoggedInState: 22 | state.loggedInState = action.loggedInState 23 | default: 24 | break 25 | } 26 | 27 | return state 28 | } 29 | 30 | func initialAuthenticationState() -> AuthenticationState { 31 | let config = OAuthConfiguration( 32 | token: gitHubClientId, 33 | secret: gitHubClientSecret, 34 | scopes: ["repo", "read:org"] 35 | ) 36 | 37 | if let authData = AuthenticationService().authenticationData() { 38 | return AuthenticationState( 39 | oAuthConfig: config, 40 | oAuthURL: nil, 41 | loggedInState: .loggedIn(authData) 42 | ) 43 | } else { 44 | return AuthenticationState( 45 | oAuthConfig: config, 46 | oAuthURL: nil, 47 | loggedInState: .notLoggedIn 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Reducers/BookmarksReducer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BookmarksReducer.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 3/8/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ReSwift 11 | import ReSwiftRouter 12 | 13 | func bookmarksReducer(state: [Bookmark]?, action: Action) -> [Bookmark] { 14 | var state = state ?? [] 15 | 16 | switch action { 17 | case let action as CreateBookmark: 18 | let bookmark = (route: action.route, routeSpecificData: action.routeSpecificData) 19 | state.append(bookmark) 20 | return state 21 | default: 22 | return state 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Reducers/GitHubAPIReducers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubAPIReducers.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import ReSwift 10 | import OctoKit 11 | import RequestKit 12 | 13 | func repositoriesReducer(state: Response<[Repository]>?, action: Action) -> Response<[Repository]>? { 14 | switch action { 15 | case let action as SetRepositories: 16 | return action.repositories 17 | default: 18 | return nil 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Routes/Routes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Routes.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import ReSwiftRouter 10 | import SafariServices 11 | 12 | let loginRoute: RouteElementIdentifier = "Login" 13 | let oAuthRoute: RouteElementIdentifier = "OAuth" 14 | let mainViewRoute: RouteElementIdentifier = "Main" 15 | let bookmarkRoute: RouteElementIdentifier = "BookMark" 16 | let repositoryDetailRoute: RouteElementIdentifier = "RepositoryDetail" 17 | 18 | let storyboard = UIStoryboard(name: "Main", bundle: nil) 19 | 20 | let loginViewControllerIdentifier = "LoginViewController" 21 | let mainViewControllerIdentifier = "MainViewController" 22 | let repositoryDetailControllerIdentifier = "RepositoryDetailViewController" 23 | let bookmarkControllerIdentifier = "BookmarkViewController" 24 | 25 | class RootRoutable: Routable { 26 | 27 | let window: UIWindow 28 | 29 | init(window: UIWindow) { 30 | self.window = window 31 | } 32 | 33 | func setToLoginViewController() -> Routable { 34 | self.window.rootViewController = storyboard.instantiateViewController(withIdentifier: loginViewControllerIdentifier) 35 | 36 | return LoginViewRoutable(self.window.rootViewController!) 37 | } 38 | 39 | func setToMainViewController() -> Routable { 40 | self.window.rootViewController = storyboard.instantiateViewController(withIdentifier: mainViewControllerIdentifier) 41 | 42 | return MainViewRoutable(self.window.rootViewController!) 43 | } 44 | 45 | func changeRouteSegment( 46 | _ from: RouteElementIdentifier, 47 | to: RouteElementIdentifier, 48 | animated: Bool, 49 | completionHandler: @escaping RoutingCompletionHandler) -> Routable 50 | { 51 | 52 | if to == loginRoute { 53 | completionHandler() 54 | return self.setToLoginViewController() 55 | } else if to == mainViewRoute { 56 | completionHandler() 57 | return self.setToMainViewController() 58 | } else { 59 | fatalError("Route not supported!") 60 | } 61 | } 62 | 63 | func pushRouteSegment( 64 | _ routeElementIdentifier: RouteElementIdentifier, 65 | animated: Bool, 66 | completionHandler: @escaping RoutingCompletionHandler) -> Routable 67 | { 68 | 69 | if routeElementIdentifier == loginRoute { 70 | completionHandler() 71 | return self.setToLoginViewController() 72 | } else if routeElementIdentifier == mainViewRoute { 73 | completionHandler() 74 | return self.setToMainViewController() 75 | } else { 76 | fatalError("Route not supported!") 77 | } 78 | } 79 | 80 | func popRouteSegment( 81 | _ routeElementIdentifier: RouteElementIdentifier, 82 | animated: Bool, 83 | completionHandler: @escaping RoutingCompletionHandler) 84 | { 85 | // TODO: this should technically never be called -> bug in router 86 | completionHandler() 87 | } 88 | 89 | } 90 | 91 | class LoginViewRoutable: Routable { 92 | 93 | let viewController: UIViewController 94 | 95 | init(_ viewController: UIViewController) { 96 | self.viewController = viewController 97 | } 98 | 99 | func pushRouteSegment( 100 | _ routeElementIdentifier: RouteElementIdentifier, 101 | animated: Bool, 102 | completionHandler: @escaping RoutingCompletionHandler) -> Routable 103 | { 104 | if routeElementIdentifier == oAuthRoute { 105 | if let url = store.state.authenticationState.oAuthURL { 106 | let safariViewController = SFSafariViewController(url: url) 107 | self.viewController.present(safariViewController, animated: true, completion: completionHandler) 108 | 109 | return OAuthRoutable() 110 | } 111 | } 112 | 113 | fatalError("Router could not proceed.") 114 | } 115 | 116 | func popRouteSegment( 117 | _ routeElementIdentifier: RouteElementIdentifier, 118 | animated: Bool, 119 | completionHandler: @escaping RoutingCompletionHandler) 120 | { 121 | if routeElementIdentifier == oAuthRoute { 122 | self.viewController.dismiss(animated: true, completion: completionHandler) 123 | } 124 | } 125 | 126 | } 127 | 128 | class MainViewRoutable: Routable { 129 | 130 | let viewController: UIViewController 131 | 132 | init(_ viewController: UIViewController) { 133 | self.viewController = viewController 134 | } 135 | 136 | func pushRouteSegment( 137 | _ routeElementIdentifier: RouteElementIdentifier, 138 | animated: Bool, 139 | completionHandler: @escaping RoutingCompletionHandler) -> Routable { 140 | if routeElementIdentifier == repositoryDetailRoute { 141 | let detailViewController = storyboard.instantiateViewController(withIdentifier: repositoryDetailControllerIdentifier) 142 | (self.viewController as! UINavigationController).pushViewController( 143 | detailViewController, 144 | animated: true, 145 | completion: completionHandler 146 | ) 147 | 148 | return RepositoryDetailRoutable() 149 | 150 | } else if routeElementIdentifier == bookmarkRoute { 151 | let bookmarkViewController = storyboard.instantiateViewController(withIdentifier: bookmarkControllerIdentifier) 152 | (self.viewController as! UINavigationController).pushViewController( 153 | bookmarkViewController, 154 | animated: true, 155 | completion: completionHandler 156 | ) 157 | 158 | return BookmarkRoutable() 159 | } 160 | 161 | fatalError("Cannot handle this route change!") 162 | } 163 | 164 | func changeRouteSegment( 165 | _ from: RouteElementIdentifier, 166 | to: RouteElementIdentifier, 167 | animated: Bool, 168 | completionHandler: @escaping RoutingCompletionHandler) -> Routable 169 | { 170 | 171 | if from == bookmarkRoute && to == repositoryDetailRoute { 172 | (self.viewController as! UINavigationController).popViewController(true) { 173 | let repositoryDetailViewController = storyboard.instantiateViewController(withIdentifier: repositoryDetailControllerIdentifier) 174 | (self.viewController as! UINavigationController).pushViewController( 175 | repositoryDetailViewController, 176 | animated: true, 177 | completion: completionHandler 178 | ) 179 | } 180 | 181 | return BookmarkRoutable() 182 | } 183 | 184 | // We can run into the following fatal error when back button on repository detail & 185 | // bookmark button on the main view controller are pressed very quickly subsequently. 186 | // This happens because the manual route update after the back button tap on the repository 187 | // detail view hasn't happened yet. 188 | // We could work around this with more hacks, but it wouldn't be useful to this example code. 189 | // A discussion/brainstorm for better ways of intercepting back button is going on here: 190 | // https://github.com/ReSwift/ReSwift-Router/issues/17 191 | fatalError("Cannot handle this route change!") 192 | } 193 | 194 | func popRouteSegment( 195 | _ routeElementIdentifier: RouteElementIdentifier, 196 | animated: Bool, 197 | completionHandler: @escaping RoutingCompletionHandler) { 198 | // no-op, since this is called when VC is already popped. 199 | completionHandler() 200 | } 201 | } 202 | 203 | class RepositoryDetailRoutable: Routable {} 204 | class BookmarkRoutable: Routable {} 205 | class OAuthRoutable: Routable {} 206 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Services/AuthenticationService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthenticationService.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 2/22/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SAMKeychain 11 | import OctoKit 12 | 13 | class AuthenticationService { 14 | 15 | func authenticationData() -> TokenConfiguration? { 16 | if let data = SAMKeychain.passwordData(forService: "GitHubAuth", account: "TokenConfiguration") { 17 | return TokenConfiguration(data: data) 18 | } else { 19 | return nil 20 | } 21 | } 22 | 23 | func saveAuthenticationData(_ token: TokenConfiguration) { 24 | let data = token.toData() 25 | SAMKeychain.setPasswordData(data, forService: "GitHubAuth", account: "TokenConfiguration") 26 | } 27 | 28 | } 29 | 30 | // TODO: Cleanup 31 | extension TokenConfiguration { 32 | 33 | init(data: Data) { 34 | let json: [String: AnyObject] = try! JSONSerialization.jsonObject(with: data, options: []) as! [String : AnyObject] 35 | let url = json["endpoint"] as! String 36 | let accessToken: String? = { 37 | if case let s = json["accesstoken"] as? String, s != "null" { 38 | return s 39 | } else { 40 | return nil 41 | } 42 | }() 43 | 44 | self.init(accessToken, url: url) 45 | } 46 | 47 | func toData() -> Data { 48 | let json: NSDictionary = [ 49 | "endpoint": self.apiEndpoint, 50 | "accesstoken": self.accessToken ?? "null" 51 | ] 52 | 53 | return try! JSONSerialization.data(withJSONObject: json, options: JSONSerialization.WritingOptions(rawValue: 0)) 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Services/BookmarkService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BookmarkService.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 7/23/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import OctoKit 11 | import ReSwiftRouter 12 | 13 | class BookmarkService { 14 | 15 | static func isRepositoryBookmarked(state: State, currentRepository: Repository) -> Bool { 16 | let bookmarkActive = !state.bookmarks.contains { route, data in 17 | guard let repository = data as? Repository else { return false } 18 | 19 | return RouteHash(route: route) == RouteHash(route: [mainViewRoute, repositoryDetailRoute]) 20 | && repository.name == currentRepository.name 21 | } 22 | 23 | return bookmarkActive 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/State/AuthenticationState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthenticationState.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import OctoKit 10 | import RequestKit 11 | 12 | struct AuthenticationState { 13 | var oAuthConfig: OAuthConfigurationType? 14 | var oAuthURL: URL? 15 | var loggedInState: LoggedInState 16 | } 17 | 18 | enum LoggedInState { 19 | case notLoggedIn 20 | case loggedIn(TokenConfiguration) 21 | } 22 | 23 | protocol OAuthConfigurationType { 24 | 25 | func authenticate() -> URL? 26 | // We're using first argument name in order to disambiguate between this protocol method and 27 | // the implementation on `OAuthConfiguration`. 28 | func handleOpenURL(openUrl: URL, completion: @escaping (TokenConfiguration) -> Void) 29 | } 30 | 31 | extension OAuthConfiguration: OAuthConfigurationType { 32 | // Since `handleOpenURL` on `OAuthConfiguration` uses a default argument, we need to trampoline 33 | // through this protocol implementation before calling it. 34 | internal func handleOpenURL(openUrl: URL, completion: @escaping (TokenConfiguration) -> Void) { 35 | self.handleOpenURL(url: openUrl, completion: completion) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/State/State.swift: -------------------------------------------------------------------------------- 1 | // 2 | // State.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import ReSwift 10 | import ReSwiftRouter 11 | import OctoKit 12 | import RequestKit 13 | 14 | struct State: StateType, HasNavigationState { 15 | var navigationState: NavigationState 16 | var authenticationState: AuthenticationState 17 | var repositories: Response<[Repository]>? 18 | var bookmarks: [Bookmark] 19 | } 20 | 21 | typealias Bookmark = (route: [RouteElementIdentifier], routeSpecificData: Any?) -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/UIKitExtensions/NavigationController+CompletionBlock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController+CompletionBlock.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 7/23/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // Extension that provides completion blocks for push/pop on navigation controllers. 12 | // Thanks to: http://stackoverflow.com/questions/9906966/completion-handler-for-uinavigationcontroller-pushviewcontrolleranimated 13 | extension UINavigationController { 14 | 15 | func pushViewController(_ viewController: UIViewController, 16 | animated: Bool, completion: @escaping () -> Void) { 17 | 18 | CATransaction.begin() 19 | CATransaction.setCompletionBlock(completion) 20 | pushViewController(viewController, animated: animated) 21 | CATransaction.commit() 22 | } 23 | 24 | func popViewController(_ animated: Bool, completion: @escaping () -> Void) { 25 | 26 | CATransaction.begin() 27 | CATransaction.setCompletionBlock(completion) 28 | self.popViewController(animated: animated) 29 | CATransaction.commit() 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/ViewControllers/BookmarkViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BookmarkViewController.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 3/12/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | import ReSwiftRouter 12 | import OctoKit 13 | import ListKit 14 | 15 | class BookmarkViewController: UIViewController, StoreSubscriber { 16 | 17 | @IBOutlet var tableView: UITableView! 18 | 19 | var dataSource: ArrayDataSource? 20 | 21 | override func viewWillAppear(_ animated: Bool) { 22 | super.viewWillAppear(animated) 23 | 24 | self.dataSource = ArrayDataSource(array: [], cellType: BookmarkTableViewCell.self) 25 | tableView.dataSource = dataSource 26 | tableView.delegate = self 27 | 28 | // Subscribe after other setup is complete 29 | store.subscribe(self) { state in 30 | state.select { $0.bookmarks } 31 | } 32 | } 33 | 34 | override func viewWillDisappear(_ animated: Bool) { 35 | super.viewWillDisappear(animated) 36 | 37 | store.unsubscribe(self) 38 | 39 | // Required to update the route, when this VC was dismissed through back button from 40 | // NavigationController, since we can't intercept the back button 41 | if store.state.navigationState.route == [mainViewRoute, bookmarkRoute] { 42 | store.dispatch(SetRouteAction([mainViewRoute])) 43 | } 44 | } 45 | 46 | func newState(state: [Bookmark]) { 47 | dataSource?.array = state 48 | tableView.reloadData() 49 | } 50 | 51 | } 52 | 53 | //MARK: UITableViewDelegate 54 | 55 | extension BookmarkViewController: UITableViewDelegate { 56 | 57 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 58 | let selectedBookmark = self.dataSource!.array[indexPath.row] 59 | let routeAction = ReSwiftRouter.SetRouteAction(selectedBookmark.route) 60 | let setDataAction = ReSwiftRouter.SetRouteSpecificData(route: 61 | selectedBookmark.route, 62 | data: selectedBookmark.routeSpecificData as Any) 63 | 64 | store.dispatch(setDataAction) 65 | store.dispatch(routeAction) 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/ViewControllers/LoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 1/5/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import OctoKit 11 | 12 | class LoginViewController: UIViewController { 13 | 14 | @IBAction func authenticateWithGitHub() { 15 | store.dispatch(authenticateUser) 16 | } 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/ViewControllers/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benjamin Encz on 2/13/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | import ReSwiftRouter 12 | import OctoKit 13 | import RequestKit 14 | import ListKit 15 | 16 | class MainViewController: UIViewController, StoreSubscriber { 17 | 18 | @IBOutlet var tableView: UITableView! 19 | 20 | var dataSource: ArrayDataSource? 21 | 22 | // MARK: View Lifecycle 23 | 24 | override func viewWillAppear(_ animated: Bool) { 25 | super.viewWillAppear(animated) 26 | 27 | store.subscribe(self) { state in 28 | state.select { $0.repositories } 29 | } 30 | 31 | // Kick off request to update list of repositories 32 | store.dispatch(fetchGitHubRepositories) 33 | 34 | if self.dataSource == nil { 35 | // If we have no intial data, let's configure it. 36 | self.dataSource = ArrayDataSource(array: [], cellType: RepositoryTableViewCell.self) 37 | } 38 | 39 | tableView.dataSource = dataSource 40 | tableView.delegate = self 41 | } 42 | 43 | override func viewWillDisappear(_ animated: Bool) { 44 | super.viewWillDisappear(animated) 45 | 46 | store.unsubscribe(self) 47 | } 48 | 49 | // MARK: State Updates 50 | 51 | func newState(state: Response<[Repository]>?) { 52 | guard let state = state else { return } 53 | 54 | if case let .success(repositories) = state { 55 | dataSource?.array = repositories 56 | tableView.reloadData() 57 | } 58 | } 59 | 60 | // MARK: Interaction 61 | 62 | @IBAction func bookmarkButtonTapped(_ sender: AnyObject) { 63 | let newRoute = [mainViewRoute, bookmarkRoute] 64 | store.dispatch(ReSwiftRouter.SetRouteAction(newRoute)) 65 | } 66 | } 67 | 68 | //MARK: UITableViewDelegate 69 | 70 | extension MainViewController: UITableViewDelegate { 71 | 72 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 73 | let selectedRepository = self.dataSource?.array[indexPath.row] 74 | let newRoute = [mainViewRoute, repositoryDetailRoute] 75 | 76 | let routeAction = ReSwiftRouter.SetRouteAction(newRoute) 77 | let setDataAction = ReSwiftRouter.SetRouteSpecificData(route: newRoute, data: selectedRepository!) 78 | store.dispatch(setDataAction) 79 | store.dispatch(routeAction) 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/ViewControllers/RepositoryDetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepositoryDetailViewController.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benjamin Encz on 3/6/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import OctoKit 11 | import ReSwift 12 | import ReSwiftRouter 13 | 14 | class RepositoryDetailViewController: UIViewController, StoreSubscriber { 15 | 16 | @IBOutlet var webView: UIWebView! 17 | @IBOutlet var bookmarkButton: UIBarButtonItem! 18 | 19 | var repository: Repository? 20 | 21 | // MARK: View Lifecycle 22 | 23 | override func viewWillAppear(_ animated: Bool) { 24 | super.viewWillAppear(animated) 25 | 26 | store.subscribe(self) { state in 27 | state.select { currentState in 28 | let currentRepository: Repository? = currentState.navigationState.getRouteSpecificState( 29 | currentState.navigationState.route 30 | ) 31 | 32 | let isCurrentRepositoryBookmarked = currentRepository.map { 33 | BookmarkService.isRepositoryBookmarked(state: currentState, currentRepository: $0) 34 | } ?? false 35 | 36 | return ( 37 | currentRepository, 38 | isCurrentRepositoryBookmarked 39 | ) 40 | } 41 | } 42 | } 43 | 44 | override func viewWillDisappear(_ animated: Bool) { 45 | super.viewWillDisappear(animated) 46 | 47 | store.unsubscribe(self) 48 | } 49 | 50 | override func didMove(toParent parent: UIViewController?) { 51 | if parent == nil { 52 | // Required to update the route, when this VC was dismissed through back button from 53 | // NavigationController, since we can't intercept the back button 54 | if store.state.navigationState.route == [mainViewRoute, repositoryDetailRoute] { 55 | store.dispatch(SetRouteAction([mainViewRoute])) 56 | } 57 | } 58 | } 59 | 60 | // MARK: State Updates 61 | 62 | func newState(state: (selectedRepository: Repository?, isBookmarked: Bool)) { 63 | // Only perform repository related updates if the repository actually changed 64 | if self.repository?.gitURL != state.selectedRepository?.gitURL { 65 | self.repository = state.selectedRepository 66 | self.title = state.selectedRepository?.name ?? "" 67 | 68 | if let url = state.selectedRepository?.htmlURL.flatMap({URL.init(string: $0)}) { 69 | let request = URLRequest(url: url) 70 | self.webView.loadRequest(request) 71 | } 72 | } 73 | 74 | self.bookmarkButton.isEnabled = state.isBookmarked 75 | } 76 | 77 | // MARK: Interaction 78 | 79 | @IBAction func bookmarkButtonTapped(_ sender: AnyObject) { 80 | store.dispatch( 81 | CreateBookmark( 82 | route: [mainViewRoute, repositoryDetailRoute], 83 | routeSpecificData: self.repository 84 | ) 85 | ) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Views/BookmarkTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BookmarkTableViewCell.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 7/23/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ListKit 11 | import OctoKit 12 | 13 | class BookmarkTableViewCell: UITableViewCell, ListKitCellProtocol { 14 | var model: Bookmark? { 15 | didSet { 16 | if let repository = model?.routeSpecificData as? Repository { 17 | self.textLabel!.text = repository.name ?? "" 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowser/Views/RepositoryTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepositoryDetailCell.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benji Encz on 7/23/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ListKit 11 | import OctoKit 12 | 13 | class RepositoryTableViewCell: UITableViewCell, ListKitCellProtocol { 14 | var model: Repository? { 15 | didSet { 16 | self.textLabel!.text = model?.name ?? "" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowserTests/GithubAuthSpec/GitHubAuthSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubAuthSpec.swift 3 | // SwiftFlowGitHubBrowser 4 | // 5 | // Created by Benjamin Encz on 2/13/16. 6 | // Copyright © 2016 Benji Encz. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import ReSwift 12 | import OctoKit 13 | import ReSwiftRouter 14 | import ReSwiftThunk 15 | @testable import SwiftFlowGitHubBrowser 16 | 17 | class GitHubAuthSpec: QuickSpec { 18 | 19 | override func spec() { 20 | 21 | describe("When receiving a success OAuth URL callback ") { 22 | 23 | var store: Store! 24 | 25 | beforeEach { 26 | let fakeOAuth = FakeOAuthConfiguration(injectedTokenConfiguration: TokenConfiguration("Token")) 27 | let state = State( 28 | navigationState: NavigationState(), 29 | authenticationState: AuthenticationState( 30 | oAuthConfig: fakeOAuth, 31 | oAuthURL: nil, 32 | loggedInState: .notLoggedIn), 33 | repositories: nil, bookmarks: []) 34 | let thunkMiddleware: Middleware = createThunksMiddleware() 35 | store = Store(reducer: appReducer, state: state, middleware: [thunkMiddleware]) 36 | 37 | let oAuthCallbackURL = URL(string: "swiftflowgithub://success")! 38 | 39 | store.dispatch(handleOpenURL(url: oAuthCallbackURL)) 40 | } 41 | 42 | it("updates the route to the main view") { 43 | expect(store.state.navigationState.route).toEventually(equal([mainViewRoute])) 44 | } 45 | 46 | it("updates the state to reflect that user is logged in") { 47 | expect { () -> TokenConfiguration? in 48 | let tokenConfiguration: TokenConfiguration? 49 | 50 | if case let .loggedIn(config) = store.state.authenticationState.loggedInState { 51 | tokenConfiguration = config 52 | } else { 53 | tokenConfiguration = nil 54 | } 55 | 56 | return tokenConfiguration 57 | }.toEventuallyNot(beNil()) 58 | } 59 | 60 | } 61 | 62 | describe("When receiving successful login action") { 63 | 64 | let store = Store(reducer: appReducer, state: nil) 65 | 66 | beforeEach { 67 | let tokenConfiguration = TokenConfiguration("Token") 68 | let loggedInAction = UpdateLoggedInState(loggedInState: .loggedIn(tokenConfiguration)) 69 | store.dispatch(loggedInAction) 70 | } 71 | 72 | it("stores the TokenConfiguration in the auth state") { 73 | let tokenConfiguration: TokenConfiguration? 74 | 75 | if case let .loggedIn(config) = store.state.authenticationState.loggedInState { 76 | tokenConfiguration = config 77 | } else { 78 | tokenConfiguration = nil 79 | } 80 | 81 | expect(tokenConfiguration?.accessToken).to(equal("Token")) 82 | } 83 | 84 | } 85 | 86 | } 87 | 88 | } 89 | 90 | struct FakeOAuthConfiguration: OAuthConfigurationType { 91 | var injectedTokenConfiguration: TokenConfiguration 92 | 93 | func authenticate() -> URL? { 94 | return nil 95 | } 96 | 97 | func handleOpenURL(openUrl: URL, completion: @escaping (TokenConfiguration) -> Void) { 98 | completion(injectedTokenConfiguration) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /SwiftFlowGitHubBrowserTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | --------------------------------------------------------------------------------