├── .gitignore ├── Externals ├── NSData_Base64 │ ├── NSData+Base64.h │ └── NSData+Base64.m └── SBJSON │ ├── JSON.h │ ├── NSObject+SBJSON.h │ ├── NSObject+SBJSON.m │ ├── NSString+SBJSON.h │ ├── NSString+SBJSON.m │ ├── SBJSON.h │ ├── SBJSON.m │ ├── SBJsonBase.h │ ├── SBJsonBase.m │ ├── SBJsonParser.h │ ├── SBJsonParser.m │ ├── SBJsonWriter.h │ └── SBJsonWriter.m ├── LICENSE ├── README.md ├── RestClient.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── RestClient ├── RestClient-Prefix.pch ├── RestClient.h ├── RestClient.m ├── RestClientBlockRunner.h ├── RestClientBlockRunner.m ├── RestClientURLConnectionInvocation.h └── RestClientURLConnectionInvocation.m └── RestClientTests ├── BlockRunnerSpec.m ├── FakeBlockRunner.h ├── FakeBlockRunner.m ├── RestClientSpec.m ├── RestClientTests-Info.plist ├── RestClientTests-Prefix.pch ├── RestClientTests.h ├── RestClientTests.m └── en.lproj └── InfoPlist.strings /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | xcuserdata -------------------------------------------------------------------------------- /Externals/NSData_Base64/NSData+Base64.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+Base64.h 3 | // base64 4 | // 5 | // Created by Matt Gallagher on 2009/06/03. 6 | // Copyright 2009 Matt Gallagher. All rights reserved. 7 | // 8 | // Permission is given to use this source code file, free of charge, in any 9 | // project, commercial or otherwise, entirely at your risk, with the condition 10 | // that any redistribution (in part or whole) of source code must retain 11 | // this copyright and permission notice. Attribution in compiled projects is 12 | // appreciated but not required. 13 | // 14 | 15 | #import 16 | 17 | void *NewBase64Decode( 18 | const char *inputBuffer, 19 | size_t length, 20 | size_t *outputLength); 21 | 22 | char *NewBase64Encode( 23 | const void *inputBuffer, 24 | size_t length, 25 | bool separateLines, 26 | size_t *outputLength); 27 | 28 | @interface NSData (Base64) 29 | 30 | + (NSData *)dataFromBase64String:(NSString *)aString; 31 | - (NSString *)base64EncodedString; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Externals/NSData_Base64/NSData+Base64.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+Base64.m 3 | // base64 4 | // 5 | // Created by Matt Gallagher on 2009/06/03. 6 | // Copyright 2009 Matt Gallagher. All rights reserved. 7 | // 8 | // Permission is given to use this source code file, free of charge, in any 9 | // project, commercial or otherwise, entirely at your risk, with the condition 10 | // that any redistribution (in part or whole) of source code must retain 11 | // this copyright and permission notice. Attribution in compiled projects is 12 | // appreciated but not required. 13 | // 14 | 15 | #import "NSData+Base64.h" 16 | 17 | // 18 | // Mapping from 6 bit pattern to ASCII character. 19 | // 20 | static unsigned char base64EncodeLookup[65] = 21 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 22 | 23 | // 24 | // Definition for "masked-out" areas of the base64DecodeLookup mapping 25 | // 26 | #define xx 65 27 | 28 | // 29 | // Mapping from ASCII character to 6 bit pattern. 30 | // 31 | static unsigned char base64DecodeLookup[256] = 32 | { 33 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 34 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 35 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63, 36 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx, 37 | xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 38 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx, 39 | xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 40 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx, 41 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 42 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 43 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 44 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 45 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 46 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 47 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 48 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 49 | }; 50 | 51 | // 52 | // Fundamental sizes of the binary and base64 encode/decode units in bytes 53 | // 54 | #define BINARY_UNIT_SIZE 3 55 | #define BASE64_UNIT_SIZE 4 56 | 57 | // 58 | // NewBase64Decode 59 | // 60 | // Decodes the base64 ASCII string in the inputBuffer to a newly malloced 61 | // output buffer. 62 | // 63 | // inputBuffer - the source ASCII string for the decode 64 | // length - the length of the string or -1 (to specify strlen should be used) 65 | // outputLength - if not-NULL, on output will contain the decoded length 66 | // 67 | // returns the decoded buffer. Must be free'd by caller. Length is given by 68 | // outputLength. 69 | // 70 | void *NewBase64Decode( 71 | const char *inputBuffer, 72 | size_t length, 73 | size_t *outputLength) 74 | { 75 | if (length == -1) 76 | { 77 | length = strlen(inputBuffer); 78 | } 79 | 80 | size_t outputBufferSize = 81 | ((length+BASE64_UNIT_SIZE-1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE; 82 | unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize); 83 | 84 | size_t i = 0; 85 | size_t j = 0; 86 | while (i < length) 87 | { 88 | // 89 | // Accumulate 4 valid characters (ignore everything else) 90 | // 91 | unsigned char accumulated[BASE64_UNIT_SIZE]; 92 | size_t accumulateIndex = 0; 93 | while (i < length) 94 | { 95 | unsigned char decode = base64DecodeLookup[inputBuffer[i++]]; 96 | if (decode != xx) 97 | { 98 | accumulated[accumulateIndex] = decode; 99 | accumulateIndex++; 100 | 101 | if (accumulateIndex == BASE64_UNIT_SIZE) 102 | { 103 | break; 104 | } 105 | } 106 | } 107 | 108 | // 109 | // Store the 6 bits from each of the 4 characters as 3 bytes 110 | // 111 | outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4); 112 | outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2); 113 | outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3]; 114 | j += accumulateIndex - 1; 115 | } 116 | 117 | if (outputLength) 118 | { 119 | *outputLength = j; 120 | } 121 | return outputBuffer; 122 | } 123 | 124 | // 125 | // NewBase64Decode 126 | // 127 | // Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced 128 | // output buffer. 129 | // 130 | // inputBuffer - the source data for the encode 131 | // length - the length of the input in bytes 132 | // separateLines - if zero, no CR/LF characters will be added. Otherwise 133 | // a CR/LF pair will be added every 64 encoded chars. 134 | // outputLength - if not-NULL, on output will contain the encoded length 135 | // (not including terminating 0 char) 136 | // 137 | // returns the encoded buffer. Must be free'd by caller. Length is given by 138 | // outputLength. 139 | // 140 | char *NewBase64Encode( 141 | const void *buffer, 142 | size_t length, 143 | bool separateLines, 144 | size_t *outputLength) 145 | { 146 | const unsigned char *inputBuffer = (const unsigned char *)buffer; 147 | 148 | #define MAX_NUM_PADDING_CHARS 2 149 | #define OUTPUT_LINE_LENGTH 64 150 | #define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE) 151 | #define CR_LF_SIZE 2 152 | 153 | // 154 | // Byte accurate calculation of final buffer size 155 | // 156 | size_t outputBufferSize = 157 | ((length / BINARY_UNIT_SIZE) 158 | + ((length % BINARY_UNIT_SIZE) ? 1 : 0)) 159 | * BASE64_UNIT_SIZE; 160 | if (separateLines) 161 | { 162 | outputBufferSize += 163 | (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE; 164 | } 165 | 166 | // 167 | // Include space for a terminating zero 168 | // 169 | outputBufferSize += 1; 170 | 171 | // 172 | // Allocate the output buffer 173 | // 174 | char *outputBuffer = (char *)malloc(outputBufferSize); 175 | if (!outputBuffer) 176 | { 177 | return NULL; 178 | } 179 | 180 | size_t i = 0; 181 | size_t j = 0; 182 | const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length; 183 | size_t lineEnd = lineLength; 184 | 185 | while (true) 186 | { 187 | if (lineEnd > length) 188 | { 189 | lineEnd = length; 190 | } 191 | 192 | for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE) 193 | { 194 | // 195 | // Inner loop: turn 48 bytes into 64 base64 characters 196 | // 197 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; 198 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) 199 | | ((inputBuffer[i + 1] & 0xF0) >> 4)]; 200 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2) 201 | | ((inputBuffer[i + 2] & 0xC0) >> 6)]; 202 | outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F]; 203 | } 204 | 205 | if (lineEnd == length) 206 | { 207 | break; 208 | } 209 | 210 | // 211 | // Add the newline 212 | // 213 | outputBuffer[j++] = '\r'; 214 | outputBuffer[j++] = '\n'; 215 | lineEnd += lineLength; 216 | } 217 | 218 | if (i + 1 < length) 219 | { 220 | // 221 | // Handle the single '=' case 222 | // 223 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; 224 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) 225 | | ((inputBuffer[i + 1] & 0xF0) >> 4)]; 226 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2]; 227 | outputBuffer[j++] = '='; 228 | } 229 | else if (i < length) 230 | { 231 | // 232 | // Handle the double '=' case 233 | // 234 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; 235 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4]; 236 | outputBuffer[j++] = '='; 237 | outputBuffer[j++] = '='; 238 | } 239 | outputBuffer[j] = 0; 240 | 241 | // 242 | // Set the output length and return the buffer 243 | // 244 | if (outputLength) 245 | { 246 | *outputLength = j; 247 | } 248 | return outputBuffer; 249 | } 250 | 251 | @implementation NSData (Base64) 252 | 253 | // 254 | // dataFromBase64String: 255 | // 256 | // Creates an NSData object containing the base64 decoded representation of 257 | // the base64 string 'aString' 258 | // 259 | // Parameters: 260 | // aString - the base64 string to decode 261 | // 262 | // returns the autoreleased NSData representation of the base64 string 263 | // 264 | + (NSData *)dataFromBase64String:(NSString *)aString 265 | { 266 | NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding]; 267 | size_t outputLength; 268 | void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength); 269 | NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength]; 270 | free(outputBuffer); 271 | return result; 272 | } 273 | 274 | // 275 | // base64EncodedString 276 | // 277 | // Creates an NSString object that contains the base 64 encoding of the 278 | // receiver's data. Lines are broken at 64 characters long. 279 | // 280 | // returns an autoreleased NSString being the base 64 representation of the 281 | // receiver. 282 | // 283 | - (NSString *)base64EncodedString 284 | { 285 | size_t outputLength; 286 | char *outputBuffer = 287 | NewBase64Encode([self bytes], [self length], true, &outputLength); 288 | 289 | NSString *result = 290 | [[[NSString alloc] 291 | initWithBytes:outputBuffer 292 | length:outputLength 293 | encoding:NSASCIIStringEncoding] 294 | autorelease]; 295 | free(outputBuffer); 296 | return result; 297 | } 298 | 299 | @end 300 | -------------------------------------------------------------------------------- /Externals/SBJSON/JSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | @mainpage A strict JSON parser and generator for Objective-C 32 | 33 | JSON (JavaScript Object Notation) is a lightweight data-interchange 34 | format. This framework provides two apis for parsing and generating 35 | JSON. One standard object-based and a higher level api consisting of 36 | categories added to existing Objective-C classes. 37 | 38 | Learn more on the http://code.google.com/p/json-framework project site. 39 | 40 | This framework does its best to be as strict as possible, both in what it 41 | accepts and what it generates. For example, it does not support trailing commas 42 | in arrays or objects. Nor does it support embedded comments, or 43 | anything else not in the JSON specification. This is considered a feature. 44 | 45 | */ 46 | 47 | #import "SBJSON.h" 48 | #import "NSObject+SBJSON.h" 49 | #import "NSString+SBJSON.h" 50 | 51 | -------------------------------------------------------------------------------- /Externals/SBJSON/NSObject+SBJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | 33 | /** 34 | @brief Adds JSON generation to Foundation classes 35 | 36 | This is a category on NSObject that adds methods for returning JSON representations 37 | of standard objects to the objects themselves. This means you can call the 38 | -JSONRepresentation method on an NSArray object and it'll do what you want. 39 | */ 40 | @interface NSObject (NSObject_SBJSON) 41 | 42 | /** 43 | @brief Returns a string containing the receiver encoded as a JSON fragment. 44 | 45 | This method is added as a category on NSObject but is only actually 46 | supported for the following objects: 47 | @li NSDictionary 48 | @li NSArray 49 | @li NSString 50 | @li NSNumber (also used for booleans) 51 | @li NSNull 52 | 53 | @deprecated Given we bill ourselves as a "strict" JSON library, this method should be removed. 54 | */ 55 | - (NSString *)JSONFragment; 56 | 57 | /** 58 | @brief Returns a string containing the receiver encoded in JSON. 59 | 60 | This method is added as a category on NSObject but is only actually 61 | supported for the following objects: 62 | @li NSDictionary 63 | @li NSArray 64 | */ 65 | - (NSString *)JSONRepresentation; 66 | 67 | @end 68 | 69 | -------------------------------------------------------------------------------- /Externals/SBJSON/NSObject+SBJSON.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "NSObject+SBJSON.h" 31 | #import "SBJsonWriter.h" 32 | 33 | @implementation NSObject (NSObject_SBJSON) 34 | 35 | - (NSString *)JSONFragment { 36 | SBJsonWriter *jsonWriter = [SBJsonWriter new]; 37 | NSString *json = [jsonWriter stringWithFragment:self]; 38 | if (!json) 39 | NSLog(@"-JSONFragment failed. Error trace is: %@", [jsonWriter errorTrace]); 40 | [jsonWriter release]; 41 | return json; 42 | } 43 | 44 | - (NSString *)JSONRepresentation { 45 | SBJsonWriter *jsonWriter = [SBJsonWriter new]; 46 | NSString *json = [jsonWriter stringWithObject:self]; 47 | if (!json) 48 | NSLog(@"-JSONRepresentation failed. Error trace is: %@", [jsonWriter errorTrace]); 49 | [jsonWriter release]; 50 | return json; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Externals/SBJSON/NSString+SBJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | /** 33 | @brief Adds JSON parsing methods to NSString 34 | 35 | This is a category on NSString that adds methods for parsing the target string. 36 | */ 37 | @interface NSString (NSString_SBJSON) 38 | 39 | 40 | /** 41 | @brief Returns the object represented in the receiver, or nil on error. 42 | 43 | Returns a a scalar object represented by the string's JSON fragment representation. 44 | 45 | @deprecated Given we bill ourselves as a "strict" JSON library, this method should be removed. 46 | */ 47 | - (id)JSONFragmentValue; 48 | 49 | /** 50 | @brief Returns the NSDictionary or NSArray represented by the current string's JSON representation. 51 | 52 | Returns the dictionary or array represented in the receiver, or nil on error. 53 | 54 | Returns the NSDictionary or NSArray represented by the current string's JSON representation. 55 | */ 56 | - (id)JSONValue; 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Externals/SBJSON/NSString+SBJSON.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007-2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "NSString+SBJSON.h" 31 | #import "SBJsonParser.h" 32 | 33 | @implementation NSString (NSString_SBJSON) 34 | 35 | - (id)JSONFragmentValue 36 | { 37 | SBJsonParser *jsonParser = [SBJsonParser new]; 38 | id repr = [jsonParser fragmentWithString:self]; 39 | if (!repr) 40 | NSLog(@"-JSONFragmentValue failed. Error trace is: %@", [jsonParser errorTrace]); 41 | [jsonParser release]; 42 | return repr; 43 | } 44 | 45 | - (id)JSONValue 46 | { 47 | SBJsonParser *jsonParser = [SBJsonParser new]; 48 | id repr = [jsonParser objectWithString:self]; 49 | if (!repr) 50 | NSLog(@"-JSONValue failed. Error trace is: %@", [jsonParser errorTrace]); 51 | [jsonParser release]; 52 | return repr; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Externals/SBJSON/SBJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007-2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import "SBJsonParser.h" 32 | #import "SBJsonWriter.h" 33 | 34 | /** 35 | @brief Facade for SBJsonWriter/SBJsonParser. 36 | 37 | Requests are forwarded to instances of SBJsonWriter and SBJsonParser. 38 | */ 39 | @interface SBJSON : SBJsonBase { 40 | 41 | @private 42 | SBJsonParser *jsonParser; 43 | SBJsonWriter *jsonWriter; 44 | } 45 | 46 | 47 | /// Return the fragment represented by the given string 48 | - (id)fragmentWithString:(NSString*)jsonrep 49 | error:(NSError**)error; 50 | 51 | /// Return the object represented by the given string 52 | - (id)objectWithString:(NSString*)jsonrep 53 | error:(NSError**)error; 54 | 55 | /// Parse the string and return the represented object (or scalar) 56 | - (id)objectWithString:(id)value 57 | allowScalar:(BOOL)x 58 | error:(NSError**)error; 59 | 60 | 61 | /// Return JSON representation of an array or dictionary 62 | - (NSString*)stringWithObject:(id)value 63 | error:(NSError**)error; 64 | 65 | /// Return JSON representation of any legal JSON value 66 | - (NSString*)stringWithFragment:(id)value 67 | error:(NSError**)error; 68 | 69 | /// Return JSON representation (or fragment) for the given object 70 | - (NSString*)stringWithObject:(id)value 71 | allowScalar:(BOOL)x 72 | error:(NSError**)error; 73 | 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Externals/SBJSON/SBJSON.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007-2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "SBJSON.h" 31 | 32 | @implementation SBJSON 33 | 34 | - (id)init { 35 | self = [super init]; 36 | if (self) { 37 | jsonWriter = [SBJsonWriter new]; 38 | jsonParser = [SBJsonParser new]; 39 | [self setMaxDepth:512]; 40 | 41 | } 42 | return self; 43 | } 44 | 45 | - (void)dealloc { 46 | [jsonWriter release]; 47 | [jsonParser release]; 48 | [super dealloc]; 49 | } 50 | 51 | #pragma mark Writer 52 | 53 | 54 | - (NSString *)stringWithObject:(id)obj { 55 | NSString *repr = [jsonWriter stringWithObject:obj]; 56 | if (repr) 57 | return repr; 58 | 59 | [errorTrace release]; 60 | errorTrace = [[jsonWriter errorTrace] mutableCopy]; 61 | return nil; 62 | } 63 | 64 | /** 65 | Returns a string containing JSON representation of the passed in value, or nil on error. 66 | If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. 67 | 68 | @param value any instance that can be represented as a JSON fragment 69 | @param allowScalar wether to return json fragments for scalar objects 70 | @param error used to return an error by reference (pass NULL if this is not desired) 71 | 72 | @deprecated Given we bill ourselves as a "strict" JSON library, this method should be removed. 73 | */ 74 | - (NSString*)stringWithObject:(id)value allowScalar:(BOOL)allowScalar error:(NSError**)error { 75 | 76 | NSString *json = allowScalar ? [jsonWriter stringWithFragment:value] : [jsonWriter stringWithObject:value]; 77 | if (json) 78 | return json; 79 | 80 | [errorTrace release]; 81 | errorTrace = [[jsonWriter errorTrace] mutableCopy]; 82 | 83 | if (error) 84 | *error = [errorTrace lastObject]; 85 | return nil; 86 | } 87 | 88 | /** 89 | Returns a string containing JSON representation of the passed in value, or nil on error. 90 | If nil is returned and @p error is not NULL, @p error can be interrogated to find the cause of the error. 91 | 92 | @param value any instance that can be represented as a JSON fragment 93 | @param error used to return an error by reference (pass NULL if this is not desired) 94 | 95 | @deprecated Given we bill ourselves as a "strict" JSON library, this method should be removed. 96 | */ 97 | - (NSString*)stringWithFragment:(id)value error:(NSError**)error { 98 | return [self stringWithObject:value 99 | allowScalar:YES 100 | error:error]; 101 | } 102 | 103 | /** 104 | Returns a string containing JSON representation of the passed in value, or nil on error. 105 | If nil is returned and @p error is not NULL, @p error can be interrogated to find the cause of the error. 106 | 107 | @param value a NSDictionary or NSArray instance 108 | @param error used to return an error by reference (pass NULL if this is not desired) 109 | */ 110 | - (NSString*)stringWithObject:(id)value error:(NSError**)error { 111 | return [self stringWithObject:value 112 | allowScalar:NO 113 | error:error]; 114 | } 115 | 116 | #pragma mark Parsing 117 | 118 | - (id)objectWithString:(NSString *)repr { 119 | id obj = [jsonParser objectWithString:repr]; 120 | if (obj) 121 | return obj; 122 | 123 | [errorTrace release]; 124 | errorTrace = [[jsonParser errorTrace] mutableCopy]; 125 | 126 | return nil; 127 | } 128 | 129 | /** 130 | Returns the object represented by the passed-in string or nil on error. The returned object can be 131 | a string, number, boolean, null, array or dictionary. 132 | 133 | @param value the json string to parse 134 | @param allowScalar whether to return objects for JSON fragments 135 | @param error used to return an error by reference (pass NULL if this is not desired) 136 | 137 | @deprecated Given we bill ourselves as a "strict" JSON library, this method should be removed. 138 | */ 139 | - (id)objectWithString:(id)value allowScalar:(BOOL)allowScalar error:(NSError**)error { 140 | 141 | id obj = allowScalar ? [jsonParser fragmentWithString:value] : [jsonParser objectWithString:value]; 142 | if (obj) 143 | return obj; 144 | 145 | [errorTrace release]; 146 | errorTrace = [[jsonParser errorTrace] mutableCopy]; 147 | 148 | if (error) 149 | *error = [errorTrace lastObject]; 150 | return nil; 151 | } 152 | 153 | /** 154 | Returns the object represented by the passed-in string or nil on error. The returned object can be 155 | a string, number, boolean, null, array or dictionary. 156 | 157 | @param repr the json string to parse 158 | @param error used to return an error by reference (pass NULL if this is not desired) 159 | 160 | @deprecated Given we bill ourselves as a "strict" JSON library, this method should be removed. 161 | */ 162 | - (id)fragmentWithString:(NSString*)repr error:(NSError**)error { 163 | return [self objectWithString:repr 164 | allowScalar:YES 165 | error:error]; 166 | } 167 | 168 | /** 169 | Returns the object represented by the passed-in string or nil on error. The returned object 170 | will be either a dictionary or an array. 171 | 172 | @param repr the json string to parse 173 | @param error used to return an error by reference (pass NULL if this is not desired) 174 | */ 175 | - (id)objectWithString:(NSString*)repr error:(NSError**)error { 176 | return [self objectWithString:repr 177 | allowScalar:NO 178 | error:error]; 179 | } 180 | 181 | 182 | 183 | #pragma mark Properties - parsing 184 | 185 | - (NSUInteger)maxDepth { 186 | return jsonParser.maxDepth; 187 | } 188 | 189 | - (void)setMaxDepth:(NSUInteger)d { 190 | jsonWriter.maxDepth = jsonParser.maxDepth = d; 191 | } 192 | 193 | 194 | #pragma mark Properties - writing 195 | 196 | - (BOOL)humanReadable { 197 | return jsonWriter.humanReadable; 198 | } 199 | 200 | - (void)setHumanReadable:(BOOL)x { 201 | jsonWriter.humanReadable = x; 202 | } 203 | 204 | - (BOOL)sortKeys { 205 | return jsonWriter.sortKeys; 206 | } 207 | 208 | - (void)setSortKeys:(BOOL)x { 209 | jsonWriter.sortKeys = x; 210 | } 211 | 212 | @end 213 | -------------------------------------------------------------------------------- /Externals/SBJSON/SBJsonBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | extern NSString * SBJSONErrorDomain; 33 | 34 | 35 | enum { 36 | EUNSUPPORTED = 1, 37 | EPARSENUM, 38 | EPARSE, 39 | EFRAGMENT, 40 | ECTRL, 41 | EUNICODE, 42 | EDEPTH, 43 | EESCAPE, 44 | ETRAILCOMMA, 45 | ETRAILGARBAGE, 46 | EEOF, 47 | EINPUT 48 | }; 49 | 50 | /** 51 | @brief Common base class for parsing & writing. 52 | 53 | This class contains the common error-handling code and option between the parser/writer. 54 | */ 55 | @interface SBJsonBase : NSObject { 56 | NSMutableArray *errorTrace; 57 | 58 | @protected 59 | NSUInteger depth, maxDepth; 60 | } 61 | 62 | /** 63 | @brief The maximum recursing depth. 64 | 65 | Defaults to 512. If the input is nested deeper than this the input will be deemed to be 66 | malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can 67 | turn off this security feature by setting the maxDepth value to 0. 68 | */ 69 | @property NSUInteger maxDepth; 70 | 71 | /** 72 | @brief Return an error trace, or nil if there was no errors. 73 | 74 | Note that this method returns the trace of the last method that failed. 75 | You need to check the return value of the call you're making to figure out 76 | if the call actually failed, before you know call this method. 77 | */ 78 | @property(copy,readonly) NSArray* errorTrace; 79 | 80 | /// @internal for use in subclasses to add errors to the stack trace 81 | - (void)addErrorWithCode:(NSUInteger)code description:(NSString*)str; 82 | 83 | /// @internal for use in subclasess to clear the error before a new parsing attempt 84 | - (void)clearErrorTrace; 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /Externals/SBJSON/SBJsonBase.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "SBJsonBase.h" 31 | NSString * SBJSONErrorDomain = @"org.brautaset.JSON.ErrorDomain"; 32 | 33 | 34 | @implementation SBJsonBase 35 | 36 | @synthesize errorTrace; 37 | @synthesize maxDepth; 38 | 39 | - (id)init { 40 | self = [super init]; 41 | if (self) 42 | self.maxDepth = 512; 43 | return self; 44 | } 45 | 46 | - (void)dealloc { 47 | [errorTrace release]; 48 | [super dealloc]; 49 | } 50 | 51 | - (void)addErrorWithCode:(NSUInteger)code description:(NSString*)str { 52 | NSDictionary *userInfo; 53 | if (!errorTrace) { 54 | errorTrace = [NSMutableArray new]; 55 | userInfo = [NSDictionary dictionaryWithObject:str forKey:NSLocalizedDescriptionKey]; 56 | 57 | } else { 58 | userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 59 | str, NSLocalizedDescriptionKey, 60 | [errorTrace lastObject], NSUnderlyingErrorKey, 61 | nil]; 62 | } 63 | 64 | NSError *error = [NSError errorWithDomain:SBJSONErrorDomain code:code userInfo:userInfo]; 65 | 66 | [self willChangeValueForKey:@"errorTrace"]; 67 | [errorTrace addObject:error]; 68 | [self didChangeValueForKey:@"errorTrace"]; 69 | } 70 | 71 | - (void)clearErrorTrace { 72 | [self willChangeValueForKey:@"errorTrace"]; 73 | [errorTrace release]; 74 | errorTrace = nil; 75 | [self didChangeValueForKey:@"errorTrace"]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /Externals/SBJSON/SBJsonParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import "SBJsonBase.h" 32 | 33 | /** 34 | @brief Options for the parser class. 35 | 36 | This exists so the SBJSON facade can implement the options in the parser without having to re-declare them. 37 | */ 38 | @protocol SBJsonParser 39 | 40 | /** 41 | @brief Return the object represented by the given string. 42 | 43 | Returns the object represented by the passed-in string or nil on error. The returned object can be 44 | a string, number, boolean, null, array or dictionary. 45 | 46 | @param repr the json string to parse 47 | */ 48 | - (id)objectWithString:(NSString *)repr; 49 | 50 | @end 51 | 52 | 53 | /** 54 | @brief The JSON parser class. 55 | 56 | JSON is mapped to Objective-C types in the following way: 57 | 58 | @li Null -> NSNull 59 | @li String -> NSMutableString 60 | @li Array -> NSMutableArray 61 | @li Object -> NSMutableDictionary 62 | @li Boolean -> NSNumber (initialised with -initWithBool:) 63 | @li Number -> NSDecimalNumber 64 | 65 | Since Objective-C doesn't have a dedicated class for boolean values, these turns into NSNumber 66 | instances. These are initialised with the -initWithBool: method, and 67 | round-trip back to JSON properly. (They won't silently suddenly become 0 or 1; they'll be 68 | represented as 'true' and 'false' again.) 69 | 70 | JSON numbers turn into NSDecimalNumber instances, 71 | as we can thus avoid any loss of precision. (JSON allows ridiculously large numbers.) 72 | 73 | */ 74 | @interface SBJsonParser : SBJsonBase { 75 | 76 | @private 77 | const char *c; 78 | } 79 | 80 | @end 81 | 82 | // don't use - exists for backwards compatibility with 2.1.x only. Will be removed in 2.3. 83 | @interface SBJsonParser (Private) 84 | - (id)fragmentWithString:(id)repr; 85 | @end 86 | 87 | 88 | -------------------------------------------------------------------------------- /Externals/SBJSON/SBJsonParser.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "SBJsonParser.h" 31 | 32 | @interface SBJsonParser () 33 | 34 | - (BOOL)scanValue:(NSObject **)o; 35 | 36 | - (BOOL)scanRestOfArray:(NSMutableArray **)o; 37 | - (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o; 38 | - (BOOL)scanRestOfNull:(NSNull **)o; 39 | - (BOOL)scanRestOfFalse:(NSNumber **)o; 40 | - (BOOL)scanRestOfTrue:(NSNumber **)o; 41 | - (BOOL)scanRestOfString:(NSMutableString **)o; 42 | 43 | // Cannot manage without looking at the first digit 44 | - (BOOL)scanNumber:(NSNumber **)o; 45 | 46 | - (BOOL)scanHexQuad:(unichar *)x; 47 | - (BOOL)scanUnicodeChar:(unichar *)x; 48 | 49 | - (BOOL)scanIsAtEnd; 50 | 51 | @end 52 | 53 | #define skipWhitespace(c) while (isspace(*c)) c++ 54 | #define skipDigits(c) while (isdigit(*c)) c++ 55 | 56 | 57 | @implementation SBJsonParser 58 | 59 | static char ctrl[0x22]; 60 | 61 | 62 | + (void)initialize { 63 | ctrl[0] = '\"'; 64 | ctrl[1] = '\\'; 65 | for (int i = 1; i < 0x20; i++) 66 | ctrl[i+1] = i; 67 | ctrl[0x21] = 0; 68 | } 69 | 70 | /** 71 | @deprecated This exists in order to provide fragment support in older APIs in one more version. 72 | It should be removed in the next major version. 73 | */ 74 | - (id)fragmentWithString:(id)repr { 75 | [self clearErrorTrace]; 76 | 77 | if (!repr) { 78 | [self addErrorWithCode:EINPUT description:@"Input was 'nil'"]; 79 | return nil; 80 | } 81 | 82 | depth = 0; 83 | c = [repr UTF8String]; 84 | 85 | id o; 86 | if (![self scanValue:&o]) { 87 | return nil; 88 | } 89 | 90 | // We found some valid JSON. But did it also contain something else? 91 | if (![self scanIsAtEnd]) { 92 | [self addErrorWithCode:ETRAILGARBAGE description:@"Garbage after JSON"]; 93 | return nil; 94 | } 95 | 96 | NSAssert1(o, @"Should have a valid object from %@", repr); 97 | return o; 98 | } 99 | 100 | - (id)objectWithString:(NSString *)repr { 101 | 102 | id o = [self fragmentWithString:repr]; 103 | if (!o) 104 | return nil; 105 | 106 | // Check that the object we've found is a valid JSON container. 107 | if (![o isKindOfClass:[NSDictionary class]] && ![o isKindOfClass:[NSArray class]]) { 108 | [self addErrorWithCode:EFRAGMENT description:@"Valid fragment, but not JSON"]; 109 | return nil; 110 | } 111 | 112 | return o; 113 | } 114 | 115 | /* 116 | In contrast to the public methods, it is an error to omit the error parameter here. 117 | */ 118 | - (BOOL)scanValue:(NSObject **)o 119 | { 120 | skipWhitespace(c); 121 | 122 | switch (*c++) { 123 | case '{': 124 | return [self scanRestOfDictionary:(NSMutableDictionary **)o]; 125 | break; 126 | case '[': 127 | return [self scanRestOfArray:(NSMutableArray **)o]; 128 | break; 129 | case '"': 130 | return [self scanRestOfString:(NSMutableString **)o]; 131 | break; 132 | case 'f': 133 | return [self scanRestOfFalse:(NSNumber **)o]; 134 | break; 135 | case 't': 136 | return [self scanRestOfTrue:(NSNumber **)o]; 137 | break; 138 | case 'n': 139 | return [self scanRestOfNull:(NSNull **)o]; 140 | break; 141 | case '-': 142 | case '0'...'9': 143 | c--; // cannot verify number correctly without the first character 144 | return [self scanNumber:(NSNumber **)o]; 145 | break; 146 | case '+': 147 | [self addErrorWithCode:EPARSENUM description: @"Leading + disallowed in number"]; 148 | return NO; 149 | break; 150 | case 0x0: 151 | [self addErrorWithCode:EEOF description:@"Unexpected end of string"]; 152 | return NO; 153 | break; 154 | default: 155 | [self addErrorWithCode:EPARSE description: @"Unrecognised leading character"]; 156 | return NO; 157 | break; 158 | } 159 | 160 | NSAssert(0, @"Should never get here"); 161 | return NO; 162 | } 163 | 164 | - (BOOL)scanRestOfTrue:(NSNumber **)o 165 | { 166 | if (!strncmp(c, "rue", 3)) { 167 | c += 3; 168 | *o = [NSNumber numberWithBool:YES]; 169 | return YES; 170 | } 171 | [self addErrorWithCode:EPARSE description:@"Expected 'true'"]; 172 | return NO; 173 | } 174 | 175 | - (BOOL)scanRestOfFalse:(NSNumber **)o 176 | { 177 | if (!strncmp(c, "alse", 4)) { 178 | c += 4; 179 | *o = [NSNumber numberWithBool:NO]; 180 | return YES; 181 | } 182 | [self addErrorWithCode:EPARSE description: @"Expected 'false'"]; 183 | return NO; 184 | } 185 | 186 | - (BOOL)scanRestOfNull:(NSNull **)o { 187 | if (!strncmp(c, "ull", 3)) { 188 | c += 3; 189 | *o = [NSNull null]; 190 | return YES; 191 | } 192 | [self addErrorWithCode:EPARSE description: @"Expected 'null'"]; 193 | return NO; 194 | } 195 | 196 | - (BOOL)scanRestOfArray:(NSMutableArray **)o { 197 | if (maxDepth && ++depth > maxDepth) { 198 | [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; 199 | return NO; 200 | } 201 | 202 | *o = [NSMutableArray arrayWithCapacity:8]; 203 | 204 | for (; *c ;) { 205 | id v; 206 | 207 | skipWhitespace(c); 208 | if (*c == ']' && c++) { 209 | depth--; 210 | return YES; 211 | } 212 | 213 | if (![self scanValue:&v]) { 214 | [self addErrorWithCode:EPARSE description:@"Expected value while parsing array"]; 215 | return NO; 216 | } 217 | 218 | [*o addObject:v]; 219 | 220 | skipWhitespace(c); 221 | if (*c == ',' && c++) { 222 | skipWhitespace(c); 223 | if (*c == ']') { 224 | [self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in array"]; 225 | return NO; 226 | } 227 | } 228 | } 229 | 230 | [self addErrorWithCode:EEOF description: @"End of input while parsing array"]; 231 | return NO; 232 | } 233 | 234 | - (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o 235 | { 236 | if (maxDepth && ++depth > maxDepth) { 237 | [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; 238 | return NO; 239 | } 240 | 241 | *o = [NSMutableDictionary dictionaryWithCapacity:7]; 242 | 243 | for (; *c ;) { 244 | id k, v; 245 | 246 | skipWhitespace(c); 247 | if (*c == '}' && c++) { 248 | depth--; 249 | return YES; 250 | } 251 | 252 | if (!(*c == '\"' && c++ && [self scanRestOfString:&k])) { 253 | [self addErrorWithCode:EPARSE description: @"Object key string expected"]; 254 | return NO; 255 | } 256 | 257 | skipWhitespace(c); 258 | if (*c != ':') { 259 | [self addErrorWithCode:EPARSE description: @"Expected ':' separating key and value"]; 260 | return NO; 261 | } 262 | 263 | c++; 264 | if (![self scanValue:&v]) { 265 | NSString *string = [NSString stringWithFormat:@"Object value expected for key: %@", k]; 266 | [self addErrorWithCode:EPARSE description: string]; 267 | return NO; 268 | } 269 | 270 | [*o setObject:v forKey:k]; 271 | 272 | skipWhitespace(c); 273 | if (*c == ',' && c++) { 274 | skipWhitespace(c); 275 | if (*c == '}') { 276 | [self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in object"]; 277 | return NO; 278 | } 279 | } 280 | } 281 | 282 | [self addErrorWithCode:EEOF description: @"End of input while parsing object"]; 283 | return NO; 284 | } 285 | 286 | - (BOOL)scanRestOfString:(NSMutableString **)o 287 | { 288 | *o = [NSMutableString stringWithCapacity:16]; 289 | do { 290 | // First see if there's a portion we can grab in one go. 291 | // Doing this caused a massive speedup on the long string. 292 | size_t len = strcspn(c, ctrl); 293 | if (len) { 294 | // check for 295 | id t = [[NSString alloc] initWithBytesNoCopy:(char*)c 296 | length:len 297 | encoding:NSUTF8StringEncoding 298 | freeWhenDone:NO]; 299 | if (t) { 300 | [*o appendString:t]; 301 | [t release]; 302 | c += len; 303 | } 304 | } 305 | 306 | if (*c == '"') { 307 | c++; 308 | return YES; 309 | 310 | } else if (*c == '\\') { 311 | unichar uc = *++c; 312 | switch (uc) { 313 | case '\\': 314 | case '/': 315 | case '"': 316 | break; 317 | 318 | case 'b': uc = '\b'; break; 319 | case 'n': uc = '\n'; break; 320 | case 'r': uc = '\r'; break; 321 | case 't': uc = '\t'; break; 322 | case 'f': uc = '\f'; break; 323 | 324 | case 'u': 325 | c++; 326 | if (![self scanUnicodeChar:&uc]) { 327 | [self addErrorWithCode:EUNICODE description: @"Broken unicode character"]; 328 | return NO; 329 | } 330 | c--; // hack. 331 | break; 332 | default: 333 | [self addErrorWithCode:EESCAPE description: [NSString stringWithFormat:@"Illegal escape sequence '0x%x'", uc]]; 334 | return NO; 335 | break; 336 | } 337 | CFStringAppendCharacters((CFMutableStringRef)*o, &uc, 1); 338 | c++; 339 | 340 | } else if (*c < 0x20) { 341 | [self addErrorWithCode:ECTRL description: [NSString stringWithFormat:@"Unescaped control character '0x%x'", *c]]; 342 | return NO; 343 | 344 | } else { 345 | NSLog(@"should not be able to get here"); 346 | } 347 | } while (*c); 348 | 349 | [self addErrorWithCode:EEOF description:@"Unexpected EOF while parsing string"]; 350 | return NO; 351 | } 352 | 353 | - (BOOL)scanUnicodeChar:(unichar *)x 354 | { 355 | unichar hi, lo; 356 | 357 | if (![self scanHexQuad:&hi]) { 358 | [self addErrorWithCode:EUNICODE description: @"Missing hex quad"]; 359 | return NO; 360 | } 361 | 362 | if (hi >= 0xd800) { // high surrogate char? 363 | if (hi < 0xdc00) { // yes - expect a low char 364 | 365 | if (!(*c == '\\' && ++c && *c == 'u' && ++c && [self scanHexQuad:&lo])) { 366 | [self addErrorWithCode:EUNICODE description: @"Missing low character in surrogate pair"]; 367 | return NO; 368 | } 369 | 370 | if (lo < 0xdc00 || lo >= 0xdfff) { 371 | [self addErrorWithCode:EUNICODE description:@"Invalid low surrogate char"]; 372 | return NO; 373 | } 374 | 375 | hi = (hi - 0xd800) * 0x400 + (lo - 0xdc00) + 0x10000; 376 | 377 | } else if (hi < 0xe000) { 378 | [self addErrorWithCode:EUNICODE description:@"Invalid high character in surrogate pair"]; 379 | return NO; 380 | } 381 | } 382 | 383 | *x = hi; 384 | return YES; 385 | } 386 | 387 | - (BOOL)scanHexQuad:(unichar *)x 388 | { 389 | *x = 0; 390 | for (int i = 0; i < 4; i++) { 391 | unichar uc = *c; 392 | c++; 393 | int d = (uc >= '0' && uc <= '9') 394 | ? uc - '0' : (uc >= 'a' && uc <= 'f') 395 | ? (uc - 'a' + 10) : (uc >= 'A' && uc <= 'F') 396 | ? (uc - 'A' + 10) : -1; 397 | if (d == -1) { 398 | [self addErrorWithCode:EUNICODE description:@"Missing hex digit in quad"]; 399 | return NO; 400 | } 401 | *x *= 16; 402 | *x += d; 403 | } 404 | return YES; 405 | } 406 | 407 | - (BOOL)scanNumber:(NSNumber **)o 408 | { 409 | const char *ns = c; 410 | 411 | // The logic to test for validity of the number formatting is relicensed 412 | // from JSON::XS with permission from its author Marc Lehmann. 413 | // (Available at the CPAN: http://search.cpan.org/dist/JSON-XS/ .) 414 | 415 | if ('-' == *c) 416 | c++; 417 | 418 | if ('0' == *c && c++) { 419 | if (isdigit(*c)) { 420 | [self addErrorWithCode:EPARSENUM description: @"Leading 0 disallowed in number"]; 421 | return NO; 422 | } 423 | 424 | } else if (!isdigit(*c) && c != ns) { 425 | [self addErrorWithCode:EPARSENUM description: @"No digits after initial minus"]; 426 | return NO; 427 | 428 | } else { 429 | skipDigits(c); 430 | } 431 | 432 | // Fractional part 433 | if ('.' == *c && c++) { 434 | 435 | if (!isdigit(*c)) { 436 | [self addErrorWithCode:EPARSENUM description: @"No digits after decimal point"]; 437 | return NO; 438 | } 439 | skipDigits(c); 440 | } 441 | 442 | // Exponential part 443 | if ('e' == *c || 'E' == *c) { 444 | c++; 445 | 446 | if ('-' == *c || '+' == *c) 447 | c++; 448 | 449 | if (!isdigit(*c)) { 450 | [self addErrorWithCode:EPARSENUM description: @"No digits after exponent"]; 451 | return NO; 452 | } 453 | skipDigits(c); 454 | } 455 | 456 | id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns 457 | length:c - ns 458 | encoding:NSUTF8StringEncoding 459 | freeWhenDone:NO]; 460 | [str autorelease]; 461 | if (str && (*o = [NSDecimalNumber decimalNumberWithString:str])) 462 | return YES; 463 | 464 | [self addErrorWithCode:EPARSENUM description: @"Failed creating decimal instance"]; 465 | return NO; 466 | } 467 | 468 | - (BOOL)scanIsAtEnd 469 | { 470 | skipWhitespace(c); 471 | return !*c; 472 | } 473 | 474 | 475 | @end 476 | -------------------------------------------------------------------------------- /Externals/SBJSON/SBJsonWriter.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import "SBJsonBase.h" 32 | 33 | /** 34 | @brief Options for the writer class. 35 | 36 | This exists so the SBJSON facade can implement the options in the writer without having to re-declare them. 37 | */ 38 | @protocol SBJsonWriter 39 | 40 | /** 41 | @brief Whether we are generating human-readable (multiline) JSON. 42 | 43 | Set whether or not to generate human-readable JSON. The default is NO, which produces 44 | JSON without any whitespace. (Except inside strings.) If set to YES, generates human-readable 45 | JSON with linebreaks after each array value and dictionary key/value pair, indented two 46 | spaces per nesting level. 47 | */ 48 | @property BOOL humanReadable; 49 | 50 | /** 51 | @brief Whether or not to sort the dictionary keys in the output. 52 | 53 | If this is set to YES, the dictionary keys in the JSON output will be in sorted order. 54 | (This is useful if you need to compare two structures, for example.) The default is NO. 55 | */ 56 | @property BOOL sortKeys; 57 | 58 | /** 59 | @brief Return JSON representation (or fragment) for the given object. 60 | 61 | Returns a string containing JSON representation of the passed in value, or nil on error. 62 | If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. 63 | 64 | @param value any instance that can be represented as a JSON fragment 65 | 66 | */ 67 | - (NSString*)stringWithObject:(id)value; 68 | 69 | @end 70 | 71 | 72 | /** 73 | @brief The JSON writer class. 74 | 75 | Objective-C types are mapped to JSON types in the following way: 76 | 77 | @li NSNull -> Null 78 | @li NSString -> String 79 | @li NSArray -> Array 80 | @li NSDictionary -> Object 81 | @li NSNumber (-initWithBool:) -> Boolean 82 | @li NSNumber -> Number 83 | 84 | In JSON the keys of an object must be strings. NSDictionary keys need 85 | not be, but attempting to convert an NSDictionary with non-string keys 86 | into JSON will throw an exception. 87 | 88 | NSNumber instances created with the +initWithBool: method are 89 | converted into the JSON boolean "true" and "false" values, and vice 90 | versa. Any other NSNumber instances are converted to a JSON number the 91 | way you would expect. 92 | 93 | */ 94 | @interface SBJsonWriter : SBJsonBase { 95 | 96 | @private 97 | BOOL sortKeys, humanReadable; 98 | } 99 | 100 | @end 101 | 102 | // don't use - exists for backwards compatibility. Will be removed in 2.3. 103 | @interface SBJsonWriter (Private) 104 | - (NSString*)stringWithFragment:(id)value; 105 | @end 106 | 107 | /** 108 | @brief Allows generation of JSON for otherwise unsupported classes. 109 | 110 | If you have a custom class that you want to create a JSON representation for you can implement 111 | this method in your class. It should return a representation of your object defined 112 | in terms of objects that can be translated into JSON. For example, a Person 113 | object might implement it like this: 114 | 115 | @code 116 | - (id)jsonProxyObject { 117 | return [NSDictionary dictionaryWithObjectsAndKeys: 118 | name, @"name", 119 | phone, @"phone", 120 | email, @"email", 121 | nil]; 122 | } 123 | @endcode 124 | 125 | */ 126 | @interface NSObject (SBProxyForJson) 127 | - (id)proxyForJson; 128 | @end 129 | 130 | -------------------------------------------------------------------------------- /Externals/SBJSON/SBJsonWriter.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2009 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "SBJsonWriter.h" 31 | 32 | @interface SBJsonWriter () 33 | 34 | - (BOOL)appendValue:(id)fragment into:(NSMutableString*)json; 35 | - (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json; 36 | - (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json; 37 | - (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json; 38 | 39 | - (NSString*)indent; 40 | 41 | @end 42 | 43 | @implementation SBJsonWriter 44 | 45 | static NSMutableCharacterSet *kEscapeChars; 46 | 47 | + (void)initialize { 48 | kEscapeChars = [[NSMutableCharacterSet characterSetWithRange: NSMakeRange(0,32)] retain]; 49 | [kEscapeChars addCharactersInString: @"\"\\"]; 50 | } 51 | 52 | 53 | @synthesize sortKeys; 54 | @synthesize humanReadable; 55 | 56 | /** 57 | @deprecated This exists in order to provide fragment support in older APIs in one more version. 58 | It should be removed in the next major version. 59 | */ 60 | - (NSString*)stringWithFragment:(id)value { 61 | [self clearErrorTrace]; 62 | depth = 0; 63 | NSMutableString *json = [NSMutableString stringWithCapacity:128]; 64 | 65 | if ([self appendValue:value into:json]) 66 | return json; 67 | 68 | return nil; 69 | } 70 | 71 | 72 | - (NSString*)stringWithObject:(id)value { 73 | 74 | if ([value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSArray class]]) { 75 | return [self stringWithFragment:value]; 76 | } 77 | 78 | if ([value respondsToSelector:@selector(proxyForJson)]) { 79 | NSString *tmp = [self stringWithObject:[value proxyForJson]]; 80 | if (tmp) 81 | return tmp; 82 | } 83 | 84 | 85 | [self clearErrorTrace]; 86 | [self addErrorWithCode:EFRAGMENT description:@"Not valid type for JSON"]; 87 | return nil; 88 | } 89 | 90 | 91 | - (NSString*)indent { 92 | return [@"\n" stringByPaddingToLength:1 + 2 * depth withString:@" " startingAtIndex:0]; 93 | } 94 | 95 | - (BOOL)appendValue:(id)fragment into:(NSMutableString*)json { 96 | if ([fragment isKindOfClass:[NSDictionary class]]) { 97 | if (![self appendDictionary:fragment into:json]) 98 | return NO; 99 | 100 | } else if ([fragment isKindOfClass:[NSArray class]]) { 101 | if (![self appendArray:fragment into:json]) 102 | return NO; 103 | 104 | } else if ([fragment isKindOfClass:[NSString class]]) { 105 | if (![self appendString:fragment into:json]) 106 | return NO; 107 | 108 | } else if ([fragment isKindOfClass:[NSNumber class]]) { 109 | if ('c' == *[fragment objCType]) 110 | [json appendString:[fragment boolValue] ? @"true" : @"false"]; 111 | else 112 | [json appendString:[fragment stringValue]]; 113 | 114 | } else if ([fragment isKindOfClass:[NSNull class]]) { 115 | [json appendString:@"null"]; 116 | } else if ([fragment respondsToSelector:@selector(proxyForJson)]) { 117 | [self appendValue:[fragment proxyForJson] into:json]; 118 | 119 | } else { 120 | [self addErrorWithCode:EUNSUPPORTED description:[NSString stringWithFormat:@"JSON serialisation not supported for %@", [fragment class]]]; 121 | return NO; 122 | } 123 | return YES; 124 | } 125 | 126 | - (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json { 127 | if (maxDepth && ++depth > maxDepth) { 128 | [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; 129 | return NO; 130 | } 131 | [json appendString:@"["]; 132 | 133 | BOOL addComma = NO; 134 | for (id value in fragment) { 135 | if (addComma) 136 | [json appendString:@","]; 137 | else 138 | addComma = YES; 139 | 140 | if ([self humanReadable]) 141 | [json appendString:[self indent]]; 142 | 143 | if (![self appendValue:value into:json]) { 144 | return NO; 145 | } 146 | } 147 | 148 | depth--; 149 | if ([self humanReadable] && [fragment count]) 150 | [json appendString:[self indent]]; 151 | [json appendString:@"]"]; 152 | return YES; 153 | } 154 | 155 | - (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json { 156 | if (maxDepth && ++depth > maxDepth) { 157 | [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; 158 | return NO; 159 | } 160 | [json appendString:@"{"]; 161 | 162 | NSString *colon = [self humanReadable] ? @" : " : @":"; 163 | BOOL addComma = NO; 164 | NSArray *keys = [fragment allKeys]; 165 | if (self.sortKeys) 166 | keys = [keys sortedArrayUsingSelector:@selector(compare:)]; 167 | 168 | for (id value in keys) { 169 | if (addComma) 170 | [json appendString:@","]; 171 | else 172 | addComma = YES; 173 | 174 | if ([self humanReadable]) 175 | [json appendString:[self indent]]; 176 | 177 | if (![value isKindOfClass:[NSString class]]) { 178 | [self addErrorWithCode:EUNSUPPORTED description: @"JSON object key must be string"]; 179 | return NO; 180 | } 181 | 182 | if (![self appendString:value into:json]) 183 | return NO; 184 | 185 | [json appendString:colon]; 186 | if (![self appendValue:[fragment objectForKey:value] into:json]) { 187 | [self addErrorWithCode:EUNSUPPORTED description:[NSString stringWithFormat:@"Unsupported value for key %@ in object", value]]; 188 | return NO; 189 | } 190 | } 191 | 192 | depth--; 193 | if ([self humanReadable] && [fragment count]) 194 | [json appendString:[self indent]]; 195 | [json appendString:@"}"]; 196 | return YES; 197 | } 198 | 199 | - (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json { 200 | 201 | [json appendString:@"\""]; 202 | 203 | NSRange esc = [fragment rangeOfCharacterFromSet:kEscapeChars]; 204 | if ( !esc.length ) { 205 | // No special chars -- can just add the raw string: 206 | [json appendString:fragment]; 207 | 208 | } else { 209 | NSUInteger length = [fragment length]; 210 | for (NSUInteger i = 0; i < length; i++) { 211 | unichar uc = [fragment characterAtIndex:i]; 212 | switch (uc) { 213 | case '"': [json appendString:@"\\\""]; break; 214 | case '\\': [json appendString:@"\\\\"]; break; 215 | case '\t': [json appendString:@"\\t"]; break; 216 | case '\n': [json appendString:@"\\n"]; break; 217 | case '\r': [json appendString:@"\\r"]; break; 218 | case '\b': [json appendString:@"\\b"]; break; 219 | case '\f': [json appendString:@"\\f"]; break; 220 | default: 221 | if (uc < 0x20) { 222 | [json appendFormat:@"\\u%04x", uc]; 223 | } else { 224 | CFStringAppendCharacters((CFMutableStringRef)json, &uc, 1); 225 | } 226 | break; 227 | 228 | } 229 | } 230 | } 231 | 232 | [json appendString:@"\""]; 233 | return YES; 234 | } 235 | 236 | 237 | @end 238 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Jeremy Lightsmith 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RestClient 2 | ========== 3 | 4 | This is meant to be a simple way of calling restful json based webservices. We prefer keeping things simple. So use synchronous calls when you can, and when you can't, use blocks so you keep all your logic in one place. 5 | 6 | This was developed on Maptini (http://maptini.com/) so thanks them for making this available. 7 | 8 | Usage 9 | ----- 10 | 11 | Simple synchronous usage: 12 | 13 | RestClient *client = [[RestClient alloc] init]; 14 | NSDictionary *response = [[client get:"http://maptini.com/users.json"] JSONValue]; 15 | [client release] 16 | 17 | Synchronous usage with prefix & headers: 18 | 19 | RestClient *client = [[RestClient alloc] initWithPrefix:@"http://maptini.com/api" 20 | andHeaders:[NSDictionary dictionaryWithObjectsAndKeys: 21 | userAPIToken, @"X-MaptiniToken", 22 | @"4.0", @"X-MaptiniVersion", nil]]; 23 | NSDictionary *response = [[client post:"/users.json" withBody:body] JSONValue]; 24 | [client release] 25 | 26 | Synchronous usage with error handling: 27 | 28 | RestClient *client = [[RestClient alloc] init]; 29 | @try { 30 | return [[client get:"http://maptini.com/users"] JSONValue]; 31 | } 32 | @catch (NSException * e) { 33 | // handle the error 34 | return ... 35 | } 36 | @finally { 37 | [client release]; 38 | } 39 | 40 | Asynchronous usage: 41 | 42 | RestClient *client = [[RestClient alloc] init]; 43 | [client get:@"/account.json" 44 | wait:true 45 | success:[[^(id json) { 46 | User *user = [[User alloc] initWithDictionary:[NSDictionary dictionaryWithDictionary:[json objectForKey:@"user"]]]; 47 | [user store]; 48 | [[NSNotificationCenter defaultCenter] postNotificationName:CMDidLogin object:nil]; 49 | [user release]; 50 | } copy] autorelease] 51 | error:^(NSError *error) { 52 | [error showWithTitle:NSLocalizedString(@"ErrorConnectionTitle", @"")]; 53 | }]; 54 | [client release]; 55 | 56 | 57 | Contact 58 | ------- 59 | 60 | Jeremy Lightsmith 61 | jeremy.lightsmith@gmail.com -------------------------------------------------------------------------------- /RestClient.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | EC0AFDBB132FDEB0007BAC46 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC0AFDBA132FDEB0007BAC46 /* Foundation.framework */; }; 11 | EC0AFDC6132FDEB0007BAC46 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC0AFDC5132FDEB0007BAC46 /* UIKit.framework */; }; 12 | EC0AFDC7132FDEB0007BAC46 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC0AFDBA132FDEB0007BAC46 /* Foundation.framework */; }; 13 | EC0AFDC9132FDEB0007BAC46 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC0AFDC8132FDEB0007BAC46 /* CoreGraphics.framework */; }; 14 | EC0AFDCC132FDEB0007BAC46 /* libRestClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC0AFDB7132FDEB0007BAC46 /* libRestClient.a */; }; 15 | EC0AFDD2132FDEB0007BAC46 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = EC0AFDD0132FDEB0007BAC46 /* InfoPlist.strings */; }; 16 | EC0AFDD5132FDEB0007BAC46 /* RestClientTests.h in Resources */ = {isa = PBXBuildFile; fileRef = EC0AFDD4132FDEB0007BAC46 /* RestClientTests.h */; }; 17 | EC0AFDD7132FDEB0007BAC46 /* RestClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDD6132FDEB0007BAC46 /* RestClientTests.m */; }; 18 | EC0AFDE6132FDF1F007BAC46 /* RestClient.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFDE0132FDF1F007BAC46 /* RestClient.h */; }; 19 | EC0AFDE7132FDF1F007BAC46 /* RestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDE1132FDF1F007BAC46 /* RestClient.m */; }; 20 | EC0AFDE8132FDF1F007BAC46 /* RestClientBlockRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFDE2132FDF1F007BAC46 /* RestClientBlockRunner.h */; }; 21 | EC0AFDE9132FDF1F007BAC46 /* RestClientBlockRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDE3132FDF1F007BAC46 /* RestClientBlockRunner.m */; }; 22 | EC0AFDEA132FDF1F007BAC46 /* RestClientURLConnectionInvocation.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFDE4132FDF1F007BAC46 /* RestClientURLConnectionInvocation.h */; }; 23 | EC0AFDEB132FDF1F007BAC46 /* RestClientURLConnectionInvocation.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDE5132FDF1F007BAC46 /* RestClientURLConnectionInvocation.m */; }; 24 | EC0AFDF0132FDF3C007BAC46 /* BlockRunnerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDEC132FDF3C007BAC46 /* BlockRunnerSpec.m */; }; 25 | EC0AFDF1132FDF3C007BAC46 /* FakeBlockRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDEE132FDF3C007BAC46 /* FakeBlockRunner.m */; }; 26 | EC0AFDF2132FDF3C007BAC46 /* RestClientSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDEF132FDF3C007BAC46 /* RestClientSpec.m */; }; 27 | EC0AFDF7132FDFD8007BAC46 /* NSData+Base64.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFDF5132FDFD8007BAC46 /* NSData+Base64.h */; }; 28 | EC0AFDF8132FDFD8007BAC46 /* NSData+Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDF6132FDFD8007BAC46 /* NSData+Base64.m */; }; 29 | EC0AFE06132FDFE4007BAC46 /* JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFDF9132FDFE4007BAC46 /* JSON.h */; }; 30 | EC0AFE07132FDFE4007BAC46 /* NSObject+SBJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFDFA132FDFE4007BAC46 /* NSObject+SBJSON.h */; }; 31 | EC0AFE08132FDFE4007BAC46 /* NSObject+SBJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDFB132FDFE4007BAC46 /* NSObject+SBJSON.m */; }; 32 | EC0AFE09132FDFE4007BAC46 /* NSString+SBJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFDFC132FDFE4007BAC46 /* NSString+SBJSON.h */; }; 33 | EC0AFE0A132FDFE4007BAC46 /* NSString+SBJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDFD132FDFE4007BAC46 /* NSString+SBJSON.m */; }; 34 | EC0AFE0B132FDFE4007BAC46 /* SBJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFDFE132FDFE4007BAC46 /* SBJSON.h */; }; 35 | EC0AFE0C132FDFE4007BAC46 /* SBJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFDFF132FDFE4007BAC46 /* SBJSON.m */; }; 36 | EC0AFE0D132FDFE4007BAC46 /* SBJsonBase.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFE00132FDFE4007BAC46 /* SBJsonBase.h */; }; 37 | EC0AFE0E132FDFE4007BAC46 /* SBJsonBase.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFE01132FDFE4007BAC46 /* SBJsonBase.m */; }; 38 | EC0AFE0F132FDFE4007BAC46 /* SBJsonParser.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFE02132FDFE4007BAC46 /* SBJsonParser.h */; }; 39 | EC0AFE10132FDFE4007BAC46 /* SBJsonParser.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFE03132FDFE4007BAC46 /* SBJsonParser.m */; }; 40 | EC0AFE11132FDFE4007BAC46 /* SBJsonWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0AFE04132FDFE4007BAC46 /* SBJsonWriter.h */; }; 41 | EC0AFE12132FDFE4007BAC46 /* SBJsonWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0AFE05132FDFE4007BAC46 /* SBJsonWriter.m */; }; 42 | /* End PBXBuildFile section */ 43 | 44 | /* Begin PBXContainerItemProxy section */ 45 | EC0AFDCA132FDEB0007BAC46 /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = EC0AFDAE132FDEB0007BAC46 /* Project object */; 48 | proxyType = 1; 49 | remoteGlobalIDString = EC0AFDB6132FDEB0007BAC46; 50 | remoteInfo = RestClient; 51 | }; 52 | /* End PBXContainerItemProxy section */ 53 | 54 | /* Begin PBXFileReference section */ 55 | EC0AFDB7132FDEB0007BAC46 /* libRestClient.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRestClient.a; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | EC0AFDBA132FDEB0007BAC46 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 57 | EC0AFDBE132FDEB0007BAC46 /* RestClient-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RestClient-Prefix.pch"; sourceTree = ""; }; 58 | EC0AFDC4132FDEB0007BAC46 /* RestClientTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RestClientTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | EC0AFDC5132FDEB0007BAC46 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 60 | EC0AFDC8132FDEB0007BAC46 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 61 | EC0AFDCF132FDEB0007BAC46 /* RestClientTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RestClientTests-Info.plist"; sourceTree = ""; }; 62 | EC0AFDD1132FDEB0007BAC46 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 63 | EC0AFDD3132FDEB0007BAC46 /* RestClientTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RestClientTests-Prefix.pch"; sourceTree = ""; }; 64 | EC0AFDD4132FDEB0007BAC46 /* RestClientTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RestClientTests.h; sourceTree = ""; }; 65 | EC0AFDD6132FDEB0007BAC46 /* RestClientTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RestClientTests.m; sourceTree = ""; }; 66 | EC0AFDE0132FDF1F007BAC46 /* RestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestClient.h; sourceTree = ""; }; 67 | EC0AFDE1132FDF1F007BAC46 /* RestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestClient.m; sourceTree = ""; }; 68 | EC0AFDE2132FDF1F007BAC46 /* RestClientBlockRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestClientBlockRunner.h; sourceTree = ""; }; 69 | EC0AFDE3132FDF1F007BAC46 /* RestClientBlockRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestClientBlockRunner.m; sourceTree = ""; }; 70 | EC0AFDE4132FDF1F007BAC46 /* RestClientURLConnectionInvocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestClientURLConnectionInvocation.h; sourceTree = ""; }; 71 | EC0AFDE5132FDF1F007BAC46 /* RestClientURLConnectionInvocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestClientURLConnectionInvocation.m; sourceTree = ""; }; 72 | EC0AFDEC132FDF3C007BAC46 /* BlockRunnerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlockRunnerSpec.m; sourceTree = ""; }; 73 | EC0AFDED132FDF3C007BAC46 /* FakeBlockRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FakeBlockRunner.h; sourceTree = ""; }; 74 | EC0AFDEE132FDF3C007BAC46 /* FakeBlockRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FakeBlockRunner.m; sourceTree = ""; }; 75 | EC0AFDEF132FDF3C007BAC46 /* RestClientSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestClientSpec.m; sourceTree = ""; }; 76 | EC0AFDF5132FDFD8007BAC46 /* NSData+Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+Base64.h"; path = "Externals/NSData_Base64/NSData+Base64.h"; sourceTree = SOURCE_ROOT; }; 77 | EC0AFDF6132FDFD8007BAC46 /* NSData+Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+Base64.m"; path = "Externals/NSData_Base64/NSData+Base64.m"; sourceTree = SOURCE_ROOT; }; 78 | EC0AFDF9132FDFE4007BAC46 /* JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSON.h; path = Externals/SBJSON/JSON.h; sourceTree = SOURCE_ROOT; }; 79 | EC0AFDFA132FDFE4007BAC46 /* NSObject+SBJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+SBJSON.h"; path = "Externals/SBJSON/NSObject+SBJSON.h"; sourceTree = SOURCE_ROOT; }; 80 | EC0AFDFB132FDFE4007BAC46 /* NSObject+SBJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+SBJSON.m"; path = "Externals/SBJSON/NSObject+SBJSON.m"; sourceTree = SOURCE_ROOT; }; 81 | EC0AFDFC132FDFE4007BAC46 /* NSString+SBJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+SBJSON.h"; path = "Externals/SBJSON/NSString+SBJSON.h"; sourceTree = SOURCE_ROOT; }; 82 | EC0AFDFD132FDFE4007BAC46 /* NSString+SBJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+SBJSON.m"; path = "Externals/SBJSON/NSString+SBJSON.m"; sourceTree = SOURCE_ROOT; }; 83 | EC0AFDFE132FDFE4007BAC46 /* SBJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBJSON.h; path = Externals/SBJSON/SBJSON.h; sourceTree = SOURCE_ROOT; }; 84 | EC0AFDFF132FDFE4007BAC46 /* SBJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SBJSON.m; path = Externals/SBJSON/SBJSON.m; sourceTree = SOURCE_ROOT; }; 85 | EC0AFE00132FDFE4007BAC46 /* SBJsonBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBJsonBase.h; path = Externals/SBJSON/SBJsonBase.h; sourceTree = SOURCE_ROOT; }; 86 | EC0AFE01132FDFE4007BAC46 /* SBJsonBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SBJsonBase.m; path = Externals/SBJSON/SBJsonBase.m; sourceTree = SOURCE_ROOT; }; 87 | EC0AFE02132FDFE4007BAC46 /* SBJsonParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBJsonParser.h; path = Externals/SBJSON/SBJsonParser.h; sourceTree = SOURCE_ROOT; }; 88 | EC0AFE03132FDFE4007BAC46 /* SBJsonParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SBJsonParser.m; path = Externals/SBJSON/SBJsonParser.m; sourceTree = SOURCE_ROOT; }; 89 | EC0AFE04132FDFE4007BAC46 /* SBJsonWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBJsonWriter.h; path = Externals/SBJSON/SBJsonWriter.h; sourceTree = SOURCE_ROOT; }; 90 | EC0AFE05132FDFE4007BAC46 /* SBJsonWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SBJsonWriter.m; path = Externals/SBJSON/SBJsonWriter.m; sourceTree = SOURCE_ROOT; }; 91 | /* End PBXFileReference section */ 92 | 93 | /* Begin PBXFrameworksBuildPhase section */ 94 | EC0AFDB4132FDEB0007BAC46 /* Frameworks */ = { 95 | isa = PBXFrameworksBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | EC0AFDBB132FDEB0007BAC46 /* Foundation.framework in Frameworks */, 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | EC0AFDC0132FDEB0007BAC46 /* Frameworks */ = { 103 | isa = PBXFrameworksBuildPhase; 104 | buildActionMask = 2147483647; 105 | files = ( 106 | EC0AFDC6132FDEB0007BAC46 /* UIKit.framework in Frameworks */, 107 | EC0AFDC7132FDEB0007BAC46 /* Foundation.framework in Frameworks */, 108 | EC0AFDC9132FDEB0007BAC46 /* CoreGraphics.framework in Frameworks */, 109 | EC0AFDCC132FDEB0007BAC46 /* libRestClient.a in Frameworks */, 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | /* End PBXFrameworksBuildPhase section */ 114 | 115 | /* Begin PBXGroup section */ 116 | EC0AFDAC132FDEB0007BAC46 = { 117 | isa = PBXGroup; 118 | children = ( 119 | EC0AFDBC132FDEB0007BAC46 /* RestClient */, 120 | EC0AFDCD132FDEB0007BAC46 /* RestClientTests */, 121 | EC0AFDB9132FDEB0007BAC46 /* Frameworks */, 122 | EC0AFDB8132FDEB0007BAC46 /* Products */, 123 | ); 124 | sourceTree = ""; 125 | }; 126 | EC0AFDB8132FDEB0007BAC46 /* Products */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | EC0AFDB7132FDEB0007BAC46 /* libRestClient.a */, 130 | EC0AFDC4132FDEB0007BAC46 /* RestClientTests.octest */, 131 | ); 132 | name = Products; 133 | sourceTree = ""; 134 | }; 135 | EC0AFDB9132FDEB0007BAC46 /* Frameworks */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | EC0AFDBA132FDEB0007BAC46 /* Foundation.framework */, 139 | EC0AFDC5132FDEB0007BAC46 /* UIKit.framework */, 140 | EC0AFDC8132FDEB0007BAC46 /* CoreGraphics.framework */, 141 | ); 142 | name = Frameworks; 143 | sourceTree = ""; 144 | }; 145 | EC0AFDBC132FDEB0007BAC46 /* RestClient */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | EC0AFDF4132FDF70007BAC46 /* NSData+Base64 */, 149 | EC0AFDF3132FDF5A007BAC46 /* SBJSON */, 150 | EC0AFDE0132FDF1F007BAC46 /* RestClient.h */, 151 | EC0AFDE1132FDF1F007BAC46 /* RestClient.m */, 152 | EC0AFDE2132FDF1F007BAC46 /* RestClientBlockRunner.h */, 153 | EC0AFDE3132FDF1F007BAC46 /* RestClientBlockRunner.m */, 154 | EC0AFDE4132FDF1F007BAC46 /* RestClientURLConnectionInvocation.h */, 155 | EC0AFDE5132FDF1F007BAC46 /* RestClientURLConnectionInvocation.m */, 156 | EC0AFDBD132FDEB0007BAC46 /* Supporting Files */, 157 | ); 158 | path = RestClient; 159 | sourceTree = ""; 160 | }; 161 | EC0AFDBD132FDEB0007BAC46 /* Supporting Files */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | EC0AFDBE132FDEB0007BAC46 /* RestClient-Prefix.pch */, 165 | ); 166 | name = "Supporting Files"; 167 | sourceTree = ""; 168 | }; 169 | EC0AFDCD132FDEB0007BAC46 /* RestClientTests */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | EC0AFDEC132FDF3C007BAC46 /* BlockRunnerSpec.m */, 173 | EC0AFDED132FDF3C007BAC46 /* FakeBlockRunner.h */, 174 | EC0AFDEE132FDF3C007BAC46 /* FakeBlockRunner.m */, 175 | EC0AFDEF132FDF3C007BAC46 /* RestClientSpec.m */, 176 | EC0AFDD4132FDEB0007BAC46 /* RestClientTests.h */, 177 | EC0AFDD6132FDEB0007BAC46 /* RestClientTests.m */, 178 | EC0AFDCE132FDEB0007BAC46 /* Supporting Files */, 179 | ); 180 | path = RestClientTests; 181 | sourceTree = ""; 182 | }; 183 | EC0AFDCE132FDEB0007BAC46 /* Supporting Files */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | EC0AFDCF132FDEB0007BAC46 /* RestClientTests-Info.plist */, 187 | EC0AFDD0132FDEB0007BAC46 /* InfoPlist.strings */, 188 | EC0AFDD3132FDEB0007BAC46 /* RestClientTests-Prefix.pch */, 189 | ); 190 | name = "Supporting Files"; 191 | sourceTree = ""; 192 | }; 193 | EC0AFDF3132FDF5A007BAC46 /* SBJSON */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | EC0AFDF9132FDFE4007BAC46 /* JSON.h */, 197 | EC0AFDFA132FDFE4007BAC46 /* NSObject+SBJSON.h */, 198 | EC0AFDFB132FDFE4007BAC46 /* NSObject+SBJSON.m */, 199 | EC0AFDFC132FDFE4007BAC46 /* NSString+SBJSON.h */, 200 | EC0AFDFD132FDFE4007BAC46 /* NSString+SBJSON.m */, 201 | EC0AFDFE132FDFE4007BAC46 /* SBJSON.h */, 202 | EC0AFDFF132FDFE4007BAC46 /* SBJSON.m */, 203 | EC0AFE00132FDFE4007BAC46 /* SBJsonBase.h */, 204 | EC0AFE01132FDFE4007BAC46 /* SBJsonBase.m */, 205 | EC0AFE02132FDFE4007BAC46 /* SBJsonParser.h */, 206 | EC0AFE03132FDFE4007BAC46 /* SBJsonParser.m */, 207 | EC0AFE04132FDFE4007BAC46 /* SBJsonWriter.h */, 208 | EC0AFE05132FDFE4007BAC46 /* SBJsonWriter.m */, 209 | ); 210 | name = SBJSON; 211 | sourceTree = ""; 212 | }; 213 | EC0AFDF4132FDF70007BAC46 /* NSData+Base64 */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | EC0AFDF5132FDFD8007BAC46 /* NSData+Base64.h */, 217 | EC0AFDF6132FDFD8007BAC46 /* NSData+Base64.m */, 218 | ); 219 | name = "NSData+Base64"; 220 | sourceTree = ""; 221 | }; 222 | /* End PBXGroup section */ 223 | 224 | /* Begin PBXHeadersBuildPhase section */ 225 | EC0AFDB5132FDEB0007BAC46 /* Headers */ = { 226 | isa = PBXHeadersBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | EC0AFDE6132FDF1F007BAC46 /* RestClient.h in Headers */, 230 | EC0AFDE8132FDF1F007BAC46 /* RestClientBlockRunner.h in Headers */, 231 | EC0AFDEA132FDF1F007BAC46 /* RestClientURLConnectionInvocation.h in Headers */, 232 | EC0AFDF7132FDFD8007BAC46 /* NSData+Base64.h in Headers */, 233 | EC0AFE06132FDFE4007BAC46 /* JSON.h in Headers */, 234 | EC0AFE07132FDFE4007BAC46 /* NSObject+SBJSON.h in Headers */, 235 | EC0AFE09132FDFE4007BAC46 /* NSString+SBJSON.h in Headers */, 236 | EC0AFE0B132FDFE4007BAC46 /* SBJSON.h in Headers */, 237 | EC0AFE0D132FDFE4007BAC46 /* SBJsonBase.h in Headers */, 238 | EC0AFE0F132FDFE4007BAC46 /* SBJsonParser.h in Headers */, 239 | EC0AFE11132FDFE4007BAC46 /* SBJsonWriter.h in Headers */, 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | }; 243 | /* End PBXHeadersBuildPhase section */ 244 | 245 | /* Begin PBXNativeTarget section */ 246 | EC0AFDB6132FDEB0007BAC46 /* RestClient */ = { 247 | isa = PBXNativeTarget; 248 | buildConfigurationList = EC0AFDDA132FDEB0007BAC46 /* Build configuration list for PBXNativeTarget "RestClient" */; 249 | buildPhases = ( 250 | EC0AFDB3132FDEB0007BAC46 /* Sources */, 251 | EC0AFDB4132FDEB0007BAC46 /* Frameworks */, 252 | EC0AFDB5132FDEB0007BAC46 /* Headers */, 253 | ); 254 | buildRules = ( 255 | ); 256 | dependencies = ( 257 | ); 258 | name = RestClient; 259 | productName = RestClient; 260 | productReference = EC0AFDB7132FDEB0007BAC46 /* libRestClient.a */; 261 | productType = "com.apple.product-type.library.static"; 262 | }; 263 | EC0AFDC3132FDEB0007BAC46 /* RestClientTests */ = { 264 | isa = PBXNativeTarget; 265 | buildConfigurationList = EC0AFDDD132FDEB0007BAC46 /* Build configuration list for PBXNativeTarget "RestClientTests" */; 266 | buildPhases = ( 267 | EC0AFDBF132FDEB0007BAC46 /* Sources */, 268 | EC0AFDC0132FDEB0007BAC46 /* Frameworks */, 269 | EC0AFDC1132FDEB0007BAC46 /* Resources */, 270 | EC0AFDC2132FDEB0007BAC46 /* ShellScript */, 271 | ); 272 | buildRules = ( 273 | ); 274 | dependencies = ( 275 | EC0AFDCB132FDEB0007BAC46 /* PBXTargetDependency */, 276 | ); 277 | name = RestClientTests; 278 | productName = RestClientTests; 279 | productReference = EC0AFDC4132FDEB0007BAC46 /* RestClientTests.octest */; 280 | productType = "com.apple.product-type.bundle"; 281 | }; 282 | /* End PBXNativeTarget section */ 283 | 284 | /* Begin PBXProject section */ 285 | EC0AFDAE132FDEB0007BAC46 /* Project object */ = { 286 | isa = PBXProject; 287 | buildConfigurationList = EC0AFDB1132FDEB0007BAC46 /* Build configuration list for PBXProject "RestClient" */; 288 | compatibilityVersion = "Xcode 3.2"; 289 | developmentRegion = English; 290 | hasScannedForEncodings = 0; 291 | knownRegions = ( 292 | en, 293 | ); 294 | mainGroup = EC0AFDAC132FDEB0007BAC46; 295 | productRefGroup = EC0AFDB8132FDEB0007BAC46 /* Products */; 296 | projectDirPath = ""; 297 | projectRoot = ""; 298 | targets = ( 299 | EC0AFDB6132FDEB0007BAC46 /* RestClient */, 300 | EC0AFDC3132FDEB0007BAC46 /* RestClientTests */, 301 | ); 302 | }; 303 | /* End PBXProject section */ 304 | 305 | /* Begin PBXResourcesBuildPhase section */ 306 | EC0AFDC1132FDEB0007BAC46 /* Resources */ = { 307 | isa = PBXResourcesBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | EC0AFDD2132FDEB0007BAC46 /* InfoPlist.strings in Resources */, 311 | EC0AFDD5132FDEB0007BAC46 /* RestClientTests.h in Resources */, 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | }; 315 | /* End PBXResourcesBuildPhase section */ 316 | 317 | /* Begin PBXShellScriptBuildPhase section */ 318 | EC0AFDC2132FDEB0007BAC46 /* ShellScript */ = { 319 | isa = PBXShellScriptBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | ); 323 | inputPaths = ( 324 | ); 325 | outputPaths = ( 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | shellPath = /bin/sh; 329 | shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; 330 | }; 331 | /* End PBXShellScriptBuildPhase section */ 332 | 333 | /* Begin PBXSourcesBuildPhase section */ 334 | EC0AFDB3132FDEB0007BAC46 /* Sources */ = { 335 | isa = PBXSourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | EC0AFDE7132FDF1F007BAC46 /* RestClient.m in Sources */, 339 | EC0AFDE9132FDF1F007BAC46 /* RestClientBlockRunner.m in Sources */, 340 | EC0AFDEB132FDF1F007BAC46 /* RestClientURLConnectionInvocation.m in Sources */, 341 | EC0AFDF8132FDFD8007BAC46 /* NSData+Base64.m in Sources */, 342 | EC0AFE08132FDFE4007BAC46 /* NSObject+SBJSON.m in Sources */, 343 | EC0AFE0A132FDFE4007BAC46 /* NSString+SBJSON.m in Sources */, 344 | EC0AFE0C132FDFE4007BAC46 /* SBJSON.m in Sources */, 345 | EC0AFE0E132FDFE4007BAC46 /* SBJsonBase.m in Sources */, 346 | EC0AFE10132FDFE4007BAC46 /* SBJsonParser.m in Sources */, 347 | EC0AFE12132FDFE4007BAC46 /* SBJsonWriter.m in Sources */, 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | }; 351 | EC0AFDBF132FDEB0007BAC46 /* Sources */ = { 352 | isa = PBXSourcesBuildPhase; 353 | buildActionMask = 2147483647; 354 | files = ( 355 | EC0AFDD7132FDEB0007BAC46 /* RestClientTests.m in Sources */, 356 | EC0AFDF0132FDF3C007BAC46 /* BlockRunnerSpec.m in Sources */, 357 | EC0AFDF1132FDF3C007BAC46 /* FakeBlockRunner.m in Sources */, 358 | EC0AFDF2132FDF3C007BAC46 /* RestClientSpec.m in Sources */, 359 | ); 360 | runOnlyForDeploymentPostprocessing = 0; 361 | }; 362 | /* End PBXSourcesBuildPhase section */ 363 | 364 | /* Begin PBXTargetDependency section */ 365 | EC0AFDCB132FDEB0007BAC46 /* PBXTargetDependency */ = { 366 | isa = PBXTargetDependency; 367 | target = EC0AFDB6132FDEB0007BAC46 /* RestClient */; 368 | targetProxy = EC0AFDCA132FDEB0007BAC46 /* PBXContainerItemProxy */; 369 | }; 370 | /* End PBXTargetDependency section */ 371 | 372 | /* Begin PBXVariantGroup section */ 373 | EC0AFDD0132FDEB0007BAC46 /* InfoPlist.strings */ = { 374 | isa = PBXVariantGroup; 375 | children = ( 376 | EC0AFDD1132FDEB0007BAC46 /* en */, 377 | ); 378 | name = InfoPlist.strings; 379 | sourceTree = ""; 380 | }; 381 | /* End PBXVariantGroup section */ 382 | 383 | /* Begin XCBuildConfiguration section */ 384 | EC0AFDD8132FDEB0007BAC46 /* Debug */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 388 | GCC_C_LANGUAGE_STANDARD = gnu99; 389 | GCC_OPTIMIZATION_LEVEL = 0; 390 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 391 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 392 | GCC_VERSION = com.apple.compilers.llvmgcc42; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 394 | GCC_WARN_UNUSED_VARIABLE = YES; 395 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 396 | SDKROOT = iphoneos; 397 | }; 398 | name = Debug; 399 | }; 400 | EC0AFDD9132FDEB0007BAC46 /* Release */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 404 | GCC_C_LANGUAGE_STANDARD = gnu99; 405 | GCC_VERSION = com.apple.compilers.llvmgcc42; 406 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 407 | GCC_WARN_UNUSED_VARIABLE = YES; 408 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 409 | SDKROOT = iphoneos; 410 | }; 411 | name = Release; 412 | }; 413 | EC0AFDDB132FDEB0007BAC46 /* Debug */ = { 414 | isa = XCBuildConfiguration; 415 | buildSettings = { 416 | ALWAYS_SEARCH_USER_PATHS = NO; 417 | DSTROOT = /tmp/RestClient.dst; 418 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 419 | GCC_PREFIX_HEADER = "RestClient/RestClient-Prefix.pch"; 420 | OTHER_LDFLAGS = "-ObjC"; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | }; 423 | name = Debug; 424 | }; 425 | EC0AFDDC132FDEB0007BAC46 /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | ALWAYS_SEARCH_USER_PATHS = NO; 429 | DSTROOT = /tmp/RestClient.dst; 430 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 431 | GCC_PREFIX_HEADER = "RestClient/RestClient-Prefix.pch"; 432 | OTHER_LDFLAGS = "-ObjC"; 433 | PRODUCT_NAME = "$(TARGET_NAME)"; 434 | }; 435 | name = Release; 436 | }; 437 | EC0AFDDE132FDEB0007BAC46 /* Debug */ = { 438 | isa = XCBuildConfiguration; 439 | buildSettings = { 440 | ALWAYS_SEARCH_USER_PATHS = NO; 441 | FRAMEWORK_SEARCH_PATHS = ( 442 | "$(SDKROOT)/Developer/Library/Frameworks", 443 | "$(DEVELOPER_LIBRARY_DIR)/Frameworks", 444 | ); 445 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 446 | GCC_PREFIX_HEADER = "RestClientTests/RestClientTests-Prefix.pch"; 447 | INFOPLIST_FILE = "RestClientTests/RestClientTests-Info.plist"; 448 | OTHER_LDFLAGS = ( 449 | "-framework", 450 | SenTestingKit, 451 | ); 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | WRAPPER_EXTENSION = octest; 454 | }; 455 | name = Debug; 456 | }; 457 | EC0AFDDF132FDEB0007BAC46 /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ALWAYS_SEARCH_USER_PATHS = NO; 461 | FRAMEWORK_SEARCH_PATHS = ( 462 | "$(SDKROOT)/Developer/Library/Frameworks", 463 | "$(DEVELOPER_LIBRARY_DIR)/Frameworks", 464 | ); 465 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 466 | GCC_PREFIX_HEADER = "RestClientTests/RestClientTests-Prefix.pch"; 467 | INFOPLIST_FILE = "RestClientTests/RestClientTests-Info.plist"; 468 | OTHER_LDFLAGS = ( 469 | "-framework", 470 | SenTestingKit, 471 | ); 472 | PRODUCT_NAME = "$(TARGET_NAME)"; 473 | WRAPPER_EXTENSION = octest; 474 | }; 475 | name = Release; 476 | }; 477 | /* End XCBuildConfiguration section */ 478 | 479 | /* Begin XCConfigurationList section */ 480 | EC0AFDB1132FDEB0007BAC46 /* Build configuration list for PBXProject "RestClient" */ = { 481 | isa = XCConfigurationList; 482 | buildConfigurations = ( 483 | EC0AFDD8132FDEB0007BAC46 /* Debug */, 484 | EC0AFDD9132FDEB0007BAC46 /* Release */, 485 | ); 486 | defaultConfigurationIsVisible = 0; 487 | defaultConfigurationName = Release; 488 | }; 489 | EC0AFDDA132FDEB0007BAC46 /* Build configuration list for PBXNativeTarget "RestClient" */ = { 490 | isa = XCConfigurationList; 491 | buildConfigurations = ( 492 | EC0AFDDB132FDEB0007BAC46 /* Debug */, 493 | EC0AFDDC132FDEB0007BAC46 /* Release */, 494 | ); 495 | defaultConfigurationIsVisible = 0; 496 | }; 497 | EC0AFDDD132FDEB0007BAC46 /* Build configuration list for PBXNativeTarget "RestClientTests" */ = { 498 | isa = XCConfigurationList; 499 | buildConfigurations = ( 500 | EC0AFDDE132FDEB0007BAC46 /* Debug */, 501 | EC0AFDDF132FDEB0007BAC46 /* Release */, 502 | ); 503 | defaultConfigurationIsVisible = 0; 504 | }; 505 | /* End XCConfigurationList section */ 506 | }; 507 | rootObject = EC0AFDAE132FDEB0007BAC46 /* Project object */; 508 | } 509 | -------------------------------------------------------------------------------- /RestClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RestClient/RestClient-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'RestClient' target in the 'RestClient' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /RestClient/RestClient.h: -------------------------------------------------------------------------------- 1 | #define RC_LOG 1 // turn this on to turn on rest client logging 2 | #define RestClientStartWaiting @"CMStartWaiting" // this NSNotification will be fired before an asynchronous call if wait == true 3 | #define RestClientStopWaiting @"CMStopWaiting" // this NSNotification will be fired after an asynchronous call if wait == false 4 | 5 | @class RestClientBlockRunner; 6 | 7 | @interface RestClient : NSObject { 8 | NSString *prefix; 9 | NSDictionary *headers; 10 | RestClientBlockRunner *blockRunner; 11 | } 12 | 13 | @property (nonatomic, retain) NSString* prefix; 14 | @property (nonatomic, retain) NSDictionary* headers; 15 | @property (nonatomic, retain) RestClientBlockRunner *blockRunner; 16 | 17 | - (id)init; 18 | - (id)initWithPrefix:(NSString *)prefix andHeaders:(NSDictionary *)headers; 19 | 20 | - (NSString *)get:(NSString *)path; 21 | - (NSString *)post:(NSString *)path withBody:(NSString *)body; 22 | - (NSString *)put:(NSString *)path withBody:(NSString *)body; 23 | - (NSString *)delete:(NSString *)path withBody:(NSString *)body; 24 | - (NSString *)delete:(NSString *)path; 25 | 26 | //async methods 27 | 28 | - (void)get: (NSString *)path wait:(BOOL)wait success:(void(^)(id))onSuccess error:(void(^)(NSError *))onError; 29 | - (void)post: (NSString *)path withBody:(NSString *)body wait:(BOOL)wait success:(void(^)(id))onSuccess error:(void(^)(NSError *))onError; 30 | - (void)put: (NSString *)path withBody:(NSString *)body wait:(BOOL)wait success:(void(^)(id))onSuccess error:(void(^)(NSError *))onError; 31 | - (void)delete: (NSString *)path wait:(BOOL)wait success:(void(^)(id))onSuccess error:(void(^)(NSError *))onError; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /RestClient/RestClient.m: -------------------------------------------------------------------------------- 1 | #import "RestClient.h" 2 | #import "RestClientBlockRunner.h" 3 | #import "RestClientURLConnectionInvocation.h" 4 | #import "NSData+Base64.h" 5 | #import "NSString+SBJSON.h" 6 | 7 | @implementation RestClient 8 | 9 | @synthesize prefix, headers, blockRunner; 10 | 11 | - (id) init { 12 | self = [super init]; 13 | self.prefix = @""; 14 | headers = [[NSDictionary alloc] init]; 15 | blockRunner = [[RestClientBlockRunner alloc] init]; 16 | return self; 17 | } 18 | 19 | - (id) initWithPrefix:(NSString *)aPrefix andHeaders:(NSDictionary *)someHeaders { 20 | self = [super init]; 21 | self.prefix = aPrefix; 22 | self.headers = someHeaders; 23 | blockRunner = [[RestClientBlockRunner alloc] init]; 24 | return self; 25 | } 26 | 27 | -(void)addHeaders:(NSMutableURLRequest*)request { 28 | NSEnumerator *enumerator = [headers keyEnumerator]; 29 | id key; 30 | while ((key = [enumerator nextObject])) { 31 | [request setValue:[headers objectForKey:key] forHTTPHeaderField:key]; 32 | } 33 | // [request setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] forHTTPHeaderField:@"X-MaptiniVersion"]; 34 | // [request setValue:[[UIDevice currentDevice] model] forHTTPHeaderField:@"X-MaptiniPlatform"]; 35 | // [request setValue:userAPIToken forHTTPHeaderField:@"X-MaptiniToken"]; 36 | } 37 | 38 | - (NSMutableURLRequest *)newRequest:(NSString*)method 39 | path:(NSString*)path 40 | body:(NSString*)body { 41 | NSMutableURLRequest* request = [[NSMutableURLRequest alloc] init]; 42 | [request setURL:[NSURL URLWithString:[NSMutableString stringWithFormat:@"%@%@", prefix, path]]]; 43 | [request setHTTPMethod:method]; 44 | if (body) { 45 | NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]; 46 | [request setValue:[NSString stringWithFormat:@"%d", [data length]] forHTTPHeaderField:@"Content-Length"]; 47 | [request setValue:@"text/x-json" forHTTPHeaderField:@"Content-Type"]; 48 | [request setHTTPBody:data]; 49 | } 50 | [self addHeaders:request]; 51 | return request; 52 | } 53 | 54 | -(NSString *)execute:(NSString*)method path:(NSString*)path body:(NSString*)body { 55 | NSMutableURLRequest* request = [self newRequest:method path:path body:body]; 56 | 57 | NSHTTPURLResponse *response = nil; 58 | NSError *error = nil; 59 | NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 60 | [request release]; 61 | if (RC_LOG) { 62 | if (body) { 63 | NSLog(@"%@ %@%@ => %i\n : %@", method, prefix, path, [response statusCode], body); 64 | } else if ([path hasSuffix:@"/version.json"]) { 65 | // ignore 66 | } else { 67 | NSLog(@"%@ %@%@ => %i", method, prefix, path, [response statusCode]); 68 | } 69 | } 70 | 71 | if (error) { 72 | if (RC_LOG) {NSLog(@"Rest Client Error UserInfo: %@", error.userInfo);} 73 | @throw [NSException exceptionWithName:[error localizedFailureReason] 74 | reason:[error localizedDescription] 75 | userInfo:[NSDictionary dictionaryWithObjectsAndKeys: 76 | error, @"error", 77 | [NSNumber numberWithInt:[error code]], @"errorCode", 78 | path, @"path", 79 | body, @"body", 80 | nil]]; 81 | } else if ([response statusCode] == 200) { 82 | return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; 83 | 84 | } else { 85 | NSString *rawResponse = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; 86 | NSLog(@"raw response : %@", rawResponse); 87 | NSString *errorMessage = [RestClientURLConnectionInvocation parseError:rawResponse]; 88 | 89 | if (RC_LOG) {NSLog(@"Rest Client Error StatusCode: %i : %@", [response statusCode], errorMessage);} 90 | @throw [NSException exceptionWithName:errorMessage 91 | reason:errorMessage 92 | userInfo:[NSDictionary dictionaryWithObjectsAndKeys: 93 | [NSNumber numberWithInt:[response statusCode]], @"statusCode", 94 | path, @"path", 95 | body, @"body", 96 | errorMessage, NSLocalizedDescriptionKey, 97 | nil]]; 98 | } 99 | } 100 | 101 | - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 102 | NSLog(@"You got challenged"); 103 | } 104 | 105 | - (NSString *) get:(NSString *)path { 106 | return [self execute:@"GET" path:path body:nil]; 107 | } 108 | 109 | - (NSString *)get:(NSString *)path withBody:(NSString *)body { 110 | return [self execute:@"GET" path:path body:nil]; 111 | } 112 | 113 | - (NSString *) post:(NSString *)path withBody:(NSString *)body { 114 | return [self execute:@"POST" path:path body:body]; 115 | } 116 | 117 | - (NSString *)put:(NSString *)path withBody:(NSString *)body { 118 | return [self execute:@"PUT" path:path body:body]; 119 | } 120 | 121 | - (NSString *)delete:(NSString *)path withBody:(NSString *)body{ 122 | return [self execute:@"DELETE" path:path body:body]; 123 | } 124 | 125 | - (NSString*) delete:(NSString *)path { 126 | return [self execute:@"DELETE" path:path body:nil]; 127 | } 128 | 129 | - (void)startWaiting { 130 | [UIApplication sharedApplication].networkActivityIndicatorVisible = true; 131 | [[NSNotificationCenter defaultCenter] postNotificationName:RestClientStartWaiting object:nil]; 132 | } 133 | 134 | - (void)stopWaiting { 135 | [UIApplication sharedApplication].networkActivityIndicatorVisible = false; 136 | [[NSNotificationCenter defaultCenter] postNotificationName:RestClientStopWaiting object:nil]; 137 | } 138 | 139 | - (void)executeAsync:(NSString*)method 140 | path:(NSString*)path 141 | body:(NSString*)body 142 | wait:(BOOL)wait 143 | success:(void (^)(id))onSuccess 144 | error:(void (^)(NSError *))onError { 145 | NSMutableURLRequest *request = [self newRequest:method path:path body:body]; 146 | RestClientURLConnectionInvocation *invocation = [[RestClientURLConnectionInvocation alloc] initWithRequest:request 147 | wait:wait 148 | success:onSuccess 149 | error:onError]; 150 | [invocation start]; 151 | [request release]; 152 | [invocation release]; 153 | } 154 | 155 | - (void) get:(NSString *)path wait:(BOOL)wait success:(void (^)(id))onSuccess error:(void (^)(NSError *))onError{ 156 | [self executeAsync:@"GET" path:path body:nil wait:wait success:onSuccess error:onError]; 157 | } 158 | 159 | - (void) post:(NSString *)path withBody:(NSString*)body wait:(BOOL)wait success:(void (^)(id))onSuccess error:(void (^)(NSError *))onError{ 160 | [self executeAsync:@"POST" path:path body:body wait:wait success:onSuccess error:onError]; 161 | } 162 | 163 | - (void) put:(NSString *)path withBody:(NSString*)body wait:(BOOL)wait success:(void (^)(id))onSuccess error:(void (^)(NSError *))onError{ 164 | [self executeAsync:@"PUT" path:path body:body wait:wait success:onSuccess error:onError]; 165 | } 166 | 167 | - (void) delete:(NSString *)path wait:(BOOL)wait success:(void (^)(id))onSuccess error:(void (^)(NSError *))onError{ 168 | [self executeAsync:@"DELETE" path:path body:nil wait:wait success:onSuccess error:onError]; 169 | } 170 | 171 | -(void)dealloc { 172 | self.prefix = nil; 173 | self.headers = nil; 174 | self.blockRunner = nil; 175 | [super dealloc]; 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /RestClient/RestClientBlockRunner.h: -------------------------------------------------------------------------------- 1 | @interface RestClientBlockRunner : NSObject { 2 | 3 | } 4 | 5 | - (void)run:(void (^)(void))block; 6 | - (void)runOperation:(NSOperation *)operation; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /RestClient/RestClientBlockRunner.m: -------------------------------------------------------------------------------- 1 | #import "RestClientBlockRunner.h" 2 | 3 | @implementation RestClientBlockRunner 4 | 5 | - (void) run:(void (^)(void))block { 6 | [self runOperation:[NSBlockOperation blockOperationWithBlock:block]]; 7 | } 8 | 9 | - (void) runOperation:(NSOperation *)operation { 10 | [operation start]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /RestClient/RestClientURLConnectionInvocation.h: -------------------------------------------------------------------------------- 1 | 2 | @interface RestClientURLConnectionInvocation : NSObject { 3 | NSMutableURLRequest *request; 4 | BOOL wait; 5 | void (^onSuccess)(id); 6 | void (^onError)(NSError *); 7 | int statusCode; 8 | NSMutableData *receivedData; 9 | } 10 | 11 | - (id)initWithRequest:(NSMutableURLRequest *)request 12 | wait:(BOOL)wait 13 | success:(void (^)(id))onSuccess 14 | error:(void (^)(NSError *))onError; 15 | - (void)start; 16 | 17 | + (NSString *)parseError:(NSString *)error; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /RestClient/RestClientURLConnectionInvocation.m: -------------------------------------------------------------------------------- 1 | #import "RestClientURLConnectionInvocation.h" 2 | #import "NSString+SBJSON.h" 3 | #import "RestClient.h" 4 | 5 | @implementation RestClientURLConnectionInvocation 6 | 7 | - (id) initWithRequest:(NSMutableURLRequest *)aRequest 8 | wait:(BOOL)aWait 9 | success:(void (^)(id))aOnSuccess 10 | error:(void (^)(NSError *))aOnError { 11 | self = [super init]; 12 | request = [aRequest retain]; 13 | wait = aWait; 14 | onSuccess = [aOnSuccess retain]; 15 | onError = [aOnError retain]; 16 | receivedData = [[NSMutableData alloc] initWithLength:0]; 17 | 18 | statusCode = -1; 19 | return self; 20 | } 21 | 22 | - (void) dealloc { 23 | [request release]; 24 | [onSuccess release]; 25 | [onError release]; 26 | [receivedData release]; 27 | [super dealloc]; 28 | } 29 | 30 | + (NSString *) parseError:(NSString *)json { 31 | id hash = [json JSONValue]; 32 | if ([hash isKindOfClass:[NSDictionary class]]) { 33 | return [hash objectForKey:@"error"]; 34 | } else { 35 | return @"Server Error"; 36 | } 37 | } 38 | 39 | - (void)startWaiting { 40 | [UIApplication sharedApplication].networkActivityIndicatorVisible = true; 41 | if (wait) { 42 | [[NSNotificationCenter defaultCenter] postNotificationName:RestClientStartWaiting object:nil]; 43 | } 44 | } 45 | 46 | - (void)stopWaiting { 47 | [UIApplication sharedApplication].networkActivityIndicatorVisible = false; 48 | if (wait) { 49 | [[NSNotificationCenter defaultCenter] postNotificationName:RestClientStopWaiting object:nil]; 50 | } 51 | } 52 | 53 | - (void) start { 54 | [self retain]; 55 | [self startWaiting]; 56 | [[NSURLConnection connectionWithRequest:request delegate:self] start]; 57 | } 58 | 59 | - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response { 60 | statusCode = [((NSHTTPURLResponse *)response) statusCode]; 61 | } 62 | 63 | - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 64 | onError(error); 65 | [self stopWaiting]; 66 | [self release]; 67 | } 68 | 69 | - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data { 70 | [receivedData appendData:data]; 71 | } 72 | 73 | - (void) connectionDidFinishLoading:(NSURLConnection *)connection { 74 | NSString *data = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding]; 75 | if (statusCode == 200) { 76 | onSuccess([data JSONValue]); 77 | } else if (statusCode != -1) { 78 | onError([NSError errorWithDomain:@"restclient" 79 | code:statusCode 80 | userInfo:[NSDictionary dictionaryWithObjectsAndKeys: 81 | [RestClientURLConnectionInvocation parseError:data], NSLocalizedDescriptionKey, 82 | nil]]); 83 | } 84 | [data release]; 85 | [self stopWaiting]; 86 | [self release]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /RestClientTests/BlockRunnerSpec.m: -------------------------------------------------------------------------------- 1 | #import "FakeBlockRunner.h" 2 | #import "RestClientBlockRunner.h" 3 | 4 | DESCRIBE(RestClientBlockRunner) { 5 | it(@"should capture blocks as operations", ^{ 6 | FakeBlockRunner *runner = [[FakeBlockRunner alloc] init]; 7 | __block int result = 0; 8 | 9 | [runner run:^{ result++; }]; 10 | 11 | assertThatInt(result, equalToInt(0)); 12 | 13 | NSOperation *operation = [runner.operations objectAtIndex:0]; 14 | [operation start]; 15 | [operation waitUntilFinished]; 16 | 17 | assertThatInt(result, equalToInt(1)); 18 | }); 19 | } 20 | DESCRIBE_END 21 | -------------------------------------------------------------------------------- /RestClientTests/FakeBlockRunner.h: -------------------------------------------------------------------------------- 1 | #include "RestClientBlockRunner.h" 2 | 3 | @interface FakeBlockRunner : RestClientBlockRunner { 4 | NSMutableArray *operations; 5 | } 6 | 7 | @property (nonatomic, retain) NSMutableArray* operations; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /RestClientTests/FakeBlockRunner.m: -------------------------------------------------------------------------------- 1 | #import "FakeBlockRunner.h" 2 | 3 | @implementation FakeBlockRunner 4 | 5 | @synthesize operations; 6 | 7 | - (id) init { 8 | self = [super init]; 9 | self.operations = [[NSMutableArray alloc] init]; 10 | return self; 11 | } 12 | 13 | - (void) dealloc { 14 | [self.operations release]; 15 | [super dealloc]; 16 | } 17 | 18 | - (void) runOperation:(NSOperation *)operation { 19 | [self.operations addObject:operation]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /RestClientTests/RestClientSpec.m: -------------------------------------------------------------------------------- 1 | #import "RestClient.h" 2 | #import "FakeClient.h" 3 | #import "FakeNotificationCenter.h" 4 | #import "SBJSON.h" 5 | #import "RestClientURLConnectionInvocation.h" 6 | 7 | DESCRIBE(RestClient) { 8 | describe(@"integration tests", ^{ 9 | __block RestClient *client; 10 | 11 | beforeEach(^{ 12 | client = [[RestClient alloc] init]; 13 | }); 14 | 15 | it(@"should actually hit the server", ^{ 16 | SBJSON *json = [[SBJSON alloc] init]; 17 | @try { 18 | client.userAPIToken = @"1IMMXTkteQ6t9pbDEwRm"; 19 | 20 | //assertThat([client get:@"/api/sessions/create?/maps.json"], is(@"OK")); 21 | 22 | assertThat([client post:@"/maps.json" 23 | withBody:@"{map:{id:\"test\",name:\"foo\",root_node:{id:\"testroot\",name:\"foo\"}}}"], 24 | is(@"OK")); 25 | assertThat([client get:@"/maps/test/version.json"], 26 | is(@"1")); 27 | 28 | assertThat([client post:@"/maps/test/commands.json" 29 | withBody:@"{command:{command_name:\"UpdateNode\",node_id:\"testroot\",field:\"name\",value:\"bob\"}}"], 30 | is(@"{\"version\":2}")); 31 | assertThat([client get:@"/maps/test/version.json"], 32 | is(@"2")); 33 | 34 | assertThat([client post:@"/maps/test/commands.json" 35 | withBody:@"{command:{command_name:\"UpdateNode\",node_id:\"testroot\",field:\"name\",value:\"roger\"}}"], 36 | is(@"{\"version\":3}")); 37 | assertThat([client get:@"/maps/test/version.json"], 38 | is(@"3")); 39 | 40 | NSString *expected = @"[{\"command_name\":\"UpdateNode\",\"node_id\":\"testroot\",\"field\":\"name\",\"value\":\"roger\",\"version\":3}]"; 41 | NSString *actual = [client get:@"/maps/test/commands.json?since=2"]; 42 | assertThat([json objectWithString:expected], is([json objectWithString:actual])); 43 | } 44 | @finally { 45 | [client delete:@"/maps/test.json"]; 46 | } 47 | }); 48 | }); 49 | 50 | describe(@"asynchronous calls", ^{ 51 | __block FakeClient *client; 52 | __block NSString *result; 53 | 54 | beforeEach(^{ 55 | result = nil; 56 | client = [[FakeClient alloc] init]; 57 | }); 58 | 59 | it(@"should do a get", ^{ 60 | [client stub:@"GET" forPath:@"/maps.json" andReturn:@"hello"]; 61 | 62 | [client get:@"/maps.json" 63 | wait:false 64 | success:^(id json) { result = [[NSString stringWithFormat:@"SUCCESS:%@", json, nil] retain]; } 65 | error:^(NSError *error) { fail(@"what?"); }]; 66 | 67 | assertThat(result, is(@"SUCCESS:hello")); 68 | [result release]; 69 | }); 70 | 71 | it(@"should handle an error", ^{ 72 | [client stub:@"GET" forPath:@"/maps.json" andThrow:303 withMessage:@"WTF, mate"]; 73 | 74 | [client get:@"/maps.json" 75 | wait:false 76 | success:^(id json) { fail(@"what?"); } 77 | error:^(NSError *error) { 78 | result = [[NSString stringWithFormat:@"ERROR:%i:%@", 79 | [error code], 80 | [[error userInfo] objectForKey:NSLocalizedDescriptionKey]] retain]; 81 | }]; 82 | 83 | assertThat(result, is(@"ERROR:303:WTF, mate")); 84 | [result release]; 85 | }); 86 | 87 | it(@"should know how to parse an error", ^{ 88 | assertThat([RestClientURLConnectionInvocation parseError:@"{\"error\":\"WTF, mate\"}"], is(@"WTF, mate")); 89 | assertThat([RestClientURLConnectionInvocation parseError:@"crap"], is(@"Server Error")); 90 | }); 91 | 92 | // post 93 | // put 94 | // delete 95 | }); 96 | } 97 | DESCRIBE_END 98 | -------------------------------------------------------------------------------- /RestClientTests/RestClientTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ___VARIABLE_bundleIdentifierPrefix:bundleIdentifier___.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RestClientTests/RestClientTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'RestClientTests' target in the 'RestClientTests' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /RestClientTests/RestClientTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // RestClientTests.h 3 | // RestClientTests 4 | // 5 | // Created by Jeremy Lightsmith on 3/15/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface RestClientTests : SenTestCase { 13 | @private 14 | 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /RestClientTests/RestClientTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RestClientTests.m 3 | // RestClientTests 4 | // 5 | // Created by Jeremy Lightsmith on 3/15/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "RestClientTests.h" 10 | 11 | 12 | @implementation RestClientTests 13 | 14 | - (void)setUp 15 | { 16 | [super setUp]; 17 | 18 | // Set-up code here. 19 | } 20 | 21 | - (void)tearDown 22 | { 23 | // Tear-down code here. 24 | 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample 29 | { 30 | STFail(@"Unit tests are not implemented yet in RestClientTests"); 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /RestClientTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | --------------------------------------------------------------------------------