├── .gitignore ├── .travis.yml ├── Package.swift ├── README.md ├── Rethink.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── Rethink.xcscmblueprint └── xcshareddata │ └── xcschemes │ └── Rethink.xcscheme ├── Sources ├── GCDAsyncSocket │ ├── GCDAsyncSocket.m │ ├── Info.plist │ └── include │ │ └── GCDAsyncSocket.h ├── Rethink │ ├── Info.plist │ ├── ReArguments.swift │ ├── ReConnection.swift │ ├── ReDatum.swift │ ├── ReProtocol.swift │ ├── ReResponse.swift │ ├── ReSocket.swift │ ├── Rethink.h │ └── Rethink.swift └── SCRAM │ ├── Info.plist │ ├── SCRAM.m │ └── include │ └── SCRAM.h └── Tests └── RethinkTests ├── Info.plist └── RethinkTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | Rethink.xcodeproj/project.xcworkspace/xcuserdata 5 | Rethink.xcodeproj/xcuserdata 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | xcode_project: Rethink.xcodeproj 8 | xcode_scheme: Rethink 9 | osx_image: xcode8 10 | 11 | before_install: 12 | - brew install rethinkdb 13 | 14 | before_script: 15 | - rethinkdb --daemon 16 | - sleep 10 17 | 18 | script: 19 | - xcodebuild clean build test -project Rethink.xcodeproj -scheme Rethink CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" 20 | 21 | after_script: 22 | - killall rethinkdb 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "Rethink", 5 | targets: [ 6 | Target(name: "Rethink", dependencies: ["GCDAsyncSocket", "SCRAM"]), 7 | Target(name: "GCDAsyncSocket"), 8 | Target(name: "SCRAM") 9 | ] 10 | ) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rethink.swift 2 | ------------- 3 | 4 | A client driver for RethinkDB in Swift. 5 | 6 | Looking for a Mac app to easily query RethinkDB? Convert and analyze large data sets at the speed of light with [Warp](http://warp.one). 7 | 8 | ### Usage 9 | 10 | ```swift 11 | R.connect(URL(string: "rethinkdb://localhost:28016")!, user: "admin", password: "") { err, connection in 12 | assert(err == nil, "Connection error: \(err)") 13 | 14 | // Connected! 15 | R.dbCreate(databaseName).run(connection) { response in 16 | assert(!response.isError, "Failed to create database: \(response)") 17 | 18 | R.db(databaseName).tableCreate(tableName).run(connection) { response in 19 | assert(!response.isError, "Failed to create table: \(response)") 20 | 21 | R.db(databaseName).table(tableName).indexWait().run(connection) { response in 22 | assert(!response.isError, "Failed to wait for index: \(response)") 23 | 24 | // Insert 1000 documents 25 | var docs: [ReDocument] = [] 26 | for i in 0..<1000 { 27 | docs.append(["foo": "bar", "id": i]) 28 | } 29 | 30 | R.db(databaseName).table(tableName).insert(docs).run(connection) { response in 31 | assert(!response.isError, "Failed to insert data: \(response)") 32 | 33 | R.db(databaseName).table(tableName).filter({ r in return r["foo"].eq(R.expr("bar")) }).run(connection) { response in 34 | ... 35 | } 36 | 37 | R.db(databaseName).table(tableName).count().run(connection) { response in 38 | ... 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | ### Status 48 | 49 | The driver implements the V1_0 protocol (which supports username/password authentication using SCRAM, and is available 50 | from RethinkDB 2.3.0). Alternatively, you can also use V0_4. Some commands and optional arguments may still be missing, 51 | but are usually easy to add to the code. 52 | 53 | The driver is written for Swift 3. The last version working in Swift 2.2 can be found at the tag 'last-swift2'. 54 | 55 | ### Installation 56 | 57 | #### Swift Package Manager (SPM) 58 | 59 | You can install the driver using Swift Package Manager by adding the following line to your ```Package.swift``` as a dependency: 60 | 61 | ``` 62 | .Package(url: "https://github.com/pixelspark/rethink-swift.git", majorVersion: 1) 63 | ``` 64 | 65 | To use the driver in an Xcode project, generate an Xcode project file using SPM: 66 | ``` 67 | swift package generate-xcodeproj 68 | ``` 69 | Open the Xcode project file and open the .xcconfig file located under the Config folder and add the following Objective-C build settings: 70 | ``` 71 | CLANG_ENABLE_MODULES = YES 72 | CLANG_ENABLE_OBJC_ARC = YES 73 | HEADER_SEARCH_PATHS = $(inherited) "$(SRCROOT)/Packages/**" 74 | ``` 75 | 76 | #### Manual 77 | 78 | Drag Rethink.xcodeproj into your own project, then add Rethink or Rethink iOS as dependency (build targets) and link to it. 79 | You should then be able to simply 'import Rethink' from Swift code. 80 | 81 | ### License 82 | 83 | ``` 84 | Rethink.swift. Copyright (c) 2015-2016 Pixelspark 85 | 86 | Permission is hereby granted, free of charge, to any person 87 | obtaining a copy of this software and associated documentation 88 | files (the "Software"), to deal in the Software without 89 | restriction, including without limitation the rights to use, 90 | copy, modify, merge, publish, distribute, sublicense, and/or sell 91 | copies of the Software, and to permit persons to whom the 92 | Software is furnished to do so, subject to the following 93 | conditions: 94 | The above copyright notice and this permission notice shall be 95 | included in all copies or substantial portions of the Software. 96 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 97 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 98 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 99 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 100 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 101 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 102 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 103 | OTHER DEALINGS IN THE SOFTWARE. 104 | ``` 105 | 106 | ### FAQ 107 | 108 | - __Can I contribute?__ 109 | 110 | Feel free to send a pull request. If you want to implement a new feature, please open 111 | an issue first, especially if it's a non backward compatible one. 112 | -------------------------------------------------------------------------------- /Rethink.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 29CA96E21D8876EB0027D914 /* GCDAsyncSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 29CA96E01D8876EB0027D914 /* GCDAsyncSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 29CA96FC1D8877040027D914 /* SCRAM.h in Headers */ = {isa = PBXBuildFile; fileRef = 29CA96FA1D8877040027D914 /* SCRAM.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 29CA970D1D887C460027D914 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 65B3CC441CA1C39C003FC6DF /* GCDAsyncSocket.m */; }; 13 | 29CA970E1D887C480027D914 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 65B3CC441CA1C39C003FC6DF /* GCDAsyncSocket.m */; }; 14 | 29CA970F1D887C5D0027D914 /* SCRAM.m in Sources */ = {isa = PBXBuildFile; fileRef = 65368F891CC6CE1600302942 /* SCRAM.m */; }; 15 | 29CA97101D887C5E0027D914 /* SCRAM.m in Sources */ = {isa = PBXBuildFile; fileRef = 65368F891CC6CE1600302942 /* SCRAM.m */; }; 16 | 29CA97111D887C740027D914 /* SCRAM.h in Headers */ = {isa = PBXBuildFile; fileRef = 29CA96FA1D8877040027D914 /* SCRAM.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 29CA97121D887C7D0027D914 /* GCDAsyncSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 29CA96E01D8876EB0027D914 /* GCDAsyncSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 655E10F61CB64E63008E3057 /* ReArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655E10F51CB64E63008E3057 /* ReArguments.swift */; }; 19 | 65646BAC1D577F6100E58137 /* Rethink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F739EB1BC9505B00B5E62D /* Rethink.swift */; }; 20 | 65646BAD1D577F6100E58137 /* ReConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A115121BD0D3C9007BC661 /* ReConnection.swift */; }; 21 | 65646BAE1D577F6100E58137 /* ReProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A115151BD0D4B8007BC661 /* ReProtocol.swift */; }; 22 | 65646BAF1D577F6100E58137 /* ReDatum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A115241BD2B364007BC661 /* ReDatum.swift */; }; 23 | 65646BB01D577F6100E58137 /* ReArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655E10F51CB64E63008E3057 /* ReArguments.swift */; }; 24 | 65646BB21D577F6100E58137 /* ReSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658278531D4FD78200D36512 /* ReSocket.swift */; }; 25 | 65646BB31D577F6100E58137 /* ReResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658278551D4FD86100D36512 /* ReResponse.swift */; }; 26 | 65646BB41D577F6B00E58137 /* Rethink.h in Headers */ = {isa = PBXBuildFile; fileRef = 65F739D41BC9502400B5E62D /* Rethink.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27 | 658278541D4FD78200D36512 /* ReSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658278531D4FD78200D36512 /* ReSocket.swift */; }; 28 | 658278561D4FD86100D36512 /* ReResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658278551D4FD86100D36512 /* ReResponse.swift */; }; 29 | 65A115131BD0D3C9007BC661 /* ReConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A115121BD0D3C9007BC661 /* ReConnection.swift */; }; 30 | 65A115161BD0D4B8007BC661 /* ReProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A115151BD0D4B8007BC661 /* ReProtocol.swift */; }; 31 | 65A115251BD2B364007BC661 /* ReDatum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A115241BD2B364007BC661 /* ReDatum.swift */; }; 32 | 65F739D51BC9502400B5E62D /* Rethink.h in Headers */ = {isa = PBXBuildFile; fileRef = 65F739D41BC9502400B5E62D /* Rethink.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33 | 65F739DC1BC9502400B5E62D /* Rethink.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65F739D11BC9502400B5E62D /* Rethink.framework */; }; 34 | 65F739E11BC9502400B5E62D /* RethinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F739E01BC9502400B5E62D /* RethinkTests.swift */; }; 35 | 65F739EC1BC9505B00B5E62D /* Rethink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F739EB1BC9505B00B5E62D /* Rethink.swift */; }; 36 | /* End PBXBuildFile section */ 37 | 38 | /* Begin PBXContainerItemProxy section */ 39 | 29CA971B1D887CCA0027D914 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 65F739C81BC9502400B5E62D /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = 29CA96DD1D8876EB0027D914; 44 | remoteInfo = GCDAsyncSocket; 45 | }; 46 | 29CA971D1D887CCE0027D914 /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = 65F739C81BC9502400B5E62D /* Project object */; 49 | proxyType = 1; 50 | remoteGlobalIDString = 29CA96F71D8877040027D914; 51 | remoteInfo = SCRAM; 52 | }; 53 | 29CA971F1D887CDB0027D914 /* PBXContainerItemProxy */ = { 54 | isa = PBXContainerItemProxy; 55 | containerPortal = 65F739C81BC9502400B5E62D /* Project object */; 56 | proxyType = 1; 57 | remoteGlobalIDString = 29CA96EA1D8876F70027D914; 58 | remoteInfo = "GCDAsyncSocket iOS"; 59 | }; 60 | 29CA97211D887CDE0027D914 /* PBXContainerItemProxy */ = { 61 | isa = PBXContainerItemProxy; 62 | containerPortal = 65F739C81BC9502400B5E62D /* Project object */; 63 | proxyType = 1; 64 | remoteGlobalIDString = 29CA97041D8877100027D914; 65 | remoteInfo = "SCRAM iOS"; 66 | }; 67 | 65F739DD1BC9502400B5E62D /* PBXContainerItemProxy */ = { 68 | isa = PBXContainerItemProxy; 69 | containerPortal = 65F739C81BC9502400B5E62D /* Project object */; 70 | proxyType = 1; 71 | remoteGlobalIDString = 65F739D01BC9502400B5E62D; 72 | remoteInfo = Rethink; 73 | }; 74 | /* End PBXContainerItemProxy section */ 75 | 76 | /* Begin PBXFileReference section */ 77 | 29CA96DE1D8876EB0027D914 /* GCDAsyncSocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GCDAsyncSocket.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | 29CA96E01D8876EB0027D914 /* GCDAsyncSocket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GCDAsyncSocket.h; path = include/GCDAsyncSocket.h; sourceTree = ""; }; 79 | 29CA96E11D8876EB0027D914 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 80 | 29CA96EB1D8876F70027D914 /* GCDAsyncSocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GCDAsyncSocket.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | 29CA96F81D8877040027D914 /* SCRAM.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SCRAM.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 82 | 29CA96FA1D8877040027D914 /* SCRAM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SCRAM.h; path = include/SCRAM.h; sourceTree = ""; }; 83 | 29CA96FB1D8877040027D914 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84 | 29CA97051D8877100027D914 /* SCRAM.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SCRAM.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 85 | 29CA97BA1D8883540027D914 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 86 | 65368F891CC6CE1600302942 /* SCRAM.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SCRAM.m; path = Sources/SCRAM/SCRAM.m; sourceTree = SOURCE_ROOT; }; 87 | 655E10F51CB64E63008E3057 /* ReArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReArguments.swift; path = Sources/Rethink/ReArguments.swift; sourceTree = SOURCE_ROOT; }; 88 | 65646BA31D577F2F00E58137 /* Rethink.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rethink.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 89 | 658278531D4FD78200D36512 /* ReSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReSocket.swift; path = Sources/Rethink/ReSocket.swift; sourceTree = SOURCE_ROOT; }; 90 | 658278551D4FD86100D36512 /* ReResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReResponse.swift; path = Sources/Rethink/ReResponse.swift; sourceTree = SOURCE_ROOT; }; 91 | 65A115121BD0D3C9007BC661 /* ReConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReConnection.swift; path = Sources/Rethink/ReConnection.swift; sourceTree = SOURCE_ROOT; }; 92 | 65A115151BD0D4B8007BC661 /* ReProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReProtocol.swift; path = Sources/Rethink/ReProtocol.swift; sourceTree = SOURCE_ROOT; }; 93 | 65A115241BD2B364007BC661 /* ReDatum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReDatum.swift; path = Sources/Rethink/ReDatum.swift; sourceTree = SOURCE_ROOT; }; 94 | 65B3CC441CA1C39C003FC6DF /* GCDAsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GCDAsyncSocket.m; path = Sources/GCDAsyncSocket/GCDAsyncSocket.m; sourceTree = SOURCE_ROOT; }; 95 | 65F739D11BC9502400B5E62D /* Rethink.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rethink.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 96 | 65F739D41BC9502400B5E62D /* Rethink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Rethink.h; path = Sources/Rethink/Rethink.h; sourceTree = SOURCE_ROOT; }; 97 | 65F739D61BC9502400B5E62D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Sources/Rethink/Info.plist; sourceTree = SOURCE_ROOT; }; 98 | 65F739DB1BC9502400B5E62D /* RethinkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RethinkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 99 | 65F739E01BC9502400B5E62D /* RethinkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RethinkTests.swift; path = RethinkTests/RethinkTests.swift; sourceTree = ""; }; 100 | 65F739E21BC9502400B5E62D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = RethinkTests/Info.plist; sourceTree = ""; }; 101 | 65F739EB1BC9505B00B5E62D /* Rethink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Rethink.swift; path = Sources/Rethink/Rethink.swift; sourceTree = SOURCE_ROOT; }; 102 | 65F739ED1BCA695F00B5E62D /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 103 | /* End PBXFileReference section */ 104 | 105 | /* Begin PBXFrameworksBuildPhase section */ 106 | 29CA96DA1D8876EB0027D914 /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | 29CA96E71D8876F70027D914 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | ); 118 | runOnlyForDeploymentPostprocessing = 0; 119 | }; 120 | 29CA96F41D8877040027D914 /* Frameworks */ = { 121 | isa = PBXFrameworksBuildPhase; 122 | buildActionMask = 2147483647; 123 | files = ( 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | 29CA97011D8877100027D914 /* Frameworks */ = { 128 | isa = PBXFrameworksBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | 65646B9F1D577F2F00E58137 /* Frameworks */ = { 135 | isa = PBXFrameworksBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | 65F739CD1BC9502400B5E62D /* Frameworks */ = { 142 | isa = PBXFrameworksBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | 65F739D81BC9502400B5E62D /* Frameworks */ = { 149 | isa = PBXFrameworksBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | 65F739DC1BC9502400B5E62D /* Rethink.framework in Frameworks */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXFrameworksBuildPhase section */ 157 | 158 | /* Begin PBXGroup section */ 159 | 29CA96DF1D8876EB0027D914 /* GCDAsyncSocket */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 29CA96E01D8876EB0027D914 /* GCDAsyncSocket.h */, 163 | 65B3CC441CA1C39C003FC6DF /* GCDAsyncSocket.m */, 164 | 29CA96E11D8876EB0027D914 /* Info.plist */, 165 | ); 166 | name = GCDAsyncSocket; 167 | path = Sources/GCDAsyncSocket; 168 | sourceTree = ""; 169 | }; 170 | 29CA96F91D8877040027D914 /* SCRAM */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 29CA96FA1D8877040027D914 /* SCRAM.h */, 174 | 65368F891CC6CE1600302942 /* SCRAM.m */, 175 | 29CA96FB1D8877040027D914 /* Info.plist */, 176 | ); 177 | name = SCRAM; 178 | path = Sources/SCRAM; 179 | sourceTree = ""; 180 | }; 181 | 65F739C71BC9502400B5E62D = { 182 | isa = PBXGroup; 183 | children = ( 184 | 29CA97BA1D8883540027D914 /* Package.swift */, 185 | 65F739ED1BCA695F00B5E62D /* README.md */, 186 | 65F739D31BC9502400B5E62D /* Rethink */, 187 | 29CA96DF1D8876EB0027D914 /* GCDAsyncSocket */, 188 | 29CA96F91D8877040027D914 /* SCRAM */, 189 | 65F739DF1BC9502400B5E62D /* RethinkTests */, 190 | 65F739D21BC9502400B5E62D /* Products */, 191 | ); 192 | sourceTree = ""; 193 | }; 194 | 65F739D21BC9502400B5E62D /* Products */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 65F739D11BC9502400B5E62D /* Rethink.framework */, 198 | 65F739DB1BC9502400B5E62D /* RethinkTests.xctest */, 199 | 65646BA31D577F2F00E58137 /* Rethink.framework */, 200 | 29CA96DE1D8876EB0027D914 /* GCDAsyncSocket.framework */, 201 | 29CA96EB1D8876F70027D914 /* GCDAsyncSocket.framework */, 202 | 29CA96F81D8877040027D914 /* SCRAM.framework */, 203 | 29CA97051D8877100027D914 /* SCRAM.framework */, 204 | ); 205 | name = Products; 206 | sourceTree = ""; 207 | }; 208 | 65F739D31BC9502400B5E62D /* Rethink */ = { 209 | isa = PBXGroup; 210 | children = ( 211 | 65F739D41BC9502400B5E62D /* Rethink.h */, 212 | 65F739EB1BC9505B00B5E62D /* Rethink.swift */, 213 | 65A115121BD0D3C9007BC661 /* ReConnection.swift */, 214 | 65A115151BD0D4B8007BC661 /* ReProtocol.swift */, 215 | 65A115241BD2B364007BC661 /* ReDatum.swift */, 216 | 655E10F51CB64E63008E3057 /* ReArguments.swift */, 217 | 658278531D4FD78200D36512 /* ReSocket.swift */, 218 | 658278551D4FD86100D36512 /* ReResponse.swift */, 219 | 65F739D61BC9502400B5E62D /* Info.plist */, 220 | ); 221 | path = Rethink; 222 | sourceTree = ""; 223 | }; 224 | 65F739DF1BC9502400B5E62D /* RethinkTests */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | 65F739E01BC9502400B5E62D /* RethinkTests.swift */, 228 | 65F739E21BC9502400B5E62D /* Info.plist */, 229 | ); 230 | name = RethinkTests; 231 | path = Tests; 232 | sourceTree = ""; 233 | }; 234 | /* End PBXGroup section */ 235 | 236 | /* Begin PBXHeadersBuildPhase section */ 237 | 29CA96DB1D8876EB0027D914 /* Headers */ = { 238 | isa = PBXHeadersBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | 29CA96E21D8876EB0027D914 /* GCDAsyncSocket.h in Headers */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | 29CA96E81D8876F70027D914 /* Headers */ = { 246 | isa = PBXHeadersBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | 29CA97121D887C7D0027D914 /* GCDAsyncSocket.h in Headers */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | 29CA96F51D8877040027D914 /* Headers */ = { 254 | isa = PBXHeadersBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 29CA96FC1D8877040027D914 /* SCRAM.h in Headers */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | 29CA97021D8877100027D914 /* Headers */ = { 262 | isa = PBXHeadersBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | 29CA97111D887C740027D914 /* SCRAM.h in Headers */, 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | 65646BA01D577F2F00E58137 /* Headers */ = { 270 | isa = PBXHeadersBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | 65646BB41D577F6B00E58137 /* Rethink.h in Headers */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | 65F739CE1BC9502400B5E62D /* Headers */ = { 278 | isa = PBXHeadersBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | 65F739D51BC9502400B5E62D /* Rethink.h in Headers */, 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | /* End PBXHeadersBuildPhase section */ 286 | 287 | /* Begin PBXNativeTarget section */ 288 | 29CA96DD1D8876EB0027D914 /* GCDAsyncSocket */ = { 289 | isa = PBXNativeTarget; 290 | buildConfigurationList = 29CA96E51D8876EB0027D914 /* Build configuration list for PBXNativeTarget "GCDAsyncSocket" */; 291 | buildPhases = ( 292 | 29CA96D91D8876EB0027D914 /* Sources */, 293 | 29CA96DA1D8876EB0027D914 /* Frameworks */, 294 | 29CA96DB1D8876EB0027D914 /* Headers */, 295 | 29CA96DC1D8876EB0027D914 /* Resources */, 296 | ); 297 | buildRules = ( 298 | ); 299 | dependencies = ( 300 | ); 301 | name = GCDAsyncSocket; 302 | productName = GCDAsyncSocket; 303 | productReference = 29CA96DE1D8876EB0027D914 /* GCDAsyncSocket.framework */; 304 | productType = "com.apple.product-type.framework"; 305 | }; 306 | 29CA96EA1D8876F70027D914 /* GCDAsyncSocket iOS */ = { 307 | isa = PBXNativeTarget; 308 | buildConfigurationList = 29CA96F01D8876F70027D914 /* Build configuration list for PBXNativeTarget "GCDAsyncSocket iOS" */; 309 | buildPhases = ( 310 | 29CA96E61D8876F70027D914 /* Sources */, 311 | 29CA96E71D8876F70027D914 /* Frameworks */, 312 | 29CA96E81D8876F70027D914 /* Headers */, 313 | 29CA96E91D8876F70027D914 /* Resources */, 314 | ); 315 | buildRules = ( 316 | ); 317 | dependencies = ( 318 | ); 319 | name = "GCDAsyncSocket iOS"; 320 | productName = "GCDAsyncSocket iOS"; 321 | productReference = 29CA96EB1D8876F70027D914 /* GCDAsyncSocket.framework */; 322 | productType = "com.apple.product-type.framework"; 323 | }; 324 | 29CA96F71D8877040027D914 /* SCRAM */ = { 325 | isa = PBXNativeTarget; 326 | buildConfigurationList = 29CA96FD1D8877040027D914 /* Build configuration list for PBXNativeTarget "SCRAM" */; 327 | buildPhases = ( 328 | 29CA96F31D8877040027D914 /* Sources */, 329 | 29CA96F41D8877040027D914 /* Frameworks */, 330 | 29CA96F51D8877040027D914 /* Headers */, 331 | 29CA96F61D8877040027D914 /* Resources */, 332 | ); 333 | buildRules = ( 334 | ); 335 | dependencies = ( 336 | ); 337 | name = SCRAM; 338 | productName = SCRAM; 339 | productReference = 29CA96F81D8877040027D914 /* SCRAM.framework */; 340 | productType = "com.apple.product-type.framework"; 341 | }; 342 | 29CA97041D8877100027D914 /* SCRAM iOS */ = { 343 | isa = PBXNativeTarget; 344 | buildConfigurationList = 29CA970A1D8877100027D914 /* Build configuration list for PBXNativeTarget "SCRAM iOS" */; 345 | buildPhases = ( 346 | 29CA97001D8877100027D914 /* Sources */, 347 | 29CA97011D8877100027D914 /* Frameworks */, 348 | 29CA97021D8877100027D914 /* Headers */, 349 | 29CA97031D8877100027D914 /* Resources */, 350 | ); 351 | buildRules = ( 352 | ); 353 | dependencies = ( 354 | ); 355 | name = "SCRAM iOS"; 356 | productName = "SCRAM iOS"; 357 | productReference = 29CA97051D8877100027D914 /* SCRAM.framework */; 358 | productType = "com.apple.product-type.framework"; 359 | }; 360 | 65646BA21D577F2F00E58137 /* Rethink iOS */ = { 361 | isa = PBXNativeTarget; 362 | buildConfigurationList = 65646BAA1D577F3000E58137 /* Build configuration list for PBXNativeTarget "Rethink iOS" */; 363 | buildPhases = ( 364 | 65646B9E1D577F2F00E58137 /* Sources */, 365 | 65646B9F1D577F2F00E58137 /* Frameworks */, 366 | 65646BA01D577F2F00E58137 /* Headers */, 367 | 65646BA11D577F2F00E58137 /* Resources */, 368 | ); 369 | buildRules = ( 370 | ); 371 | dependencies = ( 372 | 29CA97201D887CDB0027D914 /* PBXTargetDependency */, 373 | 29CA97221D887CDE0027D914 /* PBXTargetDependency */, 374 | ); 375 | name = "Rethink iOS"; 376 | productName = "Rethink iOS"; 377 | productReference = 65646BA31D577F2F00E58137 /* Rethink.framework */; 378 | productType = "com.apple.product-type.framework"; 379 | }; 380 | 65F739D01BC9502400B5E62D /* Rethink */ = { 381 | isa = PBXNativeTarget; 382 | buildConfigurationList = 65F739E51BC9502400B5E62D /* Build configuration list for PBXNativeTarget "Rethink" */; 383 | buildPhases = ( 384 | 65F739CC1BC9502400B5E62D /* Sources */, 385 | 65F739CD1BC9502400B5E62D /* Frameworks */, 386 | 65F739CE1BC9502400B5E62D /* Headers */, 387 | 65F739CF1BC9502400B5E62D /* Resources */, 388 | ); 389 | buildRules = ( 390 | ); 391 | dependencies = ( 392 | 29CA971C1D887CCA0027D914 /* PBXTargetDependency */, 393 | 29CA971E1D887CCE0027D914 /* PBXTargetDependency */, 394 | ); 395 | name = Rethink; 396 | productName = Rethink; 397 | productReference = 65F739D11BC9502400B5E62D /* Rethink.framework */; 398 | productType = "com.apple.product-type.framework"; 399 | }; 400 | 65F739DA1BC9502400B5E62D /* RethinkTests */ = { 401 | isa = PBXNativeTarget; 402 | buildConfigurationList = 65F739E81BC9502400B5E62D /* Build configuration list for PBXNativeTarget "RethinkTests" */; 403 | buildPhases = ( 404 | 65F739D71BC9502400B5E62D /* Sources */, 405 | 65F739D81BC9502400B5E62D /* Frameworks */, 406 | 65F739D91BC9502400B5E62D /* Resources */, 407 | ); 408 | buildRules = ( 409 | ); 410 | dependencies = ( 411 | 65F739DE1BC9502400B5E62D /* PBXTargetDependency */, 412 | ); 413 | name = RethinkTests; 414 | productName = RethinkTests; 415 | productReference = 65F739DB1BC9502400B5E62D /* RethinkTests.xctest */; 416 | productType = "com.apple.product-type.bundle.unit-test"; 417 | }; 418 | /* End PBXNativeTarget section */ 419 | 420 | /* Begin PBXProject section */ 421 | 65F739C81BC9502400B5E62D /* Project object */ = { 422 | isa = PBXProject; 423 | attributes = { 424 | LastSwiftUpdateCheck = 0700; 425 | LastUpgradeCheck = 0820; 426 | ORGANIZATIONNAME = Pixelspark; 427 | TargetAttributes = { 428 | 29CA96DD1D8876EB0027D914 = { 429 | CreatedOnToolsVersion = 8.0; 430 | ProvisioningStyle = Automatic; 431 | }; 432 | 29CA96EA1D8876F70027D914 = { 433 | CreatedOnToolsVersion = 8.0; 434 | ProvisioningStyle = Automatic; 435 | }; 436 | 29CA96F71D8877040027D914 = { 437 | CreatedOnToolsVersion = 8.0; 438 | ProvisioningStyle = Automatic; 439 | }; 440 | 29CA97041D8877100027D914 = { 441 | CreatedOnToolsVersion = 8.0; 442 | ProvisioningStyle = Automatic; 443 | }; 444 | 65646BA21D577F2F00E58137 = { 445 | CreatedOnToolsVersion = 8.0; 446 | DevelopmentTeam = 2T2T7HAA8S; 447 | ProvisioningStyle = Automatic; 448 | }; 449 | 65F739D01BC9502400B5E62D = { 450 | CreatedOnToolsVersion = 7.0.1; 451 | LastSwiftMigration = 0800; 452 | }; 453 | 65F739DA1BC9502400B5E62D = { 454 | CreatedOnToolsVersion = 7.0.1; 455 | LastSwiftMigration = 0800; 456 | }; 457 | }; 458 | }; 459 | buildConfigurationList = 65F739CB1BC9502400B5E62D /* Build configuration list for PBXProject "Rethink" */; 460 | compatibilityVersion = "Xcode 3.2"; 461 | developmentRegion = English; 462 | hasScannedForEncodings = 0; 463 | knownRegions = ( 464 | en, 465 | ); 466 | mainGroup = 65F739C71BC9502400B5E62D; 467 | productRefGroup = 65F739D21BC9502400B5E62D /* Products */; 468 | projectDirPath = ""; 469 | projectRoot = ""; 470 | targets = ( 471 | 65F739D01BC9502400B5E62D /* Rethink */, 472 | 65646BA21D577F2F00E58137 /* Rethink iOS */, 473 | 65F739DA1BC9502400B5E62D /* RethinkTests */, 474 | 29CA96DD1D8876EB0027D914 /* GCDAsyncSocket */, 475 | 29CA96EA1D8876F70027D914 /* GCDAsyncSocket iOS */, 476 | 29CA96F71D8877040027D914 /* SCRAM */, 477 | 29CA97041D8877100027D914 /* SCRAM iOS */, 478 | ); 479 | }; 480 | /* End PBXProject section */ 481 | 482 | /* Begin PBXResourcesBuildPhase section */ 483 | 29CA96DC1D8876EB0027D914 /* Resources */ = { 484 | isa = PBXResourcesBuildPhase; 485 | buildActionMask = 2147483647; 486 | files = ( 487 | ); 488 | runOnlyForDeploymentPostprocessing = 0; 489 | }; 490 | 29CA96E91D8876F70027D914 /* Resources */ = { 491 | isa = PBXResourcesBuildPhase; 492 | buildActionMask = 2147483647; 493 | files = ( 494 | ); 495 | runOnlyForDeploymentPostprocessing = 0; 496 | }; 497 | 29CA96F61D8877040027D914 /* Resources */ = { 498 | isa = PBXResourcesBuildPhase; 499 | buildActionMask = 2147483647; 500 | files = ( 501 | ); 502 | runOnlyForDeploymentPostprocessing = 0; 503 | }; 504 | 29CA97031D8877100027D914 /* Resources */ = { 505 | isa = PBXResourcesBuildPhase; 506 | buildActionMask = 2147483647; 507 | files = ( 508 | ); 509 | runOnlyForDeploymentPostprocessing = 0; 510 | }; 511 | 65646BA11D577F2F00E58137 /* Resources */ = { 512 | isa = PBXResourcesBuildPhase; 513 | buildActionMask = 2147483647; 514 | files = ( 515 | ); 516 | runOnlyForDeploymentPostprocessing = 0; 517 | }; 518 | 65F739CF1BC9502400B5E62D /* Resources */ = { 519 | isa = PBXResourcesBuildPhase; 520 | buildActionMask = 2147483647; 521 | files = ( 522 | ); 523 | runOnlyForDeploymentPostprocessing = 0; 524 | }; 525 | 65F739D91BC9502400B5E62D /* Resources */ = { 526 | isa = PBXResourcesBuildPhase; 527 | buildActionMask = 2147483647; 528 | files = ( 529 | ); 530 | runOnlyForDeploymentPostprocessing = 0; 531 | }; 532 | /* End PBXResourcesBuildPhase section */ 533 | 534 | /* Begin PBXSourcesBuildPhase section */ 535 | 29CA96D91D8876EB0027D914 /* Sources */ = { 536 | isa = PBXSourcesBuildPhase; 537 | buildActionMask = 2147483647; 538 | files = ( 539 | 29CA970D1D887C460027D914 /* GCDAsyncSocket.m in Sources */, 540 | ); 541 | runOnlyForDeploymentPostprocessing = 0; 542 | }; 543 | 29CA96E61D8876F70027D914 /* Sources */ = { 544 | isa = PBXSourcesBuildPhase; 545 | buildActionMask = 2147483647; 546 | files = ( 547 | 29CA970E1D887C480027D914 /* GCDAsyncSocket.m in Sources */, 548 | ); 549 | runOnlyForDeploymentPostprocessing = 0; 550 | }; 551 | 29CA96F31D8877040027D914 /* Sources */ = { 552 | isa = PBXSourcesBuildPhase; 553 | buildActionMask = 2147483647; 554 | files = ( 555 | 29CA970F1D887C5D0027D914 /* SCRAM.m in Sources */, 556 | ); 557 | runOnlyForDeploymentPostprocessing = 0; 558 | }; 559 | 29CA97001D8877100027D914 /* Sources */ = { 560 | isa = PBXSourcesBuildPhase; 561 | buildActionMask = 2147483647; 562 | files = ( 563 | 29CA97101D887C5E0027D914 /* SCRAM.m in Sources */, 564 | ); 565 | runOnlyForDeploymentPostprocessing = 0; 566 | }; 567 | 65646B9E1D577F2F00E58137 /* Sources */ = { 568 | isa = PBXSourcesBuildPhase; 569 | buildActionMask = 2147483647; 570 | files = ( 571 | 65646BAC1D577F6100E58137 /* Rethink.swift in Sources */, 572 | 65646BAD1D577F6100E58137 /* ReConnection.swift in Sources */, 573 | 65646BAE1D577F6100E58137 /* ReProtocol.swift in Sources */, 574 | 65646BAF1D577F6100E58137 /* ReDatum.swift in Sources */, 575 | 65646BB01D577F6100E58137 /* ReArguments.swift in Sources */, 576 | 65646BB21D577F6100E58137 /* ReSocket.swift in Sources */, 577 | 65646BB31D577F6100E58137 /* ReResponse.swift in Sources */, 578 | ); 579 | runOnlyForDeploymentPostprocessing = 0; 580 | }; 581 | 65F739CC1BC9502400B5E62D /* Sources */ = { 582 | isa = PBXSourcesBuildPhase; 583 | buildActionMask = 2147483647; 584 | files = ( 585 | 65A115131BD0D3C9007BC661 /* ReConnection.swift in Sources */, 586 | 658278541D4FD78200D36512 /* ReSocket.swift in Sources */, 587 | 65F739EC1BC9505B00B5E62D /* Rethink.swift in Sources */, 588 | 65A115251BD2B364007BC661 /* ReDatum.swift in Sources */, 589 | 655E10F61CB64E63008E3057 /* ReArguments.swift in Sources */, 590 | 658278561D4FD86100D36512 /* ReResponse.swift in Sources */, 591 | 65A115161BD0D4B8007BC661 /* ReProtocol.swift in Sources */, 592 | ); 593 | runOnlyForDeploymentPostprocessing = 0; 594 | }; 595 | 65F739D71BC9502400B5E62D /* Sources */ = { 596 | isa = PBXSourcesBuildPhase; 597 | buildActionMask = 2147483647; 598 | files = ( 599 | 65F739E11BC9502400B5E62D /* RethinkTests.swift in Sources */, 600 | ); 601 | runOnlyForDeploymentPostprocessing = 0; 602 | }; 603 | /* End PBXSourcesBuildPhase section */ 604 | 605 | /* Begin PBXTargetDependency section */ 606 | 29CA971C1D887CCA0027D914 /* PBXTargetDependency */ = { 607 | isa = PBXTargetDependency; 608 | target = 29CA96DD1D8876EB0027D914 /* GCDAsyncSocket */; 609 | targetProxy = 29CA971B1D887CCA0027D914 /* PBXContainerItemProxy */; 610 | }; 611 | 29CA971E1D887CCE0027D914 /* PBXTargetDependency */ = { 612 | isa = PBXTargetDependency; 613 | target = 29CA96F71D8877040027D914 /* SCRAM */; 614 | targetProxy = 29CA971D1D887CCE0027D914 /* PBXContainerItemProxy */; 615 | }; 616 | 29CA97201D887CDB0027D914 /* PBXTargetDependency */ = { 617 | isa = PBXTargetDependency; 618 | target = 29CA96EA1D8876F70027D914 /* GCDAsyncSocket iOS */; 619 | targetProxy = 29CA971F1D887CDB0027D914 /* PBXContainerItemProxy */; 620 | }; 621 | 29CA97221D887CDE0027D914 /* PBXTargetDependency */ = { 622 | isa = PBXTargetDependency; 623 | target = 29CA97041D8877100027D914 /* SCRAM iOS */; 624 | targetProxy = 29CA97211D887CDE0027D914 /* PBXContainerItemProxy */; 625 | }; 626 | 65F739DE1BC9502400B5E62D /* PBXTargetDependency */ = { 627 | isa = PBXTargetDependency; 628 | target = 65F739D01BC9502400B5E62D /* Rethink */; 629 | targetProxy = 65F739DD1BC9502400B5E62D /* PBXContainerItemProxy */; 630 | }; 631 | /* End PBXTargetDependency section */ 632 | 633 | /* Begin XCBuildConfiguration section */ 634 | 29CA96E31D8876EB0027D914 /* Debug */ = { 635 | isa = XCBuildConfiguration; 636 | buildSettings = { 637 | CLANG_ANALYZER_NONNULL = YES; 638 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 639 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 640 | CODE_SIGN_IDENTITY = "-"; 641 | COMBINE_HIDPI_IMAGES = YES; 642 | DEFINES_MODULE = YES; 643 | DYLIB_COMPATIBILITY_VERSION = 1; 644 | DYLIB_CURRENT_VERSION = 1; 645 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 646 | FRAMEWORK_VERSION = A; 647 | INFOPLIST_FILE = "$(SRCROOT)/Sources/GCDAsyncSocket/Info.plist"; 648 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 649 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 650 | MACOSX_DEPLOYMENT_TARGET = 10.11; 651 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.Rethink.GCDAsyncSocket; 652 | PRODUCT_NAME = "$(TARGET_NAME)"; 653 | SKIP_INSTALL = YES; 654 | }; 655 | name = Debug; 656 | }; 657 | 29CA96E41D8876EB0027D914 /* Release */ = { 658 | isa = XCBuildConfiguration; 659 | buildSettings = { 660 | CLANG_ANALYZER_NONNULL = YES; 661 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 662 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 663 | CODE_SIGN_IDENTITY = "-"; 664 | COMBINE_HIDPI_IMAGES = YES; 665 | DEFINES_MODULE = YES; 666 | DYLIB_COMPATIBILITY_VERSION = 1; 667 | DYLIB_CURRENT_VERSION = 1; 668 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 669 | FRAMEWORK_VERSION = A; 670 | INFOPLIST_FILE = "$(SRCROOT)/Sources/GCDAsyncSocket/Info.plist"; 671 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 672 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 673 | MACOSX_DEPLOYMENT_TARGET = 10.11; 674 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.Rethink.GCDAsyncSocket; 675 | PRODUCT_NAME = "$(TARGET_NAME)"; 676 | SKIP_INSTALL = YES; 677 | }; 678 | name = Release; 679 | }; 680 | 29CA96F11D8876F70027D914 /* Debug */ = { 681 | isa = XCBuildConfiguration; 682 | buildSettings = { 683 | CLANG_ANALYZER_NONNULL = YES; 684 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 685 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 686 | CODE_SIGN_IDENTITY = ""; 687 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 688 | DEFINES_MODULE = YES; 689 | DYLIB_COMPATIBILITY_VERSION = 1; 690 | DYLIB_CURRENT_VERSION = 1; 691 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 692 | INFOPLIST_FILE = "$(SRCROOT)/Sources/GCDAsyncSocket/Info.plist"; 693 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 694 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 695 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 696 | PRODUCT_BUNDLE_IDENTIFIER = "nl.pixelspark.Rethink.GCDAsyncSocket-iOS"; 697 | PRODUCT_NAME = GCDAsyncSocket; 698 | SDKROOT = iphoneos; 699 | SKIP_INSTALL = YES; 700 | TARGETED_DEVICE_FAMILY = "1,2"; 701 | }; 702 | name = Debug; 703 | }; 704 | 29CA96F21D8876F70027D914 /* Release */ = { 705 | isa = XCBuildConfiguration; 706 | buildSettings = { 707 | CLANG_ANALYZER_NONNULL = YES; 708 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 709 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 710 | CODE_SIGN_IDENTITY = ""; 711 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 712 | DEFINES_MODULE = YES; 713 | DYLIB_COMPATIBILITY_VERSION = 1; 714 | DYLIB_CURRENT_VERSION = 1; 715 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 716 | INFOPLIST_FILE = "$(SRCROOT)/Sources/GCDAsyncSocket/Info.plist"; 717 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 718 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 719 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 720 | PRODUCT_BUNDLE_IDENTIFIER = "nl.pixelspark.Rethink.GCDAsyncSocket-iOS"; 721 | PRODUCT_NAME = GCDAsyncSocket; 722 | SDKROOT = iphoneos; 723 | SKIP_INSTALL = YES; 724 | TARGETED_DEVICE_FAMILY = "1,2"; 725 | VALIDATE_PRODUCT = YES; 726 | }; 727 | name = Release; 728 | }; 729 | 29CA96FE1D8877040027D914 /* Debug */ = { 730 | isa = XCBuildConfiguration; 731 | buildSettings = { 732 | CLANG_ANALYZER_NONNULL = YES; 733 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 734 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 735 | CODE_SIGN_IDENTITY = "-"; 736 | COMBINE_HIDPI_IMAGES = YES; 737 | DEFINES_MODULE = YES; 738 | DYLIB_COMPATIBILITY_VERSION = 1; 739 | DYLIB_CURRENT_VERSION = 1; 740 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 741 | FRAMEWORK_VERSION = A; 742 | INFOPLIST_FILE = "$(SRCROOT)/Sources/SCRAM/Info.plist"; 743 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 744 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 745 | MACOSX_DEPLOYMENT_TARGET = 10.11; 746 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.Rethink.SCRAM; 747 | PRODUCT_NAME = "$(TARGET_NAME)"; 748 | SKIP_INSTALL = YES; 749 | }; 750 | name = Debug; 751 | }; 752 | 29CA96FF1D8877040027D914 /* Release */ = { 753 | isa = XCBuildConfiguration; 754 | buildSettings = { 755 | CLANG_ANALYZER_NONNULL = YES; 756 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 757 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 758 | CODE_SIGN_IDENTITY = "-"; 759 | COMBINE_HIDPI_IMAGES = YES; 760 | DEFINES_MODULE = YES; 761 | DYLIB_COMPATIBILITY_VERSION = 1; 762 | DYLIB_CURRENT_VERSION = 1; 763 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 764 | FRAMEWORK_VERSION = A; 765 | INFOPLIST_FILE = "$(SRCROOT)/Sources/SCRAM/Info.plist"; 766 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 767 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 768 | MACOSX_DEPLOYMENT_TARGET = 10.11; 769 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.Rethink.SCRAM; 770 | PRODUCT_NAME = "$(TARGET_NAME)"; 771 | SKIP_INSTALL = YES; 772 | }; 773 | name = Release; 774 | }; 775 | 29CA970B1D8877100027D914 /* Debug */ = { 776 | isa = XCBuildConfiguration; 777 | buildSettings = { 778 | CLANG_ANALYZER_NONNULL = YES; 779 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 780 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 781 | CODE_SIGN_IDENTITY = ""; 782 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 783 | DEFINES_MODULE = YES; 784 | DYLIB_COMPATIBILITY_VERSION = 1; 785 | DYLIB_CURRENT_VERSION = 1; 786 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 787 | INFOPLIST_FILE = "$(SRCROOT)/Sources/SCRAM/Info.plist"; 788 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 789 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 790 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 791 | PRODUCT_BUNDLE_IDENTIFIER = "nl.pixelspark.Rethink.SCRAM-iOS"; 792 | PRODUCT_NAME = SCRAM; 793 | SDKROOT = iphoneos; 794 | SKIP_INSTALL = YES; 795 | TARGETED_DEVICE_FAMILY = "1,2"; 796 | }; 797 | name = Debug; 798 | }; 799 | 29CA970C1D8877100027D914 /* Release */ = { 800 | isa = XCBuildConfiguration; 801 | buildSettings = { 802 | CLANG_ANALYZER_NONNULL = YES; 803 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 804 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 805 | CODE_SIGN_IDENTITY = ""; 806 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 807 | DEFINES_MODULE = YES; 808 | DYLIB_COMPATIBILITY_VERSION = 1; 809 | DYLIB_CURRENT_VERSION = 1; 810 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 811 | INFOPLIST_FILE = "$(SRCROOT)/Sources/SCRAM/Info.plist"; 812 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 813 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 814 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 815 | PRODUCT_BUNDLE_IDENTIFIER = "nl.pixelspark.Rethink.SCRAM-iOS"; 816 | PRODUCT_NAME = SCRAM; 817 | SDKROOT = iphoneos; 818 | SKIP_INSTALL = YES; 819 | TARGETED_DEVICE_FAMILY = "1,2"; 820 | VALIDATE_PRODUCT = YES; 821 | }; 822 | name = Release; 823 | }; 824 | 65646BA81D577F3000E58137 /* Debug */ = { 825 | isa = XCBuildConfiguration; 826 | buildSettings = { 827 | CLANG_ANALYZER_NONNULL = YES; 828 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 829 | CLANG_WARN_INFINITE_RECURSION = YES; 830 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 831 | CODE_SIGN_IDENTITY = ""; 832 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 833 | DEFINES_MODULE = YES; 834 | DEVELOPMENT_TEAM = 2T2T7HAA8S; 835 | DYLIB_COMPATIBILITY_VERSION = 1; 836 | DYLIB_CURRENT_VERSION = 1; 837 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 838 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Rethink/Info.plist"; 839 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 840 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 841 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 842 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.Rethink; 843 | PRODUCT_NAME = Rethink; 844 | SDKROOT = iphoneos; 845 | SKIP_INSTALL = YES; 846 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 847 | SWIFT_VERSION = 3.0; 848 | TARGETED_DEVICE_FAMILY = "1,2"; 849 | }; 850 | name = Debug; 851 | }; 852 | 65646BA91D577F3000E58137 /* Release */ = { 853 | isa = XCBuildConfiguration; 854 | buildSettings = { 855 | CLANG_ANALYZER_NONNULL = YES; 856 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 857 | CLANG_WARN_INFINITE_RECURSION = YES; 858 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 859 | CODE_SIGN_IDENTITY = ""; 860 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 861 | DEFINES_MODULE = YES; 862 | DEVELOPMENT_TEAM = 2T2T7HAA8S; 863 | DYLIB_COMPATIBILITY_VERSION = 1; 864 | DYLIB_CURRENT_VERSION = 1; 865 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 866 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Rethink/Info.plist"; 867 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 868 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 869 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 870 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.Rethink; 871 | PRODUCT_NAME = Rethink; 872 | SDKROOT = iphoneos; 873 | SKIP_INSTALL = YES; 874 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 875 | SWIFT_VERSION = 3.0; 876 | TARGETED_DEVICE_FAMILY = "1,2"; 877 | VALIDATE_PRODUCT = YES; 878 | }; 879 | name = Release; 880 | }; 881 | 65F739E31BC9502400B5E62D /* Debug */ = { 882 | isa = XCBuildConfiguration; 883 | buildSettings = { 884 | ALWAYS_SEARCH_USER_PATHS = NO; 885 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 886 | CLANG_CXX_LIBRARY = "libc++"; 887 | CLANG_ENABLE_MODULES = YES; 888 | CLANG_ENABLE_OBJC_ARC = YES; 889 | CLANG_WARN_BOOL_CONVERSION = YES; 890 | CLANG_WARN_CONSTANT_CONVERSION = YES; 891 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 892 | CLANG_WARN_EMPTY_BODY = YES; 893 | CLANG_WARN_ENUM_CONVERSION = YES; 894 | CLANG_WARN_INFINITE_RECURSION = YES; 895 | CLANG_WARN_INT_CONVERSION = YES; 896 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 897 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 898 | CLANG_WARN_UNREACHABLE_CODE = YES; 899 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 900 | COPY_PHASE_STRIP = NO; 901 | CURRENT_PROJECT_VERSION = 1; 902 | DEBUG_INFORMATION_FORMAT = dwarf; 903 | ENABLE_STRICT_OBJC_MSGSEND = YES; 904 | ENABLE_TESTABILITY = YES; 905 | GCC_C_LANGUAGE_STANDARD = gnu99; 906 | GCC_DYNAMIC_NO_PIC = NO; 907 | GCC_NO_COMMON_BLOCKS = YES; 908 | GCC_OPTIMIZATION_LEVEL = 0; 909 | GCC_PREPROCESSOR_DEFINITIONS = ( 910 | "DEBUG=1", 911 | "$(inherited)", 912 | ); 913 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 914 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 915 | GCC_WARN_UNDECLARED_SELECTOR = YES; 916 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 917 | GCC_WARN_UNUSED_FUNCTION = YES; 918 | GCC_WARN_UNUSED_VARIABLE = YES; 919 | MACOSX_DEPLOYMENT_TARGET = 10.10; 920 | MTL_ENABLE_DEBUG_INFO = YES; 921 | ONLY_ACTIVE_ARCH = YES; 922 | OTHER_SWIFT_FLAGS = ""; 923 | SDKROOT = macosx; 924 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 925 | VERSIONING_SYSTEM = "apple-generic"; 926 | VERSION_INFO_PREFIX = ""; 927 | }; 928 | name = Debug; 929 | }; 930 | 65F739E41BC9502400B5E62D /* Release */ = { 931 | isa = XCBuildConfiguration; 932 | buildSettings = { 933 | ALWAYS_SEARCH_USER_PATHS = NO; 934 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 935 | CLANG_CXX_LIBRARY = "libc++"; 936 | CLANG_ENABLE_MODULES = YES; 937 | CLANG_ENABLE_OBJC_ARC = YES; 938 | CLANG_WARN_BOOL_CONVERSION = YES; 939 | CLANG_WARN_CONSTANT_CONVERSION = YES; 940 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 941 | CLANG_WARN_EMPTY_BODY = YES; 942 | CLANG_WARN_ENUM_CONVERSION = YES; 943 | CLANG_WARN_INFINITE_RECURSION = YES; 944 | CLANG_WARN_INT_CONVERSION = YES; 945 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 946 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 947 | CLANG_WARN_UNREACHABLE_CODE = YES; 948 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 949 | COPY_PHASE_STRIP = NO; 950 | CURRENT_PROJECT_VERSION = 1; 951 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 952 | ENABLE_NS_ASSERTIONS = NO; 953 | ENABLE_STRICT_OBJC_MSGSEND = YES; 954 | GCC_C_LANGUAGE_STANDARD = gnu99; 955 | GCC_NO_COMMON_BLOCKS = YES; 956 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 957 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 958 | GCC_WARN_UNDECLARED_SELECTOR = YES; 959 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 960 | GCC_WARN_UNUSED_FUNCTION = YES; 961 | GCC_WARN_UNUSED_VARIABLE = YES; 962 | MACOSX_DEPLOYMENT_TARGET = 10.10; 963 | MTL_ENABLE_DEBUG_INFO = NO; 964 | OTHER_SWIFT_FLAGS = ""; 965 | SDKROOT = macosx; 966 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 967 | VERSIONING_SYSTEM = "apple-generic"; 968 | VERSION_INFO_PREFIX = ""; 969 | }; 970 | name = Release; 971 | }; 972 | 65F739E61BC9502400B5E62D /* Debug */ = { 973 | isa = XCBuildConfiguration; 974 | buildSettings = { 975 | CLANG_ENABLE_MODULES = YES; 976 | COMBINE_HIDPI_IMAGES = YES; 977 | DEFINES_MODULE = YES; 978 | DYLIB_COMPATIBILITY_VERSION = 1; 979 | DYLIB_CURRENT_VERSION = 1; 980 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 981 | FRAMEWORK_VERSION = A; 982 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Rethink/Info.plist"; 983 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 984 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 985 | MACOSX_DEPLOYMENT_TARGET = 10.10; 986 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.Rethink; 987 | PRODUCT_NAME = "$(TARGET_NAME)"; 988 | SKIP_INSTALL = YES; 989 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 990 | SWIFT_VERSION = 3.0; 991 | }; 992 | name = Debug; 993 | }; 994 | 65F739E71BC9502400B5E62D /* Release */ = { 995 | isa = XCBuildConfiguration; 996 | buildSettings = { 997 | CLANG_ENABLE_MODULES = YES; 998 | COMBINE_HIDPI_IMAGES = YES; 999 | DEFINES_MODULE = YES; 1000 | DYLIB_COMPATIBILITY_VERSION = 1; 1001 | DYLIB_CURRENT_VERSION = 1; 1002 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1003 | FRAMEWORK_VERSION = A; 1004 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Rethink/Info.plist"; 1005 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1006 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1007 | MACOSX_DEPLOYMENT_TARGET = 10.10; 1008 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.Rethink; 1009 | PRODUCT_NAME = "$(TARGET_NAME)"; 1010 | SKIP_INSTALL = YES; 1011 | SWIFT_VERSION = 3.0; 1012 | }; 1013 | name = Release; 1014 | }; 1015 | 65F739E91BC9502400B5E62D /* Debug */ = { 1016 | isa = XCBuildConfiguration; 1017 | buildSettings = { 1018 | COMBINE_HIDPI_IMAGES = YES; 1019 | INFOPLIST_FILE = "$(SRCROOT)/Tests/RethinkTests/Info.plist"; 1020 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1021 | OTHER_SWIFT_FLAGS = "-DDEBUG"; 1022 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.RethinkTests; 1023 | PRODUCT_NAME = "$(TARGET_NAME)"; 1024 | SWIFT_VERSION = 3.0; 1025 | }; 1026 | name = Debug; 1027 | }; 1028 | 65F739EA1BC9502400B5E62D /* Release */ = { 1029 | isa = XCBuildConfiguration; 1030 | buildSettings = { 1031 | COMBINE_HIDPI_IMAGES = YES; 1032 | INFOPLIST_FILE = "$(SRCROOT)/Tests/RethinkTests/Info.plist"; 1033 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1034 | OTHER_SWIFT_FLAGS = "-DDEBUG"; 1035 | PRODUCT_BUNDLE_IDENTIFIER = nl.pixelspark.RethinkTests; 1036 | PRODUCT_NAME = "$(TARGET_NAME)"; 1037 | SWIFT_VERSION = 3.0; 1038 | }; 1039 | name = Release; 1040 | }; 1041 | /* End XCBuildConfiguration section */ 1042 | 1043 | /* Begin XCConfigurationList section */ 1044 | 29CA96E51D8876EB0027D914 /* Build configuration list for PBXNativeTarget "GCDAsyncSocket" */ = { 1045 | isa = XCConfigurationList; 1046 | buildConfigurations = ( 1047 | 29CA96E31D8876EB0027D914 /* Debug */, 1048 | 29CA96E41D8876EB0027D914 /* Release */, 1049 | ); 1050 | defaultConfigurationIsVisible = 0; 1051 | defaultConfigurationName = Release; 1052 | }; 1053 | 29CA96F01D8876F70027D914 /* Build configuration list for PBXNativeTarget "GCDAsyncSocket iOS" */ = { 1054 | isa = XCConfigurationList; 1055 | buildConfigurations = ( 1056 | 29CA96F11D8876F70027D914 /* Debug */, 1057 | 29CA96F21D8876F70027D914 /* Release */, 1058 | ); 1059 | defaultConfigurationIsVisible = 0; 1060 | defaultConfigurationName = Release; 1061 | }; 1062 | 29CA96FD1D8877040027D914 /* Build configuration list for PBXNativeTarget "SCRAM" */ = { 1063 | isa = XCConfigurationList; 1064 | buildConfigurations = ( 1065 | 29CA96FE1D8877040027D914 /* Debug */, 1066 | 29CA96FF1D8877040027D914 /* Release */, 1067 | ); 1068 | defaultConfigurationIsVisible = 0; 1069 | defaultConfigurationName = Release; 1070 | }; 1071 | 29CA970A1D8877100027D914 /* Build configuration list for PBXNativeTarget "SCRAM iOS" */ = { 1072 | isa = XCConfigurationList; 1073 | buildConfigurations = ( 1074 | 29CA970B1D8877100027D914 /* Debug */, 1075 | 29CA970C1D8877100027D914 /* Release */, 1076 | ); 1077 | defaultConfigurationIsVisible = 0; 1078 | defaultConfigurationName = Release; 1079 | }; 1080 | 65646BAA1D577F3000E58137 /* Build configuration list for PBXNativeTarget "Rethink iOS" */ = { 1081 | isa = XCConfigurationList; 1082 | buildConfigurations = ( 1083 | 65646BA81D577F3000E58137 /* Debug */, 1084 | 65646BA91D577F3000E58137 /* Release */, 1085 | ); 1086 | defaultConfigurationIsVisible = 0; 1087 | defaultConfigurationName = Release; 1088 | }; 1089 | 65F739CB1BC9502400B5E62D /* Build configuration list for PBXProject "Rethink" */ = { 1090 | isa = XCConfigurationList; 1091 | buildConfigurations = ( 1092 | 65F739E31BC9502400B5E62D /* Debug */, 1093 | 65F739E41BC9502400B5E62D /* Release */, 1094 | ); 1095 | defaultConfigurationIsVisible = 0; 1096 | defaultConfigurationName = Release; 1097 | }; 1098 | 65F739E51BC9502400B5E62D /* Build configuration list for PBXNativeTarget "Rethink" */ = { 1099 | isa = XCConfigurationList; 1100 | buildConfigurations = ( 1101 | 65F739E61BC9502400B5E62D /* Debug */, 1102 | 65F739E71BC9502400B5E62D /* Release */, 1103 | ); 1104 | defaultConfigurationIsVisible = 0; 1105 | defaultConfigurationName = Release; 1106 | }; 1107 | 65F739E81BC9502400B5E62D /* Build configuration list for PBXNativeTarget "RethinkTests" */ = { 1108 | isa = XCConfigurationList; 1109 | buildConfigurations = ( 1110 | 65F739E91BC9502400B5E62D /* Debug */, 1111 | 65F739EA1BC9502400B5E62D /* Release */, 1112 | ); 1113 | defaultConfigurationIsVisible = 0; 1114 | defaultConfigurationName = Release; 1115 | }; 1116 | /* End XCConfigurationList section */ 1117 | }; 1118 | rootObject = 65F739C81BC9502400B5E62D /* Project object */; 1119 | } 1120 | -------------------------------------------------------------------------------- /Rethink.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Rethink.xcodeproj/project.xcworkspace/xcshareddata/Rethink.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "B4ECFDB54DAEDE750287282DDD72484BDC32C452", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "B4ECFDB54DAEDE750287282DDD72484BDC32C452" : 0, 8 | "A5F18CC216C059F29632AA08FA9E5DF3898315E3" : 0 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "9D490845-C9AB-4999-8F49-108ED1CB5EBC", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "B4ECFDB54DAEDE750287282DDD72484BDC32C452" : "rethink-swift\/", 13 | "A5F18CC216C059F29632AA08FA9E5DF3898315E3" : ".." 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "Rethink", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Rethink.xcodeproj", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "\/Volumes\/code\/qbe.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "A5F18CC216C059F29632AA08FA9E5DF3898315E3" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/pixelspark\/rethink-swift", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "B4ECFDB54DAEDE750287282DDD72484BDC32C452" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /Rethink.xcodeproj/xcshareddata/xcschemes/Rethink.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 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Sources/GCDAsyncSocket/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2016 Pixelspark. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Rethink/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 Pixelspark. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Rethink/ReArguments.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum ReTableReadMode: String { 4 | case single = "single" 5 | case majority = "majority" 6 | case outdated = "outdated" 7 | } 8 | 9 | public enum ReTableIdentifierFormat: String { 10 | case name = "name" 11 | case uuid = "uuid" 12 | } 13 | 14 | /** Optional arguments are instances of ReArg. */ 15 | public protocol ReArg { 16 | var serialization: (String, Any) { get } 17 | } 18 | 19 | /** Optional arguments for the R.table command. */ 20 | public enum ReTableArg: ReArg { 21 | case readMode(ReTableReadMode) 22 | case identifierFormat(ReTableIdentifierFormat) 23 | 24 | public var serialization: (String, Any) { 25 | switch self { 26 | case .readMode(let rm): return ("read_mode", rm.rawValue) 27 | case .identifierFormat(let i): return ("identifier_format", i.rawValue) 28 | } 29 | } 30 | } 31 | 32 | public enum ReFilterArg: ReArg { 33 | case `default`(AnyObject) 34 | 35 | public var serialization: (String, Any) { 36 | switch self { 37 | case .default(let a): return ("default", a) 38 | } 39 | } 40 | } 41 | 42 | public enum ReTableDurability: String { 43 | case soft = "soft" 44 | case hard = "hard" 45 | } 46 | 47 | public enum ReTableCreateArg: ReArg { 48 | case primaryKey(String) 49 | case durability(ReTableDurability) 50 | case shards(Int) 51 | case replicas(Int) 52 | 53 | public var serialization: (String, Any) { 54 | switch self { 55 | case .primaryKey(let p): return ("primary_key", p) 56 | case .durability(let d): return ("durability", d.rawValue) 57 | case .shards(let s): return ("shards", s) 58 | case .replicas(let r): return ("replicas", r) 59 | } 60 | } 61 | } 62 | 63 | public enum ReIndexCreateArg: ReArg { 64 | case multi(Bool) 65 | case geo(Bool) 66 | 67 | public var serialization: (String, Any) { 68 | switch self { 69 | case .multi(let m): return ("multi", m) 70 | case .geo(let g): return ("geo", g) 71 | } 72 | } 73 | } 74 | 75 | public enum ReIndexRenameArg: ReArg { 76 | /** If the optional argument overwrite is specified as true, a previously existing index with the new name will be 77 | deleted and the index will be renamed. If overwrite is false (the default) an error will be raised if the new index 78 | name already exists. */ 79 | case overwrite(Bool) 80 | 81 | public var serialization: (String, Any) { 82 | switch self { 83 | case .overwrite(let o): return ("overwrite", o) 84 | } 85 | } 86 | } 87 | 88 | public enum RePermission: ReArg { 89 | case read(Bool) 90 | case write(Bool) 91 | case connect(Bool) 92 | case config(Bool) 93 | 94 | public var serialization: (String, Any) { 95 | switch self { 96 | case .read(let b): return ("read", b) 97 | case .write(let b): return ("write", b) 98 | case .connect(let b): return ("connect", b) 99 | case .config(let b): return ("config", b) 100 | } 101 | } 102 | } 103 | 104 | public enum ReChangesArg: ReArg { 105 | /** squash: Controls how change notifications are batched. Acceptable values are true, false and a numeric value: 106 | - true: When multiple changes to the same document occur before a batch of notifications is sent, the changes are 107 | "squashed" into one change. The client receives a notification that will bring it fully up to date with the server. 108 | - false: All changes will be sent to the client verbatim. This is the default. 109 | - n: A numeric value (floating point). Similar to true, but the server will wait n seconds to respond in order to 110 | squash as many changes together as possible, reducing network traffic. The first batch will always be returned 111 | immediately. */ 112 | case squash(Bool, n: Double?) 113 | 114 | /** changefeedQueueSize: the number of changes the server will buffer between client reads before it starts dropping 115 | changes and generates an error (default: 100,000). */ 116 | case changeFeedQueueSize(Int) 117 | 118 | /** includeInitial: if true, the changefeed stream will begin with the current contents of the table or selection 119 | being monitored. These initial results will have new_val fields, but no old_val fields. The initial results may be 120 | intermixed with actual changes, as long as an initial result for the changed document has already been given. If an 121 | initial result for a document has been sent and a change is made to that document that would move it to the unsent 122 | part of the result set (e.g., a changefeed monitors the top 100 posters, the first 50 have been sent, and poster 48 123 | has become poster 52), an “uninitial” notification will be sent, with an old_val field but no new_val field.*/ 124 | case includeInitial(Bool) 125 | 126 | /** includeStates: if true, the changefeed stream will include special status documents consisting of the field state 127 | and a string indicating a change in the feed’s state. These documents can occur at any point in the feed between the 128 | notification documents described below. If includeStates is false (the default), the status documents will not be sent.*/ 129 | case includeStates(Bool) 130 | 131 | /** includeOffsets: if true, a changefeed stream on an orderBy.limit changefeed will include old_offset and new_offset 132 | fields in status documents that include old_val and new_val. This allows applications to maintain ordered lists of the 133 | stream’s result set. If old_offset is set and not null, the element at old_offset is being deleted; if new_offset is 134 | set and not null, then new_val is being inserted at new_offset. Setting includeOffsets to true on a changefeed that 135 | does not support it will raise an error.*/ 136 | case includeOffsets(Bool) 137 | 138 | /** includeTypes: if true, every result on a changefeed will include a type field with a string that indicates the 139 | kind of change the result represents: add, remove, change, initial, uninitial, state. Defaults to false.*/ 140 | case includeTypes(Bool) 141 | 142 | public var serialization: (String, Any) { 143 | switch self { 144 | case .squash(let b, let i): 145 | assert(!(i != nil && !b), "Do not specify a time interval when squashing is to be disabled") 146 | if let interval = i, b { 147 | return ("squash", interval) 148 | } 149 | else { 150 | return ("squash", b) 151 | } 152 | 153 | case .changeFeedQueueSize(let i): return ("changefeed_queue_size", i) 154 | case .includeInitial(let b): return ("include_initial", b) 155 | case .includeStates(let b): return ("include_states", b) 156 | case .includeOffsets(let b): return ("include_offsets", b) 157 | case .includeTypes(let b): return ("include_types", b) 158 | } 159 | } 160 | } 161 | 162 | public enum ReFoldArg: ReArg { 163 | /** When an emit function is provided, fold will: 164 | - proceed through the sequence in order and take an initial base value, as above. 165 | - for each element in the sequence, call both the combining function and a separate emitting function with the current 166 | element and previous reduction result. 167 | - optionally pass the result of the combining function to the emitting function. 168 | If provided, the emitting function must return a list. */ 169 | case emit(ReQueryLambda) 170 | case finalEmit(ReQueryLambda) 171 | 172 | public var serialization: (String, Any) { 173 | switch self { 174 | case .emit(let r): return ("emit", r) 175 | case .finalEmit(let r): return ("final_emit", r) 176 | } 177 | } 178 | } 179 | 180 | public enum ReEqJoinArg: ReArg { 181 | /** The results from eqJoin are, by default, not ordered. The optional ordered: true parameter will cause eqJoin to 182 | order the output based on the left side input stream. (If there are multiple matches on the right side for a document 183 | on the left side, their order is not guaranteed even if ordered is true.) Requiring ordered results can significantly 184 | slow down eqJoin, and in many circumstances this ordering will not be required. (See the first example, in which 185 | ordered results are obtained by using orderBy after eqJoin.) */ 186 | case ordered(Bool) 187 | case index(String) 188 | 189 | public var serialization: (String, Any) { 190 | switch self { 191 | case .ordered(let o): return ("ordered", o) 192 | case .index(let i): return ("index", i) 193 | } 194 | } 195 | } 196 | 197 | public enum ReDurability: String { 198 | case hard = "hard" 199 | case soft = "soft" 200 | } 201 | 202 | public enum ReConflictResolution: String { 203 | /** Do not insert the new document and record the conflict as an error. This is the default. */ 204 | case error = "error" 205 | 206 | /** Replace the old document in its entirety with the new one. */ 207 | case replace = "replace" 208 | 209 | /** Update fields of the old document with fields from the new one. */ 210 | case update = "update" 211 | } 212 | 213 | public enum ReInsertArg: ReArg { 214 | /** This option will override the table or query’s durability setting (set in run). In soft durability mode RethinkDB 215 | will acknowledge the write immediately after receiving and caching it, but before the write has been committed to disk. */ 216 | case durability(ReDurability) 217 | 218 | /** true: return a changes array consisting of old_val/new_val objects describing the changes made, only including the 219 | documents actually updated. false: do not return a changes array (the default). */ 220 | case returnChanges(Bool) 221 | 222 | /** Determine handling of inserting documents with the same primary key as existing entries. */ 223 | case conflict(ReConflictResolution) 224 | 225 | public var serialization: (String, Any) { 226 | switch self { 227 | case .durability(let d): return ("durability", d.rawValue) 228 | case .returnChanges(let r): return ("return_changes", r) 229 | case .conflict(let r): return ("conflict", r.rawValue) 230 | } 231 | } 232 | } 233 | 234 | public enum ReUpdateArg: ReArg { 235 | /** This option will override the table or query’s durability setting (set in run). In soft durability mode RethinkDB 236 | will acknowledge the write immediately after receiving and caching it, but before the write has been committed to disk. */ 237 | case durability(ReDurability) 238 | 239 | /** true: return a changes array consisting of old_val/new_val objects describing the changes made, only including the 240 | documents actually updated. false: do not return a changes array (the default). */ 241 | case returnChanges(Bool) 242 | 243 | /** If set to true, executes the update and distributes the result to replicas in a non-atomic fashion. This flag is 244 | required to perform non-deterministic updates, such as those that require reading data from another table. */ 245 | case nonAtomic(Bool) 246 | 247 | public var serialization: (String, Any) { 248 | switch self { 249 | case .durability(let d): return ("durability", d.rawValue) 250 | case .returnChanges(let r): return ("return_changes", r) 251 | case .nonAtomic(let b): return ("non_atomic", b) 252 | } 253 | } 254 | } 255 | 256 | public enum ReDeleteArg: ReArg { 257 | /** This option will override the table or query’s durability setting (set in run). In soft durability mode RethinkDB 258 | will acknowledge the write immediately after receiving and caching it, but before the write has been committed to disk. */ 259 | case durability(ReDurability) 260 | 261 | /** true: return a changes array consisting of old_val/new_val objects describing the changes made, only including the 262 | documents actually updated. false: do not return a changes array (the default). */ 263 | case returnChanges(Bool) 264 | 265 | public var serialization: (String, Any) { 266 | switch self { 267 | case .durability(let d): return ("durability", d.rawValue) 268 | case .returnChanges(let r): return ("return_changes", r) 269 | } 270 | } 271 | } 272 | 273 | public enum ReUnit: String { 274 | case meter = "m" 275 | case kilometer = "km" 276 | case internationalMile = "mi" 277 | case nauticalMile = "nm" 278 | case internationalFoot = "ft" 279 | } 280 | 281 | public enum ReGeoSystem: String { 282 | case wgs84 = "WGS84" 283 | case unitSphere = "unit_sphere" 284 | } 285 | 286 | public enum ReCircleArg: ReArg { 287 | /** The number of vertices in the polygon or line. Defaults to 32. */ 288 | case numVertices(Int) 289 | 290 | /** The reference ellipsoid to use for geographic coordinates. Possible values are WGS84 (the default), a common 291 | standard for Earth’s geometry, or unit_sphere, a perfect sphere of 1 meter radius. */ 292 | case geoSystem(ReGeoSystem) 293 | 294 | /** Unit for the radius distance. */ 295 | case unit(ReUnit) 296 | 297 | /** If true (the default) the circle is filled, creating a polygon; if false the circle is unfilled (creating a line). */ 298 | case fill(Bool) 299 | 300 | public var serialization: (String, Any) { 301 | switch self { 302 | case .numVertices(let n): return ("num_vertices", n) 303 | case .geoSystem(let s): return ("geo_system", s.rawValue) 304 | case .unit(let u): return ("unit", u.rawValue) 305 | case .fill(let b): return ("fill", b) 306 | } 307 | } 308 | } 309 | 310 | public enum ReDistanceArg: ReArg { 311 | case geoSystem(ReGeoSystem) 312 | case unit(ReUnit) 313 | 314 | public var serialization: (String, Any) { 315 | switch self { 316 | case .geoSystem(let s): return ("geo_system", s.rawValue) 317 | case .unit(let u): return ("unit", u.rawValue) 318 | } 319 | } 320 | } 321 | 322 | public enum ReIntersectingArg: ReArg { 323 | /** The index argument is mandatory. This command returns the same results as 324 | table.filter(r.row('index').intersects(geometry)). The total number of results is limited to the array size limit which 325 | defaults to 100,000, but can be changed with the arrayLimit option to run. */ 326 | case index(String) 327 | 328 | public var serialization: (String, Any) { 329 | switch self { 330 | case .index(let s): return ("index", s) 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /Sources/Rethink/ReConnection.swift: -------------------------------------------------------------------------------- 1 | /** Rethink.swift 2 | Copyright (c) 2016 Pixelspark 3 | Author: Tommy van der Vorst (tommy@pixelspark.nl) 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. **/ 25 | import Foundation 26 | import GCDAsyncSocket 27 | import SCRAM 28 | 29 | public class ReConnection: NSObject, GCDAsyncSocketDelegate { 30 | public let url: URL 31 | public let protocolVersion: ReProtocolVersion 32 | public var authenticationKey: String? { get { return self.url.user } } 33 | 34 | private var state = ReConnectionState.unconnected 35 | private let socket: ReSocket 36 | private var outstandingQueries: [ReQueryToken: ReResponse.Callback] = [:] 37 | private var onConnectCallback: ((String?) -> ())? = nil 38 | 39 | private static var tokenCounter = ReTokenCounter() 40 | private let queue = DispatchQueue(label: "nl.pixelspark.Rethink.ReConnectionQueue") 41 | 42 | /** Create a connection to a RethinkDB instance. The URL should be of the form 'rethinkdb://host:port'. If 43 | no port is given, the default port is used. If the server requires the use of an authentication key, put it 44 | in the 'user' part of the URL, e.g. "rethinkdb://key@server:port". */ 45 | internal init(url: URL, protocolVersion: ReProtocolVersion) { 46 | self.url = url 47 | self.protocolVersion = protocolVersion 48 | self.socket = ReSocket(queue: self.queue) 49 | } 50 | 51 | internal func connect(_ username: String = ReProtocol.defaultUser, password: String = ReProtocol.defaultPassword, callback: @escaping (ReError?) -> ()) { 52 | self.socket.connect(self.url) { err in 53 | if let e = err { 54 | return callback(ReError.fatal(e)) 55 | } 56 | 57 | // Start authentication 58 | guard let data = NSMutableData(capacity: 128) else { 59 | let e = ReError.fatal("Could not create data object") 60 | self.state = .error(e) 61 | return callback(e) 62 | } 63 | 64 | switch self.protocolVersion { 65 | case .v0_4: 66 | // Append protocol version 67 | data.append(Data.dataWithLittleEndianOf(UInt32(self.protocolVersion.protocolVersionCode))) 68 | 69 | // Append authentication key length and the key itself (as ASCII) 70 | if let authKey = self.authenticationKey?.data(using: String.Encoding.ascii) { 71 | data.append(Data.dataWithLittleEndianOf(UInt32(authKey.count))) 72 | data.append(authKey) 73 | } 74 | else { 75 | data.append(Data.dataWithLittleEndianOf(UInt32(0))) 76 | } 77 | 78 | // Append protocol type (JSON) 79 | data.append(Data.dataWithLittleEndianOf(UInt32(ReProtocol.protocolType))) 80 | 81 | case .v1_0: 82 | data.append(Data.dataWithLittleEndianOf(UInt32(self.protocolVersion.protocolVersionCode))) 83 | } 84 | 85 | self.socket.write(data as Data) { err in 86 | if let e = err { 87 | self.state = .error(ReError.fatal(e)) 88 | return callback(ReError.fatal(e)) 89 | } 90 | 91 | self.state = .handshakeSent 92 | 93 | // Let's see if we get a reply 94 | if self.socket.state == .connected { 95 | self.socket.readZeroTerminatedASCII() { s in 96 | switch self.protocolVersion { 97 | case .v0_4: 98 | if s == ReProtocol.handshakeSuccessResponse { 99 | // Start read loop 100 | self.state = .connected 101 | self.startReading() 102 | return callback(nil) 103 | } 104 | else { 105 | let e = ReError.fatal("Handshake failed, server returned: \(s ?? "")") 106 | self.state = .error(e) 107 | return callback(e) 108 | } 109 | 110 | case .v1_0: 111 | do { 112 | if let replyString = s { 113 | /* The reply is a JSON object containing the keys 'success' (should be true), 'min_protocol_version', 114 | 'max_protocol_version' and 'server_version'. */ 115 | let reply = try JSONSerialization.jsonObject(with: replyString.data(using: String.Encoding.ascii)!, options: []) 116 | 117 | if let replyDictionary = reply as? [String: AnyObject], let success = replyDictionary["success"] as? NSNumber, success.boolValue { 118 | self.performSCRAMAuthentication(username, password: password) { err in 119 | if err == nil { 120 | // Start read loop 121 | self.state = .connected 122 | self.startReading() 123 | return callback(nil) 124 | } 125 | else { 126 | self.state = .error(err!) 127 | return callback(err) 128 | } 129 | } 130 | } 131 | else { 132 | /* On error, the server will return a null-terminated error string (non JSON), 133 | or a JSON object with 'success' set to false. */ 134 | let e = ReError.fatal("Server returned \(replyString)") 135 | self.state = .error(e) 136 | return callback(e) 137 | } 138 | } 139 | else { 140 | let e = ReError.fatal("Handshake failed, server returned: \(s ?? "")") 141 | self.state = .error(e) 142 | return callback(e) 143 | } 144 | } 145 | catch let error { 146 | return callback(ReError.fatal(error.localizedDescription)) 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | private func performSCRAMAuthentication(_ username: String, password: String, callback: @escaping (ReError?) -> ()) { 156 | assert(self.protocolVersion == .v1_0, "SCRAM authentication not supported with protocol version \(self.protocolVersion)") 157 | let scram = SCRAM(username: username, password: password) 158 | var zeroByte: UInt8 = 0x00 159 | 160 | // Send authentication first message 161 | do { 162 | let firstMessage: [String: Any] = ["protocol_version": 0, "authentication_method": "SCRAM-SHA-256", "authentication": scram.clientFirstMessage] 163 | let data = try JSONSerialization.data(withJSONObject: firstMessage, options: []) 164 | var zeroTerminatedData = NSData(data: data) as Data 165 | zeroTerminatedData.append(&zeroByte, count: 1) 166 | 167 | self.socket.write(zeroTerminatedData) { err in 168 | if let e = err { 169 | return callback(ReError.fatal(e)) 170 | } 171 | 172 | // Server should send us some JSON back 173 | self.socket.readZeroTerminatedASCII() { replyString in 174 | do { 175 | if let s = replyString { 176 | if let reply = try JSONSerialization.jsonObject(with: s.data(using: String.Encoding.ascii)!, options: []) as? [String: AnyObject] { 177 | if let success = reply["success"] as? NSNumber, success.boolValue { 178 | let authData = reply["authentication"] as! String 179 | if let shouldSend = scram.receive(authData) { 180 | // Send the generated reply back 181 | let secondMessage = [ 182 | "authentication": shouldSend 183 | ] 184 | let secondReply = try JSONSerialization.data(withJSONObject: secondMessage, options: []) 185 | var zeroSecondReply = NSData(data: secondReply) as Data 186 | zeroSecondReply.append(&zeroByte, count: 1) 187 | 188 | self.socket.write(zeroSecondReply) { err in 189 | if let e = err { 190 | return callback(ReError.fatal(e)) 191 | } 192 | 193 | // Verify server signature 194 | self.socket.readZeroTerminatedASCII() { replyString in 195 | do { 196 | if let s = replyString { 197 | if let reply = try JSONSerialization.jsonObject(with: s.data(using: String.Encoding.ascii)!, options: []) as? [String: AnyObject] { 198 | if let success = reply["success"] as? NSNumber, success.boolValue { 199 | let authData = reply["authentication"] as! String 200 | scram.receive(authData) 201 | if scram.authenticated { 202 | return callback(nil) 203 | } 204 | else { 205 | return callback(ReError.fatal("SCRAM authentication invalid!")) 206 | } 207 | } 208 | else { 209 | if let errorString = reply["error"] as? String { 210 | return callback(ReError.fatal(errorString)) 211 | } 212 | else { 213 | return callback(ReError.fatal("Server returned \(s)")) 214 | } 215 | } 216 | } 217 | else { 218 | return callback(ReError.fatal("Server returned \(s)")) 219 | } 220 | } 221 | else { 222 | return callback(ReError.fatal("Server did not return a server final message in SCRAM exchange")) 223 | } 224 | } 225 | catch let error { 226 | return callback(ReError.fatal(error.localizedDescription)) 227 | } 228 | } 229 | } 230 | } 231 | else { 232 | return callback(ReError.fatal("SCRAM authentication failed")) 233 | } 234 | } 235 | else { 236 | if let errorString = reply["error"] as? String { 237 | return callback(ReError.fatal(errorString)) 238 | } 239 | else { 240 | return callback(ReError.fatal("Server returned \(s)")) 241 | } 242 | } 243 | } 244 | else { 245 | return callback(ReError.fatal("Server returned \(s)")) 246 | } 247 | } 248 | else { 249 | return callback(ReError.fatal("Server did not return a reply to our first authentication message")) 250 | } 251 | } 252 | catch let error { 253 | return callback(ReError.fatal(error.localizedDescription)) 254 | } 255 | } 256 | } 257 | } 258 | catch let error { 259 | return callback(ReError.fatal(error.localizedDescription)) 260 | } 261 | } 262 | 263 | public func close() { 264 | self.queue.async { 265 | self.socket.disconnect() 266 | self.state = .unconnected 267 | self.outstandingQueries.removeAll() 268 | } 269 | } 270 | 271 | public var connected: Bool { 272 | if case ReConnectionState.connected = state { 273 | return true 274 | } 275 | return false 276 | } 277 | 278 | public var error: ReError? { 279 | switch self.state { 280 | case .error(let e): 281 | return e 282 | default: return nil 283 | } 284 | } 285 | 286 | private func startReading() { 287 | self.socket.read(8 + 4) { data in 288 | if let d = data { 289 | let queryToken = d.readLittleEndianUInt64(0) 290 | let responseSize = d.readLittleEndianUInt32(8) 291 | 292 | self.socket.read(Int(responseSize), callback: { data in 293 | if let d = data { 294 | assert(d.count == Int(responseSize)) 295 | 296 | var called = false 297 | let continuation: ReResponse.ContinuationCallback = { [weak self] (cb: @escaping ReResponse.Callback) -> () in 298 | assert(!called, "continuation callback for query token \(queryToken) must never be called more than once") 299 | called = true 300 | self?.sendContinuation(queryToken, callback: cb) 301 | } 302 | 303 | self.queue.async { 304 | if let handler = self.outstandingQueries[queryToken] { 305 | if let response = ReResponse(json: d, continuation: continuation) { 306 | self.outstandingQueries.removeValue(forKey: queryToken) 307 | handler(response) 308 | self.startReading() 309 | } 310 | else { 311 | self.state = .error(ReError.fatal("Invalid response object from server")) 312 | } 313 | } 314 | } 315 | } 316 | else { 317 | self.state = .error(ReError.fatal("Disconnected")) 318 | } 319 | }) 320 | } 321 | else { 322 | self.state = .error(ReError.fatal("Disconnected")) 323 | } 324 | } 325 | } 326 | 327 | private func sendContinuation(_ token: ReQueryToken, callback: @escaping ReResponse.Callback) { 328 | let json = [ReProtocol.ReQueryType.continue.rawValue]; 329 | let query = try! JSONSerialization.data(withJSONObject: json, options: []) 330 | self.sendQuery(query, token: token, callback: callback) 331 | } 332 | 333 | private func dummy() { 334 | } 335 | 336 | private func sendQuery(_ query: Data, token: ReQueryToken, callback: @escaping ReResponse.Callback) { 337 | queue.async { 338 | assert(self.outstandingQueries[token] == nil, "A query with token \(token) is already outstanding") 339 | assert(self.connected, "Cannot send a query when the connection is not open") 340 | let data = NSMutableData(capacity: query.count + 8 + 4)! 341 | 342 | let reffingCallback: ReResponse.Callback = { (res) -> () in 343 | // This is used to create a reference to ReConnection, and keeps it alive at least until the query has finished. 344 | self.dummy() 345 | callback(res) 346 | } 347 | 348 | 349 | data.append(Data.dataWithLittleEndianOf(token)) 350 | data.append(Data.dataWithLittleEndianOf(UInt32(query.count))) 351 | data.append(query) 352 | self.socket.write(data as Data) { err in 353 | if let e = err { 354 | self.state = .error(ReError.fatal(e)) 355 | callback(ReResponse.error(e)) 356 | } 357 | else { 358 | self.outstandingQueries[token] = reffingCallback 359 | } 360 | } 361 | } 362 | } 363 | 364 | internal func startQuery(_ query: Data, callback: @escaping ReResponse.Callback) throws { 365 | queue.async { 366 | let token = ReConnection.tokenCounter.next() 367 | self.sendQuery(query, token: token, callback: callback) 368 | } 369 | } 370 | } 371 | 372 | private enum ReConnectionState { 373 | case unconnected // Nothing has been done yet 374 | case handshakeSent // Our handshake has been sent, we are waiting for confirmation of success 375 | case connected // Handshake has been completed, and we are awaiting respones from the server 376 | case error(ReError) // A protocol error has occurred 377 | case terminated // The connection has been terminated 378 | } 379 | 380 | private extension Data { 381 | static func dataWithLittleEndianOf(_ nr: UInt64) -> Data { 382 | var swapped = CFSwapInt64HostToLittle(nr) 383 | 384 | var bytes: [UInt8] = [0,0,0,0,0,0,0,0] 385 | for i in 0...7 { 386 | bytes[i] = UInt8(swapped & 0xFF) 387 | swapped = swapped >> 8 388 | } 389 | 390 | return Data(bytes: UnsafePointer(bytes), count: 8) 391 | } 392 | 393 | static func dataWithLittleEndianOf(_ nr: UInt32) -> Data { 394 | var swapped = CFSwapInt32HostToLittle(nr) // No-op on little endian archs 395 | 396 | var bytes: [UInt8] = [0,0,0,0] 397 | for i in 0...3 { 398 | bytes[i] = UInt8(swapped & 0xFF) 399 | swapped = swapped >> 8 400 | } 401 | 402 | return Data(bytes: UnsafePointer(bytes), count: 4) 403 | } 404 | 405 | func readLittleEndianUInt64(_ atIndex: Int = 0) -> UInt64 { 406 | assert(self.count >= atIndex + 8) 407 | 408 | var read: UInt64 = 0 409 | self.withUnsafeBytes { (buffer: UnsafePointer) in 410 | for i in (0...7).reversed() { 411 | read = (read << 8) + UInt64(buffer[atIndex + i]) 412 | } 413 | } 414 | 415 | return CFSwapInt64LittleToHost(read) 416 | } 417 | 418 | func readLittleEndianUInt32(_ atIndex: Int = 0) -> UInt32 { 419 | assert(self.count >= (atIndex + 4)) 420 | 421 | var read: UInt32 = 0 422 | self.withUnsafeBytes { (buffer: UnsafePointer) in 423 | for i in (0...3).reversed() { 424 | read = (read << 8) + UInt32(buffer[atIndex + i]) 425 | } 426 | } 427 | 428 | return CFSwapInt32LittleToHost(read) 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /Sources/Rethink/ReDatum.swift: -------------------------------------------------------------------------------- 1 | /** Rethink.swift 2 | Copyright (c) 2015 Pixelspark 3 | Author: Tommy van der Vorst (tommy@pixelspark.nl) 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. **/ 25 | import Foundation 26 | 27 | public typealias ReDocument = [String: Any] 28 | 29 | public class ReDatum: ReQueryValue { 30 | public let jsonSerialization: Any 31 | private static let reqlTypeTime = "TIME" 32 | private static let reqlTypeBinary = "BINARY" 33 | private static let reqlSpecialKey = "$reql_type$" 34 | 35 | internal init() { 36 | self.jsonSerialization = NSNull() 37 | } 38 | 39 | internal init(string: String) { 40 | self.jsonSerialization = string 41 | } 42 | 43 | internal init(double: Double) { 44 | self.jsonSerialization = double 45 | } 46 | 47 | internal init(int: Int) { 48 | self.jsonSerialization = int 49 | } 50 | 51 | internal init(bool: Bool) { 52 | self.jsonSerialization = bool 53 | } 54 | 55 | internal init(array: [ReQueryValue]) { 56 | self.jsonSerialization = [ReTerm.make_array.rawValue, array.map { return $0.jsonSerialization }] 57 | } 58 | 59 | internal init(document: ReDocument) { 60 | self.jsonSerialization = document 61 | } 62 | 63 | internal init(object: [String: ReQueryValue]) { 64 | var serialized: [String: Any] = [:] 65 | for (key, value) in object { 66 | serialized[key] = value.jsonSerialization 67 | } 68 | self.jsonSerialization = serialized 69 | } 70 | 71 | internal init(date: Date) { 72 | self.jsonSerialization = [ReDatum.reqlSpecialKey: ReDatum.reqlTypeTime, "epoch_time": date.timeIntervalSince1970, "timezone": "+00:00"] 73 | } 74 | 75 | internal init(data: Data) { 76 | self.jsonSerialization = [ReDatum.reqlSpecialKey: ReDatum.reqlTypeBinary, "data": data.base64EncodedString(options: [])] 77 | } 78 | 79 | internal init(jsonSerialization: Any) { 80 | self.jsonSerialization = jsonSerialization 81 | } 82 | 83 | internal var value: Any { get { 84 | if let d = self.jsonSerialization as? [String: Any], let t = d[ReDatum.reqlSpecialKey] as? String { 85 | if t == ReDatum.reqlTypeBinary { 86 | if let data = (self.jsonSerialization as AnyObject).value(forKey: "data") as? String { 87 | return Data(base64Encoded: data, options: [])! 88 | } 89 | else { 90 | fatalError("invalid binary datum received") 91 | } 92 | } 93 | else if t == ReDatum.reqlTypeTime { 94 | let epochTime = (self.jsonSerialization as AnyObject).value(forKey: "epoch_time") as AnyObject 95 | 96 | if let timezone = (self.jsonSerialization as AnyObject).value(forKey: "timezone") as? String { 97 | // TODO: interpret server timezone other than +00:00 (UTC) 98 | assert(timezone == "+00:00", "support for timezones other than UTC not implemented (yet)") 99 | return Date(timeIntervalSince1970: epochTime as! Double) 100 | } 101 | else { 102 | fatalError("invalid date received") 103 | } 104 | } 105 | else { 106 | fatalError("unrecognized $reql_type$ in serialized data") 107 | } 108 | } 109 | else { 110 | return self.jsonSerialization 111 | } 112 | } } 113 | } 114 | -------------------------------------------------------------------------------- /Sources/Rethink/ReProtocol.swift: -------------------------------------------------------------------------------- 1 | /** Rethink.swift 2 | Copyright (c) 2015 Pixelspark 3 | Author: Tommy van der Vorst (tommy@pixelspark.nl) 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. **/ 25 | import Foundation 26 | 27 | public enum ReProtocolVersion { 28 | case v0_4 29 | case v1_0 30 | 31 | var protocolVersionCode: UInt32 { 32 | switch self { 33 | case .v0_4: return 0x400c2d20 34 | case .v1_0: return 0x34c2bdc3 35 | } 36 | } 37 | } 38 | 39 | /** Protocol constants. */ 40 | internal class ReProtocol { 41 | static let defaultPort = 28015 42 | 43 | static let protocolType: UInt32 = 0x7e6970c7 // JSON 44 | static let handshakeSuccessResponse = "SUCCESS" 45 | static let defaultUser = "admin" 46 | static let defaultPassword = "" 47 | 48 | static let responseTypeSuccessAtom = 1 49 | static let responseTypeSuccessSequence = 2 50 | static let responseTypeSuccessPartial = 3 51 | static let responseTypeWaitComplete = 4 52 | static let responseTypeClientError = 16 53 | static let responseTypeCompileError = 17 54 | static let responseTypeRuntimeError = 18 55 | 56 | enum ReQueryType: Int { 57 | case start = 1 58 | case `continue` = 2 59 | case stop = 3 60 | case noreply_WAIT = 4 61 | } 62 | } 63 | 64 | /** These constants can be found in the RethinkDB Java driver source code: 65 | https://github.com/rethinkdb/rethinkdb/blob/next/drivers/java/term_info.json */ 66 | internal enum ReTerm: Int { 67 | case datum = 1 68 | case make_array = 2 69 | case make_obj = 3 70 | case `var` = 10 71 | case javascript = 11 72 | case uuid = 169 73 | case http = 153 74 | case error = 12 75 | case implicit_VAR = 13 76 | case db = 14 77 | case table = 15 78 | case get = 16 79 | case get_ALL = 78 80 | case eq = 17 81 | case ne = 18 82 | case lt = 19 83 | case le = 20 84 | case gt = 21 85 | case ge = 22 86 | case not = 23 87 | case add = 24 88 | case sub = 25 89 | case mul = 26 90 | case div = 27 91 | case mod = 28 92 | case floor = 183 93 | case ceil = 184 94 | case round = 185 95 | case append = 29 96 | case prepend = 80 97 | case difference = 95 98 | case set_insert = 88 99 | case set_intersection = 89 100 | case set_union = 90 101 | case set_difference = 91 102 | case slice = 30 103 | case skip = 70 104 | case limit = 71 105 | case offsets_OF = 87 106 | case contains = 93 107 | case get_field = 31 108 | case keys = 94 109 | case object = 143 110 | case has_fields = 32 111 | case with_fields = 96 112 | case pluck = 33 113 | case without = 34 114 | case merge = 35 115 | case between_deprecated = 36 116 | case between = 182 117 | case reduce = 37 118 | case map = 38 119 | case filter = 39 120 | case concat_MAP = 40 121 | case order_BY = 41 122 | case distinct = 42 123 | case count = 43 124 | case is_EMPTY = 86 125 | case union = 44 126 | case nth = 45 127 | case bracket = 170 128 | case inner_join = 48 129 | case outer_join = 49 130 | case eq_join = 50 131 | case zip = 72 132 | case range = 173 133 | case insert_at = 82 134 | case delete_at = 83 135 | case change_at = 84 136 | case splice_at = 85 137 | case coerce_to = 51 138 | case type_of = 52 139 | case update = 53 140 | case delete = 54 141 | case replace = 55 142 | case insert = 56 143 | case db_create = 57 144 | case db_drop = 58 145 | case db_list = 59 146 | case table_create = 60 147 | case table_drop = 61 148 | case table_list = 62 149 | case config = 174 150 | case status = 175 151 | case wait = 177 152 | case reconfigure = 176 153 | case rebalance = 179 154 | case sync = 138 155 | case index_create = 75 156 | case index_drop = 76 157 | case index_list = 77 158 | case index_status = 139 159 | case index_wait = 140 160 | case index_rename = 156 161 | case funcall = 64 162 | case branch = 65 163 | case or = 66 164 | case and = 67 165 | case for_each = 68 166 | case `func` = 69 167 | case asc = 73 168 | case desc = 74 169 | case info = 79 170 | case match = 97 171 | case upcase = 141 172 | case downcase = 142 173 | case sample = 81 174 | case `default` = 92 175 | case json = 98 176 | case to_json_string = 172 177 | case iso8601 = 99 178 | case to_ISO8601 = 100 179 | case epoch_time = 101 180 | case to_epoch_time = 102 181 | case now = 103 182 | case in_timezone = 104 183 | case during = 105 184 | case date = 106 185 | case time_of_day = 126 186 | case timezone = 127 187 | case year = 128 188 | case month = 129 189 | case day = 130 190 | case day_of_week = 131 191 | case day_of_year = 132 192 | case hours = 133 193 | case minutes = 134 194 | case seconds = 135 195 | case time = 136 196 | case monday = 107 197 | case tuesday = 108 198 | case wednesday = 109 199 | case thursday = 110 200 | case friday = 111 201 | case saturday = 112 202 | case sunday = 113 203 | case january = 114 204 | case february = 115 205 | case march = 116 206 | case april = 117 207 | case may = 118 208 | case june = 119 209 | case july = 120 210 | case august = 121 211 | case september = 122 212 | case october = 123 213 | case november = 124 214 | case december = 125 215 | case literal = 137 216 | case group = 144 217 | case sum = 145 218 | case avg = 146 219 | case min = 147 220 | case max = 148 221 | case split = 149 222 | case ungroup = 150 223 | case random = 151 224 | case changes = 152 225 | case args = 154 226 | case binary = 155 227 | case geojson = 157 228 | case to_geojson = 158 229 | case point = 159 230 | case line = 160 231 | case polygon = 161 232 | case distance = 162 233 | case intersects = 163 234 | case includes = 164 235 | case circle = 165 236 | case get_intersecting = 166 237 | case fill = 167 238 | case get_nearest = 168 239 | case polygon_sub = 171 240 | case minval = 180 241 | case maxval = 181 242 | case fold = 187 243 | case grant = 188 244 | } 245 | 246 | internal typealias ReQueryToken = UInt64 247 | 248 | internal class ReTokenCounter { 249 | private var nextQueryToken: UInt64 = 0x5ADFACE 250 | private let mutex = Mutex() 251 | 252 | func next() -> ReQueryToken { 253 | return self.mutex.locked { 254 | let nt = self.nextQueryToken 255 | self.nextQueryToken += 1 256 | return nt 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /Sources/Rethink/ReResponse.swift: -------------------------------------------------------------------------------- 1 | /** Rethink.swift 2 | Copyright (c) 2016 Pixelspark 3 | Author: Tommy van der Vorst (tommy@pixelspark.nl) 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. **/ 25 | import Foundation 26 | 27 | public enum ReError { 28 | case fatal(String) 29 | case other(Error) 30 | 31 | public var localizedDescription: String { 32 | switch self { 33 | case .fatal(let d): return d 34 | case .other(let e): return e.localizedDescription 35 | } 36 | } 37 | } 38 | 39 | public enum ReResponse { 40 | public typealias Callback = (ReResponse) -> () 41 | public typealias ContinuationCallback = (@escaping Callback) -> () 42 | 43 | case error(String) 44 | case Value(Any) 45 | case rows([ReDocument], ContinuationCallback?) 46 | case unknown 47 | 48 | init?(json: Data, continuation: @escaping ContinuationCallback) { 49 | do { 50 | if let d = try JSONSerialization.jsonObject(with: json, options: []) as? NSDictionary { 51 | if let type = d.value(forKey: "t") as? NSNumber { 52 | switch type.intValue { 53 | case ReProtocol.responseTypeSuccessAtom: 54 | guard let r = d.value(forKey: "r") as? [Any] else { return nil } 55 | if r.count != 1 { return nil } 56 | self = .Value(ReDatum(jsonSerialization: r.first!).value) 57 | 58 | case ReProtocol.responseTypeSuccessPartial, ReProtocol.responseTypeSuccessSequence: 59 | if let r = d.value(forKey: "r") as? [[String: Any]] { 60 | let deserialized = r.map { (document) -> ReDocument in 61 | var dedoc: ReDocument = [:] 62 | for (k, v) in document { 63 | dedoc[k] = ReDatum(jsonSerialization: v).value 64 | } 65 | return dedoc 66 | } 67 | 68 | let ccb: ContinuationCallback? = (type.intValue == ReProtocol.responseTypeSuccessPartial) ? continuation: nil 69 | self = .rows(deserialized, ccb) 70 | } 71 | else if let r = d.value(forKey: "r") as? [Any] { 72 | let deserialized = r.map { (value) -> Any in 73 | return ReDatum(jsonSerialization: value).value 74 | } 75 | 76 | self = .Value(deserialized) 77 | } 78 | else { 79 | return nil 80 | } 81 | 82 | case ReProtocol.responseTypeClientError: 83 | guard let r = d.value(forKey: "r") as? [Any] else { return nil } 84 | if r.count != 1 { return nil } 85 | self = .error("Client error: \(r.first!)") 86 | 87 | case ReProtocol.responseTypeCompileError: 88 | guard let r = d.value(forKey: "r") as? [Any] else { return nil } 89 | if r.count != 1 { return nil } 90 | self = .error("Compile error: \(r.first!)") 91 | 92 | case ReProtocol.responseTypeRuntimeError: 93 | guard let r = d.value(forKey: "r") as? [Any] else { return nil } 94 | if r.count != 1 { return nil } 95 | self = .error("Run-time error: \(r.first!)") 96 | 97 | default: 98 | self = .unknown 99 | } 100 | } 101 | else { 102 | return nil 103 | } 104 | } 105 | else { 106 | return nil 107 | } 108 | } 109 | catch { 110 | return nil 111 | } 112 | } 113 | 114 | public var isError: Bool { 115 | switch self { 116 | case .error(_): return true 117 | default: return false 118 | } 119 | } 120 | 121 | public var value: Any? { 122 | switch self { 123 | case .Value(let v): 124 | return v 125 | 126 | default: 127 | return nil 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Sources/Rethink/ReSocket.swift: -------------------------------------------------------------------------------- 1 | /** Rethink.swift 2 | Copyright (c) 2016 Pixelspark 3 | Author: Tommy van der Vorst (tommy@pixelspark.nl) 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. **/ 25 | import Foundation 26 | import GCDAsyncSocket 27 | 28 | internal enum ReSocketState { 29 | case unconnected 30 | case connecting 31 | case connected 32 | } 33 | 34 | internal class ReSocket: NSObject, GCDAsyncSocketDelegate { 35 | typealias WriteCallback = (String?) -> () 36 | typealias ReadCallback = (Data?) -> () 37 | 38 | let socket: GCDAsyncSocket 39 | internal var state: ReSocketState = .unconnected 40 | 41 | private var onConnect: ((String?) -> ())? 42 | private var writeCallbacks: [Int: WriteCallback] = [:] 43 | private var readCallbacks: [Int: ReadCallback] = [:] 44 | 45 | init(queue: DispatchQueue) { 46 | self.socket = GCDAsyncSocket(delegate: nil, delegateQueue: queue) 47 | super.init() 48 | self.socket.delegate = self 49 | } 50 | 51 | func connect(_ url: URL, withTimeout timeout: TimeInterval = 5.0, callback: @escaping (String?) -> ()) { 52 | assert(self.state == .unconnected, "Already connected or connecting") 53 | self.onConnect = callback 54 | self.state = .connecting 55 | 56 | guard let host = url.host else { return callback("Invalid URL") } 57 | let port = (url as NSURL).port ?? 28015 58 | 59 | do { 60 | try socket.connect(toHost: host, onPort: port.uint16Value, withTimeout: timeout) 61 | } 62 | catch let e { 63 | return callback(e.localizedDescription) 64 | } 65 | } 66 | 67 | @objc internal func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16) { 68 | self.state = .connected 69 | self.onConnect?(nil) 70 | } 71 | 72 | @objc internal func socketDidDisconnect(_ sock: GCDAsyncSocket, withError err: Error?) { 73 | self.state = .unconnected 74 | } 75 | 76 | func read(_ length: Int, callback: @escaping ReadCallback) { 77 | assert(length > 0, "Length cannot be zero or less") 78 | 79 | if self.state != .connected { 80 | return callback(nil) 81 | } 82 | 83 | socket.delegateQueue!.async { 84 | let tag = (self.readCallbacks.count + 1) 85 | self.readCallbacks[tag] = callback 86 | self.socket.readData(toLength: UInt(length), withTimeout: -1.0, tag: tag) 87 | } 88 | } 89 | 90 | func readZeroTerminatedASCII(_ callback: @escaping (String?) -> ()) { 91 | if self.state != .connected { 92 | return callback(nil) 93 | } 94 | 95 | let zero = Data(bytes: UnsafePointer([UInt8(0)]), count: 1) 96 | socket.delegateQueue!.async { 97 | let tag = (self.readCallbacks.count + 1) 98 | self.readCallbacks[tag] = { data in 99 | if let d = data { 100 | if let s = NSString(data: d.subdata(in: 0..<(d.count-1)), encoding: String.Encoding.ascii.rawValue) { 101 | callback(String(s)) 102 | } 103 | else { 104 | callback(nil) 105 | } 106 | } 107 | else { 108 | callback(nil) 109 | } 110 | } 111 | self.socket.readData(to: zero, withTimeout: -1.0, tag: tag) 112 | } 113 | } 114 | 115 | func write(_ data: Data, callback: @escaping WriteCallback) { 116 | if self.state != .connected { 117 | return callback("socket is not connected!") 118 | } 119 | 120 | socket.delegateQueue!.async { 121 | let tag = (self.writeCallbacks.count + 1) 122 | self.writeCallbacks[tag] = callback 123 | self.socket.write(data, withTimeout: -1.0, tag: tag) 124 | } 125 | } 126 | 127 | @objc internal func socket(_ sock: GCDAsyncSocket, didWriteDataWithTag tag: Int) { 128 | socket.delegateQueue!.async { 129 | if let cb = self.writeCallbacks[tag] { 130 | cb(nil) 131 | self.writeCallbacks.removeValue(forKey: tag) 132 | } 133 | } 134 | } 135 | 136 | @objc internal func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) { 137 | socket.delegateQueue!.async { 138 | if let cb = self.readCallbacks[tag] { 139 | cb(data) 140 | self.readCallbacks.removeValue(forKey: tag) 141 | } 142 | } 143 | } 144 | 145 | func disconnect() { 146 | self.socket.disconnect() 147 | self.state = .unconnected 148 | } 149 | 150 | deinit { 151 | self.socket.disconnect() 152 | } 153 | } 154 | 155 | /** A pthread-based recursive mutex lock. */ 156 | internal class Mutex { 157 | private var mutex: pthread_mutex_t = pthread_mutex_t() 158 | 159 | public init() { 160 | var attr: pthread_mutexattr_t = pthread_mutexattr_t() 161 | pthread_mutexattr_init(&attr) 162 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) 163 | 164 | let err = pthread_mutex_init(&self.mutex, &attr) 165 | pthread_mutexattr_destroy(&attr) 166 | 167 | switch err { 168 | case 0: 169 | // Success 170 | break 171 | 172 | default: 173 | fatalError("Could not create mutex, error \(err)") 174 | } 175 | } 176 | 177 | private final func lock() { 178 | let ret = pthread_mutex_lock(&self.mutex) 179 | switch ret { 180 | case 0: 181 | // Success 182 | break 183 | 184 | default: 185 | fatalError("Could not lock mutex: error \(ret)") 186 | } 187 | } 188 | 189 | private final func unlock() { 190 | let ret = pthread_mutex_unlock(&self.mutex) 191 | switch ret { 192 | case 0: 193 | // Success 194 | break 195 | default: 196 | fatalError("Could not unlock mutex: error \(ret)") 197 | } 198 | } 199 | 200 | deinit { 201 | assert(pthread_mutex_trylock(&self.mutex) == 0 && pthread_mutex_unlock(&self.mutex) == 0, "deinitialization of a locked mutex results in undefined behavior!") 202 | pthread_mutex_destroy(&self.mutex) 203 | } 204 | 205 | @discardableResult public final func locked(_ file: StaticString = #file, line: UInt = #line, block: () -> (T)) -> T { 206 | self.lock() 207 | defer { 208 | self.unlock() 209 | } 210 | let ret: T = block() 211 | return ret 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Sources/Rethink/Rethink.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | FOUNDATION_EXPORT double RethinkVersionNumber; 4 | FOUNDATION_EXPORT const unsigned char RethinkVersionString[]; 5 | -------------------------------------------------------------------------------- /Sources/Rethink/Rethink.swift: -------------------------------------------------------------------------------- 1 | /** Rethink.swift 2 | Copyright (c) 2015 Pixelspark 3 | Author: Tommy van der Vorst (tommy@pixelspark.nl) 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. **/ 25 | import Foundation 26 | 27 | public class R { 28 | internal static func optargs(_ args: [T]) -> Any { 29 | var dict: [String: Any] = [:] 30 | for arg in args { 31 | assert(dict[arg.serialization.0] == nil, "an optional argument may only be specified once") 32 | dict[arg.serialization.0] = arg.serialization.1 33 | } 34 | return dict 35 | } 36 | 37 | public static func connect(_ url: URL, user: String = ReProtocol.defaultUser, password: String = ReProtocol.defaultPassword, version: ReProtocolVersion = .v1_0, callback: @escaping (ReError?, ReConnection) -> ()) { 38 | let c = ReConnection(url: url, protocolVersion: version) 39 | c.connect(user, password: password) { err in 40 | callback(err, c) 41 | } 42 | } 43 | 44 | public static func uuid() -> ReQueryValue { 45 | return ReDatum(jsonSerialization: [ReTerm.uuid.rawValue]) 46 | } 47 | 48 | public static func db(_ name: String) -> ReQueryDatabase { 49 | return ReQueryDatabase(name: name) 50 | } 51 | 52 | public static func dbCreate(_ name: String) -> ReQuery { 53 | return ReDatum(jsonSerialization: [ReTerm.db_create.rawValue, [name]]) 54 | } 55 | 56 | public static func dbDrop(_ name: String) -> ReQuery { 57 | return ReDatum(jsonSerialization: [ReTerm.db_drop.rawValue, [name]]) 58 | } 59 | 60 | public static func dbList() -> ReQuerySequence { 61 | return ReQuerySequence(jsonSerialization: [ReTerm.db_list.rawValue]) 62 | } 63 | 64 | public static func point(_ longitude: Double, latitude: Double) -> ReQueryPoint { 65 | return ReQueryPoint(longitude: longitude, latitude:latitude) 66 | } 67 | 68 | public static func expr(_ string: String) -> ReQueryValue { 69 | return ReDatum(string: string) 70 | } 71 | 72 | public static func expr(_ double: Double) -> ReQueryValue { 73 | return ReDatum(double: double) 74 | } 75 | 76 | public static func expr(_ int: Int) -> ReQueryValue { 77 | return ReDatum(int: int) 78 | } 79 | 80 | public static func expr(_ binary: Data) -> ReQueryValue { 81 | return ReDatum(data: binary) 82 | } 83 | 84 | public static func expr(_ date: Date) -> ReQueryValue { 85 | return ReDatum(date: date) 86 | } 87 | 88 | public static func expr(_ bool: Bool) -> ReQueryValue { 89 | return ReDatum(bool: bool) 90 | } 91 | 92 | public static func expr() -> ReQueryValue { 93 | return ReDatum() 94 | } 95 | 96 | public static func expr(_ document: ReDocument) -> ReQueryValue { 97 | return ReDatum(document: document) 98 | } 99 | 100 | public static func expr(_ object: [String: ReQueryValue]) -> ReQueryValue { 101 | return ReDatum(object: object) 102 | } 103 | 104 | public static func expr(_ array: [ReQueryValue]) -> ReQueryValue { 105 | return ReDatum(jsonSerialization: [ReTerm.make_array.rawValue, array.map { return $0.jsonSerialization }]) 106 | } 107 | 108 | public static func not(_ value: ReQueryValue) -> ReQueryValue { 109 | return value.not() 110 | } 111 | 112 | public static func random(_ lower: Int, _ upperOpen: Int) -> ReQueryValue { 113 | return ReDatum(jsonSerialization: [ReTerm.random.rawValue, [lower, upperOpen]]) 114 | } 115 | 116 | public static func random(_ lower: Double, _ upperOpen: Double) -> ReQueryValue { 117 | return ReDatum(jsonSerialization: [ReTerm.random.rawValue, [lower, upperOpen], ["float": true]]) 118 | } 119 | 120 | public static func random(_ lower: ReQueryValue, _ upperOpen: ReQueryValue, float: Bool = false) -> ReQueryValue { 121 | return ReDatum(jsonSerialization: [ReTerm.random.rawValue, [lower.jsonSerialization, upperOpen.jsonSerialization], ["float": float]]) 122 | } 123 | 124 | public static func random() -> ReQueryValue { 125 | return ReDatum(jsonSerialization: [ReTerm.random.rawValue, []]) 126 | } 127 | 128 | public static func round(_ value: ReQueryValue) -> ReQueryValue { 129 | return value.round() 130 | } 131 | 132 | public static func ceil(_ value: ReQueryValue) -> ReQueryValue { 133 | return value.ceil() 134 | } 135 | 136 | public static func floor(_ value: ReQueryValue) -> ReQueryValue { 137 | return value.floor() 138 | } 139 | 140 | public static func now() -> ReQueryValue { 141 | return ReDatum(jsonSerialization: [ReTerm.now.rawValue]) 142 | } 143 | 144 | public static func ISO8601(_ date: String) -> ReQueryValue { 145 | return ReDatum(jsonSerialization: [ReTerm.iso8601.rawValue, [date]]) 146 | } 147 | 148 | public static func error(_ message: String) -> ReQueryValue { 149 | return ReDatum(jsonSerialization: [ReTerm.error.rawValue, [message]]) 150 | } 151 | 152 | public static func branch(_ test: ReQuery, ifTrue: ReQuery, ifFalse: ReQuery) -> ReQueryValue { 153 | return ReDatum(jsonSerialization: [ReTerm.branch.rawValue, [test.jsonSerialization, ifTrue.jsonSerialization, ifFalse.jsonSerialization]]) 154 | } 155 | 156 | public static func object(_ key: ReQueryValue, value: ReQueryValue) -> ReQueryValue { 157 | return ReDatum(jsonSerialization: [ReTerm.object.rawValue, [key.jsonSerialization, value.jsonSerialization]]) 158 | } 159 | 160 | public static func range(_ start: ReQueryValue, _ end: ReQueryValue) -> ReQuerySequence { 161 | return ReQuerySequence(jsonSerialization: [ReTerm.range.rawValue, [start.jsonSerialization, end.jsonSerialization]]) 162 | } 163 | 164 | public static func js(_ source: String) -> ReQueryValue { 165 | return ReDatum(jsonSerialization: [ReTerm.javascript.rawValue, [source]]) 166 | } 167 | 168 | public static func json(_ source: String) -> ReQueryValue { 169 | return ReDatum(jsonSerialization: [ReTerm.json.rawValue, [source]]) 170 | } 171 | 172 | public static func grant(_ userName: String, permissions: RePermission...) -> ReQueryValue { 173 | return ReDatum(jsonSerialization: [ReTerm.grant.rawValue, [userName], R.optargs(permissions)]) 174 | } 175 | 176 | public static var minVal: ReQueryValue { 177 | return ReDatum(jsonSerialization: [ReTerm.minval.rawValue]) 178 | } 179 | 180 | public static var maxVal: ReQueryValue { 181 | return ReDatum(jsonSerialization: [ReTerm.maxval.rawValue]) 182 | } 183 | 184 | /** Construct a circular line or polygon. A circle in RethinkDB is a polygon or line approximating a circle of a given 185 | radius around a given center, consisting of a specified number of vertices (default 32). 186 | 187 | The center may be specified either by two floating point numbers, the latitude (−90 to 90) and longitude (−180 to 180) 188 | of the point on a perfect sphere (see Geospatial support for more information on ReQL’s coordinate system), or by a 189 | point object. The radius is a floating point number whose units are meters by default, although that may be changed 190 | with the unit argument. */ 191 | public static func circle(_ longitude: ReQueryValue, latitude: ReQueryValue, radius: ReQueryValue, options: ReCircleArg...) -> ReQueryValue { 192 | return ReDatum(jsonSerialization: [ReTerm.circle.rawValue, [longitude.jsonSerialization, latitude.jsonSerialization, radius.jsonSerialization], R.optargs(options)]) 193 | } 194 | 195 | public static func circle(_ point: ReQueryPoint, radius: ReQueryValue, options: ReCircleArg...) -> ReQueryValue { 196 | return ReDatum(jsonSerialization: [ReTerm.circle.rawValue, [point.jsonSerialization, radius.jsonSerialization], R.optargs(options)]) 197 | } 198 | 199 | /** Compute the distance between a point and another geometry object. At least one of the geometry objects specified 200 | must be a point. 201 | 202 | If one of the objects is a polygon or a line, the point will be projected onto the line or polygon assuming a perfect 203 | sphere model before the distance is computed (using the model specified with geoSystem). As a consequence, if the 204 | polygon or line is extremely large compared to Earth’s radius and the distance is being computed with the default WGS84 205 | model, the results of distance should be considered approximate due to the deviation between the ellipsoid and spherical 206 | models. */ 207 | public func distance(_ from: ReQueryGeometry, to: ReQueryGeometry, options: ReDistanceArg...) -> ReQueryValue { 208 | return ReDatum(jsonSerialization: [ReTerm.distance.rawValue, [from.jsonSerialization, to.jsonSerialization], R.optargs(options)]) 209 | } 210 | 211 | /** Convert a GeoJSON object to a ReQL geometry object. 212 | 213 | RethinkDB only allows conversion of GeoJSON objects which have ReQL equivalents: Point, LineString, and Polygon. 214 | MultiPoint, MultiLineString, and MultiPolygon are not supported. (You could, however, store multiple points, lines and 215 | polygons in an array and use a geospatial multi index with them.) 216 | 217 | Only longitude/latitude coordinates are supported. GeoJSON objects that use Cartesian coordinates, specify an altitude, 218 | or specify their own coordinate reference system will be rejected. */ 219 | public func geoJSON(_ json: ReQueryValue) -> ReQueryGeometry { 220 | return ReQueryGeometry(jsonSerialization: [ReTerm.geojson.rawValue, [json.jsonSerialization]]) 221 | } 222 | 223 | /** Tests whether two geometry objects intersect with one another. */ 224 | public func intersects(_ geometry: ReQueryGeometry, with: ReQueryGeometry) -> ReQueryValue { 225 | return ReDatum(jsonSerialization: [ReTerm.intersects.rawValue, [geometry.jsonSerialization, with.jsonSerialization]]) 226 | } 227 | 228 | public func line(_ from: ReQueryPoint, to: ReQueryPoint) -> ReQueryLine { 229 | return ReQueryLine(jsonSerialization: [ReTerm.line.rawValue, [from.jsonSerialization, to.jsonSerialization]]) 230 | } 231 | 232 | public static func asc(key: String) -> ReQueryValue { 233 | return ReDatum(jsonSerialization: [ReTerm.asc.rawValue, [key.jsonSerialization]]) 234 | } 235 | 236 | public static func desc(key: String) -> ReQueryValue { 237 | return ReDatum(jsonSerialization: [ReTerm.desc.rawValue, [key.jsonSerialization]]) 238 | } 239 | } 240 | 241 | public class ReQueryDatabase: ReQuery { 242 | public let jsonSerialization: Any 243 | 244 | internal init(name: String) { 245 | self.jsonSerialization = [ReTerm.db.rawValue, [name]] 246 | } 247 | 248 | public func table(_ name: String, options: ReTableArg...) -> ReQueryTable { 249 | return ReQueryTable(database: self, name: name, options: options) 250 | } 251 | 252 | public func tableCreate(_ name: String, options: ReTableCreateArg...) -> ReQueryValue { 253 | return ReDatum(jsonSerialization: [ReTerm.table_create.rawValue, [self.jsonSerialization, name], R.optargs(options)]) 254 | } 255 | 256 | public func tableDrop(_ name: String) -> ReQueryValue { 257 | return ReDatum(jsonSerialization: [ReTerm.table_drop.rawValue, [self.jsonSerialization, name]]) 258 | } 259 | 260 | public func tableList() -> ReQuerySequence { 261 | return ReQuerySequence(jsonSerialization: [ReTerm.table_list.rawValue, [self.jsonSerialization]]) 262 | } 263 | 264 | public func wait() -> ReQueryValue { 265 | return ReDatum(jsonSerialization: [ReTerm.wait.rawValue, [self.jsonSerialization]]) 266 | } 267 | 268 | public func rebalance() -> ReQueryValue { 269 | return ReDatum(jsonSerialization: [ReTerm.rebalance.rawValue, [self.jsonSerialization]]) 270 | } 271 | 272 | public func grant(_ userName: String, permissions: RePermission...) -> ReQueryValue { 273 | return ReDatum(jsonSerialization: [ReTerm.grant.rawValue, [self.jsonSerialization, userName], R.optargs(permissions)]) 274 | } 275 | } 276 | 277 | public class ReQuerySequence: ReQuery { 278 | public let jsonSerialization: Any 279 | 280 | internal init(jsonSerialization: Any) { 281 | self.jsonSerialization = jsonSerialization 282 | } 283 | 284 | public func distinct() -> ReQuerySequence { 285 | return ReQuerySequence(jsonSerialization: [ReTerm.distinct.rawValue, [self.jsonSerialization]]) 286 | } 287 | 288 | public func count() -> ReQueryValue { 289 | return ReDatum(jsonSerialization: [ReTerm.count.rawValue, [self.jsonSerialization]]) 290 | } 291 | 292 | public func limit(_ count: Int) -> ReQueryStream { 293 | return ReQueryStream(jsonSerialization: [ReTerm.limit.rawValue, [self.jsonSerialization, count]]) 294 | } 295 | 296 | public func skip(_ count: Int) -> ReQueryStream { 297 | return ReQueryStream(jsonSerialization: [ReTerm.skip.rawValue, [self.jsonSerialization, count]]) 298 | } 299 | 300 | public func sample(_ count: Int) -> ReQuerySequence { 301 | return ReQuerySequence(jsonSerialization: [ReTerm.sample.rawValue, [self.jsonSerialization, count]]) 302 | } 303 | 304 | public func nth(_ index: Int) -> ReQueryValue { 305 | return ReDatum(jsonSerialization: [ReTerm.nth.rawValue, [self.jsonSerialization, index]]) 306 | } 307 | 308 | public func isEmpty() -> ReQueryValue { 309 | return ReDatum(jsonSerialization: [ReTerm.is_EMPTY.rawValue, [self.jsonSerialization]]) 310 | } 311 | 312 | public func filter(_ specification: [String: ReQueryValue], options: ReFilterArg...) -> ReQuerySequence { 313 | var serialized: [String: Any] = [:] 314 | for (k, v) in specification { 315 | serialized[k] = v.jsonSerialization 316 | } 317 | return ReQuerySequence(jsonSerialization: [ReTerm.filter.rawValue, [self.jsonSerialization, serialized], R.optargs(options)]) 318 | } 319 | 320 | public func filter(_ predicate: RePredicate) -> ReQuerySequence { 321 | let fun = ReQueryLambda(predicate) 322 | return ReQuerySequence(jsonSerialization: [ReTerm.filter.rawValue, [self.jsonSerialization, fun.jsonSerialization]]) 323 | } 324 | 325 | public func forEach(_ block: RePredicate) -> ReQuerySequence { 326 | let fun = ReQueryLambda(block) 327 | return ReQuerySequence(jsonSerialization: [ReTerm.for_each.rawValue, [self.jsonSerialization, fun.jsonSerialization]]) 328 | } 329 | 330 | public func innerJoin(_ foreign: ReQuerySequence, predicate: RePredicate) -> ReQueryStream { 331 | return ReQueryStream(jsonSerialization: [ReTerm.inner_join.rawValue, [self.jsonSerialization, foreign.jsonSerialization, ReQueryLambda(predicate).jsonSerialization]]) 332 | } 333 | 334 | public func outerJoin(_ foreign: ReQuerySequence, predicate: RePredicate) -> ReQueryStream { 335 | return ReQueryStream(jsonSerialization: [ReTerm.outer_join.rawValue, [self.jsonSerialization, foreign.jsonSerialization, ReQueryLambda(predicate).jsonSerialization]]) 336 | } 337 | 338 | public func eqJoin(_ leftField: ReQueryValue, foreign: ReQueryTable, options: ReEqJoinArg...) -> ReQuerySequence { 339 | return ReQuerySequence(jsonSerialization: [ReTerm.eq_join.rawValue, [self.jsonSerialization, leftField.jsonSerialization, foreign.jsonSerialization], R.optargs(options)]) 340 | } 341 | 342 | public func map(_ mapper: ReQueryLambda) -> ReQuerySequence { 343 | return ReQuerySequence(jsonSerialization: [ReTerm.map.rawValue, [self.jsonSerialization, mapper.jsonSerialization]]) 344 | } 345 | 346 | public func map(_ block: RePredicate) -> ReQuerySequence { 347 | return self.map(ReQueryLambda(block)) 348 | } 349 | 350 | public func withFields(_ fields: [ReQueryValue]) -> ReQuerySequence { 351 | let values = fields.map({ e in return e.jsonSerialization }) 352 | return ReQuerySequence(jsonSerialization: [ReTerm.with_fields.rawValue, [self.jsonSerialization, [ReTerm.make_array.rawValue, values]]]) 353 | } 354 | 355 | public func union(_ sequence: ReQuerySequence) -> ReQueryStream { 356 | return ReQueryStream(jsonSerialization: [ReTerm.union.rawValue, [self.jsonSerialization, sequence.jsonSerialization]]) 357 | } 358 | 359 | public func delete(_ options: ReDeleteArg...) -> ReQueryValue { 360 | return ReDatum(jsonSerialization: [ReTerm.delete.rawValue, [self.jsonSerialization], R.optargs(options)]) 361 | } 362 | 363 | public func changes(_ options: ReChangesArg...) -> ReQueryStream { 364 | return ReQueryStream(jsonSerialization: [ReTerm.changes.rawValue, [self.jsonSerialization], R.optargs(options)]) 365 | } 366 | 367 | /** In its first form, fold operates like reduce, returning a value by applying a combining function to each element 368 | in a sequence, passing the current element and the previous reduction result to the function. However, fold has the 369 | following differences from reduce: 370 | - it is guaranteed to proceed through the sequence from first element to last. 371 | - it passes an initial base value to the function with the first element in place of the previous reduction result. */ 372 | public func fold(_ base: ReQueryValue, accumulator: ReQueryLambda, options: ReFoldArg...) -> ReQueryValue { 373 | return ReDatum(jsonSerialization: [ReTerm.fold.rawValue, [self.jsonSerialization, base.jsonSerialization, accumulator.jsonSerialization], R.optargs(options)]) 374 | } 375 | 376 | public func without(fields: [ReQueryValue]) -> ReQuerySequence { 377 | let values = fields.map({ e in return e.jsonSerialization }) 378 | return ReQuerySequence(jsonSerialization: [ReTerm.without.rawValue, [self.jsonSerialization, [ReTerm.make_array.rawValue, values]]]) 379 | } 380 | 381 | public func orderBy(sortKey: ReQueryValue) -> ReQuerySequence { 382 | let querySequence: [Any] = [ 383 | ReTerm.order_BY.rawValue, 384 | [self.jsonSerialization, sortKey.jsonSerialization], 385 | ] 386 | 387 | return ReQuerySequence( 388 | jsonSerialization: querySequence 389 | ) 390 | } 391 | 392 | public func has(field: ReQueryValue) -> ReQuerySequence { 393 | return self.has(fields: [field]) 394 | } 395 | 396 | public func has(fields: [ReQueryValue]) -> ReQuerySequence { 397 | let values = fields.map({ e in return e.jsonSerialization}) 398 | return ReQuerySequence(jsonSerialization: [ReTerm.has_fields.rawValue, [self.jsonSerialization, [ReTerm.make_array.rawValue, values]]]) 399 | } 400 | } 401 | 402 | public class ReQuerySelection: ReQuerySequence { 403 | } 404 | 405 | public class ReQueryStream: ReQuerySequence { 406 | public func zip() -> ReQueryStream { 407 | return ReQueryStream(jsonSerialization: [ReTerm.zip.rawValue, [self.jsonSerialization]]) 408 | } 409 | 410 | public override func sample(_ count: Int) -> ReQueryStream { 411 | return ReQueryStream(jsonSerialization: [ReTerm.sample.rawValue, [self.jsonSerialization, count]]) 412 | } 413 | } 414 | 415 | public class ReQueryTable: ReQuerySequence { 416 | internal init(database: ReQueryDatabase, name: String, options: [ReTableArg]) { 417 | let x = R.optargs(options) 418 | super.init(jsonSerialization: [ReTerm.table.rawValue, [database.jsonSerialization, name], x]) 419 | } 420 | 421 | /** Insert documents into a table. */ 422 | public func insert(_ documents: [ReDocument], options: ReInsertArg...) -> ReQueryValue { 423 | return ReDatum(jsonSerialization: [ReTerm.insert.rawValue, [self.jsonSerialization, [ReTerm.make_array.rawValue, documents]], R.optargs(options)]) 424 | } 425 | 426 | /** Insert documents into a table. */ 427 | public func insert(_ objects: [ReQueryValue], options: ReInsertArg...) -> ReQueryValue { 428 | return ReDatum(jsonSerialization: [ReTerm.insert.rawValue, [self.jsonSerialization, [ReTerm.make_array.rawValue, objects.map { return $0.jsonSerialization }]], R.optargs(options)]) 429 | } 430 | 431 | /** Update JSON documents in a table. Accepts a JSON document, a ReQL expression, or a combination of the two. */ 432 | public func update(_ changes: ReDocument, options: ReUpdateArg...) -> ReQueryValue { 433 | return ReDatum(jsonSerialization: [ReTerm.update.rawValue, [self.jsonSerialization, changes], R.optargs(options)]) 434 | } 435 | 436 | public func update(_ changes: ReModification, options: ReUpdateArg...) -> ReQueryValue { 437 | return ReDatum(jsonSerialization: [ReTerm.update.rawValue, [self.jsonSerialization, ReQueryLambda(changes).jsonSerialization], R.optargs(options)]) 438 | } 439 | 440 | /** Replace documents in a table. Accepts a JSON document or a ReQL expression, and replaces the original document with 441 | the new one. The new document must have the same primary key as the original document. 442 | 443 | The replace command can be used to both insert and delete documents. If the “replaced” document has a primary key that 444 | doesn’t exist in the table, the document will be inserted; if an existing document is replaced with null, the document 445 | will be deleted. Since update and replace operations are performed atomically, this allows atomic inserts and deletes 446 | as well. */ 447 | public func replace(_ changes: ReDocument, options: ReUpdateArg...) -> ReQueryValue { 448 | return ReDatum(jsonSerialization: [ReTerm.replace.rawValue, [self.jsonSerialization, changes], R.optargs(options)]) 449 | } 450 | 451 | public func replace(_ changes: RePredicate, options: ReUpdateArg...) -> ReQueryValue { 452 | return ReDatum(jsonSerialization: [ReTerm.replace.rawValue, [self.jsonSerialization, ReQueryLambda(changes).jsonSerialization], R.optargs(options)]) 453 | } 454 | 455 | /** Create a new secondary index on a table. Secondary indexes improve the speed of many read queries at the slight 456 | cost of increased storage space and decreased write performance. 457 | 458 | The indexFunction can be an anonymous function or a binary representation obtained from the function field of 459 | indexStatus. If successful, createIndex will return an object of the form {"created": 1}. */ 460 | public func indexCreate(_ indexName: String, indexFunction: ReQueryLambda? = nil, options: ReIndexCreateArg...) -> ReQueryValue { 461 | if let fn = indexFunction { 462 | return ReDatum(jsonSerialization: [ReTerm.index_create.rawValue, [self.jsonSerialization, indexName, fn.jsonSerialization], R.optargs(options)]) 463 | } 464 | return ReDatum(jsonSerialization: [ReTerm.index_create.rawValue, [self.jsonSerialization, indexName], R.optargs(options)]) 465 | } 466 | 467 | public func indexWait() -> ReQueryValue { 468 | return ReDatum(jsonSerialization: [ReTerm.index_wait.rawValue, [self.jsonSerialization]]) 469 | } 470 | 471 | public func indexDrop(_ name: String) -> ReQueryValue { 472 | return ReDatum(jsonSerialization: [ReTerm.index_drop.rawValue, [self.jsonSerialization, name]]) 473 | } 474 | 475 | public func indexList() -> ReQuerySequence { 476 | return ReQuerySequence(jsonSerialization: [ReTerm.index_list.rawValue, [self.jsonSerialization]]) 477 | } 478 | 479 | /** Rename an existing secondary index on a table. */ 480 | public func indexRename(_ renameIndex: String, to: String, options: ReIndexRenameArg...) -> ReQueryValue { 481 | return ReDatum(jsonSerialization: [ReTerm.index_rename.rawValue, [self.jsonSerialization, renameIndex, to], R.optargs(options)]) 482 | } 483 | 484 | /** Get the status of the specified indexes on this table, or the status of all indexes on this table if no indexes 485 | are specified. */ 486 | public func indexStatus(_ indices: String...) -> ReQueryValue { 487 | var params: [Any] = [self.jsonSerialization] 488 | indices.forEach { params.append($0) } 489 | return ReDatum(jsonSerialization: [ReTerm.index_status.rawValue, params]) 490 | } 491 | 492 | public func status() -> ReQuerySelection { 493 | return ReQuerySelection(jsonSerialization: [ReTerm.status.rawValue, [self.jsonSerialization]]) 494 | } 495 | 496 | public func sync() -> ReQueryValue { 497 | return ReDatum(jsonSerialization: [ReTerm.sync.rawValue, [self.jsonSerialization]]) 498 | } 499 | 500 | public func wait() -> ReQueryValue { 501 | return ReDatum(jsonSerialization: [ReTerm.wait.rawValue, [self.jsonSerialization]]) 502 | } 503 | 504 | public func get(_ primaryKey: Any) -> ReQueryRow { 505 | return ReQueryRow(jsonSerialization: [ReTerm.get.rawValue, [self.jsonSerialization, primaryKey]]) 506 | } 507 | 508 | public func getAll(_ key: ReQueryValue, index: String) -> ReQuerySequence { 509 | return ReQuerySequence(jsonSerialization: [ReTerm.get_ALL.rawValue, [self.jsonSerialization, key.jsonSerialization], ["index": index]]) 510 | } 511 | 512 | public func rebalance() -> ReQueryValue { 513 | return ReDatum(jsonSerialization: [ReTerm.rebalance.rawValue, [self.jsonSerialization]]) 514 | } 515 | 516 | public func getAll(key: [ReQueryValue], index: String) -> ReQuerySequence { 517 | return ReQuerySequence(jsonSerialization: [ReTerm.get_ALL.rawValue, 518 | [self.jsonSerialization] + key.map({$0.jsonSerialization}), 519 | ["index": index] 520 | ]) 521 | } 522 | 523 | public func orderBy(index: ReQueryValue) -> ReQuerySequence { 524 | var querySequence: [Any] = [ReTerm.order_BY.rawValue, [self.jsonSerialization]] 525 | querySequence.append(["index": index.jsonSerialization]) 526 | return ReQuerySequence(jsonSerialization: querySequence) 527 | } 528 | 529 | public func between(_ lower: ReQueryValue, _ upper: ReQueryValue) -> ReQuerySequence { 530 | return ReQuerySequence(jsonSerialization: [ReTerm.between.rawValue, [self.jsonSerialization, lower.jsonSerialization, upper.jsonSerialization]]) 531 | } 532 | 533 | public override func distinct() -> ReQueryStream { 534 | return ReQueryStream(jsonSerialization: [ReTerm.distinct.rawValue, [self.jsonSerialization]]) 535 | } 536 | 537 | public func grant(_ userName: String, permissions: RePermission...) -> ReQueryValue { 538 | return ReDatum(jsonSerialization: [ReTerm.grant.rawValue, [self.jsonSerialization, userName], R.optargs(permissions)]) 539 | } 540 | 541 | public func getIntersecting(_ geometry: ReQueryGeometry, options: ReIntersectingArg...) -> ReQuerySelection { 542 | return ReQuerySelection(jsonSerialization: [ReTerm.get_intersecting.rawValue, [self.jsonSerialization, geometry.jsonSerialization], R.optargs(options)]) 543 | } 544 | } 545 | 546 | public protocol ReQuery { 547 | var jsonSerialization: Any { get } 548 | } 549 | 550 | public protocol ReQueryValue: ReQuery { 551 | } 552 | 553 | public class ReQueryRow: ReDatum { 554 | public func update(_ changes: ReDocument) -> ReQueryValue { 555 | return ReDatum(jsonSerialization: [ReTerm.update.rawValue, [self.jsonSerialization, changes]]) 556 | } 557 | 558 | public func delete(_ options: ReDeleteArg...) -> ReQueryValue { 559 | return ReDatum(jsonSerialization: [ReTerm.delete.rawValue, [self.jsonSerialization], R.optargs(options)]) 560 | } 561 | 562 | public func keys() -> ReQueryValue { 563 | return ReDatum(jsonSerialization: [ReTerm.keys.rawValue, [self.jsonSerialization]]) 564 | } 565 | } 566 | 567 | public enum ReTypeName: String { 568 | case number = "number" 569 | case string = "string" 570 | case array = "array" 571 | case object = "object" 572 | case binary = "binary" 573 | } 574 | 575 | public extension ReQuery { 576 | typealias Callback = (ReResponse) -> () 577 | 578 | public func run(_ connection: ReConnection, callback: @escaping Callback) { 579 | let query: [Any] = [ReProtocol.ReQueryType.start.rawValue, self.jsonSerialization]; 580 | 581 | do { 582 | let json = try JSONSerialization.data(withJSONObject: query, options: []) 583 | //let ss = NSString(data: json, encoding: NSUTF8StringEncoding)! 584 | //print("JSON=\(ss)") 585 | try connection.startQuery(json, callback: callback) 586 | } 587 | catch { 588 | callback(ReResponse.error("An unknown error occurred")) 589 | } 590 | } 591 | 592 | public func typeOf() -> ReQueryValue { 593 | return ReDatum(jsonSerialization: [ReTerm.type_of.rawValue, [self.jsonSerialization]]) 594 | } 595 | 596 | public func coerceTo(_ type: ReTypeName) -> ReQueryValue { 597 | return ReDatum(jsonSerialization: [ReTerm.coerce_to.rawValue, [self.jsonSerialization, type.rawValue]]) 598 | } 599 | 600 | public func coerceTo(_ type: ReQueryValue) -> ReQueryValue { 601 | return ReDatum(jsonSerialization: [ReTerm.coerce_to.rawValue, [self.jsonSerialization, type.jsonSerialization]]) 602 | } 603 | 604 | public func info() -> ReQueryValue { 605 | return ReDatum(jsonSerialization: [ReTerm.info.rawValue, [self.jsonSerialization]]) 606 | } 607 | } 608 | 609 | extension String: ReQueryValue { 610 | public var jsonSerialization: Any { get { return self } } 611 | } 612 | 613 | extension Int: ReQueryValue { 614 | public var jsonSerialization: Any { get { return self } } 615 | } 616 | 617 | extension Double: ReQueryValue { 618 | public var jsonSerialization: Any { get { return self } } 619 | } 620 | 621 | extension Bool: ReQueryValue { 622 | public var jsonSerialization: Any { get { return self } } 623 | } 624 | 625 | public extension ReQueryValue { 626 | public func add(_ value: ReQueryValue) -> ReQueryValue { 627 | return ReDatum(jsonSerialization: [ReTerm.add.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 628 | } 629 | 630 | public func sub(_ value: ReQueryValue) -> ReQueryValue { 631 | return ReDatum(jsonSerialization: [ReTerm.sub.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 632 | } 633 | 634 | public func mul(_ value: ReQueryValue) -> ReQueryValue { 635 | return ReDatum(jsonSerialization: [ReTerm.mul.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 636 | } 637 | 638 | public func div(_ value: ReQueryValue) -> ReQueryValue { 639 | return ReDatum(jsonSerialization: [ReTerm.div.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 640 | } 641 | 642 | public func mod(_ value: ReQueryValue) -> ReQueryValue { 643 | return ReDatum(jsonSerialization: [ReTerm.mod.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 644 | } 645 | 646 | public func and(_ value: ReQueryValue) -> ReQueryValue { 647 | return ReDatum(jsonSerialization: [ReTerm.and.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 648 | } 649 | 650 | public func or(_ value: ReQueryValue) -> ReQueryValue { 651 | return ReDatum(jsonSerialization: [ReTerm.or.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 652 | } 653 | 654 | public func eq(_ value: ReQueryValue) -> ReQueryValue { 655 | return ReDatum(jsonSerialization: [ReTerm.eq.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 656 | } 657 | 658 | public func ne(_ value: ReQueryValue) -> ReQueryValue { 659 | return ReDatum(jsonSerialization: [ReTerm.ne.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 660 | } 661 | 662 | public func gt(_ value: ReQueryValue) -> ReQueryValue { 663 | return ReDatum(jsonSerialization: [ReTerm.gt.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 664 | } 665 | 666 | public func ge(_ value: ReQueryValue) -> ReQueryValue { 667 | return ReDatum(jsonSerialization: [ReTerm.ge.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 668 | } 669 | 670 | public func lt(_ value: ReQueryValue) -> ReQueryValue { 671 | return ReDatum(jsonSerialization: [ReTerm.lt.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 672 | } 673 | 674 | public func le(_ value: ReQueryValue) -> ReQueryValue { 675 | return ReDatum(jsonSerialization: [ReTerm.le.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 676 | } 677 | 678 | public func not() -> ReQueryValue { 679 | return ReDatum(jsonSerialization: [ReTerm.not.rawValue, [self.jsonSerialization]]) 680 | } 681 | 682 | public func round() -> ReQueryValue { 683 | return ReDatum(jsonSerialization: [ReTerm.round.rawValue, [self.jsonSerialization]]) 684 | } 685 | 686 | public func ceil() -> ReQueryValue { 687 | return ReDatum(jsonSerialization: [ReTerm.ceil.rawValue, [self.jsonSerialization]]) 688 | } 689 | 690 | public func floor() -> ReQueryValue { 691 | return ReDatum(jsonSerialization: [ReTerm.floor.rawValue, [self.jsonSerialization]]) 692 | } 693 | 694 | public func toEpochTime() -> ReQueryValue { 695 | return ReDatum(jsonSerialization: [ReTerm.to_epoch_time.rawValue, [self.jsonSerialization]]) 696 | } 697 | 698 | public func defaults(_ value: ReQueryValue) -> ReQueryValue { 699 | return ReDatum(jsonSerialization: [ReTerm.default.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 700 | } 701 | 702 | public func match(_ value: ReQueryValue) -> ReQueryValue { 703 | return ReDatum(jsonSerialization: [ReTerm.match.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 704 | } 705 | 706 | public func toJSON() -> ReQueryValue { 707 | return ReDatum(jsonSerialization: [ReTerm.to_json_string.rawValue, [self.jsonSerialization]]) 708 | } 709 | 710 | public func toJsonString() -> ReQueryValue { 711 | return self.toJSON() 712 | } 713 | 714 | public func upcase() -> ReQueryValue { 715 | return ReDatum(jsonSerialization: [ReTerm.upcase.rawValue, [self.jsonSerialization]]) 716 | } 717 | 718 | public func downcase() -> ReQueryValue { 719 | return ReDatum(jsonSerialization: [ReTerm.downcase.rawValue, [self.jsonSerialization]]) 720 | } 721 | 722 | public func xor(_ other: ReQueryValue) -> ReQueryValue { 723 | return self.and(other.not()).or(self.not().and(other)) 724 | } 725 | 726 | public func merge(_ value: ReQueryValue) -> ReQueryValue { 727 | return ReDatum(jsonSerialization: [ReTerm.merge.rawValue, [self.jsonSerialization, value.jsonSerialization]]) 728 | } 729 | 730 | public func branch(_ ifTrue: ReQueryValue, _ ifFalse: ReQueryValue) -> ReQueryValue { 731 | return R.branch(self, ifTrue: ifTrue, ifFalse: ifFalse) 732 | } 733 | 734 | public subscript(key: String) -> ReQueryValue { 735 | return ReDatum(jsonSerialization: [ReTerm.bracket.rawValue, [self.jsonSerialization, key]]) 736 | } 737 | 738 | public subscript(key: ReQueryValue) -> ReQueryValue { 739 | return ReDatum(jsonSerialization: [ReTerm.bracket.rawValue, [self.jsonSerialization, key.jsonSerialization]]) 740 | } 741 | 742 | public func without(fields: [ReQueryValue]) -> ReQuerySequence { 743 | let values = fields.map({ e in return e.jsonSerialization }) 744 | return ReQuerySequence(jsonSerialization: [ReTerm.without.rawValue, [self.jsonSerialization, [ReTerm.make_array.rawValue, values]]]) 745 | } 746 | } 747 | 748 | public func +(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 749 | return lhs.add(rhs) 750 | } 751 | 752 | public func -(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 753 | return lhs.sub(rhs) 754 | } 755 | 756 | public func *(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 757 | return lhs.mul(rhs) 758 | } 759 | 760 | public func /(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 761 | return lhs.div(rhs) 762 | } 763 | 764 | public func %(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 765 | return lhs.mod(rhs) 766 | } 767 | 768 | public func &&(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 769 | return lhs.and(rhs) 770 | } 771 | 772 | public func ||(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 773 | return lhs.or(rhs) 774 | } 775 | 776 | public func ==(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 777 | return lhs.eq(rhs) 778 | } 779 | 780 | public func !=(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 781 | return lhs.ne(rhs) 782 | } 783 | 784 | public func >(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 785 | return lhs.gt(rhs) 786 | } 787 | 788 | public func >=(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 789 | return lhs.ge(rhs) 790 | } 791 | 792 | public func <(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 793 | return lhs.lt(rhs) 794 | } 795 | 796 | public func <=(lhs: ReQueryValue, rhs: ReQueryValue) -> ReQueryValue { 797 | return lhs.le(rhs) 798 | } 799 | 800 | public prefix func !(lhs: ReQueryValue) -> ReQueryValue { 801 | return lhs.not() 802 | } 803 | 804 | public typealias ReModification = (ReQueryValue) -> ([String: ReQuery]) 805 | 806 | public typealias RePredicate = (ReQueryValue) -> (ReQuery) 807 | 808 | public class ReQueryLambda: ReQuery { 809 | public let jsonSerialization: Any 810 | private static var parameterCounter = 0 811 | private static let mutex = Mutex() 812 | 813 | init(_ block: RePredicate) { 814 | let p = ReQueryLambda.mutex.locked { () -> Int in 815 | ReQueryLambda.parameterCounter += 1 816 | return ReQueryLambda.parameterCounter 817 | } 818 | 819 | let parameter = ReDatum(jsonSerialization: p) 820 | let parameterAccess = ReDatum(jsonSerialization: [ReTerm.var.rawValue, [parameter.jsonSerialization]]) 821 | 822 | self.jsonSerialization = [ 823 | ReTerm.func.rawValue, [ 824 | [ReTerm.make_array.rawValue, [parameter.jsonSerialization]], 825 | block(parameterAccess).jsonSerialization 826 | ] 827 | ] 828 | } 829 | 830 | init(_ block: ReModification) { 831 | ReQueryLambda.parameterCounter += 1 832 | let p = ReQueryLambda.parameterCounter 833 | let parameter = ReDatum(jsonSerialization: p) 834 | let parameterAccess = ReDatum(jsonSerialization: [ReTerm.var.rawValue, [parameter.jsonSerialization]]) 835 | 836 | let changes = block(parameterAccess) 837 | var serializedChanges: [String: Any] = [:] 838 | for (k, v) in changes { 839 | serializedChanges[k] = v.jsonSerialization 840 | } 841 | 842 | self.jsonSerialization = [ 843 | ReTerm.func.rawValue, [ 844 | [ReTerm.make_array.rawValue, [parameter.jsonSerialization]], 845 | serializedChanges 846 | ] 847 | ] 848 | } 849 | } 850 | 851 | public class ReQueryGeometry: ReDatum { 852 | /** Compute the distance between a point and another geometry object. At least one of the geometry objects specified 853 | must be a point. 854 | 855 | If one of the objects is a polygon or a line, the point will be projected onto the line or polygon assuming a perfect 856 | sphere model before the distance is computed (using the model specified with geoSystem). As a consequence, if the 857 | polygon or line is extremely large compared to Earth’s radius and the distance is being computed with the default WGS84 858 | model, the results of distance should be considered approximate due to the deviation between the ellipsoid and spherical 859 | models. */ 860 | public func distance(_ geometry: ReQueryGeometry, options: ReDistanceArg...) -> ReQueryValue { 861 | return ReDatum(jsonSerialization: [ReTerm.distance.rawValue, [self.jsonSerialization, geometry.jsonSerialization], R.optargs(options)]) 862 | } 863 | 864 | /** Convert a ReQL geometry object to a GeoJSON object. */ 865 | public func toGeoJSON() -> ReQueryValue { 866 | return ReDatum(jsonSerialization: [ReTerm.to_geojson.rawValue, [self.jsonSerialization]]) 867 | } 868 | 869 | /** Tests whether two geometry objects intersect with one another. */ 870 | public func intersects(_ geometry: ReQueryGeometry) -> ReQueryValue { 871 | return ReDatum(jsonSerialization: [ReTerm.intersects.rawValue, [self.jsonSerialization, geometry.jsonSerialization]]) 872 | } 873 | } 874 | 875 | public class ReQueryPolygon: ReQueryGeometry { 876 | 877 | } 878 | 879 | public class ReQueryLine: ReQueryGeometry { 880 | /** Convert a Line object into a Polygon object. If the last point does not specify the same coordinates as the first 881 | point, polygon will close the polygon by connecting them. 882 | 883 | Longitude (−180 to 180) and latitude (−90 to 90) of vertices are plotted on a perfect sphere. See Geospatial support 884 | for more information on ReQL’s coordinate system. 885 | 886 | If the last point does not specify the same coordinates as the first point, polygon will close the polygon by 887 | connecting them. You cannot directly construct a polygon with holes in it using polygon, but you can use polygonSub to 888 | use a second polygon within the interior of the first to define a hole. */ 889 | public func fill() -> ReQueryPolygon { 890 | return ReQueryPolygon(jsonSerialization: [ReTerm.fill.rawValue, [self.jsonSerialization]]) 891 | } 892 | } 893 | 894 | public class ReQueryPoint: ReQueryGeometry { 895 | init(longitude: Double, latitude: Double) { 896 | super.init(jsonSerialization: [ReTerm.point.rawValue, [longitude, latitude]]) 897 | } 898 | } 899 | -------------------------------------------------------------------------------- /Sources/SCRAM/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2016 Pixelspark. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/SCRAM/SCRAM.m: -------------------------------------------------------------------------------- 1 | /** SCRAM-SHA-256 implementation (RFC5802 http://tools.ietf.org/html/rfc5802). 2 | dapted from XMPPSCRAMSHA1Authentication.m (iPhoneXMPP), created by David Chiles on 3/21/14. **/ 3 | 4 | #import "SCRAM.h" 5 | #import 6 | 7 | #if ! __has_feature(objc_arc) 8 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 9 | #endif 10 | 11 | @implementation NSData (SCRAM) 12 | 13 | static char encodingTable[64] = { 14 | 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 15 | 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 16 | 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 17 | 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; 18 | 19 | - (NSData*) xorWithData:(NSData*)data2 { 20 | NSMutableData *result = self.mutableCopy; 21 | 22 | char *dataPtr = (char *)result.mutableBytes; 23 | 24 | char *keyData = (char *)data2.bytes; 25 | 26 | char *keyPtr = keyData; 27 | int keyIndex = 0; 28 | 29 | for (int x = 0; x < self.length; x++) { 30 | *dataPtr = *dataPtr ^ *keyPtr; 31 | dataPtr++; 32 | keyPtr++; 33 | 34 | if (++keyIndex == data2.length) { 35 | keyIndex = 0; 36 | keyPtr = keyData; 37 | } 38 | } 39 | return result; 40 | } 41 | 42 | - (NSData*) hashWithAlgorithm:(CCHmacAlgorithm) algorithm key:(NSData*) key { 43 | unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH]; 44 | CCHmac(algorithm, [key bytes], [key length], [self bytes], [self length], cHMAC); 45 | return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)]; 46 | } 47 | 48 | - (NSData*) hashPasswordWithAlgorithm:(CCHmacAlgorithm) algorithm salt:(NSData *)saltData iterations:(NSUInteger)rounds { 49 | NSMutableData *mutableSaltData = [saltData mutableCopy]; 50 | UInt8 zeroHex = 0x00; 51 | UInt8 oneHex = 0x01; 52 | NSData *zeroData = [[NSData alloc] initWithBytes:&zeroHex length:sizeof(zeroHex)]; 53 | NSData *oneData = [[NSData alloc] initWithBytes:&oneHex length:sizeof(oneHex)]; 54 | 55 | [mutableSaltData appendData:zeroData]; 56 | [mutableSaltData appendData:zeroData]; 57 | [mutableSaltData appendData:zeroData]; 58 | [mutableSaltData appendData:oneData]; 59 | 60 | NSData *result = [mutableSaltData hashWithAlgorithm:algorithm key:self]; 61 | NSData *previous = [result copy]; 62 | 63 | for (int i = 1; i < rounds; i++) { 64 | previous = [previous hashWithAlgorithm:algorithm key:self]; 65 | result = [result xorWithData:previous]; 66 | } 67 | 68 | return result; 69 | } 70 | 71 | - (NSData*) sha256Digest { 72 | unsigned char result[CC_SHA256_DIGEST_LENGTH]; 73 | CC_SHA256([self bytes], (CC_LONG)[self length], result); 74 | return [NSData dataWithBytes:result length:CC_SHA256_DIGEST_LENGTH]; 75 | } 76 | 77 | - (NSString*) base64Encoded 78 | { 79 | const unsigned char *bytes = [self bytes]; 80 | NSMutableString *result = [NSMutableString stringWithCapacity:[self length]]; 81 | unsigned long ixtext = 0; 82 | unsigned long lentext = [self length]; 83 | long ctremaining = 0; 84 | unsigned char inbuf[3], outbuf[4]; 85 | unsigned short i = 0; 86 | unsigned short charsonline = 0, ctcopy = 0; 87 | unsigned long ix = 0; 88 | 89 | while( YES ) 90 | { 91 | ctremaining = lentext - ixtext; 92 | if( ctremaining <= 0 ) break; 93 | 94 | for( i = 0; i < 3; i++ ) { 95 | ix = ixtext + i; 96 | if( ix < lentext ) inbuf[i] = bytes[ix]; 97 | else inbuf [i] = 0; 98 | } 99 | 100 | outbuf [0] = (inbuf [0] & 0xFC) >> 2; 101 | outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4); 102 | outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6); 103 | outbuf [3] = inbuf [2] & 0x3F; 104 | ctcopy = 4; 105 | 106 | switch( ctremaining ) 107 | { 108 | case 1: 109 | ctcopy = 2; 110 | break; 111 | case 2: 112 | ctcopy = 3; 113 | break; 114 | } 115 | 116 | for( i = 0; i < ctcopy; i++ ) 117 | [result appendFormat:@"%c", encodingTable[outbuf[i]]]; 118 | 119 | for( i = ctcopy; i < 4; i++ ) 120 | [result appendString:@"="]; 121 | 122 | ixtext += 3; 123 | charsonline += 4; 124 | } 125 | 126 | return [NSString stringWithString:result]; 127 | } 128 | 129 | - (NSData*) base64Decoded 130 | { 131 | const unsigned char *bytes = [self bytes]; 132 | NSMutableData *result = [NSMutableData dataWithCapacity:[self length]]; 133 | 134 | unsigned long ixtext = 0; 135 | unsigned long lentext = [self length]; 136 | unsigned char ch = 0; 137 | unsigned char inbuf[4] = {0, 0, 0, 0}; 138 | unsigned char outbuf[3] = {0, 0, 0}; 139 | short i = 0, ixinbuf = 0; 140 | BOOL flignore = NO; 141 | BOOL flendtext = NO; 142 | 143 | while( YES ) 144 | { 145 | if( ixtext >= lentext ) break; 146 | ch = bytes[ixtext++]; 147 | flignore = NO; 148 | 149 | if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A'; 150 | else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26; 151 | else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52; 152 | else if( ch == '+' ) ch = 62; 153 | else if( ch == '=' ) flendtext = YES; 154 | else if( ch == '/' ) ch = 63; 155 | else flignore = YES; 156 | 157 | if( ! flignore ) 158 | { 159 | short ctcharsinbuf = 3; 160 | BOOL flbreak = NO; 161 | 162 | if( flendtext ) 163 | { 164 | if( ! ixinbuf ) break; 165 | if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1; 166 | else ctcharsinbuf = 2; 167 | ixinbuf = 3; 168 | flbreak = YES; 169 | } 170 | 171 | inbuf [ixinbuf++] = ch; 172 | 173 | if( ixinbuf == 4 ) 174 | { 175 | ixinbuf = 0; 176 | outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 ); 177 | outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 ); 178 | outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F ); 179 | 180 | for( i = 0; i < ctcharsinbuf; i++ ) 181 | [result appendBytes:&outbuf[i] length:1]; 182 | } 183 | 184 | if( flbreak ) break; 185 | } 186 | } 187 | 188 | return [NSData dataWithData:result]; 189 | } 190 | 191 | @end 192 | 193 | @interface SCRAM () 194 | @property (nonatomic) BOOL awaitingChallenge; 195 | @property (nonatomic, strong) NSString *username; 196 | @property (nonatomic, strong) NSString *password; 197 | @property (nonatomic, strong) NSString *clientNonce; 198 | @property (nonatomic, strong) NSString *combinedNonce; 199 | @property (nonatomic, strong) NSString *salt; 200 | @property (nonatomic, strong) NSNumber *count; 201 | @property (nonatomic, strong) NSString *serverMessage1; 202 | @property (nonatomic, strong) NSString *clientFirstMessageBare; 203 | @property (nonatomic, strong) NSData *serverSignatureData; 204 | @property (nonatomic, strong) NSData *clientProofData; 205 | @property (nonatomic) CCHmacAlgorithm hashAlgorithm; 206 | 207 | + (NSDictionary *) dictionaryFromChallenge:(NSString*) challenge; 208 | @end 209 | 210 | @implementation SCRAM 211 | 212 | - (id)initWithUsername:(NSString*)user password:(NSString *)password nonce:(NSString*)nonce { 213 | if ((self = [super init])) { 214 | self.authenticated = FALSE; 215 | self.username = user; 216 | self.password = password; 217 | self.clientNonce = nonce; 218 | self.hashAlgorithm = kCCHmacAlgSHA256; 219 | self.awaitingChallenge = YES; 220 | } 221 | return self; 222 | } 223 | 224 | - (id)initWithUsername:(NSString*)user password:(NSString *)password { 225 | if ((self = [super init])) { 226 | self.authenticated = FALSE; 227 | self.username = user; 228 | self.password = password; 229 | self.clientNonce = [[NSUUID UUID] UUIDString]; 230 | self.hashAlgorithm = kCCHmacAlgSHA256; 231 | self.awaitingChallenge = YES; 232 | } 233 | return self; 234 | } 235 | 236 | - (NSString* _Nullable) handleAuth1:(NSString*) authResponse 237 | { 238 | NSDictionary *auth = [SCRAM dictionaryFromChallenge:authResponse]; 239 | 240 | NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; 241 | [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; 242 | 243 | self.serverMessage1 = authResponse; 244 | self.combinedNonce = [auth objectForKey:@"r"]; 245 | self.salt = [auth objectForKey:@"s"]; 246 | self.count = [numberFormatter numberFromString:[auth objectForKey:@"i"]]; 247 | 248 | //We have all the necessary information to calculate client proof and server signature 249 | if ([self calculateProofs]) { 250 | self.awaitingChallenge = NO; 251 | return [self clientFinalMessage]; 252 | } 253 | else { 254 | return nil; 255 | } 256 | } 257 | 258 | - (void) handleAuth2:(NSString*) authResponse { 259 | NSDictionary *auth = [SCRAM dictionaryFromChallenge:authResponse]; 260 | NSString *receivedServerSignature = [auth objectForKey:@"v"]; 261 | 262 | NSData* decoded = [[receivedServerSignature dataUsingEncoding:NSUTF8StringEncoding] base64Decoded]; 263 | if([self.serverSignatureData isEqualToData:decoded]){ 264 | self.authenticated = YES; 265 | } 266 | else { 267 | self.authenticated = NO; 268 | } 269 | } 270 | 271 | - (NSString* _Nullable) receive:(NSString*) auth { 272 | if (self.awaitingChallenge) { 273 | return [self handleAuth1:auth]; 274 | } 275 | else { 276 | [self handleAuth2:auth]; 277 | return nil; 278 | } 279 | } 280 | 281 | - (NSString*) clientFirstMessage { 282 | self.clientFirstMessageBare = [NSString stringWithFormat:@"n=%@,r=%@", self.username, self.clientNonce]; 283 | 284 | return [NSString stringWithFormat:@"n,,%@",self.clientFirstMessageBare]; 285 | } 286 | 287 | - (NSString*) clientFinalMessage { 288 | NSString *clientProofString = [self.clientProofData base64Encoded]; 289 | return [NSString stringWithFormat:@"c=biws,r=%@,p=%@", self.combinedNonce, clientProofString]; 290 | } 291 | 292 | - (BOOL) calculateProofs { 293 | // Check to see that we have a password, salt and iteration count above 4096 (from RFC5802) 294 | if(!self.salt.length) { 295 | return NO; 296 | } 297 | 298 | if(self.password.length > 0 && self.count.integerValue < 4096) { 299 | return NO; 300 | } 301 | 302 | NSData *passwordData = [self.password dataUsingEncoding:NSUTF8StringEncoding]; 303 | NSData *saltData = [[self.salt dataUsingEncoding:NSUTF8StringEncoding] base64Decoded]; 304 | 305 | NSData *saltedPasswordData = [passwordData hashPasswordWithAlgorithm:self.hashAlgorithm salt:saltData iterations:[self.count unsignedIntValue]]; 306 | 307 | NSData *clientKeyData = [[@"Client Key" dataUsingEncoding:NSUTF8StringEncoding] hashWithAlgorithm:self.hashAlgorithm key:saltedPasswordData]; 308 | NSData *serverKeyData = [[@"Server Key" dataUsingEncoding:NSUTF8StringEncoding] hashWithAlgorithm:self.hashAlgorithm key:saltedPasswordData]; 309 | NSData *storedKeyData = [clientKeyData sha256Digest]; 310 | 311 | NSData *authMessageData = [[NSString stringWithFormat:@"%@,%@,c=biws,r=%@",self.clientFirstMessageBare,self.serverMessage1,self.combinedNonce] dataUsingEncoding:NSUTF8StringEncoding]; 312 | 313 | NSData *clientSignatureData = [authMessageData hashWithAlgorithm:self.hashAlgorithm key:storedKeyData]; 314 | self.serverSignatureData = [authMessageData hashWithAlgorithm:self.hashAlgorithm key:serverKeyData]; 315 | self.clientProofData = [clientKeyData xorWithData:clientSignatureData]; 316 | 317 | //check to see that we calculated some client proof and server signature 318 | if (self.clientProofData && self.serverSignatureData) { 319 | return YES; 320 | } 321 | else { 322 | return NO; 323 | } 324 | } 325 | 326 | + (NSDictionary*) dictionaryFromChallenge:(NSString*) challenge { 327 | // The value of the challenge stanza is base 64 encoded. 328 | // Once "decoded", it's just a string of key=value pairs separated by commas. 329 | NSArray *components = [challenge componentsSeparatedByString:@","]; 330 | NSMutableDictionary *auth = [NSMutableDictionary dictionaryWithCapacity:5]; 331 | 332 | for (NSString *component in components) 333 | { 334 | NSRange separator = [component rangeOfString:@"="]; 335 | if (separator.location != NSNotFound) 336 | { 337 | NSMutableString *key = [[component substringToIndex:separator.location] mutableCopy]; 338 | NSMutableString *value = [[component substringFromIndex:separator.location+1] mutableCopy]; 339 | 340 | if(key) CFStringTrimWhitespace((__bridge CFMutableStringRef)key); 341 | if(value) CFStringTrimWhitespace((__bridge CFMutableStringRef)value); 342 | 343 | if ([value hasPrefix:@"\""] && [value hasSuffix:@"\""] && [value length] > 2) 344 | { 345 | // Strip quotes from value 346 | [value deleteCharactersInRange:NSMakeRange(0, 1)]; 347 | [value deleteCharactersInRange:NSMakeRange([value length]-1, 1)]; 348 | } 349 | 350 | if(key && value) 351 | { 352 | [auth setObject:value forKey:key]; 353 | } 354 | } 355 | } 356 | 357 | return auth; 358 | } 359 | 360 | @end -------------------------------------------------------------------------------- /Sources/SCRAM/include/SCRAM.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRAM_h 2 | #define SCRAM_h 3 | 4 | #import 5 | 6 | @interface SCRAM : NSObject { 7 | } 8 | 9 | - (instancetype _Nonnull) initWithUsername:(NSString* _Nonnull)user password:(NSString* _Nonnull)password; 10 | - (instancetype _Nonnull) initWithUsername:(NSString* _Nonnull)user password:(NSString* _Nonnull)password nonce:(NSString* _Nonnull)nonce; 11 | - (NSString* _Nullable) receive:(NSString* _Nonnull) auth; 12 | 13 | @property (nonatomic, nonnull, readonly) NSString* clientFirstMessage; 14 | @property (nonatomic, nonnull, readonly) NSString* clientFinalMessage; 15 | @property (nonatomic) BOOL authenticated; 16 | 17 | @end 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /Tests/RethinkTests/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 | -------------------------------------------------------------------------------- /Tests/RethinkTests/RethinkTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Rethink 3 | import SCRAM 4 | 5 | class RethinkTests: XCTestCase { 6 | private func asyncTest(_ block: (_ callback: @escaping () -> ()) -> ()) { 7 | let expectFinish = self.expectation(description: "Async tests") 8 | 9 | block { 10 | expectFinish.fulfill() 11 | } 12 | 13 | self.waitForExpectations(timeout: 15.0) { (err) -> Void in 14 | if let e = err { 15 | // Note: referencing self here deliberately to prevent test from being destroyed prematurely 16 | print("Error=\(e) \(self)") 17 | } 18 | } 19 | } 20 | 21 | func testSCRAM() { 22 | let s = SCRAM(username: "user", password: "pencil", nonce: "rOprNGfwEbeRWgbNEkqO") 23 | XCTAssert(!s.authenticated) 24 | XCTAssert(s.clientFirstMessage == "n,,n=user,r=rOprNGfwEbeRWgbNEkqO") 25 | 26 | let c2 = s.receive("r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096") 27 | XCTAssert(!s.authenticated) 28 | XCTAssert(c2! == "c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=") 29 | XCTAssert(!s.authenticated) 30 | XCTAssert(s.receive("v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=") == nil) 31 | XCTAssert(s.authenticated) 32 | } 33 | 34 | func testBasicCommands() { 35 | asyncTest { testDoneCallback in 36 | R.connect(URL(string: "rethinkdb://localhost:28015")!) { (err, connection) in 37 | XCTAssert(err == nil, "Connection error: \(err)") 38 | 39 | print("Connected!") 40 | let databaseName = "swift_test" 41 | let tableName = "swift_test" 42 | 43 | R.uuid().run(connection) { (response) in 44 | XCTAssert(!response.isError, "Failed to UUID: \(response)") 45 | } 46 | 47 | let date = Date() 48 | R.expr(date).run(connection) { (response) in 49 | XCTAssert(!response.isError && response.value is NSDate, "Failed to date: \(response)") 50 | print(response) 51 | } 52 | 53 | R.now().run(connection) { (response) in 54 | XCTAssert(!response.isError && response.value is NSDate, "Failed to date: \(response)") 55 | print(response) 56 | } 57 | 58 | R.range(1, 10).map { e in return e.mul(10) }.run(connection) { response in 59 | if let r = response.value as? [Int] { 60 | XCTAssert(r == Array(1..<10).map { return $0 * 10 }) 61 | } 62 | else { 63 | XCTAssert(false, "invalid response") 64 | } 65 | } 66 | 67 | var outstanding = 100 68 | var outstandingChanges = 1000 69 | var reader : ReResponse.Callback? = nil 70 | reader = { (response) -> () in 71 | XCTAssert(!response.isError, "Failed to fetch documents: \(response)") 72 | 73 | switch response { 74 | case .rows(_, let cont): 75 | if cont == nil { 76 | outstanding -= 1 77 | print("Outstanding=\(outstanding) outstanding changes=\(outstandingChanges)") 78 | if outstanding == 0 && outstandingChanges == 0 { 79 | R.dbDrop(databaseName).run(connection) { (response) in 80 | XCTAssert(!response.isError, "Failed to drop database: \(response)") 81 | testDoneCallback() 82 | } 83 | } 84 | } 85 | cont?(reader!) 86 | 87 | default: 88 | print("Unknown response") 89 | } 90 | } 91 | 92 | R.dbCreate(databaseName).run(connection) { (response) in 93 | XCTAssert(!response.isError, "Failed to create database: \(response)") 94 | 95 | R.dbList().run(connection) { (response) in 96 | XCTAssert(!response.isError, "Failed to dbList: \(response)") 97 | XCTAssert(response.value is NSArray && (response.value as! NSArray).contains(databaseName), "Created database not listed in response") 98 | } 99 | 100 | R.db(databaseName).tableCreate(tableName).run(connection) { (response) in 101 | XCTAssert(!response.isError, "Failed to create table: \(response)") 102 | 103 | R.db(databaseName).table(tableName).indexWait().run(connection) { (response) in 104 | XCTAssert(!response.isError, "Failed to wait for index: \(response)") 105 | 106 | R.db(databaseName).table(tableName).changes().run(connection) { response in 107 | XCTAssert(!response.isError, "Failed to obtain changes: \(response)") 108 | 109 | var consumeChanges: ((_ response: ReResponse) -> ())? = nil 110 | 111 | consumeChanges = { (response: ReResponse) -> () in 112 | if case ReResponse.rows(let docs, let cb) = response { 113 | outstandingChanges -= docs.count 114 | print("Received \(docs.count) changes, need \(outstandingChanges) more") 115 | cb!(consumeChanges!) 116 | } 117 | else { 118 | print("Received unexpected response for .changes request: \(response)") 119 | } 120 | } 121 | 122 | consumeChanges!(response) 123 | 124 | } 125 | 126 | // Insert 1000 documents 127 | var docs: [ReDocument] = [] 128 | for i in 0..<1000 { 129 | docs.append(["foo": "bar", "id": i]) 130 | } 131 | 132 | R.db(databaseName).table(tableName).insert(docs).run(connection) { (response) in 133 | XCTAssert(!response.isError, "Failed to insert data: \(response)") 134 | 135 | R.db(databaseName).table(tableName).filter({ r in return r["foo"].eq(R.expr("bar")) }).count().run(connection) { (response) in 136 | XCTAssert(!response.isError, "Failed to count: \(response)") 137 | XCTAssert(response.value is NSNumber && (response.value as! NSNumber).intValue == 1000, "Not all documents were inserted, or count is failing: \(response)") 138 | 139 | for _ in 0..