├── .gitignore ├── FireData.podspec ├── FireData.xcodeproj └── project.pbxproj ├── FireData ├── FireData-Prefix.pch ├── FireData.h ├── FireData.m ├── FireDataISO8601DateFormatter.h ├── FireDataISO8601DateFormatter.m ├── NSManagedObject+FireData.h └── NSManagedObject+FireData.m ├── FireDataTests ├── FireDataTests-Info.plist ├── FireDataTests.h ├── FireDataTests.m └── en.lproj │ └── InfoPlist.strings ├── LICENSE ├── README.md └── Vendor ├── Firebase └── Firebase.framework │ ├── Firebase │ ├── Headers │ └── Versions │ ├── A │ ├── Firebase │ └── Headers │ │ ├── FDataSnapshot.h │ │ ├── FEventType.h │ │ ├── FMutableData.h │ │ ├── FQuery.h │ │ ├── FTransactionResult.h │ │ └── Firebase.h │ └── Current └── iso-8601-date-formatter ├── .hgtags ├── ISO8601DateFormatter.h ├── ISO8601DateFormatter.m ├── LICENSE.txt ├── Makefile ├── README.md ├── test_files ├── 2005-2006.txt ├── 2005.txt ├── 2006.txt ├── 2009-2010.txt ├── januaries.out ├── januaries.txt ├── januaries3.txt └── testunparser-expected.out ├── testparser.m ├── testparser.sh.in ├── testparser.sh.py ├── testunparser.sh ├── testunparsewithtime.m ├── timetrial.m ├── unparse-date.m ├── unparse-ordinaldate.m └── unparse-weekdate.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | */build/* 3 | build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | 18 | */private 19 | .DS_Store 20 | -------------------------------------------------------------------------------- /FireData.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "FireData" 3 | s.version = "0.9.0" 4 | s.summary = "Seamlessly integrate Firebase with Core Data." 5 | s.homepage = "http://github.com/overcommitted/FireData" 6 | s.license = 'MIT ' 7 | s.author = { "Jonathan Younger" => "jonathan@daikini.com" } 8 | s.source = { :git => "https://github.com/overcommitted/FireData.git", :tag => "0.9.0" } 9 | s.platform = :ios, '6.0' 10 | s.source_files = 'FireData' 11 | s.public_header_files = 'FireData/FireData.h' 12 | s.frameworks = 'CoreData', 'Firebase' 13 | s.requires_arc = true 14 | s.dependency 'Firebase', '~> 1.0' 15 | s.dependency 'ISO8601DateFormatter', '~> 0.6' 16 | end 17 | -------------------------------------------------------------------------------- /FireData.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | ABBDD13C16FA4376002D7BE3 /* Framework */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = ABBDD13D16FA4376002D7BE3 /* Build configuration list for PBXAggregateTarget "Framework" */; 13 | buildPhases = ( 14 | ABBDD14216FA43A7002D7BE3 /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | ABBDD14116FA4383002D7BE3 /* PBXTargetDependency */, 18 | ); 19 | name = Framework; 20 | productName = Framework; 21 | }; 22 | /* End PBXAggregateTarget section */ 23 | 24 | /* Begin PBXBuildFile section */ 25 | ABBDD08C16FA31DB002D7BE3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABBDD08B16FA31DB002D7BE3 /* Foundation.framework */; }; 26 | ABBDD09116FA31DB002D7BE3 /* FireData.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = ABBDD09016FA31DB002D7BE3 /* FireData.h */; }; 27 | ABBDD09316FA31DB002D7BE3 /* FireData.m in Sources */ = {isa = PBXBuildFile; fileRef = ABBDD09216FA31DB002D7BE3 /* FireData.m */; }; 28 | ABBDD09B16FA31DC002D7BE3 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABBDD09A16FA31DC002D7BE3 /* SenTestingKit.framework */; }; 29 | ABBDD09D16FA31DC002D7BE3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABBDD09C16FA31DC002D7BE3 /* UIKit.framework */; }; 30 | ABBDD09E16FA31DC002D7BE3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABBDD08B16FA31DB002D7BE3 /* Foundation.framework */; }; 31 | ABBDD0A116FA31DC002D7BE3 /* libFireData.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ABBDD08816FA31DB002D7BE3 /* libFireData.a */; }; 32 | ABBDD0A716FA31DC002D7BE3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = ABBDD0A516FA31DC002D7BE3 /* InfoPlist.strings */; }; 33 | ABBDD0AA16FA31DC002D7BE3 /* FireDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ABBDD0A916FA31DC002D7BE3 /* FireDataTests.m */; }; 34 | ABBDD0BD16FA34A0002D7BE3 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABBDD0BC16FA34A0002D7BE3 /* CoreData.framework */; }; 35 | ABBDD0C016FA359E002D7BE3 /* NSManagedObject+FireData.m in Sources */ = {isa = PBXBuildFile; fileRef = ABBDD0BF16FA359D002D7BE3 /* NSManagedObject+FireData.m */; }; 36 | ABBDD0C116FA359E002D7BE3 /* NSManagedObject+FireData.m in Sources */ = {isa = PBXBuildFile; fileRef = ABBDD0BF16FA359D002D7BE3 /* NSManagedObject+FireData.m */; }; 37 | ABBDD11516FA3891002D7BE3 /* ISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = ABBDD0FE16FA3891002D7BE3 /* ISO8601DateFormatter.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 38 | ABBDD11616FA3891002D7BE3 /* ISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = ABBDD0FE16FA3891002D7BE3 /* ISO8601DateFormatter.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 39 | ABBDD13416FA392D002D7BE3 /* FireDataISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = ABBDD13316FA392D002D7BE3 /* FireDataISO8601DateFormatter.m */; }; 40 | ABBDD13516FA392D002D7BE3 /* FireDataISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = ABBDD13316FA392D002D7BE3 /* FireDataISO8601DateFormatter.m */; }; 41 | ABBDD13A16FA3C52002D7BE3 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABBDD0BC16FA34A0002D7BE3 /* CoreData.framework */; }; 42 | /* End PBXBuildFile section */ 43 | 44 | /* Begin PBXContainerItemProxy section */ 45 | ABBDD09F16FA31DC002D7BE3 /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = ABBDD08016FA31DB002D7BE3 /* Project object */; 48 | proxyType = 1; 49 | remoteGlobalIDString = ABBDD08716FA31DB002D7BE3; 50 | remoteInfo = FireData; 51 | }; 52 | ABBDD14016FA4383002D7BE3 /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = ABBDD08016FA31DB002D7BE3 /* Project object */; 55 | proxyType = 1; 56 | remoteGlobalIDString = ABBDD08716FA31DB002D7BE3; 57 | remoteInfo = FireData; 58 | }; 59 | /* End PBXContainerItemProxy section */ 60 | 61 | /* Begin PBXCopyFilesBuildPhase section */ 62 | ABBDD08616FA31DB002D7BE3 /* CopyFiles */ = { 63 | isa = PBXCopyFilesBuildPhase; 64 | buildActionMask = 2147483647; 65 | dstPath = Headers; 66 | dstSubfolderSpec = 16; 67 | files = ( 68 | ABBDD09116FA31DB002D7BE3 /* FireData.h in CopyFiles */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXCopyFilesBuildPhase section */ 73 | 74 | /* Begin PBXFileReference section */ 75 | ABBDD08816FA31DB002D7BE3 /* libFireData.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFireData.a; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | ABBDD08B16FA31DB002D7BE3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 77 | ABBDD08F16FA31DB002D7BE3 /* FireData-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FireData-Prefix.pch"; sourceTree = ""; }; 78 | ABBDD09016FA31DB002D7BE3 /* FireData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FireData.h; sourceTree = ""; }; 79 | ABBDD09216FA31DB002D7BE3 /* FireData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FireData.m; sourceTree = ""; }; 80 | ABBDD09916FA31DC002D7BE3 /* FireDataTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FireDataTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | ABBDD09A16FA31DC002D7BE3 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; 82 | ABBDD09C16FA31DC002D7BE3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 83 | ABBDD0A416FA31DC002D7BE3 /* FireDataTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FireDataTests-Info.plist"; sourceTree = ""; }; 84 | ABBDD0A616FA31DC002D7BE3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 85 | ABBDD0A816FA31DC002D7BE3 /* FireDataTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FireDataTests.h; sourceTree = ""; }; 86 | ABBDD0A916FA31DC002D7BE3 /* FireDataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FireDataTests.m; sourceTree = ""; }; 87 | ABBDD0BC16FA34A0002D7BE3 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 88 | ABBDD0BE16FA359D002D7BE3 /* NSManagedObject+FireData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+FireData.h"; sourceTree = ""; }; 89 | ABBDD0BF16FA359D002D7BE3 /* NSManagedObject+FireData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+FireData.m"; sourceTree = ""; }; 90 | ABBDD0FD16FA3891002D7BE3 /* ISO8601DateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ISO8601DateFormatter.h; sourceTree = ""; }; 91 | ABBDD0FE16FA3891002D7BE3 /* ISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ISO8601DateFormatter.m; sourceTree = ""; }; 92 | ABBDD0FF16FA3891002D7BE3 /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; 93 | ABBDD10116FA3891002D7BE3 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = ""; }; 94 | ABBDD13216FA392D002D7BE3 /* FireDataISO8601DateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FireDataISO8601DateFormatter.h; sourceTree = ""; }; 95 | ABBDD13316FA392D002D7BE3 /* FireDataISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FireDataISO8601DateFormatter.m; sourceTree = ""; }; 96 | ABBDD15316FA4982002D7BE3 /* Firebase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Firebase.framework; sourceTree = ""; }; 97 | /* End PBXFileReference section */ 98 | 99 | /* Begin PBXFrameworksBuildPhase section */ 100 | ABBDD08516FA31DB002D7BE3 /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | ABBDD0BD16FA34A0002D7BE3 /* CoreData.framework in Frameworks */, 105 | ABBDD08C16FA31DB002D7BE3 /* Foundation.framework in Frameworks */, 106 | ); 107 | runOnlyForDeploymentPostprocessing = 0; 108 | }; 109 | ABBDD09516FA31DC002D7BE3 /* Frameworks */ = { 110 | isa = PBXFrameworksBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | ABBDD13A16FA3C52002D7BE3 /* CoreData.framework in Frameworks */, 114 | ABBDD09B16FA31DC002D7BE3 /* SenTestingKit.framework in Frameworks */, 115 | ABBDD09D16FA31DC002D7BE3 /* UIKit.framework in Frameworks */, 116 | ABBDD09E16FA31DC002D7BE3 /* Foundation.framework in Frameworks */, 117 | ABBDD0A116FA31DC002D7BE3 /* libFireData.a in Frameworks */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | ABBDD07F16FA31DB002D7BE3 = { 125 | isa = PBXGroup; 126 | children = ( 127 | ABBDD08D16FA31DB002D7BE3 /* FireData */, 128 | ABBDD0A216FA31DC002D7BE3 /* FireDataTests */, 129 | ABBDD0B716FA348C002D7BE3 /* Vendor */, 130 | ABBDD08A16FA31DB002D7BE3 /* Frameworks */, 131 | ABBDD08916FA31DB002D7BE3 /* Products */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | ABBDD08916FA31DB002D7BE3 /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | ABBDD08816FA31DB002D7BE3 /* libFireData.a */, 139 | ABBDD09916FA31DC002D7BE3 /* FireDataTests.octest */, 140 | ); 141 | name = Products; 142 | sourceTree = ""; 143 | }; 144 | ABBDD08A16FA31DB002D7BE3 /* Frameworks */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | ABBDD0BC16FA34A0002D7BE3 /* CoreData.framework */, 148 | ABBDD08B16FA31DB002D7BE3 /* Foundation.framework */, 149 | ABBDD09A16FA31DC002D7BE3 /* SenTestingKit.framework */, 150 | ABBDD09C16FA31DC002D7BE3 /* UIKit.framework */, 151 | ); 152 | name = Frameworks; 153 | sourceTree = ""; 154 | }; 155 | ABBDD08D16FA31DB002D7BE3 /* FireData */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | ABBDD09016FA31DB002D7BE3 /* FireData.h */, 159 | ABBDD09216FA31DB002D7BE3 /* FireData.m */, 160 | ABBDD13216FA392D002D7BE3 /* FireDataISO8601DateFormatter.h */, 161 | ABBDD13316FA392D002D7BE3 /* FireDataISO8601DateFormatter.m */, 162 | ABBDD0BE16FA359D002D7BE3 /* NSManagedObject+FireData.h */, 163 | ABBDD0BF16FA359D002D7BE3 /* NSManagedObject+FireData.m */, 164 | ABBDD08E16FA31DB002D7BE3 /* Supporting Files */, 165 | ); 166 | path = FireData; 167 | sourceTree = ""; 168 | }; 169 | ABBDD08E16FA31DB002D7BE3 /* Supporting Files */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | ABBDD08F16FA31DB002D7BE3 /* FireData-Prefix.pch */, 173 | ); 174 | name = "Supporting Files"; 175 | sourceTree = ""; 176 | }; 177 | ABBDD0A216FA31DC002D7BE3 /* FireDataTests */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | ABBDD0A816FA31DC002D7BE3 /* FireDataTests.h */, 181 | ABBDD0A916FA31DC002D7BE3 /* FireDataTests.m */, 182 | ABBDD0A316FA31DC002D7BE3 /* Supporting Files */, 183 | ); 184 | path = FireDataTests; 185 | sourceTree = ""; 186 | }; 187 | ABBDD0A316FA31DC002D7BE3 /* Supporting Files */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | ABBDD0A416FA31DC002D7BE3 /* FireDataTests-Info.plist */, 191 | ABBDD0A516FA31DC002D7BE3 /* InfoPlist.strings */, 192 | ); 193 | name = "Supporting Files"; 194 | sourceTree = ""; 195 | }; 196 | ABBDD0B716FA348C002D7BE3 /* Vendor */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | ABBDD0B816FA348C002D7BE3 /* Firebase */, 200 | ABBDD0FB16FA3891002D7BE3 /* iso-8601-date-formatter */, 201 | ); 202 | path = Vendor; 203 | sourceTree = ""; 204 | }; 205 | ABBDD0B816FA348C002D7BE3 /* Firebase */ = { 206 | isa = PBXGroup; 207 | children = ( 208 | ABBDD15316FA4982002D7BE3 /* Firebase.framework */, 209 | ); 210 | path = Firebase; 211 | sourceTree = ""; 212 | }; 213 | ABBDD0FB16FA3891002D7BE3 /* iso-8601-date-formatter */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | ABBDD0FD16FA3891002D7BE3 /* ISO8601DateFormatter.h */, 217 | ABBDD0FE16FA3891002D7BE3 /* ISO8601DateFormatter.m */, 218 | ABBDD0FF16FA3891002D7BE3 /* LICENSE.txt */, 219 | ABBDD10116FA3891002D7BE3 /* README.md */, 220 | ); 221 | path = "iso-8601-date-formatter"; 222 | sourceTree = ""; 223 | }; 224 | /* End PBXGroup section */ 225 | 226 | /* Begin PBXNativeTarget section */ 227 | ABBDD08716FA31DB002D7BE3 /* FireData */ = { 228 | isa = PBXNativeTarget; 229 | buildConfigurationList = ABBDD0AD16FA31DC002D7BE3 /* Build configuration list for PBXNativeTarget "FireData" */; 230 | buildPhases = ( 231 | ABBDD08416FA31DB002D7BE3 /* Sources */, 232 | ABBDD08516FA31DB002D7BE3 /* Frameworks */, 233 | ABBDD08616FA31DB002D7BE3 /* CopyFiles */, 234 | ABBDD13B16FA3FA1002D7BE3 /* Prepare Framework */, 235 | ); 236 | buildRules = ( 237 | ); 238 | dependencies = ( 239 | ); 240 | name = FireData; 241 | productName = FireData; 242 | productReference = ABBDD08816FA31DB002D7BE3 /* libFireData.a */; 243 | productType = "com.apple.product-type.library.static"; 244 | }; 245 | ABBDD09816FA31DC002D7BE3 /* FireDataTests */ = { 246 | isa = PBXNativeTarget; 247 | buildConfigurationList = ABBDD0B016FA31DC002D7BE3 /* Build configuration list for PBXNativeTarget "FireDataTests" */; 248 | buildPhases = ( 249 | ABBDD09416FA31DC002D7BE3 /* Sources */, 250 | ABBDD09516FA31DC002D7BE3 /* Frameworks */, 251 | ABBDD09616FA31DC002D7BE3 /* Resources */, 252 | ABBDD09716FA31DC002D7BE3 /* ShellScript */, 253 | ); 254 | buildRules = ( 255 | ); 256 | dependencies = ( 257 | ABBDD0A016FA31DC002D7BE3 /* PBXTargetDependency */, 258 | ); 259 | name = FireDataTests; 260 | productName = FireDataTests; 261 | productReference = ABBDD09916FA31DC002D7BE3 /* FireDataTests.octest */; 262 | productType = "com.apple.product-type.bundle"; 263 | }; 264 | /* End PBXNativeTarget section */ 265 | 266 | /* Begin PBXProject section */ 267 | ABBDD08016FA31DB002D7BE3 /* Project object */ = { 268 | isa = PBXProject; 269 | attributes = { 270 | LastUpgradeCheck = 0460; 271 | ORGANIZATIONNAME = "Overcommitted, LLC."; 272 | }; 273 | buildConfigurationList = ABBDD08316FA31DB002D7BE3 /* Build configuration list for PBXProject "FireData" */; 274 | compatibilityVersion = "Xcode 3.2"; 275 | developmentRegion = English; 276 | hasScannedForEncodings = 0; 277 | knownRegions = ( 278 | en, 279 | ); 280 | mainGroup = ABBDD07F16FA31DB002D7BE3; 281 | productRefGroup = ABBDD08916FA31DB002D7BE3 /* Products */; 282 | projectDirPath = ""; 283 | projectRoot = ""; 284 | targets = ( 285 | ABBDD08716FA31DB002D7BE3 /* FireData */, 286 | ABBDD09816FA31DC002D7BE3 /* FireDataTests */, 287 | ABBDD13C16FA4376002D7BE3 /* Framework */, 288 | ); 289 | }; 290 | /* End PBXProject section */ 291 | 292 | /* Begin PBXResourcesBuildPhase section */ 293 | ABBDD09616FA31DC002D7BE3 /* Resources */ = { 294 | isa = PBXResourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | ABBDD0A716FA31DC002D7BE3 /* InfoPlist.strings in Resources */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | /* End PBXResourcesBuildPhase section */ 302 | 303 | /* Begin PBXShellScriptBuildPhase section */ 304 | ABBDD09716FA31DC002D7BE3 /* ShellScript */ = { 305 | isa = PBXShellScriptBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | ); 309 | inputPaths = ( 310 | ); 311 | outputPaths = ( 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | shellPath = /bin/sh; 315 | shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; 316 | }; 317 | ABBDD13B16FA3FA1002D7BE3 /* Prepare Framework */ = { 318 | isa = PBXShellScriptBuildPhase; 319 | buildActionMask = 12; 320 | files = ( 321 | ); 322 | inputPaths = ( 323 | ); 324 | name = "Prepare Framework"; 325 | outputPaths = ( 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | shellPath = /bin/sh; 329 | shellScript = "set -e\n\nmkdir -p \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/A/Headers\"\n\n# Link the \"Current\" version to \"A\"\n/bin/ln -sfh A \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/Current\"\n/bin/ln -sfh Versions/Current/Headers \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Headers\"\n/bin/ln -sfh \"Versions/Current/${PRODUCT_NAME}\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}\"\n\n# The -a ensures that the headers maintain the source modification date so that we don't constantly\n# cause propagating rebuilds of files that import these headers.\n/bin/cp -a \"${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/A/Headers\"\n"; 330 | }; 331 | ABBDD14216FA43A7002D7BE3 /* ShellScript */ = { 332 | isa = PBXShellScriptBuildPhase; 333 | buildActionMask = 2147483647; 334 | files = ( 335 | ); 336 | inputPaths = ( 337 | ); 338 | outputPaths = ( 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | shellPath = /bin/sh; 342 | shellScript = "set -e\nset +u\n# Avoid recursively calling this script.\nif [[ $SF_MASTER_SCRIPT_RUNNING ]]\nthen\nexit 0\nfi\nset -u\nexport SF_MASTER_SCRIPT_RUNNING=1\n\nSF_TARGET_NAME=${PROJECT_NAME}\nSF_EXECUTABLE_PATH=\"lib${SF_TARGET_NAME}.a\"\nSF_WRAPPER_NAME=\"${SF_TARGET_NAME}.framework\"\n\n# The following conditionals come from\n# https://github.com/kstenerud/iOS-Universal-Framework\n\nif [[ \"$SDK_NAME\" =~ ([A-Za-z]+) ]]\nthen\nSF_SDK_PLATFORM=${BASH_REMATCH[1]}\nelse\necho \"Could not find platform name from SDK_NAME: $SDK_NAME\"\nexit 1\nfi\n\nif [[ \"$SDK_NAME\" =~ ([0-9]+.*$) ]]\nthen\nSF_SDK_VERSION=${BASH_REMATCH[1]}\nelse\necho \"Could not find sdk version from SDK_NAME: $SDK_NAME\"\nexit 1\nfi\n\nif [[ \"$SF_SDK_PLATFORM\" = \"iphoneos\" ]]\nthen\nSF_OTHER_PLATFORM=iphonesimulator\nelse\nSF_OTHER_PLATFORM=iphoneos\nfi\n\nif [[ \"$BUILT_PRODUCTS_DIR\" =~ (.*)$SF_SDK_PLATFORM$ ]]\nthen\nSF_OTHER_BUILT_PRODUCTS_DIR=\"${BASH_REMATCH[1]}${SF_OTHER_PLATFORM}\"\nelse\necho \"Could not find platform name from build products directory: $BUILT_PRODUCTS_DIR\"\nexit 1\nfi\n\n# Build the other platform.\nxcodebuild -project \"${PROJECT_FILE_PATH}\" -target \"${TARGET_NAME}\" -configuration \"${CONFIGURATION}\" -sdk ${SF_OTHER_PLATFORM}${SF_SDK_VERSION} BUILD_DIR=\"${BUILD_DIR}\" OBJROOT=\"${OBJROOT}\" BUILD_ROOT=\"${BUILD_ROOT}\" SYMROOT=\"${SYMROOT}\" $ACTION\n\n# Smash the two static libraries into one fat binary and store it in the .framework\nlipo -create \"${BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}\" \"${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}\" -output \"${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}\"\n\n# Copy the binary to the other architecture folder to have a complete framework in both.\ncp -a \"${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}\" \"${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}\"\n"; 343 | }; 344 | /* End PBXShellScriptBuildPhase section */ 345 | 346 | /* Begin PBXSourcesBuildPhase section */ 347 | ABBDD08416FA31DB002D7BE3 /* Sources */ = { 348 | isa = PBXSourcesBuildPhase; 349 | buildActionMask = 2147483647; 350 | files = ( 351 | ABBDD09316FA31DB002D7BE3 /* FireData.m in Sources */, 352 | ABBDD0C016FA359E002D7BE3 /* NSManagedObject+FireData.m in Sources */, 353 | ABBDD11516FA3891002D7BE3 /* ISO8601DateFormatter.m in Sources */, 354 | ABBDD13416FA392D002D7BE3 /* FireDataISO8601DateFormatter.m in Sources */, 355 | ); 356 | runOnlyForDeploymentPostprocessing = 0; 357 | }; 358 | ABBDD09416FA31DC002D7BE3 /* Sources */ = { 359 | isa = PBXSourcesBuildPhase; 360 | buildActionMask = 2147483647; 361 | files = ( 362 | ABBDD0AA16FA31DC002D7BE3 /* FireDataTests.m in Sources */, 363 | ABBDD0C116FA359E002D7BE3 /* NSManagedObject+FireData.m in Sources */, 364 | ABBDD11616FA3891002D7BE3 /* ISO8601DateFormatter.m in Sources */, 365 | ABBDD13516FA392D002D7BE3 /* FireDataISO8601DateFormatter.m in Sources */, 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | }; 369 | /* End PBXSourcesBuildPhase section */ 370 | 371 | /* Begin PBXTargetDependency section */ 372 | ABBDD0A016FA31DC002D7BE3 /* PBXTargetDependency */ = { 373 | isa = PBXTargetDependency; 374 | target = ABBDD08716FA31DB002D7BE3 /* FireData */; 375 | targetProxy = ABBDD09F16FA31DC002D7BE3 /* PBXContainerItemProxy */; 376 | }; 377 | ABBDD14116FA4383002D7BE3 /* PBXTargetDependency */ = { 378 | isa = PBXTargetDependency; 379 | target = ABBDD08716FA31DB002D7BE3 /* FireData */; 380 | targetProxy = ABBDD14016FA4383002D7BE3 /* PBXContainerItemProxy */; 381 | }; 382 | /* End PBXTargetDependency section */ 383 | 384 | /* Begin PBXVariantGroup section */ 385 | ABBDD0A516FA31DC002D7BE3 /* InfoPlist.strings */ = { 386 | isa = PBXVariantGroup; 387 | children = ( 388 | ABBDD0A616FA31DC002D7BE3 /* en */, 389 | ); 390 | name = InfoPlist.strings; 391 | sourceTree = ""; 392 | }; 393 | /* End PBXVariantGroup section */ 394 | 395 | /* Begin XCBuildConfiguration section */ 396 | ABBDD0AB16FA31DC002D7BE3 /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ALWAYS_SEARCH_USER_PATHS = NO; 400 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 401 | CLANG_CXX_LIBRARY = "libc++"; 402 | CLANG_ENABLE_OBJC_ARC = YES; 403 | CLANG_WARN_CONSTANT_CONVERSION = YES; 404 | CLANG_WARN_EMPTY_BODY = YES; 405 | CLANG_WARN_ENUM_CONVERSION = YES; 406 | CLANG_WARN_INT_CONVERSION = YES; 407 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 408 | COPY_PHASE_STRIP = NO; 409 | DEAD_CODE_STRIPPING = NO; 410 | GCC_C_LANGUAGE_STANDARD = gnu99; 411 | GCC_DYNAMIC_NO_PIC = NO; 412 | GCC_OPTIMIZATION_LEVEL = 0; 413 | GCC_PREPROCESSOR_DEFINITIONS = ( 414 | "DEBUG=1", 415 | "$(inherited)", 416 | ); 417 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 418 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 419 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 420 | GCC_WARN_UNUSED_VARIABLE = YES; 421 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 422 | ONLY_ACTIVE_ARCH = YES; 423 | SDKROOT = iphoneos; 424 | STRIP_STYLE = "non-global"; 425 | }; 426 | name = Debug; 427 | }; 428 | ABBDD0AC16FA31DC002D7BE3 /* Release */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | ALWAYS_SEARCH_USER_PATHS = NO; 432 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 433 | CLANG_CXX_LIBRARY = "libc++"; 434 | CLANG_ENABLE_OBJC_ARC = YES; 435 | CLANG_WARN_CONSTANT_CONVERSION = YES; 436 | CLANG_WARN_EMPTY_BODY = YES; 437 | CLANG_WARN_ENUM_CONVERSION = YES; 438 | CLANG_WARN_INT_CONVERSION = YES; 439 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 440 | COPY_PHASE_STRIP = NO; 441 | DEAD_CODE_STRIPPING = NO; 442 | GCC_C_LANGUAGE_STANDARD = gnu99; 443 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 445 | GCC_WARN_UNUSED_VARIABLE = YES; 446 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 447 | SDKROOT = iphoneos; 448 | STRIP_STYLE = "non-global"; 449 | VALIDATE_PRODUCT = YES; 450 | }; 451 | name = Release; 452 | }; 453 | ABBDD0AE16FA31DC002D7BE3 /* Debug */ = { 454 | isa = XCBuildConfiguration; 455 | buildSettings = { 456 | DSTROOT = /tmp/FireData.dst; 457 | FRAMEWORK_SEARCH_PATHS = ( 458 | "$(inherited)", 459 | "\"$(SRCROOT)/Vendor/Firebase\"", 460 | ); 461 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 462 | GCC_PREFIX_HEADER = "FireData/FireData-Prefix.pch"; 463 | ONLY_ACTIVE_ARCH = NO; 464 | OTHER_LDFLAGS = "-ObjC"; 465 | PRODUCT_NAME = "$(TARGET_NAME)"; 466 | PUBLIC_HEADERS_FOLDER_PATH = Headers; 467 | SKIP_INSTALL = YES; 468 | }; 469 | name = Debug; 470 | }; 471 | ABBDD0AF16FA31DC002D7BE3 /* Release */ = { 472 | isa = XCBuildConfiguration; 473 | buildSettings = { 474 | DSTROOT = /tmp/FireData.dst; 475 | FRAMEWORK_SEARCH_PATHS = ( 476 | "$(inherited)", 477 | "\"$(SRCROOT)/Vendor/Firebase\"", 478 | ); 479 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 480 | GCC_PREFIX_HEADER = "FireData/FireData-Prefix.pch"; 481 | ONLY_ACTIVE_ARCH = NO; 482 | OTHER_LDFLAGS = "-ObjC"; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | PUBLIC_HEADERS_FOLDER_PATH = Headers; 485 | SKIP_INSTALL = YES; 486 | }; 487 | name = Release; 488 | }; 489 | ABBDD0B116FA31DC002D7BE3 /* Debug */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | FRAMEWORK_SEARCH_PATHS = ( 493 | "\"$(SDKROOT)/Developer/Library/Frameworks\"", 494 | "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", 495 | "\"$(SRCROOT)/Vendor/Firebase\"", 496 | ); 497 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 498 | GCC_PREFIX_HEADER = "FireData/FireData-Prefix.pch"; 499 | INFOPLIST_FILE = "FireDataTests/FireDataTests-Info.plist"; 500 | PRODUCT_NAME = "$(TARGET_NAME)"; 501 | WRAPPER_EXTENSION = octest; 502 | }; 503 | name = Debug; 504 | }; 505 | ABBDD0B216FA31DC002D7BE3 /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | FRAMEWORK_SEARCH_PATHS = ( 509 | "\"$(SDKROOT)/Developer/Library/Frameworks\"", 510 | "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", 511 | "\"$(SRCROOT)/Vendor/Firebase\"", 512 | ); 513 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 514 | GCC_PREFIX_HEADER = "FireData/FireData-Prefix.pch"; 515 | INFOPLIST_FILE = "FireDataTests/FireDataTests-Info.plist"; 516 | PRODUCT_NAME = "$(TARGET_NAME)"; 517 | WRAPPER_EXTENSION = octest; 518 | }; 519 | name = Release; 520 | }; 521 | ABBDD13E16FA4376002D7BE3 /* Debug */ = { 522 | isa = XCBuildConfiguration; 523 | buildSettings = { 524 | ONLY_ACTIVE_ARCH = NO; 525 | PRODUCT_NAME = "$(TARGET_NAME)"; 526 | }; 527 | name = Debug; 528 | }; 529 | ABBDD13F16FA4376002D7BE3 /* Release */ = { 530 | isa = XCBuildConfiguration; 531 | buildSettings = { 532 | ONLY_ACTIVE_ARCH = NO; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | }; 535 | name = Release; 536 | }; 537 | /* End XCBuildConfiguration section */ 538 | 539 | /* Begin XCConfigurationList section */ 540 | ABBDD08316FA31DB002D7BE3 /* Build configuration list for PBXProject "FireData" */ = { 541 | isa = XCConfigurationList; 542 | buildConfigurations = ( 543 | ABBDD0AB16FA31DC002D7BE3 /* Debug */, 544 | ABBDD0AC16FA31DC002D7BE3 /* Release */, 545 | ); 546 | defaultConfigurationIsVisible = 0; 547 | defaultConfigurationName = Release; 548 | }; 549 | ABBDD0AD16FA31DC002D7BE3 /* Build configuration list for PBXNativeTarget "FireData" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | ABBDD0AE16FA31DC002D7BE3 /* Debug */, 553 | ABBDD0AF16FA31DC002D7BE3 /* Release */, 554 | ); 555 | defaultConfigurationIsVisible = 0; 556 | }; 557 | ABBDD0B016FA31DC002D7BE3 /* Build configuration list for PBXNativeTarget "FireDataTests" */ = { 558 | isa = XCConfigurationList; 559 | buildConfigurations = ( 560 | ABBDD0B116FA31DC002D7BE3 /* Debug */, 561 | ABBDD0B216FA31DC002D7BE3 /* Release */, 562 | ); 563 | defaultConfigurationIsVisible = 0; 564 | }; 565 | ABBDD13D16FA4376002D7BE3 /* Build configuration list for PBXAggregateTarget "Framework" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | ABBDD13E16FA4376002D7BE3 /* Debug */, 569 | ABBDD13F16FA4376002D7BE3 /* Release */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | }; 573 | /* End XCConfigurationList section */ 574 | }; 575 | rootObject = ABBDD08016FA31DB002D7BE3 /* Project object */; 576 | } 577 | -------------------------------------------------------------------------------- /FireData/FireData-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'FireData' target in the 'FireData' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /FireData/FireData.h: -------------------------------------------------------------------------------- 1 | // 2 | // FireData.h 3 | // FireData 4 | // 5 | // Created by Jonathan Younger on 3/20/13. 6 | // Copyright (c) 2013 Overcommitted, LLC. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | 27 | #import 28 | #import 29 | #import 30 | 31 | 32 | @interface FireData : NSObject 33 | 34 | /** 35 | * Set this property to the Core Data unique key attribute used by Firebase. 36 | * @default firebaseKey 37 | * @return The Core Data unique key attribute 38 | */ 39 | @property (copy, nonatomic) NSString *coreDataKeyAttribute; 40 | 41 | /** 42 | * Set this property to the Core Data data attribute used by Firebase. 43 | * @default firebaseData 44 | * @return The Core Data data attribute 45 | */ 46 | @property (copy, nonatomic) NSString *coreDataDataAttribute; 47 | 48 | /** 49 | * @return A new unique key 50 | */ 51 | + (NSString *)firebaseKey; 52 | 53 | /** 54 | * observeManagedObjectContext: is used to listen for data changes for the specified managed object context. 55 | * 56 | * @param managedObjectContext The managed object context to listen for changes on. 57 | */ 58 | - (void)observeManagedObjectContext:(NSManagedObjectContext *)managedObjectContext; 59 | 60 | /** 61 | * setWriteManagedObjectContext:withCompletionBlock: is used to write changes from Firebase to the specified managed object context. 62 | * 63 | * @param managedObjectContext The managed object context to write changes to. 64 | * @param block The block that should be called to save changes written to the managed object context. 65 | */ 66 | - (void)setWriteManagedObjectContext:(NSManagedObjectContext *)writeManagedObjectContext withCompletionBlock:(void (^)(NSManagedObjectContext *error))block; 67 | 68 | /** 69 | * linkCoreDataEntity:withFirebase: is used to specify which Core Data entity to synchronize changes with the specified Firebase. 70 | * 71 | * @param coreDataEntity The Core Data entity name to listen for changes to. 72 | * @param firebase The Firebase reference to listen for changes to. 73 | */ 74 | - (void)linkCoreDataEntity:(NSString *)coreDataEntity withFirebase:(Firebase *)firebase; 75 | 76 | /** 77 | * unlinkCoreDataEntity: is used to remove the link between the Core Data entity and the associated Firebase. 78 | * 79 | * @param coreDataEntity The Core Data entity name to unlink. 80 | */ 81 | - (void)unlinkCoreDataEntity:(NSString *)coreDataEntity; 82 | 83 | /** 84 | * Starts observing changes between Core Data and Firebase. 85 | */ 86 | - (void)startObserving; 87 | 88 | /** 89 | * Stops observing changes between Core Data and Firebase. 90 | */ 91 | - (void)stopObserving; 92 | 93 | /** 94 | * Replace all Firebase data with values from Core Data. 95 | */ 96 | - (void)replaceFirebaseFromCoreData; 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /FireData/FireData.m: -------------------------------------------------------------------------------- 1 | // 2 | // FireData.m 3 | // FireData 4 | // 5 | // Created by Jonathan Younger on 3/20/13. 6 | // Copyright (c) 2013 Overcommitted, LLC. All rights reserved. 7 | // 8 | 9 | #import "FireData.h" 10 | #import "NSManagedObject+FireData.h" 11 | 12 | typedef void (^fcdm_void_managedobjectcontext) (NSManagedObjectContext *context); 13 | 14 | @interface FireData () 15 | @property (strong, nonatomic) NSManagedObjectContext *observedManagedObjectContext; 16 | @property (strong, nonatomic) NSManagedObjectContext *writeManagedObjectContext; 17 | @property (strong, nonatomic) NSMutableDictionary *linkedEntities; 18 | @property (copy, nonatomic) fcdm_void_managedobjectcontext writeManagedObjectContextCompletionBlock; 19 | @end 20 | 21 | @interface FireData (CoreData) 22 | - (void)managedObjectContextObjectsDidChange:(NSNotification *)notification; 23 | - (void)managedObjectContextDidSave:(NSNotification *)notification; 24 | - (NSString *)coreDataEntityForFirebase:(Firebase *)firebase; 25 | - (BOOL)isCoreDataEntityLinked:(NSString *)entity; 26 | - (NSManagedObject *)fetchCoreDataManagedObjectWithEntityName:(NSString *)entityName firebaseKey:(NSString *)firebaseKey; 27 | - (void)deleteCoreDataManagedObjectsThatNoLongerExistInFirebase:(Firebase *)firebase; 28 | @end 29 | 30 | @interface FireData (Firebase) 31 | - (Firebase *)firebaseForCoreDataEntity:(NSString *)entity; 32 | - (void)observeFirebase:(Firebase *)firebase; 33 | - (void)updateFirebase:(Firebase *)firebase withManagedObject:(NSManagedObject *)managedObject; 34 | @end 35 | 36 | @implementation FireData 37 | - (void)dealloc 38 | { 39 | [self stopObserving]; 40 | } 41 | 42 | + (NSString *)firebaseKey 43 | { 44 | return [[NSUUID UUID] UUIDString]; 45 | } 46 | 47 | - (id)init 48 | { 49 | self = [super init]; 50 | if (self) { 51 | _coreDataKeyAttribute = @"firebaseKey"; 52 | _coreDataDataAttribute = @"firebaseData"; 53 | _linkedEntities = [[NSMutableDictionary alloc] init]; 54 | } 55 | return self; 56 | } 57 | 58 | - (void)observeManagedObjectContext:(NSManagedObjectContext *)managedObjectContext 59 | { 60 | [self removeObserverForManagedObjectContext]; 61 | self.observedManagedObjectContext = managedObjectContext; 62 | } 63 | 64 | - (void)removeObserverForManagedObjectContext 65 | { 66 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 67 | if (self.observedManagedObjectContext) { 68 | [notificationCenter removeObserver:self name:NSManagedObjectContextObjectsDidChangeNotification object:self.observedManagedObjectContext]; 69 | [notificationCenter removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.observedManagedObjectContext]; 70 | } 71 | 72 | self.observedManagedObjectContext = nil; 73 | } 74 | 75 | - (void)setWriteManagedObjectContext:(NSManagedObjectContext *)writeManagedObjectContext withCompletionBlock:(void (^)(NSManagedObjectContext *error))block 76 | { 77 | self.writeManagedObjectContext = writeManagedObjectContext; 78 | self.writeManagedObjectContextCompletionBlock = [block copy]; 79 | } 80 | 81 | - (void)linkCoreDataEntity:(NSString *)coreDataEntity withFirebase:(Firebase *)firebase 82 | { 83 | [self.linkedEntities setObject:firebase forKey:coreDataEntity]; 84 | } 85 | 86 | - (void)unlinkCoreDataEntity:(NSString *)coreDataEntity 87 | { 88 | [[self.linkedEntities objectForKey:coreDataEntity] removeAllObservers]; 89 | [self.linkedEntities removeObjectForKey:coreDataEntity]; 90 | } 91 | 92 | - (void)startObserving 93 | { 94 | [self.linkedEntities enumerateKeysAndObjectsUsingBlock:^(NSString *coreDataEntity, Firebase *firebase, BOOL *stop) { 95 | [self deleteCoreDataManagedObjectsThatNoLongerExistInFirebase:firebase]; 96 | [self observeFirebase:firebase]; 97 | }]; 98 | 99 | if (self.observedManagedObjectContext) { 100 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 101 | [notificationCenter addObserver:self selector:@selector(managedObjectContextObjectsDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.observedManagedObjectContext]; 102 | [notificationCenter addObserver:self selector:@selector(managedObjectContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.observedManagedObjectContext]; 103 | } 104 | } 105 | 106 | - (void)stopObserving 107 | { 108 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 109 | NSArray *firebases = [self.linkedEntities allValues]; 110 | [firebases makeObjectsPerformSelector:@selector(removeAllObservers)]; 111 | } 112 | 113 | - (void)replaceFirebaseFromCoreData 114 | { 115 | [self.linkedEntities enumerateKeysAndObjectsUsingBlock:^(NSString *coreDataEntity, Firebase *firebase, BOOL *stop) { 116 | [firebase removeValue]; 117 | 118 | NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:coreDataEntity]; 119 | [fetchRequest setFetchBatchSize:25]; 120 | NSArray *managedObjects = [self.observedManagedObjectContext executeFetchRequest:fetchRequest error:nil]; 121 | for (NSManagedObject *managedObject in managedObjects) { 122 | [self updateFirebase:firebase withManagedObject:managedObject]; 123 | }; 124 | }]; 125 | } 126 | 127 | 128 | @end 129 | 130 | @implementation FireData (CoreData) 131 | - (void)managedObjectContextObjectsDidChange:(NSNotification *)notification 132 | { 133 | NSMutableSet *managedObjects = [[NSMutableSet alloc] init]; 134 | [managedObjects unionSet:[[notification userInfo] objectForKey:NSInsertedObjectsKey]]; 135 | [managedObjects unionSet:[[notification userInfo] objectForKey:NSUpdatedObjectsKey]]; 136 | 137 | for (NSManagedObject *managedObject in managedObjects) { 138 | if (![self isCoreDataEntityLinked:[[managedObject entity] name]]) return; 139 | 140 | if (![managedObject primitiveValueForKey:self.coreDataKeyAttribute]) { 141 | [managedObject setPrimitiveValue:[[self class] firebaseKey] forKey:self.coreDataKeyAttribute]; 142 | } 143 | 144 | if (![[managedObject changedValues] objectForKey:self.coreDataDataAttribute]) { 145 | [managedObject setPrimitiveValue:nil forKey:self.coreDataDataAttribute]; 146 | } 147 | }; 148 | } 149 | 150 | - (void)managedObjectContextDidSave:(NSNotification *)notification 151 | { 152 | NSSet *deletedObjects = [[notification userInfo] objectForKey:NSDeletedObjectsKey]; 153 | for (NSManagedObject *managedObject in deletedObjects) { 154 | Firebase *firebase = [self firebaseForCoreDataEntity:[[managedObject entity] name]]; 155 | if (firebase) { 156 | Firebase *child = [firebase childByAppendingPath:[managedObject valueForKey:self.coreDataKeyAttribute]]; 157 | [child removeValue]; 158 | } 159 | }; 160 | 161 | NSMutableSet *managedObjects = [[NSMutableSet alloc] init]; 162 | [managedObjects unionSet:[[notification userInfo] objectForKey:NSInsertedObjectsKey]]; 163 | [managedObjects unionSet:[[notification userInfo] objectForKey:NSUpdatedObjectsKey]]; 164 | 165 | NSSet *changedObjects = [managedObjects filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"%K == nil", self.coreDataDataAttribute]]; 166 | for (NSManagedObject *managedObject in changedObjects) { 167 | Firebase *firebase = [self firebaseForCoreDataEntity:[[managedObject entity] name]]; 168 | if (firebase) { 169 | [self updateFirebase:firebase withManagedObject:managedObject]; 170 | } 171 | }; 172 | } 173 | 174 | - (NSString *)coreDataEntityForFirebase:(Firebase *)firebase 175 | { 176 | return [[self.linkedEntities allKeysForObject:firebase] lastObject]; 177 | } 178 | 179 | - (BOOL)isCoreDataEntityLinked:(NSString *)entity 180 | { 181 | return [self firebaseForCoreDataEntity:entity] != nil; 182 | } 183 | 184 | - (NSManagedObject *)fetchCoreDataManagedObjectWithEntityName:(NSString *)entityName firebaseKey:(NSString *)firebaseKey 185 | { 186 | NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName]; 187 | [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K == %@", self.coreDataKeyAttribute, firebaseKey]]; 188 | [fetchRequest setFetchLimit:1]; 189 | return [[self.writeManagedObjectContext executeFetchRequest:fetchRequest error:nil] lastObject]; 190 | } 191 | 192 | - (void)updateCoreDataEntity:(NSString *)entityName firebaseKey:(NSString *)firebaseKey properties:(NSDictionary *)properties 193 | { 194 | if ((id)properties == [NSNull null]) return; 195 | 196 | NSManagedObject *managedObject = [self fetchCoreDataManagedObjectWithEntityName:entityName firebaseKey:firebaseKey]; 197 | if (!managedObject) { 198 | managedObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.writeManagedObjectContext]; 199 | [managedObject setValue:firebaseKey forKey:self.coreDataKeyAttribute]; 200 | } 201 | 202 | [managedObject firedata_setPropertiesForKeysWithDictionary:properties coreDataKeyAttribute:self.coreDataKeyAttribute coreDataDataAttribute:self.coreDataDataAttribute]; 203 | 204 | if ([self.writeManagedObjectContext hasChanges] && self.writeManagedObjectContextCompletionBlock) { 205 | self.writeManagedObjectContextCompletionBlock(self.writeManagedObjectContext); 206 | } 207 | } 208 | 209 | - (void)deleteCoreDataManagedObjectsThatNoLongerExistInFirebase:(Firebase *)firebase 210 | { 211 | NSString *coreDataEntity = [self coreDataEntityForFirebase:firebase]; 212 | if (!coreDataEntity) return; 213 | 214 | void (^identifierBlock)(FDataSnapshot *snapshot) = ^(FDataSnapshot *snapshot) { 215 | NSMutableArray *uniqueIdentifiers = [[NSMutableArray alloc] init]; 216 | for (FDataSnapshot *child in snapshot.children) { 217 | [uniqueIdentifiers addObject:child.name]; 218 | }; 219 | 220 | NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:coreDataEntity]; 221 | [fetchRequest setIncludesPropertyValues:NO]; 222 | [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"NOT (%K IN %@)", self.coreDataKeyAttribute, uniqueIdentifiers]]; 223 | 224 | NSArray *objects = [self.writeManagedObjectContext executeFetchRequest:fetchRequest error:nil]; 225 | for (NSManagedObject *managedObject in objects) { 226 | [self.writeManagedObjectContext deleteObject:managedObject]; 227 | } 228 | 229 | if (self.writeManagedObjectContextCompletionBlock) { 230 | self.writeManagedObjectContextCompletionBlock(self.writeManagedObjectContext); 231 | } 232 | }; 233 | [firebase observeSingleEventOfType:FEventTypeValue withBlock:identifierBlock]; 234 | } 235 | @end 236 | 237 | @implementation FireData (Firebase) 238 | - (Firebase *)firebaseForCoreDataEntity:(NSString *)entity 239 | { 240 | return [self.linkedEntities objectForKey:entity]; 241 | } 242 | 243 | - (void)observeFirebase:(Firebase *)firebase 244 | { 245 | void (^updatedBlock)(FDataSnapshot *snapshot) = ^(FDataSnapshot *snapshot) { 246 | NSString *coreDataEntity = [self coreDataEntityForFirebase:firebase]; 247 | if (!coreDataEntity) return; 248 | [self updateCoreDataEntity:coreDataEntity firebaseKey:snapshot.name properties:snapshot.value]; 249 | }; 250 | [firebase observeEventType:FEventTypeChildAdded withBlock:updatedBlock]; 251 | [firebase observeEventType:FEventTypeChildChanged withBlock:updatedBlock]; 252 | 253 | void (^removedBlock)(FDataSnapshot *snapshot) = ^(FDataSnapshot *snapshot) { 254 | NSString *coreDataEntity = [self coreDataEntityForFirebase:firebase]; 255 | if (!coreDataEntity) return; 256 | 257 | NSManagedObject *managedObject = [self fetchCoreDataManagedObjectWithEntityName:coreDataEntity firebaseKey:snapshot.name]; 258 | if (managedObject) { 259 | [self.writeManagedObjectContext deleteObject:managedObject]; 260 | 261 | if (self.writeManagedObjectContextCompletionBlock) { 262 | self.writeManagedObjectContextCompletionBlock(self.writeManagedObjectContext); 263 | } 264 | } 265 | }; 266 | [firebase observeEventType:FEventTypeChildRemoved withBlock:removedBlock]; 267 | } 268 | 269 | - (void)updateFirebase:(Firebase *)firebase withManagedObject:(NSManagedObject *)managedObject 270 | { 271 | NSDictionary *properties = [managedObject firedata_propertiesDictionaryWithCoreDataKeyAttribute:self.coreDataKeyAttribute coreDataDataAttribute:self.coreDataDataAttribute]; 272 | Firebase *child = [firebase childByAppendingPath:[managedObject valueForKey:self.coreDataKeyAttribute]]; 273 | NSString *childName = child.name; 274 | [child setValue:properties withCompletionBlock:^(NSError *error) { 275 | if (error) { 276 | NSLog(@"Error updating %@: %@", childName, error); 277 | } 278 | }]; 279 | } 280 | @end 281 | -------------------------------------------------------------------------------- /FireData/FireDataISO8601DateFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // FireDataISO8601DateFormatter.h 3 | // FireData 4 | // 5 | // Created by Jonathan Younger on 3/20/13. 6 | // Copyright (c) 2013 Overcommitted, LLC. All rights reserved. 7 | // 8 | 9 | #import "ISO8601DateFormatter.h" 10 | 11 | @interface FireDataISO8601DateFormatter : ISO8601DateFormatter 12 | + (FireDataISO8601DateFormatter *)sharedFormatter; 13 | @end 14 | -------------------------------------------------------------------------------- /FireData/FireDataISO8601DateFormatter.m: -------------------------------------------------------------------------------- 1 | // 2 | // FireDataISO8601DateFormatter.m 3 | // FireData 4 | // 5 | // Created by Jonathan Younger on 3/20/13. 6 | // Copyright (c) 2013 Overcommitted, LLC. All rights reserved. 7 | // 8 | 9 | #import "FireDataISO8601DateFormatter.h" 10 | 11 | @implementation FireDataISO8601DateFormatter 12 | + (FireDataISO8601DateFormatter *)sharedFormatter 13 | { 14 | NSMutableDictionary *dictionary = [[NSThread currentThread] threadDictionary]; 15 | static NSString *dateFormatterKey = @"FireDataISO8601DateFormatter"; 16 | 17 | FireDataISO8601DateFormatter *dateFormatter = [dictionary objectForKey:dateFormatterKey]; 18 | if (dateFormatter == nil) { 19 | dateFormatter = [[FireDataISO8601DateFormatter alloc] init]; 20 | dateFormatter.includeTime = YES; 21 | [dictionary setObject:dateFormatter forKey:dateFormatterKey]; 22 | } 23 | return dateFormatter; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /FireData/NSManagedObject+FireData.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSManagedObject+Firebase.h 3 | // Firebase 4 | // 5 | // Created by Jonathan Younger on 2/26/13. 6 | // Copyright (c) 2013 Overcommitted, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSManagedObject (FireData) 12 | - (NSDictionary *)firedata_propertiesDictionaryWithCoreDataKeyAttribute:(NSString *)coreDataKeyAttribute coreDataDataAttribute:(NSString *)coreDataDataAttribute; 13 | - (void)firedata_setPropertiesForKeysWithDictionary:(NSDictionary *)keyedValues coreDataKeyAttribute:(NSString *)coreDataKeyAttribute coreDataDataAttribute:(NSString *)coreDataDataAttribute; 14 | @end 15 | -------------------------------------------------------------------------------- /FireData/NSManagedObject+FireData.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSManagedObject+Firebase.m 3 | // Firebase 4 | // 5 | // Created by Jonathan Younger on 2/26/13. 6 | // Copyright (c) 2013 Overcommitted, LLC. All rights reserved. 7 | // 8 | 9 | #import "NSManagedObject+FireData.h" 10 | #import "FireDataISO8601DateFormatter.h" 11 | 12 | #define FirebaseSyncData [[NSUUID UUID] UUIDString] 13 | 14 | @implementation NSManagedObject (FireData) 15 | 16 | - (NSDictionary *)firedata_propertiesDictionaryWithCoreDataKeyAttribute:(NSString *)coreDataKeyAttribute coreDataDataAttribute:(NSString *)coreDataDataAttribute 17 | { 18 | NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; 19 | FireDataISO8601DateFormatter *dateFormatter = [FireDataISO8601DateFormatter sharedFormatter]; 20 | 21 | for (id property in [[self entity] properties]) { 22 | NSString *name = [property name]; 23 | if ([name isEqualToString:coreDataKeyAttribute] || [name isEqualToString:coreDataDataAttribute]) continue; 24 | 25 | if ([property isKindOfClass:[NSAttributeDescription class]]) { 26 | NSAttributeDescription *attributeDescription = (NSAttributeDescription *)property; 27 | if ([attributeDescription isTransient]) continue; 28 | 29 | NSString *name = [attributeDescription name]; 30 | id value = [self valueForKey:name]; 31 | 32 | NSAttributeType attributeType = [attributeDescription attributeType]; 33 | if ((attributeType == NSDateAttributeType) && ([value isKindOfClass:[NSDate class]]) && (dateFormatter != nil)) { 34 | value = [dateFormatter stringFromDate:value]; 35 | } 36 | [properties setValue:value forKey:name]; 37 | } else if ([property isKindOfClass:[NSRelationshipDescription class]]) { 38 | NSRelationshipDescription *relationshipDescription = (NSRelationshipDescription *)property; 39 | NSString *name = [relationshipDescription name]; 40 | 41 | if ([relationshipDescription isToMany]) { 42 | NSMutableArray *items = [[NSMutableArray alloc] init]; 43 | for (NSManagedObject *managedObject in [self valueForKey:name]) { 44 | [items addObject:[managedObject valueForKey:coreDataKeyAttribute]]; 45 | } 46 | [properties setValue:[items sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] forKey:name]; 47 | } else { 48 | NSManagedObject *managedObject = [self valueForKey:name]; 49 | [properties setValue:[managedObject valueForKey:coreDataKeyAttribute] forKey:name]; 50 | } 51 | } 52 | } 53 | 54 | return [NSDictionary dictionaryWithDictionary:properties]; 55 | } 56 | 57 | - (void)firedata_setPropertiesForKeysWithDictionary:(NSDictionary *)keyedValues coreDataKeyAttribute:(NSString *)coreDataKeyAttribute coreDataDataAttribute:(NSString *)coreDataDataAttribute 58 | { 59 | FireDataISO8601DateFormatter *dateFormatter = [FireDataISO8601DateFormatter sharedFormatter]; 60 | for (NSPropertyDescription *propertyDescription in [[self entity] properties]) { 61 | NSString *name = [propertyDescription name]; 62 | if ([name isEqualToString:coreDataKeyAttribute] || [name isEqualToString:coreDataDataAttribute]) continue; 63 | 64 | if ([propertyDescription isKindOfClass:[NSAttributeDescription class]]) { 65 | id value = [keyedValues objectForKey:name]; 66 | 67 | NSAttributeType attributeType = [(NSAttributeDescription *)propertyDescription attributeType]; 68 | if ((attributeType == NSStringAttributeType) && ([value isKindOfClass:[NSNumber class]])) { 69 | value = [value stringValue]; 70 | } else if (((attributeType == NSInteger16AttributeType) || (attributeType == NSInteger32AttributeType) || (attributeType == NSInteger64AttributeType) || (attributeType == NSBooleanAttributeType)) && ([value isKindOfClass:[NSString class]])) { 71 | value = [NSNumber numberWithInteger:[value integerValue]]; 72 | } else if ((attributeType == NSFloatAttributeType) && ([value isKindOfClass:[NSString class]])) { 73 | value = [NSNumber numberWithDouble:[value doubleValue]]; 74 | } else if ((attributeType == NSDateAttributeType) && ([value isKindOfClass:[NSString class]]) && (dateFormatter != nil)) { 75 | value = [dateFormatter dateFromString:value]; 76 | } 77 | 78 | [self setValue:value forKey:name]; 79 | } else if ([propertyDescription isKindOfClass:[NSRelationshipDescription class]]) { 80 | NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[[(NSRelationshipDescription *)propertyDescription destinationEntity] name]]; 81 | 82 | if ([(NSRelationshipDescription *)propertyDescription isToMany]) { 83 | NSArray *identifiers = [keyedValues objectForKey:name]; 84 | NSMutableSet *items = [self mutableSetValueForKey:name]; 85 | for (NSString *identifier in identifiers) { 86 | [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K == %@", coreDataKeyAttribute, identifier]]; 87 | NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil]; 88 | if ([objects count] == 1) { 89 | NSManagedObject *managedObject = objects[0]; 90 | if (![items containsObject:managedObject]) { 91 | [managedObject setValue:FirebaseSyncData forKey:coreDataDataAttribute]; 92 | [items addObject:managedObject]; 93 | } 94 | } 95 | } 96 | } else { 97 | [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K == %@", coreDataKeyAttribute, [keyedValues objectForKey:name]]]; 98 | NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil]; 99 | if ([objects count] == 1) { 100 | NSManagedObject *managedObject = objects[0]; 101 | if (![[self valueForKey:name] isEqual:managedObject]) { 102 | [managedObject setValue:FirebaseSyncData forKey:coreDataDataAttribute]; 103 | [self setValue:managedObject forKey:name]; 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | if ([[self changedValues] count] > 0) { 111 | [self setValue:FirebaseSyncData forKey:coreDataDataAttribute]; 112 | } 113 | } 114 | @end 115 | -------------------------------------------------------------------------------- /FireDataTests/FireDataTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.overcommitted.${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 | -------------------------------------------------------------------------------- /FireDataTests/FireDataTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // FireDataTests.h 3 | // FireDataTests 4 | // 5 | // Created by Jonathan Younger on 3/20/13. 6 | // Copyright (c) 2013 Overcommitted, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FireDataTests : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /FireDataTests/FireDataTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FireDataTests.m 3 | // FireDataTests 4 | // 5 | // Created by Jonathan Younger on 3/20/13. 6 | // Copyright (c) 2013 Overcommitted, LLC. All rights reserved. 7 | // 8 | 9 | #import "FireDataTests.h" 10 | 11 | @implementation FireDataTests 12 | 13 | - (void)setUp 14 | { 15 | [super setUp]; 16 | 17 | // Set-up code here. 18 | } 19 | 20 | - (void)tearDown 21 | { 22 | // Tear-down code here. 23 | 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample 28 | { 29 | STFail(@"Unit tests are not implemented yet in FireDataTests"); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /FireDataTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Overcommitted, LLC. 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FireData 2 | ======== 3 | FireData seamlessly integrates Core Data with [Firebase](http://www.firebase.com). 4 | 5 | 6 | Usage 7 | ----- 8 | Include both Firebase and FireData in your application. 9 | 10 | #import 11 | #import 12 | 13 | Initialize an instance of FireData 14 | 15 | FireData *firedata = [[FireData alloc] init]; 16 | 17 | Listen for changes from the default managed object context 18 | 19 | [firedata observeManagedObjectContext:self.managedObjectContext]; 20 | 21 | Create a new managed object context to write changes from Firebase; set its parent to the default managed object context. 22 | 23 | NSManagedObjectContext *writingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 24 | [writingContext setParentContext:self.managedObjectContext]; 25 | [firedata setWriteManagedObjectContext:writingContext withCompletionBlock:^(NSManagedObjectContext *context) { 26 | NSError *error = nil; 27 | if ([context save:&error]) { 28 | if (![self.managedObjectContext save:&error]) { 29 | NSLog(@"Error saving: %@", error); 30 | } 31 | } else { 32 | NSLog(@"Error saving: %@", error); 33 | } 34 | }]; 35 | 36 | Get a reference to Firebase 37 | 38 | Firebase *firebase = [[Firebase alloc] initWithUrl:@"https://EXAMPLE.firebaseio.com/"]; 39 | 40 | Link the Core Data and Firebase references that are to be synced 41 | 42 | [firedata linkCoreDataEntity:@"Book" withFirebase:[firebase childByAppendingPath:@"books"]]; 43 | 44 | Check the existing data in Firebase 45 | 46 | [firebase observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) { 47 | // If Firebase is empty then replace with the data from Core Data 48 | if (snapshot.value == [NSNull null]) { 49 | [firedata replaceFirebaseFromCoreData]; 50 | } 51 | 52 | // Start observing changes between Core Data and Firebase 53 | [firedata startObserving]; 54 | }]; 55 | 56 | Hold on to FireData 57 | 58 | self.firedata = firedata; 59 | 60 | 61 | Example Application 62 | ------------------- 63 | 64 | * [CoreDataBooks](https://github.com/daikini/FireBooks) sample application that has been updated to support Firebase using FireData. 65 | 66 | 67 | Known Issues 68 | ------------ 69 | 70 | * [Firebase](http://www.firebase.com) does not currently persistent offline changes to disk. Full offline support backed by disk will be coming in the future.[[1]](https://groups.google.com/d/msg/firebase-talk/lVFOh9Wqwog/FvqWiiuP-_MJ) 71 | 72 | 73 | License 74 | ------- 75 | [MIT](https://github.com/overcommitted/FireData/blob/master/LICENSE). 76 | 77 | 78 | [1] https://groups.google.com/d/msg/firebase-talk/lVFOh9Wqwog/FvqWiiuP-_MJ -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Firebase: -------------------------------------------------------------------------------- 1 | Versions/Current/Firebase -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Versions/A/Firebase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Manoj85/FireData/a9f5a30809de69eb857a1f63a6e9ea9a21fc6c4b/Vendor/Firebase/Firebase.framework/Versions/A/Firebase -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Versions/A/Headers/FDataSnapshot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Firebase iOS Client Library 3 | * 4 | * Copyright © 2013 Firebase - All Rights Reserved 5 | * https://www.firebase.com 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binaryform must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, 21 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | 31 | @class Firebase; 32 | 33 | /** 34 | * An FDataSnapshot contains data from a Firebase location. Any time you read 35 | * Firebase data, you receive the data as an FDataSnapshot. 36 | * 37 | * FDataSnapshots are passed to the blocks you attach with observeEventType:withBlock: or observeSingleEvent:withBlock:. 38 | * They are efficiently-generated immutable copies of the data at a Firebase location. 39 | * They can't be modified and will never change. To modify data at a location, 40 | * use a Firebase reference (e.g. with setValue:). 41 | */ 42 | @interface FDataSnapshot : NSObject 43 | 44 | 45 | /** @name Navigating and inspecting a snapshot */ 46 | 47 | /** 48 | * Get an FDataSnapshot for the location at the specified relative path. 49 | * The relative path can either be a simple child name (e.g. 'fred') 50 | * or a deeper slash-separated path (e.g. 'fred/name/first'). If the child 51 | * location has no data, an empty FDataSnapshot is returned. 52 | * 53 | * @param childPathString A relative path to the location of child data. 54 | * @return The FDataSnapshot for the child location. 55 | */ 56 | - (FDataSnapshot *) childSnapshotForPath:(NSString *)childPathString; 57 | 58 | 59 | /** 60 | * Return YES if the specified child exists. 61 | * 62 | * @param childPathString A relative path to the location of a potential child. 63 | * @return YES if data exists at the specified childPathString, else false. 64 | */ 65 | - (BOOL) hasChild:(NSString *)childPathString; 66 | 67 | 68 | /** 69 | * Return YES if the DataSnapshot has any children. 70 | * 71 | * @return YES if this snapshot has any children, else NO. 72 | */ 73 | - (BOOL) hasChildren; 74 | 75 | 76 | /** @name Data export */ 77 | 78 | /** 79 | * Returns the raw value at this location, coupled with any metadata, such as priority. 80 | * 81 | * Priorities, where they exist, are accessible under the ".priority" key in instances of NSDictionary. 82 | * For leaf locations with priorities, the value will be under the ".value" key. 83 | */ 84 | - (id) valueInExportFormat; 85 | 86 | 87 | /** @name Properties */ 88 | 89 | /** 90 | * Returns the contents of this data snapshot as native types. 91 | * 92 | * Data types returned: 93 | * * NSDictionary 94 | * * NSArray 95 | * * NSNumber (also includes booleans) 96 | * * NSString 97 | * 98 | * @return The data as a native object. 99 | */ 100 | @property (strong, readonly, nonatomic) id value; 101 | 102 | 103 | /** 104 | * Get the number of children for this DataSnapshot. 105 | * 106 | * @return An integer indicating the number of children. 107 | */ 108 | @property (readonly, nonatomic) NSUInteger childrenCount; 109 | 110 | 111 | /** 112 | * Get a Firebase reference for the location that this data came from 113 | * 114 | * @return A Firebase instance for the location of this data 115 | */ 116 | @property (nonatomic, readonly, strong) Firebase* ref; 117 | 118 | 119 | /** 120 | * The name of the location that generated this FDataSnapshot. 121 | * 122 | * @return An NSString containing the name for the location of this FDataSnapshot. 123 | */ 124 | @property (strong, readonly, nonatomic) NSString* name; 125 | 126 | 127 | /** 128 | * An iterator for snapshots of the child nodes in this snapshot. 129 | * You can use the native for..in syntax: 130 | * 131 | * for (FDataSnapshot* child in snapshot.children) { 132 | * ... 133 | * } 134 | * 135 | * @return An NSEnumerator of the children 136 | */ 137 | @property (strong, readonly, nonatomic) NSEnumerator* children; 138 | 139 | /** 140 | * The priority of the data in this FDataSnapshot. 141 | * 142 | * @return The priority as a string, or nil if no priority was set. 143 | */ 144 | @property (strong, readonly, nonatomic) id priority; 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Versions/A/Headers/FEventType.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Firebase iOS Client Library 3 | * 4 | * Copyright © 2013 Firebase - All Rights Reserved 5 | * https://www.firebase.com 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binaryform must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, 21 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef Firebase_FEventType_h 30 | #define Firebase_FEventType_h 31 | 32 | /** 33 | * This enum is the set of events that you can observe at a Firebase location. 34 | */ 35 | typedef enum { 36 | FEventTypeChildAdded, // 0, fired when a new child node is added to a location 37 | FEventTypeChildRemoved, // 1, fired when a child node is removed from a location 38 | FEventTypeChildChanged, // 2, fired when a child node at a location changes 39 | FEventTypeChildMoved, // 3, fired when a child node moves relative to the other child nodes at a location 40 | FEventTypeValue // 4, fired when any data changes at a location 41 | } FEventType; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Versions/A/Headers/FMutableData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Firebase iOS Client Library 3 | * 4 | * Copyright © 2013 Firebase - All Rights Reserved 5 | * https://www.firebase.com 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binaryform must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, 21 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | 31 | /** 32 | * An FMutableData instance is populated with data from a Firebase location. 33 | * When you are using runTransactionBlock:, you will be given an instance containing the current 34 | * data at that location. Your block will be responsible for updating that instance to the data 35 | * you wish to save at that location, and then returning [FTransactionResult successWithData:yourData]. 36 | * 37 | * To modify the data, set its value property to any of the native types support by Firebase: 38 | * * NSNumber (includes BOOL) 39 | * * NSDictionary 40 | * * NSArray 41 | * * NSString 42 | * * nil / [NSNull null] to remove the data 43 | * 44 | * Note that changes made to a child FMutableData instance will be visible to the parent. 45 | */ 46 | @interface FMutableData : NSObject 47 | 48 | 49 | /** @name Inspecting and navigating the data */ 50 | 51 | 52 | /** 53 | * @return YES if this data contains child nodes. 54 | */ 55 | - (BOOL) hasChildren; 56 | 57 | 58 | /** 59 | * @return YES if this data contains a child at the specified relative path 60 | */ 61 | - (BOOL) hasChildAtPath:(NSString *)path; 62 | 63 | 64 | /** 65 | * Used to obtain an FMutableData instance that encapsulates the data at the given relative path. 66 | * Note that changes made to the child will be visible to the parent. 67 | * 68 | * @param path A path string, consisting either of a single segment, like 'child', or multiple segments, 'a/deeper/child' 69 | * @return An FMutableData instance containing the data at the given path 70 | */ 71 | - (FMutableData *) childDataByAppendingPath:(NSString *)path; 72 | 73 | 74 | /** @name Properties */ 75 | 76 | 77 | /** 78 | * @return An FMutableData instance containing the data at the parent location, or nil if this is the top-most location 79 | */ 80 | @property (strong, readonly, nonatomic) FMutableData* parent; 81 | 82 | 83 | /** 84 | * To modify the data contained by this instance of FMutableData, set this to any of the native types support by Firebase: 85 | * * NSNumber (includes BOOL) 86 | * * NSDictionary 87 | * * NSArray 88 | * * NSString 89 | * * nil / [NSNull null] to remove the data 90 | * 91 | * Note that setting the value will override the priority at this location. 92 | * 93 | * @return The current data at this location as a native object 94 | */ 95 | @property (strong, nonatomic) id value; 96 | 97 | 98 | /** 99 | * Set this property to update the priority of the data at this location. Can be set to the following types: 100 | * * NSNumber 101 | * * NSString 102 | * * nil / [NSNull null] to remove the priority 103 | * 104 | * @return The priority of the data at this location 105 | */ 106 | @property (strong, nonatomic) id priority; 107 | 108 | 109 | /** 110 | * @return The number of child nodes at this location 111 | */ 112 | @property (readonly, nonatomic) NSUInteger childrenCount; 113 | 114 | 115 | /** 116 | * Used to iterate over the children at this location. You can use the native for .. in syntax: 117 | * 118 | * for (FMutableData* child in data.children) { 119 | * ... 120 | * } 121 | * 122 | * Note that this enumerator operates on an immutable copy of the child list. So, you can modify the instance 123 | * during iteration, but the new additions will not be visible until you get a new enumerator. 124 | */ 125 | @property (readonly, nonatomic, strong) NSEnumerator* children; 126 | 127 | 128 | /** 129 | * @return The name of this node, or nil if it is the top-most location 130 | */ 131 | @property (readonly, nonatomic, strong) NSString* name; 132 | 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Versions/A/Headers/FQuery.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Firebase iOS Client Library 3 | * 4 | * Copyright © 2013 Firebase - All Rights Reserved 5 | * https://www.firebase.com 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binaryform must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, 21 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "FEventType.h" 31 | #import "FDataSnapshot.h" 32 | 33 | typedef NSUInteger FirebaseHandle; 34 | 35 | /** 36 | * An FQuery instance represents a query over the data at a particular location. 37 | * 38 | * You create one by calling one of the query methods (queryStartingAtPriority:, queryEndingAtPriority:, etc.) 39 | * on a Firebase reference. The query methods can be chained to further specify the data you are interested in 40 | * observing 41 | */ 42 | @interface FQuery : NSObject 43 | 44 | 45 | /** @name Attaching observers to read data */ 46 | 47 | 48 | /** 49 | * observeEventType:withBlock: is used to listen for data changes at a particular location. 50 | * This is the primary way to read data from Firebase. Your block will be triggered 51 | * for the initial data and again whenever the data changes. 52 | * 53 | * Use removeObserverWithHandle: to stop receiving updates. 54 | * 55 | * @param eventType The type of event to listen for. 56 | * @param block The block that should be called with initial data and updates. 57 | * @return A handle used to unregister this block later using removeObserverWithHandle: 58 | */ 59 | - (FirebaseHandle) observeEventType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block; 60 | 61 | 62 | /** 63 | * observeEventType:andPreviousSiblingWithBlock: is used to listen for data changes at a particular location. 64 | * This is the primary way to read data from Firebase. Your block will be triggered 65 | * for the initial data and again whenever the data changes. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and 66 | * FEventTypeChildChanged events, your block will be passed the name of the previous node by priority order. 67 | * 68 | * Use removeObserverWithHandle: to stop receiving updates. 69 | * 70 | * @param eventType The type of event to listen for. 71 | * @param block The block that should be called with initial data and updates, as well as the previous child's name. 72 | * @return A handle used to unregister this block later using removeObserverWithHandle: 73 | */ 74 | - (FirebaseHandle) observeEventType:(FEventType)eventType andPreviousSiblingNameWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevName))block; 75 | 76 | 77 | /** 78 | * observeEventType:withBlock: is used to listen for data changes at a particular location. 79 | * This is the primary way to read data from Firebase. Your block will be triggered 80 | * for the initial data and again whenever the data changes. 81 | * 82 | * The cancelBlock will be called if you will no longer receive new events due to no longer having permission. 83 | * 84 | * Use removeObserverWithHandle: to stop receiving updates. 85 | * 86 | * @param eventType The type of event to listen for. 87 | * @param block The block that should be called with initial data and updates. 88 | * @param cancelBlock The block that should be called if this client no longer has permission to receive these events 89 | * @return A handle used to unregister this block later using removeObserverWithHandle: 90 | */ 91 | - (FirebaseHandle) observeEventType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block withCancelBlock:(void (^)(void))cancelBlock; 92 | 93 | 94 | /** 95 | * observeEventType:andPreviousSiblingWithBlock: is used to listen for data changes at a particular location. 96 | * This is the primary way to read data from Firebase. Your block will be triggered 97 | * for the initial data and again whenever the data changes. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and 98 | * FEventTypeChildChanged events, your block will be passed the name of the previous node by priority order. 99 | * 100 | * The cancelBlock will be called if you will no longer receive new events due to no longer having permission. 101 | * 102 | * Use removeObserverWithHandle: to stop receiving updates. 103 | * 104 | * @param eventType The type of event to listen for. 105 | * @param block The block that should be called with initial data and updates, as well as the previous child's name. 106 | * @param cancelBlock The block that should be called if this client no longer has permission to receive these events 107 | * @return A handle used to unregister this block later using removeObserverWithHandle: 108 | */ 109 | - (FirebaseHandle) observeEventType:(FEventType)eventType andPreviousSiblingNameWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevName))block withCancelBlock:(void (^)(void))cancelBlock; 110 | 111 | 112 | /** 113 | * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. 114 | * 115 | * @param eventType The type of event to listen for. 116 | * @param block The block that should be called with initial data and updates. 117 | */ 118 | - (void) observeSingleEventOfType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block; 119 | 120 | 121 | /** 122 | * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and 123 | * FEventTypeChildChanged events, your block will be passed the name of the previous node by priority order. 124 | * 125 | * @param eventType The type of event to listen for. 126 | * @param block The block that should be called with initial data and updates. 127 | */ 128 | - (void) observeSingleEventOfType:(FEventType)eventType andPreviousSiblingNameWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevName))block; 129 | 130 | 131 | /** 132 | * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. 133 | * 134 | * The cancelBlock will be called if you do not have permission to read data at this location. 135 | * 136 | * @param eventType The type of event to listen for. 137 | * @param block The block that should be called with initial data and updates. 138 | * @param cancelBlock The block that will be called if you don't have permission to access this data 139 | */ 140 | - (void) observeSingleEventOfType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block withCancelBlock:(void (^)(void))cancelBlock; 141 | 142 | 143 | /** 144 | * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and 145 | * FEventTypeChildChanged events, your block will be passed the name of the previous node by priority order. 146 | * 147 | * The cancelBlock will be called if you do not have permission to read data at this location. 148 | * 149 | * @param eventType The type of event to listen for. 150 | * @param block The block that should be called with initial data and updates. 151 | * @param cancelBlock The block that will be called if you don't have permission to access this data 152 | */ 153 | - (void) observeSingleEventOfType:(FEventType)eventType andPreviousSiblingNameWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevName))block withCancelBlock:(void (^)(void))cancelBlock; 154 | 155 | /** @name Detaching observers */ 156 | 157 | /** 158 | * Detach a block previously attached with observeEventType:withBlock:. 159 | * 160 | * @param handle The handle returned by the call to observeEventType:withBlock: which we are trying to remove. 161 | */ 162 | - (void) removeObserverWithHandle:(FirebaseHandle)handle; 163 | 164 | 165 | /** 166 | * Detach all blocks previously attached to this Firebase location with observeEventType:withBlock: 167 | */ 168 | - (void) removeAllObservers; 169 | 170 | 171 | /** @name Querying and limiting */ 172 | 173 | 174 | /** 175 | * queryStartingAtPriority: is used to generate a reference to a limited view of the data at this location. 176 | * The FQuery instance returned by queryStartingAtPriority: will respond to events at nodes with a priority 177 | * greater than or equal to startPriority 178 | * 179 | * @param startPriority The lower bound, inclusive, for the priority of data visible to the returned FQuery 180 | * @return An FQuery instance, limited to data with priority greater than or equal to startPriority 181 | */ 182 | - (FQuery *) queryStartingAtPriority:(id)startPriority; 183 | 184 | 185 | /** 186 | * queryStartingAtPriority:andChildName: is used to generate a reference to a limited view of the data at this location. 187 | * The FQuery instance returned by queryStartingAtPriority:andChildName will respond to events at nodes with a priority 188 | * greater than startPriority, or equal to startPriority and with a name greater than or equal to childName 189 | * 190 | * @param startPriority The lower bound, inclusive, for the priority of data visible to the returned FQuery 191 | * @param childName The lower bound, inclusive, for the name of nodes with priority equal to startPriority 192 | * @return An FQuery instance, limited to data with priority greater than or equal to startPriority 193 | */ 194 | - (FQuery *) queryStartingAtPriority:(id)startPriority andChildName:(NSString *)childName; 195 | 196 | 197 | /** 198 | * queryEndingAtPriority: is used to generate a reference to a limited view of the data at this location. 199 | * The FQuery instance returned by queryEndingAtPriority: will respond to events at nodes with a priority 200 | * less than or equal to startPriority and with a name greater than or equal to childName 201 | * 202 | * @param endPriority The upper bound, inclusive, for the priority of data visible to the returned FQuery 203 | * @return An FQuery instance, limited to data with priority less than or equal to endPriority 204 | */ 205 | - (FQuery *) queryEndingAtPriority:(id)endPriority; 206 | 207 | 208 | /** 209 | * queryEndingAtPriority:andChildName: is used to generate a reference to a limited view of the data at this location. 210 | * The FQuery instance returned by queryEndingAtPriority:andChildNAme will respond to events at nodes with a priority 211 | * less than endPriority, or equal to endPriority and with a name less than or equal to childName 212 | * 213 | * @param endPriority The upper bound, inclusive, for the priority of data visible to the returned FQuery 214 | * @param childName The upper bound, inclusive, for the name of nodes with priority equal to endPriority 215 | * @return An FQuery instance, limited to data with priority less than endPriority or equal to endPriority and with a name less than or equal to childName 216 | */ 217 | - (FQuery *) queryEndingAtPriority:(id)endPriority andChildName:(NSString *)childName; 218 | 219 | 220 | /** 221 | * queryLimitedToNumberOfChildren: is used to generate a reference to a limited view of the data at this location. 222 | * The FQuery instance returned by queryLimitedToNumberOfChildren: will respond to events at from at most limit child nodes 223 | * 224 | * @param limit The upper bound, inclusive, for the number of child nodes to receive events for 225 | * @return An FQuery instance, limited to at most limit child nodes. 226 | */ 227 | - (FQuery *) queryLimitedToNumberOfChildren:(NSUInteger)limit; 228 | 229 | @end 230 | -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Versions/A/Headers/FTransactionResult.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Firebase iOS Client Library 3 | * 4 | * Copyright © 2013 Firebase - All Rights Reserved 5 | * https://www.firebase.com 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binaryform must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, 21 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #import 30 | #import "FMutableData.h" 31 | 32 | @interface FTransactionResult : NSObject 33 | 34 | /** 35 | * Used for runTransactionBlock:. Indicates that the new value should be saved at this location 36 | * 37 | * @param value An FMutableData instance containing the new value to be set 38 | * @return An FTransactionResult instance that can be used as a return value from the block given to runTransactionBlock: 39 | */ 40 | + (FTransactionResult *) successWithValue:(FMutableData *)value; 41 | 42 | 43 | /** 44 | * Used for runTransactionBlock:. Indicates that the current transaction should no longer proceed. 45 | * 46 | * @return An FTransactionResult instance that can be used as a return value from the block given to runTransactionBlock: 47 | */ 48 | + (FTransactionResult *) abort; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Versions/A/Headers/Firebase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Firebase iOS Client Library 3 | * 4 | * Copyright © 2013 Firebase - All Rights Reserved 5 | * https://www.firebase.com 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, this 11 | * list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binaryform must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, 21 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | 30 | #import 31 | #import "FQuery.h" 32 | #import "FDataSnapshot.h" 33 | #import "FMutableData.h" 34 | #import "FTransactionResult.h" 35 | 36 | /** 37 | * A Firebase reference represents a particular location in your Firebase 38 | * and can be used for reading or writing data to that Firebase location. 39 | * 40 | * This class is the starting point for all Firebase operations. After you've 41 | * initialized it with initWithUrl: you can use it 42 | * to read data (ie. observeEventType:withBlock:), write data (ie. setValue:), and to create new 43 | * Firebase references (ie. child:). 44 | */ 45 | @interface Firebase : FQuery 46 | 47 | 48 | /** @name Initializing a Firebase object */ 49 | 50 | /** 51 | * Initialize this Firebase reference with an absolute URL. 52 | * 53 | * @param url The Firebase URL (ie: https://SampleChat.firebaseIO-demo.com) 54 | */ 55 | - (id)initWithUrl:(NSString *)url; 56 | 57 | /** @name Getting references to children locations */ 58 | 59 | /** 60 | * Get a Firebase reference for the location at the specified relative path. 61 | * The relative path can either be a simple child name (e.g. 'fred') or a 62 | * deeper slash-separated path (e.g. 'fred/name/first'). 63 | * 64 | * @param pathString A relative path from this location to the desired child location. 65 | * @return A Firebase reference for the specified relative path. 66 | */ 67 | - (Firebase *) childByAppendingPath:(NSString *)pathString; 68 | 69 | 70 | /** 71 | * childByAutoId generates a new child location using a unique name and returns a 72 | * Firebase reference to it. This is useful when the children of a Firebase 73 | * location represent a list of items. 74 | * 75 | * The unique name generated by childByAutoId: is prefixed with a client-generated 76 | * timestamp so that the resulting list will be chronologically-sorted. 77 | * 78 | * @return A Firebase reference for the generated location. 79 | */ 80 | - (Firebase *) childByAutoId; 81 | 82 | 83 | /** @name Writing data */ 84 | 85 | /** 86 | * Write data to this Firebase location. This will overwrite any data 87 | * at this location and all child locations. 88 | * 89 | * Data types that can be set are: 90 | * * NSString -- @"Hello World" 91 | * * NSNumber (also includes boolean) -- @YES, @43, @4.333 92 | * * NSDictionary -- @{@"key": @"value"} 93 | * * NSArray 94 | * 95 | * The effect of the write will be visible immediately and the corresponding 96 | * events will be triggered. Synchronization of the data to the Firebase 97 | * servers will also be started. 98 | * 99 | * Passing null for the new value is equivalent to calling remove:; 100 | * all data at this location or any child location will be deleted. 101 | * 102 | * Note that setValue: will remove any priority stored at this location, so if priority 103 | * is meant to be preserved, you should use setValue:andPriority: instead. 104 | * 105 | * @param value The value to be written. 106 | */ 107 | - (void) setValue:(id)value; 108 | 109 | 110 | /** 111 | * The same as setValue: with a block that gets triggered after the write operation has 112 | * been committed to the Firebase servers. 113 | * 114 | * @param value The value to be written. 115 | * @param block The block to be called after the write has been committed to the Firebase servers. 116 | */ 117 | - (void) setValue:(id)value withCompletionBlock:(void (^)(NSError* error))block; 118 | 119 | 120 | /** 121 | * The same as setValue: with an additional priority to be attached to the data being written. 122 | * Priorities are used to order items. 123 | * 124 | * @param value The value to be written. 125 | * @param priority The priority to be attached to that data. 126 | */ 127 | - (void) setValue:(id)value andPriority:(id)priority; 128 | 129 | 130 | /** 131 | * The same as setValue:andPriority: with a block that gets triggered after the write operation has 132 | * been committed to the Firebase servers. 133 | * 134 | * @param value The value to be written. 135 | * @param priority The priority to be attached to that data. 136 | * @param block The block to be called after the write has been committed to the Firebase servers. 137 | */ 138 | - (void) setValue:(id)value andPriority:(id)priority withCompletionBlock:(void (^)(NSError* error))block; 139 | 140 | 141 | /** 142 | * Remove the data at this Firebase location. Any data at child locations will also be deleted. 143 | * 144 | * The effect of the delete will be visible immediately and the corresponding events 145 | * will be triggered. Synchronization of the delete to the Firebase servers will 146 | * also be started. 147 | * 148 | * remove: is equivalent to calling setValue:nil 149 | */ 150 | - (void) removeValue; 151 | 152 | 153 | /** 154 | * The same as remove: with a block that gets triggered after the remove operation has 155 | * been committed to the Firebase servers. 156 | * 157 | * @param block The block to be called after the remove has been committed to the Firebase servers. 158 | */ 159 | - (void) removeValueWithCompletionBlock:(void (^)(NSError* error))block; 160 | 161 | /** 162 | * Set a priority for the data at this Firebase location. 163 | * Priorities can be used to provide a custom ordering for the children at a location 164 | * (if no priorities are specified, the children are ordered by name). 165 | * 166 | * You cannot set a priority on an empty location. For this reason 167 | * setValue:andPriority: should be used when setting initial data with a specific priority 168 | * and setPriority: should be used when updating the priority of existing data. 169 | * 170 | * Children are sorted based on this priority using the following rules: 171 | * 172 | * Children with no priority (a null priority) come first. They are ordered lexicographically by name. 173 | * Children with a priority that is parsable as a number come next. They are 174 | * sorted numerically by priority first (small to large) and lexicographically by name second (A to z). 175 | * Children with non-numeric priorities come last. They are sorted lexicographically 176 | * by priority first and lexicographically by name second. 177 | * Setting the priority to null removes any existing priority. 178 | * Note that priorities are parsed and ordered as IEEE 754 double-precision floating-point numbers. 179 | * 180 | * @param priority The priority to set at the specified location. 181 | */ 182 | - (void) setPriority:(id)priority; 183 | 184 | 185 | /** 186 | * The same as setPriority: with a block block that is called once the priority has 187 | * been committed to the Firebase servers. 188 | * 189 | * @param priority The priority to set at the specified location. 190 | * @param block The block that is triggered after the priority has been written on the servers. 191 | */ 192 | - (void) setPriority:(id)priority withCompletionBlock:(void (^)(NSError* error))block; 193 | 194 | /** 195 | * Update changes the values of the keys specified in the dictionary without overwriting other 196 | * keys at this location. 197 | * 198 | * @param value A dictionary of the keys to change and their new values 199 | */ 200 | - (void) updateChildValues:(NSDictionary *)values; 201 | 202 | /** 203 | * The same as update: with a block block that is called once the update has been committed to the 204 | * Firebase servers 205 | * 206 | * @param value A dictionary of the keys to change and their new values 207 | * @param block The block that is triggered after the update has been written on the Firebase servers 208 | */ 209 | - (void) updateChildValues:(NSDictionary *)values withCompletionBlock:(void (^)(NSError* error))block; 210 | 211 | 212 | /** @name Attaching observers to read data */ 213 | 214 | /** 215 | * observeEventType:withBlock: is used to listen for data changes at a particular location. 216 | * This is the primary way to read data from Firebase. Your block will be triggered 217 | * for the initial data and again whenever the data changes. 218 | * 219 | * Use removeObserverWithHandle: to stop receiving updates. 220 | * 221 | * @param eventType The type of event to listen for. 222 | * @param block The block that should be called with initial data and updates. 223 | * @return A handle used to unregister this block later using removeObserverWithHandle: 224 | */ 225 | - (FirebaseHandle) observeEventType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block; 226 | 227 | 228 | /** 229 | * observeEventType:andPreviousSiblingWithBlock: is used to listen for data changes at a particular location. 230 | * This is the primary way to read data from Firebase. Your block will be triggered 231 | * for the initial data and again whenever the data changes. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and 232 | * FEventTypeChildChanged events, your block will be passed the name of the previous node by priority order. 233 | * 234 | * Use removeObserverWithHandle: to stop receiving updates. 235 | * 236 | * @param eventType The type of event to listen for. 237 | * @param block The block that should be called with initial data and updates, as well as the previous child's name. 238 | * @return A handle used to unregister this block later using removeObserverWithHandle: 239 | */ 240 | - (FirebaseHandle) observeEventType:(FEventType)eventType andPreviousSiblingNameWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevName))block; 241 | 242 | 243 | /** 244 | * observeEventType:withBlock: is used to listen for data changes at a particular location. 245 | * This is the primary way to read data from Firebase. Your block will be triggered 246 | * for the initial data and again whenever the data changes. 247 | * 248 | * The cancelBlock will be called if you will no longer receive new events due to no longer having permission. 249 | * 250 | * Use removeObserverWithHandle: to stop receiving updates. 251 | * 252 | * @param eventType The type of event to listen for. 253 | * @param block The block that should be called with initial data and updates. 254 | * @param cancelBlock The block that should be called if this client no longer has permission to receive these events 255 | * @return A handle used to unregister this block later using removeObserverWithHandle: 256 | */ 257 | - (FirebaseHandle) observeEventType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block withCancelBlock:(void (^)(void))cancelBlock; 258 | 259 | 260 | /** 261 | * observeEventType:andPreviousSiblingWithBlock: is used to listen for data changes at a particular location. 262 | * This is the primary way to read data from Firebase. Your block will be triggered 263 | * for the initial data and again whenever the data changes. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and 264 | * FEventTypeChildChanged events, your block will be passed the name of the previous node by priority order. 265 | * 266 | * The cancelBlock will be called if you will no longer receive new events due to no longer having permission. 267 | * 268 | * Use removeObserverWithHandle: to stop receiving updates. 269 | * 270 | * @param eventType The type of event to listen for. 271 | * @param block The block that should be called with initial data and updates, as well as the previous child's name. 272 | * @param cancelBlock The block that should be called if this client no longer has permission to receive these events 273 | * @return A handle used to unregister this block later using removeObserverWithHandle: 274 | */ 275 | - (FirebaseHandle) observeEventType:(FEventType)eventType andPreviousSiblingNameWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevName))block withCancelBlock:(void (^)(void))cancelBlock; 276 | 277 | 278 | /** 279 | * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. 280 | * 281 | * @param eventType The type of event to listen for. 282 | * @param block The block that should be called with initial data and updates. 283 | */ 284 | - (void) observeSingleEventOfType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block; 285 | 286 | 287 | /** 288 | * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and 289 | * FEventTypeChildChanged events, your block will be passed the name of the previous node by priority order. 290 | * 291 | * @param eventType The type of event to listen for. 292 | * @param block The block that should be called with initial data and updates. 293 | */ 294 | - (void) observeSingleEventOfType:(FEventType)eventType andPreviousSiblingNameWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevName))block; 295 | 296 | 297 | /** 298 | * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. 299 | * 300 | * The cancelBlock will be called if you do not have permission to read data at this location. 301 | * 302 | * @param eventType The type of event to listen for. 303 | * @param block The block that should be called with initial data and updates. 304 | * @param cancelBlock The block that will be called if you don't have permission to access this data 305 | */ 306 | - (void) observeSingleEventOfType:(FEventType)eventType withBlock:(void (^)(FDataSnapshot* snapshot))block withCancelBlock:(void (^)(void))cancelBlock; 307 | 308 | 309 | /** 310 | * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. In addition, for FEventTypeChildAdded, FEventTypeChildMoved, and 311 | * FEventTypeChildChanged events, your block will be passed the name of the previous node by priority order. 312 | * 313 | * The cancelBlock will be called if you do not have permission to read data at this location. 314 | * 315 | * @param eventType The type of event to listen for. 316 | * @param block The block that should be called with initial data and updates. 317 | * @param cancelBlock The block that will be called if you don't have permission to access this data 318 | */ 319 | - (void) observeSingleEventOfType:(FEventType)eventType andPreviousSiblingNameWithBlock:(void (^)(FDataSnapshot* snapshot, NSString* prevName))block withCancelBlock:(void (^)(void))cancelBlock; 320 | 321 | /** @name Detaching observers */ 322 | 323 | /** 324 | * Detach a block previously attached with observeEventType:withBlock:. 325 | * 326 | * @param handle The handle returned by the call to observeEventType:withBlock: which we are trying to remove. 327 | */ 328 | - (void) removeObserverWithHandle:(FirebaseHandle)handle; 329 | 330 | 331 | /** 332 | * Detach all blocks previously attached to this Firebase location with observeEventType:withBlock: 333 | */ 334 | - (void) removeAllObservers; 335 | 336 | /** @name Querying and limiting */ 337 | 338 | /** 339 | * queryStartingAtPriority: is used to generate a reference to a limited view of the data at this location. 340 | * The FQuery instance returned by queryStartingAtPriority: will respond to events at nodes with a priority 341 | * greater than or equal to startPriority 342 | * 343 | * @param startPriority The lower bound, inclusive, for the priority of data visible to the returned FQuery 344 | * @return An FQuery instance, limited to data with priority greater than or equal to startPriority 345 | */ 346 | - (FQuery *) queryStartingAtPriority:(id)startPriority; 347 | 348 | 349 | /** 350 | * queryStartingAtPriority:andChildName: is used to generate a reference to a limited view of the data at this location. 351 | * The FQuery instance returned by queryStartingAtPriority:andChildName will respond to events at nodes with a priority 352 | * greater than startPriority, or equal to startPriority and with a name greater than or equal to childName 353 | * 354 | * @param startPriority The lower bound, inclusive, for the priority of data visible to the returned FQuery 355 | * @param childName The lower bound, inclusive, for the name of nodes with priority equal to startPriority 356 | * @return An FQuery instance, limited to data with priority greater than or equal to startPriority 357 | */ 358 | - (FQuery *) queryStartingAtPriority:(id)startPriority andChildName:(NSString *)childName; 359 | 360 | 361 | /** 362 | * queryEndingAtPriority: is used to generate a reference to a limited view of the data at this location. 363 | * The FQuery instance returned by queryEndingAtPriority: will respond to events at nodes with a priority 364 | * less than or equal to startPriority and with a name greater than or equal to childName 365 | * 366 | * @param endPriority The upper bound, inclusive, for the priority of data visible to the returned FQuery 367 | * @return An FQuery instance, limited to data with priority less than or equal to endPriority 368 | */ 369 | - (FQuery *) queryEndingAtPriority:(id)endPriority; 370 | 371 | 372 | /** 373 | * queryEndingAtPriority:andChildName: is used to generate a reference to a limited view of the data at this location. 374 | * The FQuery instance returned by queryEndingAtPriority:andChildNAme will respond to events at nodes with a priority 375 | * less than endPriority, or equal to endPriority and with a name less than or equal to childName 376 | * 377 | * @param endPriority The upper bound, inclusive, for the priority of data visible to the returned FQuery 378 | * @param childName The upper bound, inclusive, for the name of nodes with priority equal to endPriority 379 | * @return An FQuery instance, limited to data with priority less than endPriority or equal to endPriority and with a name less than or equal to childName 380 | */ 381 | - (FQuery *) queryEndingAtPriority:(id)endPriority andChildName:(NSString *)childName; 382 | 383 | 384 | 385 | /** 386 | * queryLimitedToNumberOfChildren: is used to generate a reference to a limited view of the data at this location. 387 | * The FQuery instance returned by queryLimitedToNumberOfChildren: will respond to events at from at most limit child nodes 388 | * 389 | * @param limit The upper bound, inclusive, for the number of child nodes to receive events for 390 | * @return An FQuery instance, limited to at most limit child nodes. 391 | */ 392 | - (FQuery *) queryLimitedToNumberOfChildren:(NSUInteger)limit; 393 | 394 | /** @name Managing presence */ 395 | 396 | /** 397 | * Ensure the data at this location is set to the specified value when 398 | * the client is disconnected (due to closing the browser, navigating 399 | * to a new page, or network issues). 400 | * 401 | * onDisconnectSetValue: is especially useful for implementing "presence" systems, 402 | * where a value should be changed or cleared when a user disconnects 403 | * so that he appears "offline" to other users. 404 | * 405 | * @param value The value to be set after the connection is lost. 406 | */ 407 | - (void) onDisconnectSetValue:(id)value; 408 | 409 | 410 | /** 411 | * Ensure the data at this location is set to the specified value when 412 | * the client is disconnected (due to closing the browser, navigating 413 | * to a new page, or network issues). 414 | * 415 | * The completion block will be triggered when the operation has been successfully queued up on the Firebase servers 416 | * 417 | * @param value The value to be set after the connection is lost. 418 | * @param block Block to be triggered when the operation has been queued up on the Firebase servers 419 | */ 420 | - (void) onDisconnectSetValue:(id)value withCompletionBlock:(void (^)(NSError* error))block; 421 | 422 | 423 | /** 424 | * Ensure the data at this location is set to the specified value and priority when 425 | * the client is disconnected (due to closing the browser, navigating 426 | * to a new page, or network issues). 427 | * 428 | * @param value The value to be set after the connection is lost. 429 | * @param priority The priority to be set after the connection is lost. 430 | */ 431 | - (void) onDisconnectSetValue:(id)value andPriority:(id)priority; 432 | 433 | 434 | /** 435 | * Ensure the data at this location is set to the specified value and priority when 436 | * the client is disconnected (due to closing the browser, navigating 437 | * to a new page, or network issues). 438 | * 439 | * The completion block will be triggered when the operation has been successfully queued up on the Firebase servers 440 | * 441 | * @param value The value to be set after the connection is lost. 442 | * @param priority The priority to be set after the connection is lost. 443 | * @param block Block to be triggered when the operation has been queued up on the Firebase servers 444 | */ 445 | - (void) onDisconnectSetValue:(id)value andPriority:(id)priority withCompletionBlock:(void (^)(NSError* error))block; 446 | 447 | 448 | /** 449 | * Ensure the data at this location is removed when 450 | * the client is disconnected (due to closing the app, navigating 451 | * to a new page, or network issues). 452 | * 453 | * onDisconnectRemoveValue is especially useful for implementing "presence" systems. 454 | */ 455 | - (void) onDisconnectRemoveValue; 456 | 457 | 458 | /** 459 | * Ensure the data at this location is removed when 460 | * the client is disconnected (due to closing the app, navigating 461 | * to a new page, or network issues). 462 | * 463 | * onDisconnectRemoveValueWithCompletionBlock: is especially useful for implementing "presence" systems. 464 | * 465 | * @param block Block to be triggered when the operation has been queued up on the Firebase servers 466 | */ 467 | - (void) onDisconnectRemoveValueWithCompletionBlock:(void (^)(NSError* error))block; 468 | 469 | 470 | 471 | /** 472 | * Ensure the data has the specified child values updated when 473 | * the client is disconnected (due to closing the browser, navigating 474 | * to a new page, or network issues). 475 | * 476 | * 477 | * @param values A dictionary of child node names and the values to set them to after the connection is lost. 478 | */ 479 | - (void) onDisconnectUpdateChildValues:(NSDictionary *)values; 480 | 481 | 482 | /** 483 | * Ensure the data has the specified child values updated when 484 | * the client is disconnected (due to closing the browser, navigating 485 | * to a new page, or network issues). 486 | * 487 | * 488 | * @param values A dictionary of child node names and the values to set them to after the connection is lost. 489 | * @param block A block that will be called once the operation has been queued up on the Firebase servers 490 | */ 491 | - (void) onDisconnectUpdateChildValues:(NSDictionary *)values withCompletionBlock:(void (^)(NSError* error))block; 492 | 493 | 494 | /** 495 | * Cancel any operations that are set to run on disconnect. If you previously called onDisconnectSetValue:, 496 | * onDisconnectRemoveValue:, or onDisconnectUpdateChildValues:, and no longer want the values updated when the 497 | * connection is lost, call cancelDisconnectOperations: 498 | */ 499 | - (void) cancelDisconnectOperations; 500 | 501 | 502 | /** 503 | * Cancel any operations that are set to run on disconnect. If you previously called onDisconnectSetValue:, 504 | * onDisconnectRemoveValue:, or onDisconnectUpdateChildValues:, and no longer want the values updated when the 505 | * connection is lost, call cancelDisconnectOperations: 506 | * 507 | * @param block A block that will be triggered once the Firebase servers have acknowledged the cancel request. 508 | */ 509 | - (void) cancelDisconnectOperationsWithCompletionBlock:(void (^)(NSError* error))block; 510 | 511 | 512 | /** @name Authenticating */ 513 | 514 | /** 515 | * Authenticate access to this Firebase using the provided credentials. The completion block will be called with 516 | * the results of the authenticated attempt, and the cancelBlock will be called if the credentials become invalid 517 | * at some point after authentication has succeeded. 518 | * 519 | * @param block This block will be called with the results of the authentication attempt 520 | * @param cancelBlock This block will be called if at any time in the future the credentials become invalid 521 | */ 522 | - (void) authWithCredential:(NSString *)credential withCompletionBlock:(void (^) (NSError* error, id data))block withCancelBlock:(void (^)(NSError* error))cancelBlock; 523 | 524 | 525 | /** 526 | * Removes any credentials associated with this Firebase 527 | */ 528 | - (void) unauth; 529 | 530 | 531 | /** @name Transactions */ 532 | 533 | /** 534 | * Performs an optimistic-concurrency transactional update to the data at this location. Your block will be called with an FMutableData 535 | * instance that contains the current data at this location. Your block should update this data to the value you 536 | * wish to write to this location, and then return an instance of FTransactionResult with the new data. 537 | * 538 | * If, when the operation reaches the server, it turns out that this client had stale data, your block will be run 539 | * again with the latest data from the server. 540 | * 541 | * When your block is run, you may decide to abort the transaction by return [FTransactionResult abort]. 542 | * 543 | * @param block This block receives the current data at this location and must return an instance of FTransactionResult 544 | */ 545 | - (void) runTransactionBlock:(FTransactionResult* (^) (FMutableData* currentData))block; 546 | 547 | 548 | /** 549 | * Performs an optimistic-concurrency transactional update to the data at this location. Your block will be called with an FMutableData 550 | * instance that contains the current data at this location. Your block should update this data to the value you 551 | * wish to write to this location, and then return an instance of FTransactionResult with the new data. 552 | * 553 | * If, when the operation reaches the server, it turns out that this client had stale data, your block will be run 554 | * again with the latest data from the server. 555 | * 556 | * When your block is run, you may decide to abort the transaction by return [FTransactionResult abort]. 557 | * 558 | * @param block This block receives the current data at this location and must return an instance of FTransactionResult 559 | * @param completionBlock This block will be triggered once the transaction is complete, whether it was successful or not. It will indicate if there was an error, whether or not the data was committed, and what the current value of the data at this location is. 560 | */ 561 | - (void) runTransactionBlock:(FTransactionResult* (^) (FMutableData* currentData))block andCompletionBlock:(void (^) (NSError* error, BOOL committed, FDataSnapshot* snapshot))completionBlock; 562 | 563 | 564 | 565 | /** 566 | * Performs an optimistic-concurrency transactional update to the data at this location. Your block will be called with an FMutableData 567 | * instance that contains the current data at this location. Your block should update this data to the value you 568 | * wish to write to this location, and then return an instance of FTransactionResult with the new data. 569 | * 570 | * If, when the operation reaches the server, it turns out that this client had stale data, your block will be run 571 | * again with the latest data from the server. 572 | * 573 | * When your block is run, you may decide to abort the transaction by return [FTransactionResult abort]. 574 | * 575 | * Since your block may be run multiple times, this client could see several immediate states that don't exist on the server. You can suppress those immediate states until the server confirms the final state of the transaction. 576 | * 577 | * @param block This block receives the current data at this location and must return an instance of FTransactionResult 578 | * @param completionBlock This block will be triggered once the transaction is complete, whether it was successful or not. It will indicate if there was an error, whether or not the data was committed, and what the current value of the data at this location is. 579 | * @param localEvents Set this to NO to suppress events raised for intermediate states, and only get events based on the final state of the transaction. 580 | */ 581 | - (void) runTransactionBlock:(FTransactionResult* (^) (FMutableData* currentData))block andCompletionBlock:(void (^) (NSError* error, BOOL committed, FDataSnapshot* snapshot))completionBlock withLocalEvents:(BOOL)localEvents; 582 | 583 | 584 | /** @name Retrieving String Representation */ 585 | 586 | /** 587 | * Gets the absolute URL of this Firebase location. 588 | * 589 | * @return The absolute URL of the referenced Firebase location. 590 | */ 591 | - (NSString *) description; 592 | 593 | /** @name Properties */ 594 | 595 | /** 596 | * Get a Firebase reference for the parent location. 597 | * If this instance refers to the root of your Firebase, it has no parent, 598 | * and therefore parent( ) will return null. 599 | * 600 | * @return A Firebase reference for the parent location. 601 | */ 602 | @property (strong, readonly, nonatomic) Firebase* parent; 603 | 604 | 605 | /** 606 | * Get a Firebase reference for the root location 607 | * 608 | * @return a new Firebase reference to root location. 609 | */ 610 | @property (strong, readonly, nonatomic) Firebase* root; 611 | 612 | 613 | /** 614 | * Gets last token in a Firebase location (e.g. 'fred' in https://SampleChat.firebaseIO-demo.com/users/fred) 615 | * 616 | * @return The name of the location this reference points to. 617 | */ 618 | @property (strong, readonly, nonatomic) NSString* name; 619 | 620 | /** @name Global configuration and settings */ 621 | 622 | /** Set the default dispatch queue for event blocks. 623 | */ 624 | + (void) setDispatchQueue:(dispatch_queue_t)queue; 625 | 626 | /** Retrieve the Firebase SDK version. */ 627 | + (NSString *) sdkVersion; 628 | 629 | @end 630 | -------------------------------------------------------------------------------- /Vendor/Firebase/Firebase.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/.hgtags: -------------------------------------------------------------------------------- 1 | e190de54c50791dcfdcaa32ce978a684a9fff236 0.4 2 | 0082342cb70688b993b311340df4e4fb9c99531d 0.5 3 | 37825820b1c1140188fc1542b2b543bf5f45be42 0.6 4 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/ISO8601DateFormatter.h: -------------------------------------------------------------------------------- 1 | /*ISO8601DateFormatter.h 2 | * 3 | *Created by Peter Hosey on 2009-04-11. 4 | *Copyright 2009 Peter Hosey. All rights reserved. 5 | */ 6 | 7 | #import 8 | 9 | /*This class converts dates to and from ISO 8601 strings. A good introduction to ISO 8601: 10 | * 11 | *Parsing can be done strictly, or not. When you parse loosely, leading whitespace is ignored, as is anything after the date. 12 | *The loose parser will return an NSDate for this string: @" \t\r\n\f\t 2006-03-02!!!" 13 | *Leading non-whitespace will not be ignored; the string will be rejected, and nil returned. See the README that came with this addition. 14 | * 15 | *The strict parser will only accept a string if the date is the entire string. The above string would be rejected immediately, solely on these grounds. 16 | *Also, the loose parser provides some extensions that the strict parser doesn't. 17 | *For example, the standard says for "-DDD" (an ordinal date in the implied year) that the logical representation (meaning, hierarchically) would be "--DDD", but because that extra hyphen is "superfluous", it was omitted. 18 | *The loose parser will accept the extra hyphen; the strict parser will not. 19 | *A full list of these extensions is in the README file. 20 | */ 21 | 22 | /*The format to either expect or produce. 23 | *Calendar format is YYYY-MM-DD. 24 | *Ordinal format is YYYY-DDD, where DDD ranges from 1 to 366; for example, 2009-32 is 2009-02-01. 25 | *Week format is YYYY-Www-D, where ww ranges from 1 to 53 (the 'W' is literal) and D ranges from 1 to 7; for example, 2009-W05-07. 26 | */ 27 | enum { 28 | ISO8601DateFormatCalendar, 29 | ISO8601DateFormatOrdinal, 30 | ISO8601DateFormatWeek, 31 | }; 32 | typedef NSUInteger ISO8601DateFormat; 33 | 34 | //The default separator for time values. Currently, this is ':'. 35 | extern unichar ISO8601DefaultTimeSeparatorCharacter; 36 | 37 | @interface ISO8601DateFormatter: NSFormatter 38 | { 39 | NSString *lastUsedFormatString; 40 | NSDateFormatter *unparsingFormatter; 41 | 42 | NSCalendar *parsingCalendar, *unparsingCalendar; 43 | 44 | NSTimeZone *defaultTimeZone; 45 | ISO8601DateFormat format; 46 | unichar timeSeparator; 47 | BOOL includeTime; 48 | BOOL parsesStrictly; 49 | } 50 | 51 | //Call this if you get a memory warning. 52 | + (void) purgeGlobalCaches; 53 | 54 | @property(nonatomic, retain) NSTimeZone *defaultTimeZone; 55 | 56 | #pragma mark Parsing 57 | 58 | //As a formatter, this object converts strings to dates. 59 | 60 | @property BOOL parsesStrictly; 61 | 62 | - (NSDateComponents *) dateComponentsFromString:(NSString *)string; 63 | - (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone; 64 | - (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange; 65 | 66 | - (NSDate *) dateFromString:(NSString *)string; 67 | - (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone; 68 | - (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange; 69 | 70 | #pragma mark Unparsing 71 | 72 | @property ISO8601DateFormat format; 73 | @property BOOL includeTime; 74 | @property unichar timeSeparator; 75 | 76 | - (NSString *) stringFromDate:(NSDate *)date; 77 | - (NSString *) stringFromDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone; 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/ISO8601DateFormatter.m: -------------------------------------------------------------------------------- 1 | /*ISO8601DateFormatter.m 2 | * 3 | *Created by Peter Hosey on 2009-04-11. 4 | *Copyright 2009 Peter Hosey. All rights reserved. 5 | */ 6 | 7 | #import 8 | #import "ISO8601DateFormatter.h" 9 | 10 | #ifndef DEFAULT_TIME_SEPARATOR 11 | # define DEFAULT_TIME_SEPARATOR ':' 12 | #endif 13 | unichar ISO8601DefaultTimeSeparatorCharacter = DEFAULT_TIME_SEPARATOR; 14 | 15 | //Unicode date formats. 16 | #define ISO_CALENDAR_DATE_FORMAT @"yyyy-MM-dd" 17 | //#define ISO_WEEK_DATE_FORMAT @"YYYY-'W'ww-ee" //Doesn't actually work because NSDateComponents counts the weekday starting at 1. 18 | #define ISO_ORDINAL_DATE_FORMAT @"yyyy-DDD" 19 | #define ISO_TIME_FORMAT @"HH:mm:ss" 20 | #define ISO_TIME_WITH_TIMEZONE_FORMAT ISO_TIME_FORMAT @"Z" 21 | //printf formats. 22 | #define ISO_TIMEZONE_UTC_FORMAT @"Z" 23 | #define ISO_TIMEZONE_OFFSET_FORMAT @"%+.2d%.2d" 24 | 25 | @interface ISO8601DateFormatter(UnparsingPrivate) 26 | 27 | - (NSString *) replaceColonsInString:(NSString *)timeFormat withTimeSeparator:(unichar)timeSep; 28 | 29 | - (NSString *) stringFromDate:(NSDate *)date formatString:(NSString *)dateFormat timeZone:(NSTimeZone *)timeZone; 30 | - (NSString *) weekDateStringForDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone; 31 | 32 | @end 33 | 34 | static NSMutableDictionary *timeZonesByOffset; 35 | 36 | @implementation ISO8601DateFormatter 37 | 38 | + (void) initialize { 39 | if (!timeZonesByOffset) { 40 | timeZonesByOffset = [[NSMutableDictionary alloc] init]; 41 | } 42 | } 43 | 44 | + (void) purgeGlobalCaches { 45 | NSMutableDictionary *oldCache = timeZonesByOffset; 46 | timeZonesByOffset = nil; 47 | [oldCache release]; 48 | } 49 | 50 | - (NSCalendar *) makeCalendarWithDesiredConfiguration { 51 | NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease]; 52 | calendar.firstWeekday = 2; //Monday 53 | calendar.timeZone = [NSTimeZone defaultTimeZone]; 54 | return calendar; 55 | } 56 | 57 | - (id) init { 58 | if ((self = [super init])) { 59 | parsingCalendar = [[self makeCalendarWithDesiredConfiguration] retain]; 60 | unparsingCalendar = [[self makeCalendarWithDesiredConfiguration] retain]; 61 | 62 | format = ISO8601DateFormatCalendar; 63 | timeSeparator = ISO8601DefaultTimeSeparatorCharacter; 64 | includeTime = NO; 65 | parsesStrictly = NO; 66 | } 67 | return self; 68 | } 69 | - (void) dealloc { 70 | [defaultTimeZone release]; 71 | 72 | [unparsingFormatter release]; 73 | [lastUsedFormatString release]; 74 | [parsingCalendar release]; 75 | [unparsingCalendar release]; 76 | 77 | [super dealloc]; 78 | } 79 | 80 | @synthesize defaultTimeZone; 81 | - (void) setDefaultTimeZone:(NSTimeZone *)tz { 82 | if (defaultTimeZone != tz) { 83 | [defaultTimeZone release]; 84 | defaultTimeZone = [tz retain]; 85 | 86 | unparsingCalendar.timeZone = defaultTimeZone; 87 | } 88 | } 89 | 90 | //The following properties are only here because GCC doesn't like @synthesize in category implementations. 91 | 92 | #pragma mark Parsing 93 | 94 | @synthesize parsesStrictly; 95 | 96 | static NSUInteger read_segment(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits); 97 | static NSUInteger read_segment_4digits(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits); 98 | static NSUInteger read_segment_2digits(const unsigned char *str, const unsigned char **next); 99 | static double read_double(const unsigned char *str, const unsigned char **next); 100 | static BOOL is_leap_year(NSUInteger year); 101 | 102 | /*Valid ISO 8601 date formats: 103 | * 104 | *YYYYMMDD 105 | *YYYY-MM-DD 106 | *YYYY-MM 107 | *YYYY 108 | *YY //century 109 | * //Implied century: YY is 00-99 110 | * YYMMDD 111 | * YY-MM-DD 112 | * -YYMM 113 | * -YY-MM 114 | * -YY 115 | * //Implied year 116 | * --MMDD 117 | * --MM-DD 118 | * --MM 119 | * //Implied year and month 120 | * ---DD 121 | * //Ordinal dates: DDD is the number of the day in the year (1-366) 122 | *YYYYDDD 123 | *YYYY-DDD 124 | * YYDDD 125 | * YY-DDD 126 | * -DDD 127 | * //Week-based dates: ww is the number of the week, and d is the number (1-7) of the day in the week 128 | *yyyyWwwd 129 | *yyyy-Www-d 130 | *yyyyWww 131 | *yyyy-Www 132 | *yyWwwd 133 | *yy-Www-d 134 | *yyWww 135 | *yy-Www 136 | * //Year of the implied decade 137 | *-yWwwd 138 | *-y-Www-d 139 | *-yWww 140 | *-y-Www 141 | * //Week and day of implied year 142 | * -Wwwd 143 | * -Www-d 144 | * //Week only of implied year 145 | * -Www 146 | * //Day only of implied week 147 | * -W-d 148 | */ 149 | 150 | - (NSDateComponents *) dateComponentsFromString:(NSString *)string { 151 | return [self dateComponentsFromString:string timeZone:NULL]; 152 | } 153 | - (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone { 154 | return [self dateComponentsFromString:string timeZone:outTimeZone range:NULL]; 155 | } 156 | - (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange { 157 | NSDate *now = [NSDate date]; 158 | 159 | NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease]; 160 | NSDateComponents *nowComponents = [parsingCalendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:now]; 161 | 162 | NSUInteger 163 | //Date 164 | year, 165 | month_or_week = 0U, 166 | day = 0U, 167 | //Time 168 | hour = 0U; 169 | NSTimeInterval 170 | minute = 0.0, 171 | second = 0.0; 172 | //Time zone 173 | NSInteger tz_hour = 0; 174 | NSInteger tz_minute = 0; 175 | 176 | enum { 177 | monthAndDate, 178 | week, 179 | dateOnly 180 | } dateSpecification = monthAndDate; 181 | 182 | BOOL strict = self.parsesStrictly; 183 | unichar timeSep = self.timeSeparator; 184 | 185 | if (strict) timeSep = ISO8601DefaultTimeSeparatorCharacter; 186 | NSAssert(timeSep != '\0', @"Time separator must not be NUL."); 187 | 188 | BOOL isValidDate = ([string length] > 0U); 189 | NSTimeZone *timeZone = nil; 190 | 191 | const unsigned char *ch = (const unsigned char *)[string UTF8String]; 192 | 193 | NSRange range = { 0U, 0U }; 194 | const unsigned char *start_of_date = NULL; 195 | if (strict && isspace(*ch)) { 196 | range.location = NSNotFound; 197 | isValidDate = NO; 198 | } else { 199 | //Skip leading whitespace. 200 | NSUInteger i = 0U; 201 | for(NSUInteger len = strlen((const char *)ch); i < len; ++i) { 202 | if (!isspace(ch[i])) 203 | break; 204 | } 205 | 206 | range.location = i; 207 | ch += i; 208 | start_of_date = ch; 209 | 210 | NSUInteger segment; 211 | NSUInteger num_leading_hyphens = 0U, num_digits = 0U; 212 | 213 | if (*ch == 'T') { 214 | //There is no date here, only a time. Set the date to now; then we'll parse the time. 215 | isValidDate = isdigit(*++ch); 216 | 217 | year = nowComponents.year; 218 | month_or_week = nowComponents.month; 219 | day = nowComponents.day; 220 | } else { 221 | while(*ch == '-') { 222 | ++num_leading_hyphens; 223 | ++ch; 224 | } 225 | 226 | segment = read_segment(ch, &ch, &num_digits); 227 | switch(num_digits) { 228 | case 0: 229 | if (*ch == 'W') { 230 | if ((ch[1] == '-') && isdigit(ch[2]) && ((num_leading_hyphens == 1U) || ((num_leading_hyphens == 2U) && !strict))) { 231 | year = nowComponents.year; 232 | month_or_week = 1U; 233 | ch += 2; 234 | goto parseDayAfterWeek; 235 | } else if (num_leading_hyphens == 1U) { 236 | year = nowComponents.year; 237 | goto parseWeekAndDay; 238 | } else 239 | isValidDate = NO; 240 | } else 241 | isValidDate = NO; 242 | break; 243 | 244 | case 8: //YYYY MM DD 245 | if (num_leading_hyphens > 0U) 246 | isValidDate = NO; 247 | else { 248 | day = segment % 100U; 249 | segment /= 100U; 250 | month_or_week = segment % 100U; 251 | year = segment / 100U; 252 | } 253 | break; 254 | 255 | case 6: //YYMMDD (implicit century) 256 | if (num_leading_hyphens > 0U) 257 | isValidDate = NO; 258 | else { 259 | day = segment % 100U; 260 | segment /= 100U; 261 | month_or_week = segment % 100U; 262 | year = nowComponents.year; 263 | year -= (year % 100U); 264 | year += segment / 100U; 265 | } 266 | break; 267 | 268 | case 4: 269 | switch(num_leading_hyphens) { 270 | case 0: //YYYY 271 | year = segment; 272 | 273 | if (*ch == '-') ++ch; 274 | 275 | if (!isdigit(*ch)) { 276 | if (*ch == 'W') 277 | goto parseWeekAndDay; 278 | else 279 | month_or_week = day = 1U; 280 | } else { 281 | segment = read_segment(ch, &ch, &num_digits); 282 | switch(num_digits) { 283 | case 4: //MMDD 284 | day = segment % 100U; 285 | month_or_week = segment / 100U; 286 | break; 287 | 288 | case 2: //MM 289 | month_or_week = segment; 290 | 291 | if (*ch == '-') ++ch; 292 | if (!isdigit(*ch)) 293 | day = 1U; 294 | else 295 | day = read_segment(ch, &ch, NULL); 296 | break; 297 | 298 | case 3: //DDD 299 | day = segment % 1000U; 300 | dateSpecification = dateOnly; 301 | if (strict && (day > (365U + is_leap_year(year)))) 302 | isValidDate = NO; 303 | break; 304 | 305 | default: 306 | isValidDate = NO; 307 | } 308 | } 309 | break; 310 | 311 | case 1: //YYMM 312 | month_or_week = segment % 100U; 313 | year = segment / 100U; 314 | 315 | if (*ch == '-') ++ch; 316 | if (!isdigit(*ch)) 317 | day = 1U; 318 | else 319 | day = read_segment(ch, &ch, NULL); 320 | 321 | break; 322 | 323 | case 2: //MMDD 324 | day = segment % 100U; 325 | month_or_week = segment / 100U; 326 | year = nowComponents.year; 327 | 328 | break; 329 | 330 | default: 331 | isValidDate = NO; 332 | } //switch(num_leading_hyphens) (4 digits) 333 | break; 334 | 335 | case 1: 336 | if (strict) { 337 | //Two digits only - never just one. 338 | if (num_leading_hyphens == 1U) { 339 | if (*ch == '-') ++ch; 340 | if (*++ch == 'W') { 341 | year = nowComponents.year; 342 | year -= (year % 10U); 343 | year += segment; 344 | goto parseWeekAndDay; 345 | } else 346 | isValidDate = NO; 347 | } else 348 | isValidDate = NO; 349 | break; 350 | } 351 | case 2: 352 | switch(num_leading_hyphens) { 353 | case 0: 354 | if (*ch == '-') { 355 | //Implicit century 356 | year = nowComponents.year; 357 | year -= (year % 100U); 358 | year += segment; 359 | 360 | if (*++ch == 'W') 361 | goto parseWeekAndDay; 362 | else if (!isdigit(*ch)) { 363 | goto centuryOnly; 364 | } else { 365 | //Get month and/or date. 366 | segment = read_segment_4digits(ch, &ch, &num_digits); 367 | NSLog(@"(%@) parsing month; segment is %lu and ch is %s", string, (unsigned long)segment, ch); 368 | switch(num_digits) { 369 | case 4: //YY-MMDD 370 | day = segment % 100U; 371 | month_or_week = segment / 100U; 372 | break; 373 | 374 | case 1: //YY-M; YY-M-DD (extension) 375 | if (strict) { 376 | isValidDate = NO; 377 | break; 378 | } 379 | case 2: //YY-MM; YY-MM-DD 380 | month_or_week = segment; 381 | if (*ch == '-') { 382 | if (isdigit(*++ch)) 383 | day = read_segment_2digits(ch, &ch); 384 | else 385 | day = 1U; 386 | } else 387 | day = 1U; 388 | break; 389 | 390 | case 3: //Ordinal date. 391 | day = segment; 392 | dateSpecification = dateOnly; 393 | break; 394 | } 395 | } 396 | } else if (*ch == 'W') { 397 | year = nowComponents.year; 398 | year -= (year % 100U); 399 | year += segment; 400 | 401 | parseWeekAndDay: //*ch should be 'W' here. 402 | if (!isdigit(*++ch)) { 403 | //Not really a week-based date; just a year followed by '-W'. 404 | if (strict) 405 | isValidDate = NO; 406 | else 407 | month_or_week = day = 1U; 408 | } else { 409 | month_or_week = read_segment_2digits(ch, &ch); 410 | if (*ch == '-') ++ch; 411 | parseDayAfterWeek: 412 | day = isdigit(*ch) ? read_segment_2digits(ch, &ch) : 1U; 413 | dateSpecification = week; 414 | } 415 | } else { 416 | //Century only. Assume current year. 417 | centuryOnly: 418 | year = segment * 100U + nowComponents.year % 100U; 419 | month_or_week = day = 1U; 420 | } 421 | break; 422 | 423 | case 1:; //-YY; -YY-MM (implicit century) 424 | NSLog(@"(%@) found %lu digits and one hyphen, so this is either -YY or -YY-MM; segment (year) is %lu", string, (unsigned long)num_digits, (unsigned long)segment); 425 | NSUInteger current_year = nowComponents.year; 426 | NSUInteger current_century = (current_year % 100U); 427 | year = segment + (current_year - current_century); 428 | if (num_digits == 1U) //implied decade 429 | year += current_century - (current_year % 10U); 430 | 431 | if (*ch == '-') { 432 | ++ch; 433 | month_or_week = read_segment_2digits(ch, &ch); 434 | NSLog(@"(%@) month is %lu", string, (unsigned long)month_or_week); 435 | } 436 | 437 | day = 1U; 438 | break; 439 | 440 | case 2: //--MM; --MM-DD 441 | year = nowComponents.year; 442 | month_or_week = segment; 443 | if (*ch == '-') { 444 | ++ch; 445 | day = read_segment_2digits(ch, &ch); 446 | } 447 | break; 448 | 449 | case 3: //---DD 450 | year = nowComponents.year; 451 | month_or_week = nowComponents.month; 452 | day = segment; 453 | break; 454 | 455 | default: 456 | isValidDate = NO; 457 | } //switch(num_leading_hyphens) (2 digits) 458 | break; 459 | 460 | case 7: //YYYY DDD (ordinal date) 461 | if (num_leading_hyphens > 0U) 462 | isValidDate = NO; 463 | else { 464 | day = segment % 1000U; 465 | year = segment / 1000U; 466 | dateSpecification = dateOnly; 467 | if (strict && (day > (365U + is_leap_year(year)))) 468 | isValidDate = NO; 469 | } 470 | break; 471 | 472 | case 3: //--DDD (ordinal date, implicit year) 473 | //Technically, the standard only allows one hyphen. But it says that two hyphens is the logical implementation, and one was dropped for brevity. So I have chosen to allow the missing hyphen. 474 | if ((num_leading_hyphens < 1U) || ((num_leading_hyphens > 2U) && !strict)) 475 | isValidDate = NO; 476 | else { 477 | day = segment; 478 | year = nowComponents.year; 479 | dateSpecification = dateOnly; 480 | if (strict && (day > (365U + is_leap_year(year)))) 481 | isValidDate = NO; 482 | } 483 | break; 484 | 485 | default: 486 | isValidDate = NO; 487 | } 488 | } 489 | 490 | if (isValidDate) { 491 | if (isspace(*ch) || (*ch == 'T')) ++ch; 492 | 493 | if (isdigit(*ch)) { 494 | hour = read_segment_2digits(ch, &ch); 495 | if (*ch == timeSep) { 496 | ++ch; 497 | if ((timeSep == ',') || (timeSep == '.')) { 498 | //We can't do fractional minutes when '.' is the segment separator. 499 | //Only allow whole minutes and whole seconds. 500 | minute = read_segment_2digits(ch, &ch); 501 | if (*ch == timeSep) { 502 | ++ch; 503 | second = read_segment_2digits(ch, &ch); 504 | } 505 | } else { 506 | //Allow a fractional minute. 507 | //If we don't get a fraction, look for a seconds segment. 508 | //Otherwise, the fraction of a minute is the seconds. 509 | minute = read_double(ch, &ch); 510 | second = modf(minute, &minute); 511 | if (second > DBL_EPSILON) 512 | second *= 60.0; //Convert fraction (e.g. .5) into seconds (e.g. 30). 513 | else if (*ch == timeSep) { 514 | ++ch; 515 | second = read_double(ch, &ch); 516 | } 517 | } 518 | } 519 | 520 | if (!strict) { 521 | if (isspace(*ch)) ++ch; 522 | } 523 | 524 | switch(*ch) { 525 | case 'Z': 526 | timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"]; 527 | break; 528 | 529 | case '+': 530 | case '-':; 531 | BOOL negative = (*ch == '-'); 532 | if (isdigit(*++ch)) { 533 | //Read hour offset. 534 | segment = *ch - '0'; 535 | if (isdigit(*++ch)) { 536 | segment *= 10U; 537 | segment += *(ch++) - '0'; 538 | } 539 | tz_hour = (NSInteger)segment; 540 | if (negative) tz_hour = -tz_hour; 541 | 542 | //Optional separator. 543 | if (*ch == timeSep) ++ch; 544 | 545 | if (isdigit(*ch)) { 546 | //Read minute offset. 547 | segment = *ch - '0'; 548 | if (isdigit(*++ch)) { 549 | segment *= 10U; 550 | segment += *ch - '0'; 551 | } 552 | tz_minute = segment; 553 | if (negative) tz_minute = -tz_minute; 554 | } 555 | 556 | NSInteger timeZoneOffset = (tz_hour * 3600) + (tz_minute * 60); 557 | NSNumber *offsetNum = [NSNumber numberWithInteger:timeZoneOffset]; 558 | timeZone = [timeZonesByOffset objectForKey:offsetNum]; 559 | if (!timeZone) { 560 | timeZone = [NSTimeZone timeZoneForSecondsFromGMT:timeZoneOffset]; 561 | if (timeZone) 562 | [timeZonesByOffset setObject:timeZone forKey:offsetNum]; 563 | } 564 | } 565 | } 566 | } 567 | } 568 | 569 | if (isValidDate) { 570 | components.year = year; 571 | components.day = day; 572 | components.hour = hour; 573 | components.minute = (NSInteger)minute; 574 | components.second = (NSInteger)second; 575 | 576 | switch(dateSpecification) { 577 | case monthAndDate: 578 | components.month = month_or_week; 579 | break; 580 | 581 | case week:; 582 | //Adapted from . 583 | //This works by converting the week date into an ordinal date, then letting the next case handle it. 584 | NSUInteger prevYear = year - 1U; 585 | NSUInteger YY = prevYear % 100U; 586 | NSUInteger C = prevYear - YY; 587 | NSUInteger G = YY + YY / 4U; 588 | NSUInteger isLeapYear = (((C / 100U) % 4U) * 5U); 589 | NSUInteger Jan1Weekday = (isLeapYear + G) % 7U; 590 | enum { monday, tuesday, wednesday, thursday/*, friday, saturday, sunday*/ }; 591 | components.day = ((8U - Jan1Weekday) + (7U * (Jan1Weekday > thursday))) + (day - 1U) + (7U * (month_or_week - 2)); 592 | 593 | case dateOnly: //An "ordinal date". 594 | break; 595 | } 596 | } 597 | } //if (!(strict && isdigit(ch[0]))) 598 | 599 | if (outRange) { 600 | if (isValidDate) 601 | range.length = ch - start_of_date; 602 | else 603 | range.location = NSNotFound; 604 | 605 | *outRange = range; 606 | } 607 | if (outTimeZone) { 608 | *outTimeZone = timeZone; 609 | } 610 | 611 | return components; 612 | } 613 | 614 | - (NSDate *) dateFromString:(NSString *)string { 615 | return [self dateFromString:string timeZone:NULL]; 616 | } 617 | - (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone { 618 | return [self dateFromString:string timeZone:outTimeZone range:NULL]; 619 | } 620 | - (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange { 621 | NSTimeZone *timeZone = nil; 622 | NSDateComponents *components = [self dateComponentsFromString:string timeZone:&timeZone range:outRange]; 623 | if (outTimeZone) 624 | *outTimeZone = timeZone; 625 | parsingCalendar.timeZone = timeZone; 626 | 627 | return [parsingCalendar dateFromComponents:components]; 628 | } 629 | 630 | - (BOOL)getObjectValue:(id *)outValue forString:(NSString *)string errorDescription:(NSString **)error { 631 | NSDate *date = [self dateFromString:string]; 632 | if (outValue) 633 | *outValue = date; 634 | return (date != nil); 635 | } 636 | 637 | #pragma mark Unparsing 638 | 639 | @synthesize format; 640 | @synthesize includeTime; 641 | @synthesize timeSeparator; 642 | 643 | - (NSString *) replaceColonsInString:(NSString *)timeFormat withTimeSeparator:(unichar)timeSep { 644 | if (timeSep != ':') { 645 | NSMutableString *timeFormatMutable = [[timeFormat mutableCopy] autorelease]; 646 | [timeFormatMutable replaceOccurrencesOfString:@":" 647 | withString:[NSString stringWithCharacters:&timeSep length:1U] 648 | options:NSBackwardsSearch | NSLiteralSearch 649 | range:(NSRange){ 0UL, [timeFormat length] }]; 650 | timeFormat = timeFormatMutable; 651 | } 652 | return timeFormat; 653 | } 654 | 655 | - (NSString *) stringFromDate:(NSDate *)date { 656 | NSTimeZone *timeZone = self.defaultTimeZone; 657 | if (!timeZone) timeZone = [NSTimeZone defaultTimeZone]; 658 | return [self stringFromDate:date timeZone:timeZone]; 659 | } 660 | 661 | - (NSString *) stringFromDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone { 662 | switch (self.format) { 663 | case ISO8601DateFormatCalendar: 664 | return [self stringFromDate:date formatString:ISO_CALENDAR_DATE_FORMAT timeZone:timeZone]; 665 | case ISO8601DateFormatWeek: 666 | return [self weekDateStringForDate:date timeZone:timeZone]; 667 | case ISO8601DateFormatOrdinal: 668 | return [self stringFromDate:date formatString:ISO_ORDINAL_DATE_FORMAT timeZone:timeZone]; 669 | default: 670 | [NSException raise:NSInternalInconsistencyException format:@"self.format was %lu, not calendar (%d), week (%d), or ordinal (%d)", (unsigned long)self.format, ISO8601DateFormatCalendar, ISO8601DateFormatWeek, ISO8601DateFormatOrdinal]; 671 | return nil; 672 | } 673 | } 674 | 675 | - (NSString *) stringFromDate:(NSDate *)date formatString:(NSString *)dateFormat timeZone:(NSTimeZone *)timeZone { 676 | if (includeTime) 677 | dateFormat = [dateFormat stringByAppendingFormat:@"'T'%@", [self replaceColonsInString:ISO_TIME_FORMAT withTimeSeparator:self.timeSeparator]]; 678 | 679 | unparsingCalendar.timeZone = timeZone; 680 | 681 | if (dateFormat != lastUsedFormatString) { 682 | [unparsingFormatter release]; 683 | unparsingFormatter = nil; 684 | 685 | [lastUsedFormatString release]; 686 | lastUsedFormatString = [dateFormat retain]; 687 | } 688 | 689 | if (!unparsingFormatter) { 690 | unparsingFormatter = [[NSDateFormatter alloc] init]; 691 | unparsingFormatter.formatterBehavior = NSDateFormatterBehavior10_4; 692 | unparsingFormatter.dateFormat = dateFormat; 693 | unparsingFormatter.calendar = unparsingCalendar; 694 | } 695 | 696 | NSString *str = [unparsingFormatter stringForObjectValue:date]; 697 | 698 | if (includeTime) { 699 | NSInteger offset = [timeZone secondsFromGMT]; 700 | offset /= 60; //bring down to minutes 701 | if (offset == 0) 702 | str = [str stringByAppendingString:ISO_TIMEZONE_UTC_FORMAT]; 703 | else 704 | str = [str stringByAppendingFormat:ISO_TIMEZONE_OFFSET_FORMAT, (int)(offset / 60), (int)(offset % 60)]; 705 | } 706 | 707 | //Undo the change we made earlier 708 | unparsingCalendar.timeZone = self.defaultTimeZone; 709 | 710 | return str; 711 | } 712 | 713 | - (NSString *) stringForObjectValue:(id)value { 714 | NSParameterAssert([value isKindOfClass:[NSDate class]]); 715 | 716 | return [self stringFromDate:(NSDate *)value]; 717 | } 718 | 719 | /*Adapted from: 720 | * Algorithm for Converting Gregorian Dates to ISO 8601 Week Date 721 | * Rick McCarty, 1999 722 | * http://personal.ecu.edu/mccartyr/ISOwdALG.txt 723 | */ 724 | - (NSString *) weekDateStringForDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone { 725 | unparsingCalendar.timeZone = timeZone; 726 | NSDateComponents *components = [unparsingCalendar components:NSYearCalendarUnit | NSWeekdayCalendarUnit | NSDayCalendarUnit fromDate:date]; 727 | 728 | //Determine the ordinal date. 729 | NSDateComponents *startOfYearComponents = [unparsingCalendar components:NSYearCalendarUnit fromDate:date]; 730 | startOfYearComponents.month = 1; 731 | startOfYearComponents.day = 1; 732 | NSDateComponents *ordinalComponents = [unparsingCalendar components:NSDayCalendarUnit fromDate:[unparsingCalendar dateFromComponents:startOfYearComponents] toDate:date options:0]; 733 | ordinalComponents.day += 1; 734 | 735 | enum { 736 | monday, tuesday, wednesday, thursday, friday, saturday, sunday 737 | }; 738 | enum { 739 | january = 1, february, march, 740 | april, may, june, 741 | july, august, september, 742 | october, november, december 743 | }; 744 | 745 | NSInteger year = components.year; 746 | NSInteger week = 0; 747 | //The old unparser added 6 to [calendarDate dayOfWeek], which was zero-based; components.weekday is one-based, so we now add only 5. 748 | NSInteger dayOfWeek = (components.weekday + 5) % 7; 749 | NSInteger dayOfYear = ordinalComponents.day; 750 | 751 | NSInteger prevYear = year - 1; 752 | 753 | BOOL yearIsLeapYear = is_leap_year(year); 754 | BOOL prevYearIsLeapYear = is_leap_year(prevYear); 755 | 756 | NSInteger YY = prevYear % 100; 757 | NSInteger C = prevYear - YY; 758 | NSInteger G = YY + YY / 4; 759 | NSInteger Jan1Weekday = (((((C / 100) % 4) * 5) + G) % 7); 760 | 761 | NSInteger weekday = ((dayOfYear + Jan1Weekday) - 1) % 7; 762 | 763 | if((dayOfYear <= (7 - Jan1Weekday)) && (Jan1Weekday > thursday)) { 764 | week = 52 + ((Jan1Weekday == friday) || ((Jan1Weekday == saturday) && prevYearIsLeapYear)); 765 | --year; 766 | } else { 767 | NSInteger lengthOfYear = 365 + yearIsLeapYear; 768 | if((lengthOfYear - dayOfYear) < (thursday - weekday)) { 769 | ++year; 770 | week = 1; 771 | } else { 772 | NSInteger J = dayOfYear + (sunday - weekday) + Jan1Weekday; 773 | week = J / 7 - (Jan1Weekday > thursday); 774 | } 775 | } 776 | 777 | NSString *timeString; 778 | if(includeTime) { 779 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 780 | unichar timeSep = self.timeSeparator; 781 | if (!timeSep) timeSep = ISO8601DefaultTimeSeparatorCharacter; 782 | formatter.dateFormat = [self replaceColonsInString:ISO_TIME_WITH_TIMEZONE_FORMAT withTimeSeparator:timeSep]; 783 | 784 | timeString = [formatter stringForObjectValue:date]; 785 | 786 | [formatter release]; 787 | } else 788 | timeString = @""; 789 | 790 | return [NSString stringWithFormat:@"%lu-W%02lu-%02lu%@", (unsigned long)year, (unsigned long)week, ((unsigned long)dayOfWeek) + 1U, timeString]; 791 | } 792 | 793 | @end 794 | 795 | static NSUInteger read_segment(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits) { 796 | NSUInteger num_digits = 0U; 797 | NSUInteger value = 0U; 798 | 799 | while(isdigit(*str)) { 800 | value *= 10U; 801 | value += *str - '0'; 802 | ++num_digits; 803 | ++str; 804 | } 805 | 806 | if (next) *next = str; 807 | if (out_num_digits) *out_num_digits = num_digits; 808 | 809 | return value; 810 | } 811 | static NSUInteger read_segment_4digits(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits) { 812 | NSUInteger num_digits = 0U; 813 | NSUInteger value = 0U; 814 | 815 | if (isdigit(*str)) { 816 | value += *(str++) - '0'; 817 | ++num_digits; 818 | } 819 | 820 | if (isdigit(*str)) { 821 | value *= 10U; 822 | value += *(str++) - '0'; 823 | ++num_digits; 824 | } 825 | 826 | if (isdigit(*str)) { 827 | value *= 10U; 828 | value += *(str++) - '0'; 829 | ++num_digits; 830 | } 831 | 832 | if (isdigit(*str)) { 833 | value *= 10U; 834 | value += *(str++) - '0'; 835 | ++num_digits; 836 | } 837 | 838 | if (next) *next = str; 839 | if (out_num_digits) *out_num_digits = num_digits; 840 | 841 | return value; 842 | } 843 | static NSUInteger read_segment_2digits(const unsigned char *str, const unsigned char **next) { 844 | NSUInteger value = 0U; 845 | 846 | if (isdigit(*str)) 847 | value += *str - '0'; 848 | 849 | if (isdigit(*++str)) { 850 | value *= 10U; 851 | value += *(str++) - '0'; 852 | } 853 | 854 | if (next) *next = str; 855 | 856 | return value; 857 | } 858 | 859 | //strtod doesn't support ',' as a separator. This does. 860 | static double read_double(const unsigned char *str, const unsigned char **next) { 861 | double value = 0.0; 862 | 863 | if (str) { 864 | NSUInteger int_value = 0; 865 | 866 | while(isdigit(*str)) { 867 | int_value *= 10U; 868 | int_value += (*(str++) - '0'); 869 | } 870 | value = int_value; 871 | 872 | if (((*str == ',') || (*str == '.'))) { 873 | ++str; 874 | 875 | register double multiplier, multiplier_multiplier; 876 | multiplier = multiplier_multiplier = 0.1; 877 | 878 | while(isdigit(*str)) { 879 | value += (*(str++) - '0') * multiplier; 880 | multiplier *= multiplier_multiplier; 881 | } 882 | } 883 | } 884 | 885 | if (next) *next = str; 886 | 887 | return value; 888 | } 889 | 890 | static BOOL is_leap_year(NSUInteger year) { 891 | return \ 892 | ((year % 4U) == 0U) 893 | && (((year % 100U) != 0U) 894 | || ((year % 400U) == 0U)); 895 | } 896 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2006–2011 Peter Hosey 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 7 | Neither the name of Peter Hosey nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/Makefile: -------------------------------------------------------------------------------- 1 | CLANG=/usr/bin/clang 2 | CC=$(CLANG) 3 | CFLAGS+=-std=c99 -g -Werror -Wmissing-field-initializers -Wreturn-type -Wmissing-braces -Wparentheses -Wswitch -Wunused-function -Wunused-label -Wunused-variable -Wunused-value -Wshadow -Wsign-compare -Wnewline-eof -Wshorten-64-to-32 -Wundeclared-selector -Wmissing-prototypes -Wformat -Wunknown-pragmas 4 | LDFLAGS+=-framework Foundation 5 | 6 | all: testparser unparse-weekdate unparse-ordinaldate unparse-date 7 | test: all parser-test unparser-test 8 | analysis: ISO8601DateFormatter-analysis.plist 9 | 10 | parser-test: testparser testparser.sh 11 | ./testparser.sh 12 | unparser-test: testunparser.sh unparse-weekdate unparse-ordinaldate unparse-date 13 | ./testunparser.sh > testunparser.out 14 | diff -qs test_files/testunparser-expected.out testunparser.out 15 | 16 | .PHONY: all test analysis parser-test unparser-test 17 | 18 | testparser: testparser.o ISO8601DateFormatter.o 19 | 20 | testparser.sh: testparser.sh.in 21 | python testparser.sh.py 22 | 23 | unparse-weekdate: unparse-weekdate.o ISO8601DateFormatter.o ISO8601DateFormatter.o 24 | unparse-ordinaldate: unparse-ordinaldate.o ISO8601DateFormatter.o ISO8601DateFormatter.o 25 | unparse-date: unparse-date.o ISO8601DateFormatter.o ISO8601DateFormatter.o 26 | 27 | testunparsewithtime: testunparsewithtime.o ISO8601DateFormatter.o 28 | 29 | ISO8601DateFormatter-analysis.plist: ISO8601DateFormatter.m 30 | $(CLANG) $^ --analyze -o /dev/null 31 | 32 | timetrial: timetrial.o ISO8601DateFormatter.o 33 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/README.md: -------------------------------------------------------------------------------- 1 | # ISO 8601: The only date format worth using 2 | 3 | Obligatory relevant [xkcd](http://xkcd.com/): 4 | 5 | [![Seriously now. "ISO 8601 was published on 06/05/88 and most recently amended on 12/01/04."](http://imgs.xkcd.com/comics/iso_8601.png)](http://xkcd.com/1179/) 6 | 7 | ## How to use this code in your program 8 | 9 | Add the source files to your project. 10 | 11 | ### Parsing 12 | 13 | Create an ISO 8601 date formatter, then call [formatter dateFromString:myString]. The method will return either an NSDate or nil. 14 | 15 | There are a total of six parser methods. The one that contains the actual parser is -[ISO8601DateFormatter dateComponentsFromString:timeZone:range:]. The other five are based on this one. 16 | 17 | The "outTimeZone" parameter, when not set to NULL, is a pointer to an NSTimeZone *variable. If the string specified a time zone, you'll receive the time zone object in that variable. If the string didn't specify a time zone, you'll receive nil. 18 | 19 | The "outRange" parameter, when not set to NULL, is a pointer to NSRange storage. You will receive the range of the parsed substring in that storage. 20 | 21 | ### Unparsing 22 | 23 | Create an ISO 8601 date formatter, then call [formatter stringFromDate:myDate]. The method will return a string. 24 | 25 | The formatter has several properties that control its behavior: 26 | 27 | * You can set the format of the resulting strings. By default, the formatter will generate calendar-date strings; your other options are week dates and ordinal dates. 28 | * You can set a default time zone; by default, it will use [NSTimeZone defaultTimeZone]. 29 | * You can enable a strict mode, wherein the formatter enforces sanity checks on the string. By default, the parser will afford you quite a bit of leeway. 30 | * You can set whether to include the time in the string, and if so, what hour-minute separator to use (default ':'). 31 | 32 | ## How to test that this code works 33 | 34 | 'make test' will perform all tests. If you want to perform only *some* tests: 35 | 36 | ### Parsing 37 | 38 | Type 'make parser-test'. make will build the test program (testparser), then invoke testparser.sh.py to generate testparser.sh. Then make will invoke testparser.sh, which will invoke the test program with various dates. 39 | 40 | If you don't want to use my tests, 'make testparser' will create the test program without running it. You can then invoke testparser yourself with any date you want to. If it doesn't give you the result you expected, contact me, making sure to provide me with both the input and the output. 41 | 42 | ### Unparsing 43 | 44 | Type 'make unparser-test'. make will build the test programs, then invoke testunparser.sh. This shell script invokes each test program for -01-01 of every year from 1991 to 2010, writing the output to a file, and then runs diff -qs between that file (testunparser.out) and a file (testunparser-expected.out) containing known correct output. diff should report that the files are identical. 45 | 46 | Three test programs are included: unparse-date, unparse-weekdate, and unparse-ordinal date. If you don't want to use my tests, you can make these test programs separately. Each takes a date specified by ISO 8601 (parsed with my own ISO 8601 parser), and outputs a string that should represent the same date. 47 | 48 | ## Notes 49 | 50 | ### Version history 51 | 52 | This version is 0.6. Changes from 0.5: 53 | 54 | * When not set to strict parsing, allow a space before the time-zone specification, for greater compatibility with NSDate output (`description`) and input (`dateWithString:`). 27603efc8a77 55 | * Bug fix: We no longer try to format the formatter. 83415de9f527 56 | * Bug fix: Hours are now zero-padded correctly ([#4](https://bitbucket.org/boredzo/iso-8601-parser-unparser/issue/4/time-zones-are-emitted-without-leading)). a5608e189ebe af0c6b397428 57 | * Fixed many various compiler warnings. Some fixes contributed by Sparks. 8be3d6f7c6f2 78ae31de2170 68dc351e9fdb e7ea87db8621 873f499ae6db 58 | * Now 75 times faster. 6a023812bd2b 05dc35d6b505 3b2225e0ce8c d59720ad015a 10f526956963 297b8dae4095 796be11aa596 cadf0f0c8199 61d2959c6921 59 | * iOS users can now tell the ISO 8601 date formatter class to drop its caches (which you should do when you receive a memory warning). 2bb1725914b1 60 | * Added a couple of command-line options to the calendar-format-unparser test tool. c644aadb2b14 61 | * The parser test tool now displays parsed dates in GMT rather than the local time zone. 788c1455ecb1 62 | * Added a test tool to test unparsing with the time included. a89a9a8b3d61 63 | * You can now “make analysis” to run the Clang Static Analyzer on the date formatter. b3dd33841f42 64 | * The test tools are now built using Clang. 0723d3aa6596 65 | 66 | Changes in 0.5 from 0.4: 67 | 68 | * Rewrote as an NSFormatter subclass using NSCalendar. 69 | * Making it a formatter makes it much easier to use with Bindings. 70 | * Using NSCalendar means we're no longer using NSCalendarDate, which Apple has said they will deprecate at some point. 71 | * Fixed a bug in week date generation: One subtraction could give a negative result, which was a problem because my implementation of the algorithm used unsigned integers. I've changed it to use signed integers, so the result truly is negative now. I also added a test case for this. 72 | 73 | Changes in 0.4 from 0.3: 74 | 75 | * Added the ability to use a time separator other than ':'. 76 | 77 | Changes in 0.3 from 0.2: 78 | 79 | * Colin Barrett noticed that I used %m instead of %M when creating the time strings. Oops. 80 | * Colin also noticed that I had the ?: in -ISO8601DateStringWithTime: the wrong way around. Oops again. 81 | 82 | Changes in 0.2 from 0.1: 83 | 84 | * The unparser is new. The has been munged to allow both components together, 85 | * The parser has not changed. 86 | 87 | ## Implementation details 88 | ### Parsing 89 | 90 | Whitespace before a date, and anything after a date, is ignored. Thus, " T23 and all's well" is a valid date for the purpose of this method. (Yes, T23 is a valid ISO 8601 date. It means 23:00:00, or 11 PM.) 91 | 92 | All of the frills of ISO 8601 are supported, except for extended dates (years longer than 4 digits). Specifically, you can use week-based dates (2006-W2 for the second week of 2006), ordinal dates (2006-365 for December 31), decimal minutes (11:30.5 == 11:30:30), and decimal seconds (11:30:10.5). All methods of specifying a time zone are supported. 93 | 94 | ISO 8601 leaves quite a bit up to the parties exchanging dates. I hope I've chosen reasonable defaults. For example (note that I'm writing this on 2006-02-24): 95 | 96 | * If the month or month and date are missing, 1 is assumed. "2006" == "2006-01-01". 97 | * If the year or year and month are missing, the current ones are assumed. "--02-01" == "2006-02-01". "---28" == "2006-02-28". 98 | * In the case of week-based dates, with the day missing, this implementation returns the first day of that week: 2006-W1 is 2006-01-01, 2006-W2 is 2006-01-08, etc. 99 | * For any date without a time, midnight on that date is used. 100 | * ISO 8601 permits the choice of either T0 or T24 for midnight. This implementation uses T0. T24 will get you T0 on the following day. 101 | * If no time-zone is specified, local time (as returned by [NSTimeZone localTimeZone]) is used. 102 | 103 | When a date is parsed that has a year but no century, this implementation adds the current century. 104 | 105 | The implementation is tolerant of out-of-range numbers. For example, "2005-13-40T24:62:89" == 1:02 AM on 2006-02-10. Notice that the month (13 > 12), date (40 > 31), hour (24 > 23), minute (62 > 59), and second (89 > 59) are all out-of-range. 106 | 107 | As mentioned above, there is a "strict" mode that enforces sanity checks. In particular, the date must be the entire contents of the string, and numbers are range-checked. If you have any suggestions on how to make this mode more strict, contact me. 108 | 109 | ### Unparsing 110 | 111 | I use [Rick McCarty's algorithm for converting calendar dates to week dates](http://personal.ecu.edu/mccartyr/ISOwdAlg.txt), slightly tweaked. 112 | 113 | ## Bugs 114 | 115 | ### Parsing 116 | 117 | * This method won't extract a date from just anywhere in a string, only immediately after the start of the string (or any leading whitespace). There are two solutions: either require you to invoke the parser on a string that is only an ISO 8601 date, with nothing before or after (bad for parsing purposes), or make the parser able to find an ISO 8601 date as a substring. I won't do the first one, and barring a patch, I probably won't do the second one either. 118 | 119 | * Date ranges (also specified by ISO 8601) are not supported; this method will only return one date. To handle ranges would require at least one more method. 120 | 121 | * There is no method to analyze a date string and tell you what was found in it (year, month, week, day, ordinal day, etc.). Feel free to submit a patch. 122 | 123 | ## Copyright 124 | 125 | This code is copyright 2006–2011 Peter Hosey. It is under the BSD license; see LICENSE.txt for the full text of the license. 126 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/test_files/2005-2006.txt: -------------------------------------------------------------------------------- 1 | December 2005 2 | M Tu W Th F S S 3 | 1 2 3 4 4 | 5 6 7 8 9 10 11 5 | 12 13 14 15 16 17 18 6 | 19 20 21 22 23 24 25 7 | 26 27 28 29 30 31 8 | 9 | January 2006 10 | M Tu W Th F S S 11 | 1 <- NOT W01 12 | 2 3 4 5 6 7 8 <- W01 13 | 9 10 11 12 13 14 15 14 | 16 17 18 19 20 21 22 15 | 23 24 25 26 27 28 29 16 | 30 31 17 | 18 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/test_files/2005.txt: -------------------------------------------------------------------------------- 1 | 2005 2 | 3 | January 4 | CW Mo Tu We Th Fr Sa Su 5 | 53 1 2 6 | 01 3 4 5 6 7 8 9 7 | 02 10 11 12 13 14 15 16 8 | 03 17 18 19 20 21 22 23 9 | 04 24 25 26 27 28 29 30 10 | 05 31 11 | 12 | February 13 | CW Mo Tu We Th Fr Sa Su 14 | 05 1 2 3 4 5 6 15 | 06 7 8 9 10 11 12 13 16 | 07 14 15 16 17 18 19 20 17 | 08 21 22 23 24 25 26 27 18 | 09 28 19 | 20 | March 21 | CW Mo Tu We Th Fr Sa Su 22 | 09 1 2 3 4 5 6 23 | 10 7 8 9 10 11 12 13 24 | 11 14 15 16 17 18 19 20 25 | 12 21 22 23 24 25 26 27 26 | 13 28 29 30 31 27 | 28 | April 29 | CW Mo Tu We Th Fr Sa Su 30 | 13 1 2 3 31 | 14 4 5 6 7 8 9 10 32 | 15 11 12 13 14 15 16 17 33 | 16 18 19 20 21 22 23 24 34 | 17 25 26 27 28 29 30 35 | 36 | May 37 | CW Mo Tu We Th Fr Sa Su 38 | 17 1 39 | 18 2 3 4 5 6 7 8 40 | 19 9 10 11 12 13 14 15 41 | 20 16 17 18 19 20 21 22 42 | 21 23 24 25 26 27 28 29 43 | 22 30 31 44 | 45 | June 46 | CW Mo Tu We Th Fr Sa Su 47 | 22 1 2 3 4 5 48 | 23 6 7 8 9 10 11 12 49 | 24 13 14 15 16 17 18 19 50 | 25 20 21 22 23 24 25 26 51 | 26 27 28 29 30 52 | 53 | July 54 | CW Mo Tu We Th Fr Sa Su 55 | 26 1 2 3 56 | 27 4 5 6 7 8 9 10 57 | 28 11 12 13 14 15 16 17 58 | 29 18 19 20 21 22 23 24 59 | 30 25 26 27 28 29 30 31 60 | 61 | August 62 | CW Mo Tu We Th Fr Sa Su 63 | 31 1 2 3 4 5 6 7 64 | 32 8 9 10 11 12 13 14 65 | 33 15 16 17 18 19 20 21 66 | 34 22 23 24 25 26 27 28 67 | 35 29 30 31 68 | 69 | September 70 | CW Mo Tu We Th Fr Sa Su 71 | 35 1 2 3 4 72 | 36 5 6 7 8 9 10 11 73 | 37 12 13 14 15 16 17 18 74 | 38 19 20 21 22 23 24 25 75 | 39 26 27 28 29 30 76 | 77 | October 78 | CW Mo Tu We Th Fr Sa Su 79 | 39 1 2 80 | 40 3 4 5 6 7 8 9 81 | 41 10 11 12 13 14 15 16 82 | 42 17 18 19 20 21 22 23 83 | 43 24 25 26 27 28 29 30 84 | 44 31 85 | 86 | November 87 | CW Mo Tu We Th Fr Sa Su 88 | 44 1 2 3 4 5 6 89 | 45 7 8 9 10 11 12 13 90 | 46 14 15 16 17 18 19 20 91 | 47 21 22 23 24 25 26 27 92 | 48 28 29 30 93 | 94 | December 95 | CW Mo Tu We Th Fr Sa Su 96 | 48 1 2 3 4 97 | 49 5 6 7 8 9 10 11 98 | 50 12 13 14 15 16 17 18 99 | 51 19 20 21 22 23 24 25 100 | 52 26 27 28 29 30 31 101 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/test_files/2006.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Manoj85/FireData/a9f5a30809de69eb857a1f63a6e9ea9a21fc6c4b/Vendor/iso-8601-date-formatter/test_files/2006.txt -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/test_files/2009-2010.txt: -------------------------------------------------------------------------------- 1 | December 2009 2 | M Tu W Th F S S 3 | 1 2 3 4 5 6 4 | 7 8 9 10 11 12 13 5 | 14 15 16 17 18 19 20 6 | 21 22 23 24 25 26 27 7 | 28 29 30 31 8 | 9 | January 2010 10 | M Tu W Th F S S 11 | 1 2 3 12 | 4 5 6 7 8 9 10 13 | 11 12 13 14 15 16 17 14 | 18 19 20 21 22 23 24 15 | 25 26 27 28 29 30 31 16 | 17 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/test_files/januaries.out: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2001-01-01: 2000-W52-01 4 | 2001-01-02: 2000-W52-02 5 | WRONG! This should say W01-0[12]. 6 | 7 | 8 | 9 | 10 | 11 | 2002-01-01: 2002-W01-02 12 | 2002-01-02: 2002-W01-03 13 | RIGHT! 14 | 15 | 16 | 17 | 18 | 19 | 2003-01-01: 2003-W01-03 20 | 2003-01-02: 2003-W01-04 21 | RIGHT! 22 | 23 | 24 | 25 | 26 | 27 | 2004-01-01: 2004-W01-04 28 | 2004-01-02: 2004-W01-05 29 | RIGHT! 30 | 31 | 32 | 33 | 34 | 35 | 2005-01-01: 2004-W53-06 36 | 2005-01-02: 2004-W53-07 37 | RIGHT! 38 | 39 | 40 | 41 | 42 | 43 | 44 | 2006-01-01: 2005-W52-07 45 | 2006-01-02: 2006-W01-01 46 | RIGHT! 47 | 48 | 49 | 50 | 51 | 52 | 53 | 2007-01-01: 2006-W52-01 54 | 2007-01-02: 2006-W52-02 55 | WRONG! See 2001. 56 | 57 | 58 | 59 | 60 | 61 | 2008-01-01: 2008-W01-02 62 | 2008-01-02: 2008-W01-03 63 | RIGHT! 64 | 65 | 66 | 67 | 68 | 69 | 2009-01-01: 2009-W01-04 70 | 2009-01-02: 2009-W01-05 71 | RIGHT! 72 | 73 | 74 | 75 | 76 | 77 | 2010-01-01: 2010-W01-05 78 | 2010-01-02: 2010-W01-06 79 | WRONG! This should be 2009-W5[23]-0[56]. 80 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/test_files/januaries.txt: -------------------------------------------------------------------------------- 1 | January 1991 January 2001 2 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 3 | 1 2 3 4 5 6 53/1 #W01-02 1 2 3 4 5 6 7 01 #W01-01 4 | 7 8 9 10 11 12 13 02 8 9 10 11 12 13 14 02 5 | 14 15 16 17 18 19 20 03 15 16 17 18 19 20 21 03 6 | 21 22 23 24 25 26 27 04 22 23 24 25 26 27 28 04 7 | 28 29 30 31 05 29 30 31 05 8 | 9 | January 1992 January 2002 10 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 11 | 1 2 3 4 5 53/1 #W01-03 1 2 3 4 5 6 53/1 #W01-02 12 | 6 7 8 9 10 11 12 02 7 8 9 10 11 12 13 02 13 | 13 14 15 16 17 18 19 03 14 15 16 17 18 19 20 03 14 | 20 21 22 23 24 25 26 04 21 22 23 24 25 26 27 04 15 | 27 28 29 30 31 05 28 29 30 31 05 16 | 17 | January 1993 January 2003 18 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 19 | 1 2 3 53/0 #W53-05 1 2 3 4 5 53/1 #W01-03 20 | 4 5 6 7 8 9 10 01 6 7 8 9 10 11 12 02 21 | 11 12 13 14 15 16 17 02 13 14 15 16 17 18 19 03 22 | 18 19 20 21 22 23 24 03 20 21 22 23 24 25 26 04 23 | 25 26 27 28 29 30 31 04 27 28 29 30 31 05 24 | 25 | January 1994 January 2004 26 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 27 | 1 2 52/0 #W52-06 1 2 3 4 53/1 #W01-04 28 | 3 4 5 6 7 8 9 01 5 6 7 8 9 10 11 02 29 | 10 11 12 13 14 15 16 02 12 13 14 15 16 17 18 03 30 | 17 18 19 20 21 22 23 03 19 20 21 22 23 24 25 04 31 | 24 25 26 27 28 29 30 04 26 27 28 29 30 31 05 32 | 31 05 33 | 34 | January 1995 January 2005 35 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 36 | 1 52/0 #W52-07 1 2 53/0 #W53-06 37 | 2 3 4 5 6 7 8 01 3 4 5 6 7 8 9 01 38 | 9 10 11 12 13 14 15 02 10 11 12 13 14 15 16 02 39 | 16 17 18 19 20 21 22 03 17 18 19 20 21 22 23 03 40 | 23 24 25 26 27 28 29 04 24 25 26 27 28 29 30 04 41 | 30 31 05 31 05 42 | 43 | January 1996 January 2006 44 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 45 | 1 2 3 4 5 6 7 01 #W01-01 1 52/0 #W52-07 46 | 8 9 10 11 12 13 14 02 2 3 4 5 6 7 8 01 47 | 15 16 17 18 19 20 21 03 9 10 11 12 13 14 15 02 48 | 22 23 24 25 26 27 28 04 16 17 18 19 20 21 22 03 49 | 29 30 31 05 23 24 25 26 27 28 29 04 50 | 30 31 05 51 | 52 | January 1997 January 2007 53 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 54 | 1 2 3 4 5 53/1 #W01-03 1 2 3 4 5 6 7 01 #W01-01 55 | 6 7 8 9 10 11 12 02 8 9 10 11 12 13 14 02 56 | 13 14 15 16 17 18 19 03 15 16 17 18 19 20 21 03 57 | 20 21 22 23 24 25 26 04 22 23 24 25 26 27 28 04 58 | 27 28 29 30 31 05 29 30 31 05 59 | 60 | January 1998 January 2008 61 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 62 | 1 2 3 4 53/1 #W01-04 1 2 3 4 5 6 53/1 #W01-02 63 | 5 6 7 8 9 10 11 02 7 8 9 10 11 12 13 02 64 | 12 13 14 15 16 17 18 03 14 15 16 17 18 19 20 03 65 | 19 20 21 22 23 24 25 04 21 22 23 24 25 26 27 04 66 | 26 27 28 29 30 31 05 28 29 30 31 05 67 | 68 | January 1999 January 2009 69 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 70 | 1 2 3 53/0 #W53-05 1 2 3 4 53/1 #W01-04 71 | 4 5 6 7 8 9 10 01 5 6 7 8 9 10 11 02 72 | 11 12 13 14 15 16 17 02 12 13 14 15 16 17 18 03 73 | 18 19 20 21 22 23 24 03 19 20 21 22 23 24 25 04 74 | 25 26 27 28 29 30 31 04 26 27 28 29 30 31 05 75 | 76 | January 2000 January 2010 77 | Mo Tu We Th Fr Sa Su CW Mo Tu We Th Fr Sa Su CW 78 | 1 2 52/0 #W52-06 1 2 3 53/0 #W53-05 79 | 3 4 5 6 7 8 9 01 4 5 6 7 8 9 10 01 80 | 10 11 12 13 14 15 16 02 11 12 13 14 15 16 17 02 81 | 17 18 19 20 21 22 23 03 18 19 20 21 22 23 24 03 82 | 24 25 26 27 28 29 30 04 25 26 27 28 29 30 31 04 83 | 31 05 84 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/test_files/januaries3.txt: -------------------------------------------------------------------------------- 1 | 2 | January 1991 3 | Mo Tu We Th Fr Sa Su CW 4 | 1 2 3 4 5 6 53/1 5 | 7 8 9 10 11 12 13 02 6 | 14 15 16 17 18 19 20 03 7 | 21 22 23 24 25 26 27 04 8 | 28 29 30 31 05 9 | 10 | 11 | January 1992 12 | Mo Tu We Th Fr Sa Su CW 13 | 1 2 3 4 5 53/1 14 | 6 7 8 9 10 11 12 02 15 | 13 14 15 16 17 18 19 03 16 | 20 21 22 23 24 25 26 04 17 | 27 28 29 30 31 05 18 | 19 | 20 | January 1993 21 | Mo Tu We Th Fr Sa Su CW 22 | 1 2 3 53/0 23 | 4 5 6 7 8 9 10 01 24 | 11 12 13 14 15 16 17 02 25 | 18 19 20 21 22 23 24 03 26 | 25 26 27 28 29 30 31 04 27 | 28 | 29 | January 1994 30 | Mo Tu We Th Fr Sa Su CW 31 | 1 2 52/0 32 | 3 4 5 6 7 8 9 01 33 | 10 11 12 13 14 15 16 02 34 | 17 18 19 20 21 22 23 03 35 | 24 25 26 27 28 29 30 04 36 | 31 05 37 | 38 | January 1995 39 | Mo Tu We Th Fr Sa Su CW 40 | 1 52/0 41 | 2 3 4 5 6 7 8 01 42 | 9 10 11 12 13 14 15 02 43 | 16 17 18 19 20 21 22 03 44 | 23 24 25 26 27 28 29 04 45 | 30 31 05 46 | 47 | January 1996 48 | Mo Tu We Th Fr Sa Su CW 49 | 1 2 3 4 5 6 7 01 50 | 8 9 10 11 12 13 14 02 51 | 15 16 17 18 19 20 21 03 52 | 22 23 24 25 26 27 28 04 53 | 29 30 31 05 54 | 55 | 56 | January 1997 57 | Mo Tu We Th Fr Sa Su CW 58 | 1 2 3 4 5 53/1 59 | 6 7 8 9 10 11 12 02 60 | 13 14 15 16 17 18 19 03 61 | 20 21 22 23 24 25 26 04 62 | 27 28 29 30 31 05 63 | 64 | 65 | January 1998 66 | Mo Tu We Th Fr Sa Su CW 67 | 1 2 3 4 53/1 68 | 5 6 7 8 9 10 11 02 69 | 12 13 14 15 16 17 18 03 70 | 19 20 21 22 23 24 25 04 71 | 26 27 28 29 30 31 05 72 | 73 | 74 | January 1999 75 | Mo Tu We Th Fr Sa Su CW 76 | 1 2 3 53/0 77 | 4 5 6 7 8 9 10 01 78 | 11 12 13 14 15 16 17 02 79 | 18 19 20 21 22 23 24 03 80 | 25 26 27 28 29 30 31 04 81 | 82 | 83 | January 2000 84 | Mo Tu We Th Fr Sa Su CW 85 | 1 2 52/0 86 | 3 4 5 6 7 8 9 01 87 | 10 11 12 13 14 15 16 02 88 | 17 18 19 20 21 22 23 03 89 | 24 25 26 27 28 29 30 04 90 | 31 05 91 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/test_files/testunparser-expected.out: -------------------------------------------------------------------------------- 1 | ./unparse-date 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01 2 | 1991-01-01: 1991-01-01 3 | 1992-01-01: 1992-01-01 4 | 1993-01-01: 1993-01-01 5 | 1994-01-01: 1994-01-01 6 | 1995-01-01: 1995-01-01 7 | 1996-01-01: 1996-01-01 8 | 1997-01-01: 1997-01-01 9 | 1998-01-01: 1998-01-01 10 | 1999-01-01: 1999-01-01 11 | 2000-01-01: 2000-01-01 12 | 2001-01-01: 2001-01-01 13 | 2002-01-01: 2002-01-01 14 | 2003-01-01: 2003-01-01 15 | 2004-01-01: 2004-01-01 16 | 2005-01-01: 2005-01-01 17 | 2006-01-01: 2006-01-01 18 | 2007-01-01: 2007-01-01 19 | 2008-01-01: 2008-01-01 20 | 2009-01-01: 2009-01-01 21 | 2010-01-01: 2010-01-01 22 | 23 | ./unparse-weekdate 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01 24 | 1991-01-01: 1991-W01-02 25 | 1992-01-01: 1992-W01-03 26 | 1993-01-01: 1992-W53-05 27 | 1994-01-01: 1993-W52-06 28 | 1995-01-01: 1994-W52-07 29 | 1996-01-01: 1996-W01-01 30 | 1997-01-01: 1997-W01-03 31 | 1998-01-01: 1998-W01-04 32 | 1999-01-01: 1998-W53-05 33 | 2000-01-01: 1999-W52-06 34 | 2001-01-01: 2001-W01-01 35 | 2002-01-01: 2002-W01-02 36 | 2003-01-01: 2003-W01-03 37 | 2004-01-01: 2004-W01-04 38 | 2005-01-01: 2004-W53-06 39 | 2006-01-01: 2005-W52-07 40 | 2007-01-01: 2007-W01-01 41 | 2008-01-01: 2008-W01-02 42 | 2009-01-01: 2009-W01-04 43 | 2010-01-01: 2009-W53-05 44 | 45 | ./unparse-ordinaldate 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01 46 | 1991-01-01: 1991-001 47 | 1992-01-01: 1992-001 48 | 1993-01-01: 1993-001 49 | 1994-01-01: 1994-001 50 | 1995-01-01: 1995-001 51 | 1996-01-01: 1996-001 52 | 1997-01-01: 1997-001 53 | 1998-01-01: 1998-001 54 | 1999-01-01: 1999-001 55 | 2000-01-01: 2000-001 56 | 2001-01-01: 2001-001 57 | 2002-01-01: 2002-001 58 | 2003-01-01: 2003-001 59 | 2004-01-01: 2004-001 60 | 2005-01-01: 2005-001 61 | 2006-01-01: 2006-001 62 | 2007-01-01: 2007-001 63 | 2008-01-01: 2008-001 64 | 2009-01-01: 2009-001 65 | 2010-01-01: 2010-001 66 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/testparser.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "ISO8601DateFormatter.h" 3 | 4 | int main(int argc, const char **argv) { 5 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 6 | 7 | BOOL parseStrictly = NO; 8 | if((argc > 1) && (strcmp(argv[1], "--strict") == 0)) { 9 | --argc;++argv; 10 | parseStrictly = YES; 11 | } 12 | 13 | [NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:+0]]; 14 | 15 | ISO8601DateFormatter *formatter = [[[ISO8601DateFormatter alloc] init] autorelease]; 16 | formatter.parsesStrictly = parseStrictly; 17 | 18 | while(--argc) { 19 | NSString *str = [NSString stringWithUTF8String:*++argv]; 20 | NSLog(@"Parsing strictly: %hhi", parseStrictly); 21 | NSDate *date = [formatter dateFromString:str]; 22 | fputs([[NSString stringWithFormat:@"%@ %C %@\n", str, 0x2192, date] UTF8String], stdout); 23 | } 24 | 25 | [pool release]; 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/testparser.sh.in: -------------------------------------------------------------------------------- 1 | 2 | #Tests with a date only 3 | ./testparser 2006-02-24 \ 4 | 2006-12 \ 5 | 2006 \ 6 | 2006- \ 7 | 2006-02- \ 8 | 2006-0224 \ 9 | 200602-24 \ 10 | 20060224 \ 11 | 2001-W1-1 \ 12 | 2002-W1-1 \ 13 | 2003-W1-1 \ 14 | 2004-W1-1 \ 15 | 2010-W1-1 \ 16 | 2000-W1-1 \ 17 | 2006-W1-1 \ 18 | 2006-W1-2 \ 19 | 2006-W02 \ 20 | 2006-W2 \ 21 | 2006-W02-01 \ 22 | 2006-W02-1 \ 23 | 2006-W2-01 \ 24 | 2006-W2-1 \ 25 | 2006-W2-2 \ 26 | 2006-W2-8 \ 27 | 2006-W3-1 \ 28 | 2006-W22 \ 29 | 2006-W22-11 \ 30 | 06-02-24 \ 31 | 06-12 \ 32 | 06-02- \ 33 | 06-0224 \ 34 | 0602-24 \ 35 | 060224 \ 36 | 06-W22 \ 37 | 06-W2 \ 38 | 06-W22-11 \ 39 | 06-W2-1 \ 40 | -6-02-24 \ 41 | -6-12 \ 42 | -6 \ 43 | -6- \ 44 | -6-02- \ 45 | -6-0224 \ 46 | -602-24 \ 47 | -6-W22 \ 48 | -6-W2 \ 49 | -6-W22-11 \ 50 | -6-W2-1 \ 51 | --0224 \ 52 | --02-24 \ 53 | --2-24 \ 54 | --02-2 \ 55 | --02 \ 56 | ---24 \ 57 | -W2 \ 58 | -W2-11 \ 59 | -W-3 \ 60 | --W-3 \ 61 | 2006-001 \ 62 | 2006-002 \ 63 | 2006-032 \ 64 | 2006-055 \ 65 | 2006-365 \ 66 | 2004-001 \ 67 | 2004-366 \ 68 | -055 \ 69 | --055 \ 70 | 2006T 71 | #Current year in x century 72 | ./testparser 20 \ 73 | 1 74 | #x year in current century 75 | ./testparser -06 76 | #x year and month in current century 77 | ./testparser -06-02 78 | #x year, month, and date in current century 79 | ./testparser 06-02-24 80 | #x month and date in current year 81 | ./testparser --02-24 82 | #x date in current year and month 83 | ./testparser ---24 84 | 85 | #Tests with a time only 86 | ./testparser T22:63:24-11:21 \ 87 | T22:63:24+50:70 \ 88 | T22:1:2 \ 89 | T22:1Z \ 90 | T22: \ 91 | T22 \ 92 | T2 \ 93 | T2:2:2 94 | #Tests with both a date and a time 95 | ./testparser 2006-02-24T02:43:24 \ 96 | 2006-02-24T22:43:24 \ 97 | 2006-02-24T22:63:24 \ 98 | 2006-12T12:34 \ 99 | 2006T22 100 | #Tests with a date, a time, and a time zone 101 | ./testparser 2006-02-24T22:63:24-01:00 \ 102 | 2006-02-24T22:63:24Z \ 103 | 2006-02-24T22:63:24-1 \ 104 | 2006-02-24T22:63:24-01 \ 105 | 2006-02-24T22:63:24-01:32 \ 106 | 2006-02-24T22:63:24-01:0 \ 107 | 2006-02-24T22:63:24-01:00 \ 108 | 2006-02-24T22:63:24-01:01 \ 109 | 2006-02-24T22:63:24-01:11 \ 110 | 2006-02-24T22:63:24-11:21 111 | 112 | #Invalid dates 113 | ./testparser '' \ 114 | T \ 115 | 2006-W \ 116 | 2006-366 \ 117 | 2006-400 \ 118 | 2004-367 \ 119 | -2006-02-24T02:43:24 \ 120 | -2006-02-24T22:43:24 \ 121 | -2006-02-24T22:63:24 \ 122 | -2006-12T12:34 \ 123 | -2006T22 \ 124 | -60224 \ 125 | --2006-02-24T02:43:24 \ 126 | --2006-02-24T22:43:24 \ 127 | --2006-02-24T22:63:24 \ 128 | --2006-12T12:34 \ 129 | --2006T22 \ 130 | ---2006-02-24T02:43:24 \ 131 | ---2006-02-24T22:43:24 \ 132 | ---2006-02-24T22:63:24 \ 133 | ---2006-12T12:34 \ 134 | ---2006T22 135 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/testparser.sh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | outfile = file('testparser.sh', 'w') 4 | 5 | outfile.write('#!/bin/sh\n') 6 | 7 | hash = '#' 8 | colon = ':' 9 | shell_prompt = '% ' 10 | echo_format = "echo '%s'\n" 11 | newline = '\n' 12 | echo_by_itself = 'echo\n' 13 | 14 | import re 15 | bs_exp = re.compile('(\\\\*)\n') 16 | escape_newline_exp = re.compile('\\\\\n') 17 | empty = '' 18 | 19 | import fileinput 20 | holding = [] 21 | for line in fileinput.input(['testparser.sh.in']): 22 | if len(line) <= 1: 23 | #Empty line. 24 | continue 25 | 26 | if(len(bs_exp.search(line).group(1)) % 2): 27 | holding.append(line[:-1]) 28 | continue 29 | elif holding: 30 | holding.append(line) 31 | line = '\n'.join(holding) 32 | del holding[:] 33 | 34 | line = escape_newline_exp.sub(empty, line) 35 | 36 | is_comment = line.startswith(hash) 37 | if is_comment: 38 | line_for_display = line[:-1].strip(hash) + colon 39 | else: 40 | line_for_display = shell_prompt + line[:-1] 41 | 42 | echo_line = echo_format % (line_for_display,) 43 | 44 | lines = [newline, echo_line] 45 | if is_comment: 46 | lines.insert(1, echo_by_itself) 47 | else: 48 | lines.append(line) 49 | outfile.writelines(lines) 50 | 51 | outfile.close() 52 | 53 | # Make it executable. 54 | import os 55 | os.chmod('testparser.sh', 0755) 56 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/testunparser.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh -f 2 | echo './unparse-date 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01' 3 | ./unparse-date 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01 4 | echo 5 | echo './unparse-weekdate 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01' 6 | ./unparse-weekdate 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01 7 | echo 8 | echo './unparse-ordinaldate 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01' 9 | ./unparse-ordinaldate 199{1,2,3,4,5,6,7,8,9}-01-01 200{0,1,2,3,4,5,6,7,8,9}-01-01 2010-01-01 10 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/testunparsewithtime.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "ISO8601DateFormatter.h" 3 | 4 | static void testFormatStrings(int hour, int minute); 5 | 6 | int main(void) { 7 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 8 | ISO8601DateFormatter *formatter = [[[ISO8601DateFormatter alloc] init] autorelease]; 9 | formatter.includeTime = YES; 10 | NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:336614400.0]; 11 | NSLog(@"2011-09-01 at 5 PM ET: %@", [formatter stringFromDate:date]); 12 | 13 | testFormatStrings(11, 6); 14 | testFormatStrings(2, 6); 15 | testFormatStrings(-2, 6); 16 | 17 | [pool drain]; 18 | return EXIT_SUCCESS; 19 | } 20 | 21 | static void testFormatStrings(int hour, int minute) { 22 | NSArray *formatStrings = [NSArray arrayWithObjects: 23 | @"%@: %02d:%02d", 24 | @"%@: %+02d:%02d", 25 | @"%@: %0+2d:%02d", 26 | @"%@: %02+d:%02d", 27 | @"%@: %+.2d:%02d", 28 | nil]; 29 | NSLog(@"Testing with NSLog:"); 30 | for (NSString *format in formatStrings) { 31 | NSLog(format, format, hour, minute); 32 | } 33 | printf("Testing with printf:\n"); 34 | for (NSString *format in formatStrings) { 35 | format = [format stringByReplacingOccurrencesOfString:@"%@" withString:@"%s"]; 36 | printf([[format stringByAppendingString:@"\n"] UTF8String], [format UTF8String], hour, minute); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/timetrial.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "ISO8601DateFormatter.h" 4 | 5 | int main(void) { 6 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 7 | 8 | sleep(1); 9 | 10 | ISO8601DateFormatter *formatter = [[[ISO8601DateFormatter alloc] init] autorelease]; 11 | NSString *inString = @"2011-04-12T13:15:17-0800"; 12 | NSUInteger numResults = 0; 13 | NSDate *start, *end; 14 | enum { numReps = 10000 }; 15 | 16 | NSLog(@"Timing ISO8601DateFormatter"); 17 | 18 | start = [NSDate date]; 19 | for (NSUInteger i = 10000; i > 0; --i) { 20 | NSDate *date = [formatter dateFromString:inString]; 21 | NSString *outString = [formatter stringFromDate:date]; 22 | if (outString) ++numResults; 23 | } 24 | end = [NSDate date]; 25 | NSLog(@"Time taken: %f seconds", [end timeIntervalSinceDate:start]); 26 | NSLog(@"Number of dates and strings computed: %lu each", (unsigned long)numResults); 27 | NSLog(@"Time taken per date: %f seconds", [end timeIntervalSinceDate:start] / numReps); 28 | 29 | [pool drain]; 30 | pool = [[NSAutoreleasePool alloc] init]; 31 | 32 | sleep(1); 33 | 34 | numResults = 0; 35 | 36 | NSLog(@"Timing C standard library parsing and unparsing"); 37 | 38 | struct tm timeInfo; 39 | time_t then; 40 | char buffer[80] = { 0 }; 41 | NSTimeInterval timeZoneOffset = [[NSTimeZone localTimeZone] secondsFromGMT]; 42 | 43 | start = [NSDate date]; 44 | for (NSUInteger i = 10000; i > 0; --i) { 45 | strptime([inString cStringUsingEncoding:NSUTF8StringEncoding], "%Y-%m-%dT%H:%M:%S%z", &timeInfo); 46 | timeInfo.tm_isdst = -1; 47 | then = mktime(&timeInfo); 48 | 49 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:then + timeZoneOffset]; 50 | 51 | struct tm *outputTimeInfo; 52 | time_t outputTime = [date timeIntervalSince1970] - timeZoneOffset; 53 | outputTimeInfo = localtime(&outputTime); 54 | strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S%z", outputTimeInfo); 55 | 56 | NSString *outString = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding]; 57 | if (outString) ++numResults; 58 | } 59 | end = [NSDate date]; 60 | NSLog(@"Time taken: %f seconds", [end timeIntervalSinceDate:start]); 61 | NSLog(@"Number of dates and strings computed: %lu each", (unsigned long)numResults); 62 | NSLog(@"Time taken per date: %f seconds", [end timeIntervalSinceDate:start] / numReps); 63 | 64 | sleep(1); 65 | 66 | [pool drain]; 67 | return EXIT_SUCCESS; 68 | } 69 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/unparse-date.m: -------------------------------------------------------------------------------- 1 | #import "ISO8601DateFormatter.h" 2 | 3 | int main(int argc, const char **argv) { 4 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 5 | 6 | ISO8601DateFormatter *formatter = [[[ISO8601DateFormatter alloc] init] autorelease]; 7 | formatter.format = ISO8601DateFormatCalendar; 8 | 9 | BOOL forceUTC = NO; 10 | 11 | while (argv[1]) { 12 | if (strcmp(argv[1], "--include-time") == 0) 13 | formatter.includeTime = YES; 14 | else if (strcmp(argv[1], "--force-utc") == 0) 15 | forceUTC = YES; 16 | else 17 | break; 18 | --argc; 19 | ++argv; 20 | } 21 | 22 | while(--argc) { 23 | NSString *arg = [NSString stringWithUTF8String:*++argv]; 24 | NSTimeZone *timeZone = nil; 25 | NSDate *date = [formatter dateFromString:arg timeZone:&timeZone]; 26 | if (forceUTC) 27 | timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"]; 28 | printf("%s\n", [[NSString stringWithFormat:@"%@:\t%@", arg, [formatter stringFromDate:date timeZone:timeZone]] UTF8String]); 29 | } 30 | 31 | [pool release]; 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/unparse-ordinaldate.m: -------------------------------------------------------------------------------- 1 | #import "ISO8601DateFormatter.h" 2 | 3 | int main(int argc, const char **argv) { 4 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 5 | 6 | ISO8601DateFormatter *formatter = [[[ISO8601DateFormatter alloc] init] autorelease]; 7 | formatter.format = ISO8601DateFormatOrdinal; 8 | 9 | while(--argc) { 10 | NSString *arg = [NSString stringWithUTF8String:*++argv]; 11 | NSTimeZone *timeZone = nil; 12 | printf("%s\n", [[NSString stringWithFormat:@"%@:\t%@", arg, [formatter stringFromDate:[formatter dateFromString:arg timeZone:&timeZone] timeZone:timeZone]] UTF8String]); 13 | } 14 | 15 | [pool release]; 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /Vendor/iso-8601-date-formatter/unparse-weekdate.m: -------------------------------------------------------------------------------- 1 | #import "ISO8601DateFormatter.h" 2 | 3 | int main(int argc, const char **argv) { 4 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 5 | 6 | ISO8601DateFormatter *formatter = [[[ISO8601DateFormatter alloc] init] autorelease]; 7 | formatter.format = ISO8601DateFormatWeek; 8 | 9 | while(--argc) { 10 | NSString *arg = [NSString stringWithUTF8String:*++argv]; 11 | NSTimeZone *timeZone = nil; 12 | printf("%s\n", [[NSString stringWithFormat:@"%@:\t%@", arg, [formatter stringFromDate:[formatter dateFromString:arg timeZone:&timeZone] timeZone:timeZone]] UTF8String]); 13 | } 14 | 15 | [pool release]; 16 | return 0; 17 | } 18 | --------------------------------------------------------------------------------