├── .gitignore ├── Demo ├── Podfile ├── RealmJSONDemo.xcodeproj │ └── project.pbxproj ├── RealmJSONDemo │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── MCAppDelegate.h │ ├── MCAppDelegate.m │ ├── MCEpisode.h │ ├── MCEpisode.m │ ├── MCTableViewController.h │ ├── MCTableViewController.m │ ├── RealmJSONDemo-Info.plist │ ├── RealmJSONDemo-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── RealmJSONDemoTests │ ├── RealmJSONDemoTests-Info.plist │ ├── RealmJSONDemoTests.m │ └── en.lproj │ └── InfoPlist.strings ├── LICENSE ├── README.md ├── Realm+JSON.podspec └── Realm+JSON ├── MCJSONDateTransformer.h ├── MCJSONDateTransformer.m ├── MCJSONNonNullStringTransformer.h ├── MCJSONNonNullStringTransformer.m ├── MCJSONPrimaryKeyTransformer.h ├── MCJSONPrimaryKeyTransformer.m ├── MCJSONValueTransformer.h ├── MCJSONValueTransformer.m ├── RLMObject+Copying.h ├── RLMObject+Copying.m ├── RLMObject+JSON.h └── RLMObject+JSON.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | .DS_Store 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | *.xcworkspace 16 | !default.xcworkspace 17 | xcuserdata 18 | profile 19 | *.moved-aside 20 | DerivedData 21 | .idea/ 22 | 23 | # CocoaPods 24 | Pods 25 | Podfile.lock 26 | -------------------------------------------------------------------------------- /Demo/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, '7.0' 3 | 4 | pod 'AFNetworking' 5 | pod 'Realm+JSON', :path => '../' 6 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | archiveVersion 6 | 1 7 | classes 8 | 9 | objectVersion 10 | 46 11 | objects 12 | 13 | 1EF2988E0D4955BC37C2AB0C 14 | 15 | includeInIndex 16 | 1 17 | isa 18 | PBXFileReference 19 | lastKnownFileType 20 | text.xcconfig 21 | name 22 | Pods.debug.xcconfig 23 | path 24 | Pods/Target Support Files/Pods/Pods.debug.xcconfig 25 | sourceTree 26 | <group> 27 | 28 | 34D5F9921984ACD800BE1BBF 29 | 30 | children 31 | 32 | 34D5F9A41984ACD900BE1BBF 33 | 34D5F9C61984ACD900BE1BBF 34 | 34D5F99D1984ACD900BE1BBF 35 | 34D5F99C1984ACD900BE1BBF 36 | 9290BFDD1548386FF432BF83 37 | 38 | isa 39 | PBXGroup 40 | sourceTree 41 | <group> 42 | 43 | 34D5F9931984ACD800BE1BBF 44 | 45 | attributes 46 | 47 | CLASSPREFIX 48 | MC 49 | LastUpgradeCheck 50 | 0510 51 | ORGANIZATIONNAME 52 | Matthew Cheok 53 | TargetAttributes 54 | 55 | 34D5F9BE1984ACD900BE1BBF 56 | 57 | TestTargetID 58 | 34D5F99A1984ACD900BE1BBF 59 | 60 | 61 | 62 | buildConfigurationList 63 | 34D5F9961984ACD800BE1BBF 64 | compatibilityVersion 65 | Xcode 3.2 66 | developmentRegion 67 | English 68 | hasScannedForEncodings 69 | 0 70 | isa 71 | PBXProject 72 | knownRegions 73 | 74 | en 75 | Base 76 | 77 | mainGroup 78 | 34D5F9921984ACD800BE1BBF 79 | productRefGroup 80 | 34D5F99C1984ACD900BE1BBF 81 | projectDirPath 82 | 83 | projectReferences 84 | 85 | projectRoot 86 | 87 | targets 88 | 89 | 34D5F99A1984ACD900BE1BBF 90 | 34D5F9BE1984ACD900BE1BBF 91 | 92 | 93 | 34D5F9961984ACD800BE1BBF 94 | 95 | buildConfigurations 96 | 97 | 34D5F9CE1984ACD900BE1BBF 98 | 34D5F9CF1984ACD900BE1BBF 99 | 100 | defaultConfigurationIsVisible 101 | 0 102 | defaultConfigurationName 103 | Release 104 | isa 105 | XCConfigurationList 106 | 107 | 34D5F9971984ACD900BE1BBF 108 | 109 | buildActionMask 110 | 2147483647 111 | files 112 | 113 | 34D5F9D81984AEE300BE1BBF 114 | 34D5F9AF1984ACD900BE1BBF 115 | 34D5F9AB1984ACD900BE1BBF 116 | 34D5F9DB1984C84700BE1BBF 117 | 118 | isa 119 | PBXSourcesBuildPhase 120 | runOnlyForDeploymentPostprocessing 121 | 0 122 | 123 | 34D5F9981984ACD900BE1BBF 124 | 125 | buildActionMask 126 | 2147483647 127 | files 128 | 129 | 34D5F9A11984ACD900BE1BBF 130 | 34D5F9A31984ACD900BE1BBF 131 | 34D5F99F1984ACD900BE1BBF 132 | 81665805815743B5997ED31A 133 | 134 | isa 135 | PBXFrameworksBuildPhase 136 | runOnlyForDeploymentPostprocessing 137 | 0 138 | 139 | 34D5F9991984ACD900BE1BBF 140 | 141 | buildActionMask 142 | 2147483647 143 | files 144 | 145 | 34D5F9BA1984ACD900BE1BBF 146 | 34D5F9A91984ACD900BE1BBF 147 | 34D5F9B21984ACD900BE1BBF 148 | 149 | isa 150 | PBXResourcesBuildPhase 151 | runOnlyForDeploymentPostprocessing 152 | 0 153 | 154 | 34D5F99A1984ACD900BE1BBF 155 | 156 | buildConfigurationList 157 | 34D5F9D01984ACD900BE1BBF 158 | buildPhases 159 | 160 | 90A24D59A82C40FDA4D09950 161 | 34D5F9971984ACD900BE1BBF 162 | 34D5F9981984ACD900BE1BBF 163 | 34D5F9991984ACD900BE1BBF 164 | F0A39AD6E14A4087B2748160 165 | 166 | buildRules 167 | 168 | dependencies 169 | 170 | isa 171 | PBXNativeTarget 172 | name 173 | RealmJSONDemo 174 | productName 175 | RealmJSONDemo 176 | productReference 177 | 34D5F99B1984ACD900BE1BBF 178 | productType 179 | com.apple.product-type.application 180 | 181 | 34D5F99B1984ACD900BE1BBF 182 | 183 | explicitFileType 184 | wrapper.application 185 | includeInIndex 186 | 0 187 | isa 188 | PBXFileReference 189 | path 190 | RealmJSONDemo.app 191 | sourceTree 192 | BUILT_PRODUCTS_DIR 193 | 194 | 34D5F99C1984ACD900BE1BBF 195 | 196 | children 197 | 198 | 34D5F99B1984ACD900BE1BBF 199 | 34D5F9BF1984ACD900BE1BBF 200 | 201 | isa 202 | PBXGroup 203 | name 204 | Products 205 | sourceTree 206 | <group> 207 | 208 | 34D5F99D1984ACD900BE1BBF 209 | 210 | children 211 | 212 | 34D5F99E1984ACD900BE1BBF 213 | 34D5F9A01984ACD900BE1BBF 214 | 34D5F9A21984ACD900BE1BBF 215 | 34D5F9C01984ACD900BE1BBF 216 | CCDF13328EF748B29194D191 217 | 218 | isa 219 | PBXGroup 220 | name 221 | Frameworks 222 | sourceTree 223 | <group> 224 | 225 | 34D5F99E1984ACD900BE1BBF 226 | 227 | isa 228 | PBXFileReference 229 | lastKnownFileType 230 | wrapper.framework 231 | name 232 | Foundation.framework 233 | path 234 | System/Library/Frameworks/Foundation.framework 235 | sourceTree 236 | SDKROOT 237 | 238 | 34D5F99F1984ACD900BE1BBF 239 | 240 | fileRef 241 | 34D5F99E1984ACD900BE1BBF 242 | isa 243 | PBXBuildFile 244 | 245 | 34D5F9A01984ACD900BE1BBF 246 | 247 | isa 248 | PBXFileReference 249 | lastKnownFileType 250 | wrapper.framework 251 | name 252 | CoreGraphics.framework 253 | path 254 | System/Library/Frameworks/CoreGraphics.framework 255 | sourceTree 256 | SDKROOT 257 | 258 | 34D5F9A11984ACD900BE1BBF 259 | 260 | fileRef 261 | 34D5F9A01984ACD900BE1BBF 262 | isa 263 | PBXBuildFile 264 | 265 | 34D5F9A21984ACD900BE1BBF 266 | 267 | isa 268 | PBXFileReference 269 | lastKnownFileType 270 | wrapper.framework 271 | name 272 | UIKit.framework 273 | path 274 | System/Library/Frameworks/UIKit.framework 275 | sourceTree 276 | SDKROOT 277 | 278 | 34D5F9A31984ACD900BE1BBF 279 | 280 | fileRef 281 | 34D5F9A21984ACD900BE1BBF 282 | isa 283 | PBXBuildFile 284 | 285 | 34D5F9A41984ACD900BE1BBF 286 | 287 | children 288 | 289 | 34D5F9AD1984ACD900BE1BBF 290 | 34D5F9AE1984ACD900BE1BBF 291 | 34D5F9B01984ACD900BE1BBF 292 | 34D5F9D61984AEE300BE1BBF 293 | 34D5F9D71984AEE300BE1BBF 294 | 34D5F9D91984C84700BE1BBF 295 | 34D5F9DA1984C84700BE1BBF 296 | 34D5F9B91984ACD900BE1BBF 297 | 34D5F9A51984ACD900BE1BBF 298 | 299 | isa 300 | PBXGroup 301 | path 302 | RealmJSONDemo 303 | sourceTree 304 | <group> 305 | 306 | 34D5F9A51984ACD900BE1BBF 307 | 308 | children 309 | 310 | 34D5F9A61984ACD900BE1BBF 311 | 34D5F9A71984ACD900BE1BBF 312 | 34D5F9AA1984ACD900BE1BBF 313 | 34D5F9AC1984ACD900BE1BBF 314 | 315 | isa 316 | PBXGroup 317 | name 318 | Supporting Files 319 | sourceTree 320 | <group> 321 | 322 | 34D5F9A61984ACD900BE1BBF 323 | 324 | isa 325 | PBXFileReference 326 | lastKnownFileType 327 | text.plist.xml 328 | path 329 | RealmJSONDemo-Info.plist 330 | sourceTree 331 | <group> 332 | 333 | 34D5F9A71984ACD900BE1BBF 334 | 335 | children 336 | 337 | 34D5F9A81984ACD900BE1BBF 338 | 339 | isa 340 | PBXVariantGroup 341 | name 342 | InfoPlist.strings 343 | sourceTree 344 | <group> 345 | 346 | 34D5F9A81984ACD900BE1BBF 347 | 348 | isa 349 | PBXFileReference 350 | lastKnownFileType 351 | text.plist.strings 352 | name 353 | en 354 | path 355 | en.lproj/InfoPlist.strings 356 | sourceTree 357 | <group> 358 | 359 | 34D5F9A91984ACD900BE1BBF 360 | 361 | fileRef 362 | 34D5F9A71984ACD900BE1BBF 363 | isa 364 | PBXBuildFile 365 | 366 | 34D5F9AA1984ACD900BE1BBF 367 | 368 | isa 369 | PBXFileReference 370 | lastKnownFileType 371 | sourcecode.c.objc 372 | path 373 | main.m 374 | sourceTree 375 | <group> 376 | 377 | 34D5F9AB1984ACD900BE1BBF 378 | 379 | fileRef 380 | 34D5F9AA1984ACD900BE1BBF 381 | isa 382 | PBXBuildFile 383 | 384 | 34D5F9AC1984ACD900BE1BBF 385 | 386 | isa 387 | PBXFileReference 388 | lastKnownFileType 389 | sourcecode.c.h 390 | path 391 | RealmJSONDemo-Prefix.pch 392 | sourceTree 393 | <group> 394 | 395 | 34D5F9AD1984ACD900BE1BBF 396 | 397 | isa 398 | PBXFileReference 399 | lastKnownFileType 400 | sourcecode.c.h 401 | path 402 | MCAppDelegate.h 403 | sourceTree 404 | <group> 405 | 406 | 34D5F9AE1984ACD900BE1BBF 407 | 408 | isa 409 | PBXFileReference 410 | lastKnownFileType 411 | sourcecode.c.objc 412 | path 413 | MCAppDelegate.m 414 | sourceTree 415 | <group> 416 | 417 | 34D5F9AF1984ACD900BE1BBF 418 | 419 | fileRef 420 | 34D5F9AE1984ACD900BE1BBF 421 | isa 422 | PBXBuildFile 423 | 424 | 34D5F9B01984ACD900BE1BBF 425 | 426 | children 427 | 428 | 34D5F9B11984ACD900BE1BBF 429 | 430 | isa 431 | PBXVariantGroup 432 | name 433 | Main.storyboard 434 | sourceTree 435 | <group> 436 | 437 | 34D5F9B11984ACD900BE1BBF 438 | 439 | isa 440 | PBXFileReference 441 | lastKnownFileType 442 | file.storyboard 443 | name 444 | Base 445 | path 446 | Base.lproj/Main.storyboard 447 | sourceTree 448 | <group> 449 | 450 | 34D5F9B21984ACD900BE1BBF 451 | 452 | fileRef 453 | 34D5F9B01984ACD900BE1BBF 454 | isa 455 | PBXBuildFile 456 | 457 | 34D5F9B91984ACD900BE1BBF 458 | 459 | isa 460 | PBXFileReference 461 | lastKnownFileType 462 | folder.assetcatalog 463 | path 464 | Images.xcassets 465 | sourceTree 466 | <group> 467 | 468 | 34D5F9BA1984ACD900BE1BBF 469 | 470 | fileRef 471 | 34D5F9B91984ACD900BE1BBF 472 | isa 473 | PBXBuildFile 474 | 475 | 34D5F9BB1984ACD900BE1BBF 476 | 477 | buildActionMask 478 | 2147483647 479 | files 480 | 481 | 34D5F9CD1984ACD900BE1BBF 482 | 483 | isa 484 | PBXSourcesBuildPhase 485 | runOnlyForDeploymentPostprocessing 486 | 0 487 | 488 | 34D5F9BC1984ACD900BE1BBF 489 | 490 | buildActionMask 491 | 2147483647 492 | files 493 | 494 | 34D5F9C11984ACD900BE1BBF 495 | 34D5F9C31984ACD900BE1BBF 496 | 34D5F9C21984ACD900BE1BBF 497 | 498 | isa 499 | PBXFrameworksBuildPhase 500 | runOnlyForDeploymentPostprocessing 501 | 0 502 | 503 | 34D5F9BD1984ACD900BE1BBF 504 | 505 | buildActionMask 506 | 2147483647 507 | files 508 | 509 | 34D5F9CB1984ACD900BE1BBF 510 | 511 | isa 512 | PBXResourcesBuildPhase 513 | runOnlyForDeploymentPostprocessing 514 | 0 515 | 516 | 34D5F9BE1984ACD900BE1BBF 517 | 518 | buildConfigurationList 519 | 34D5F9D31984ACD900BE1BBF 520 | buildPhases 521 | 522 | 34D5F9BB1984ACD900BE1BBF 523 | 34D5F9BC1984ACD900BE1BBF 524 | 34D5F9BD1984ACD900BE1BBF 525 | 526 | buildRules 527 | 528 | dependencies 529 | 530 | 34D5F9C51984ACD900BE1BBF 531 | 532 | isa 533 | PBXNativeTarget 534 | name 535 | RealmJSONDemoTests 536 | productName 537 | RealmJSONDemoTests 538 | productReference 539 | 34D5F9BF1984ACD900BE1BBF 540 | productType 541 | com.apple.product-type.bundle.unit-test 542 | 543 | 34D5F9BF1984ACD900BE1BBF 544 | 545 | explicitFileType 546 | wrapper.cfbundle 547 | includeInIndex 548 | 0 549 | isa 550 | PBXFileReference 551 | path 552 | RealmJSONDemoTests.xctest 553 | sourceTree 554 | BUILT_PRODUCTS_DIR 555 | 556 | 34D5F9C01984ACD900BE1BBF 557 | 558 | isa 559 | PBXFileReference 560 | lastKnownFileType 561 | wrapper.framework 562 | name 563 | XCTest.framework 564 | path 565 | Library/Frameworks/XCTest.framework 566 | sourceTree 567 | DEVELOPER_DIR 568 | 569 | 34D5F9C11984ACD900BE1BBF 570 | 571 | fileRef 572 | 34D5F9C01984ACD900BE1BBF 573 | isa 574 | PBXBuildFile 575 | 576 | 34D5F9C21984ACD900BE1BBF 577 | 578 | fileRef 579 | 34D5F99E1984ACD900BE1BBF 580 | isa 581 | PBXBuildFile 582 | 583 | 34D5F9C31984ACD900BE1BBF 584 | 585 | fileRef 586 | 34D5F9A21984ACD900BE1BBF 587 | isa 588 | PBXBuildFile 589 | 590 | 34D5F9C41984ACD900BE1BBF 591 | 592 | containerPortal 593 | 34D5F9931984ACD800BE1BBF 594 | isa 595 | PBXContainerItemProxy 596 | proxyType 597 | 1 598 | remoteGlobalIDString 599 | 34D5F99A1984ACD900BE1BBF 600 | remoteInfo 601 | RealmJSONDemo 602 | 603 | 34D5F9C51984ACD900BE1BBF 604 | 605 | isa 606 | PBXTargetDependency 607 | target 608 | 34D5F99A1984ACD900BE1BBF 609 | targetProxy 610 | 34D5F9C41984ACD900BE1BBF 611 | 612 | 34D5F9C61984ACD900BE1BBF 613 | 614 | children 615 | 616 | 34D5F9CC1984ACD900BE1BBF 617 | 34D5F9C71984ACD900BE1BBF 618 | 619 | isa 620 | PBXGroup 621 | path 622 | RealmJSONDemoTests 623 | sourceTree 624 | <group> 625 | 626 | 34D5F9C71984ACD900BE1BBF 627 | 628 | children 629 | 630 | 34D5F9C81984ACD900BE1BBF 631 | 34D5F9C91984ACD900BE1BBF 632 | 633 | isa 634 | PBXGroup 635 | name 636 | Supporting Files 637 | sourceTree 638 | <group> 639 | 640 | 34D5F9C81984ACD900BE1BBF 641 | 642 | isa 643 | PBXFileReference 644 | lastKnownFileType 645 | text.plist.xml 646 | path 647 | RealmJSONDemoTests-Info.plist 648 | sourceTree 649 | <group> 650 | 651 | 34D5F9C91984ACD900BE1BBF 652 | 653 | children 654 | 655 | 34D5F9CA1984ACD900BE1BBF 656 | 657 | isa 658 | PBXVariantGroup 659 | name 660 | InfoPlist.strings 661 | sourceTree 662 | <group> 663 | 664 | 34D5F9CA1984ACD900BE1BBF 665 | 666 | isa 667 | PBXFileReference 668 | lastKnownFileType 669 | text.plist.strings 670 | name 671 | en 672 | path 673 | en.lproj/InfoPlist.strings 674 | sourceTree 675 | <group> 676 | 677 | 34D5F9CB1984ACD900BE1BBF 678 | 679 | fileRef 680 | 34D5F9C91984ACD900BE1BBF 681 | isa 682 | PBXBuildFile 683 | 684 | 34D5F9CC1984ACD900BE1BBF 685 | 686 | isa 687 | PBXFileReference 688 | lastKnownFileType 689 | sourcecode.c.objc 690 | path 691 | RealmJSONDemoTests.m 692 | sourceTree 693 | <group> 694 | 695 | 34D5F9CD1984ACD900BE1BBF 696 | 697 | fileRef 698 | 34D5F9CC1984ACD900BE1BBF 699 | isa 700 | PBXBuildFile 701 | 702 | 34D5F9CE1984ACD900BE1BBF 703 | 704 | buildSettings 705 | 706 | ALWAYS_SEARCH_USER_PATHS 707 | NO 708 | CLANG_CXX_LANGUAGE_STANDARD 709 | gnu++0x 710 | CLANG_CXX_LIBRARY 711 | libc++ 712 | CLANG_ENABLE_MODULES 713 | YES 714 | CLANG_ENABLE_OBJC_ARC 715 | YES 716 | CLANG_WARN_BOOL_CONVERSION 717 | YES 718 | CLANG_WARN_CONSTANT_CONVERSION 719 | YES 720 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE 721 | YES_ERROR 722 | CLANG_WARN_EMPTY_BODY 723 | YES 724 | CLANG_WARN_ENUM_CONVERSION 725 | YES 726 | CLANG_WARN_INT_CONVERSION 727 | YES 728 | CLANG_WARN_OBJC_ROOT_CLASS 729 | YES_ERROR 730 | CLANG_WARN__DUPLICATE_METHOD_MATCH 731 | YES 732 | CODE_SIGN_IDENTITY[sdk=iphoneos*] 733 | iPhone Developer 734 | COPY_PHASE_STRIP 735 | NO 736 | GCC_C_LANGUAGE_STANDARD 737 | gnu99 738 | GCC_DYNAMIC_NO_PIC 739 | NO 740 | GCC_OPTIMIZATION_LEVEL 741 | 0 742 | GCC_PREPROCESSOR_DEFINITIONS 743 | 744 | DEBUG=1 745 | $(inherited) 746 | 747 | GCC_SYMBOLS_PRIVATE_EXTERN 748 | NO 749 | GCC_WARN_64_TO_32_BIT_CONVERSION 750 | YES 751 | GCC_WARN_ABOUT_RETURN_TYPE 752 | YES_ERROR 753 | GCC_WARN_UNDECLARED_SELECTOR 754 | YES 755 | GCC_WARN_UNINITIALIZED_AUTOS 756 | YES_AGGRESSIVE 757 | GCC_WARN_UNUSED_FUNCTION 758 | YES 759 | GCC_WARN_UNUSED_VARIABLE 760 | YES 761 | IPHONEOS_DEPLOYMENT_TARGET 762 | 7.1 763 | ONLY_ACTIVE_ARCH 764 | YES 765 | SDKROOT 766 | iphoneos 767 | 768 | isa 769 | XCBuildConfiguration 770 | name 771 | Debug 772 | 773 | 34D5F9CF1984ACD900BE1BBF 774 | 775 | buildSettings 776 | 777 | ALWAYS_SEARCH_USER_PATHS 778 | NO 779 | CLANG_CXX_LANGUAGE_STANDARD 780 | gnu++0x 781 | CLANG_CXX_LIBRARY 782 | libc++ 783 | CLANG_ENABLE_MODULES 784 | YES 785 | CLANG_ENABLE_OBJC_ARC 786 | YES 787 | CLANG_WARN_BOOL_CONVERSION 788 | YES 789 | CLANG_WARN_CONSTANT_CONVERSION 790 | YES 791 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE 792 | YES_ERROR 793 | CLANG_WARN_EMPTY_BODY 794 | YES 795 | CLANG_WARN_ENUM_CONVERSION 796 | YES 797 | CLANG_WARN_INT_CONVERSION 798 | YES 799 | CLANG_WARN_OBJC_ROOT_CLASS 800 | YES_ERROR 801 | CLANG_WARN__DUPLICATE_METHOD_MATCH 802 | YES 803 | CODE_SIGN_IDENTITY[sdk=iphoneos*] 804 | iPhone Developer 805 | COPY_PHASE_STRIP 806 | YES 807 | ENABLE_NS_ASSERTIONS 808 | NO 809 | GCC_C_LANGUAGE_STANDARD 810 | gnu99 811 | GCC_WARN_64_TO_32_BIT_CONVERSION 812 | YES 813 | GCC_WARN_ABOUT_RETURN_TYPE 814 | YES_ERROR 815 | GCC_WARN_UNDECLARED_SELECTOR 816 | YES 817 | GCC_WARN_UNINITIALIZED_AUTOS 818 | YES_AGGRESSIVE 819 | GCC_WARN_UNUSED_FUNCTION 820 | YES 821 | GCC_WARN_UNUSED_VARIABLE 822 | YES 823 | IPHONEOS_DEPLOYMENT_TARGET 824 | 7.1 825 | SDKROOT 826 | iphoneos 827 | VALIDATE_PRODUCT 828 | YES 829 | 830 | isa 831 | XCBuildConfiguration 832 | name 833 | Release 834 | 835 | 34D5F9D01984ACD900BE1BBF 836 | 837 | buildConfigurations 838 | 839 | 34D5F9D11984ACD900BE1BBF 840 | 34D5F9D21984ACD900BE1BBF 841 | 842 | defaultConfigurationIsVisible 843 | 0 844 | defaultConfigurationName 845 | Release 846 | isa 847 | XCConfigurationList 848 | 849 | 34D5F9D11984ACD900BE1BBF 850 | 851 | baseConfigurationReference 852 | 1EF2988E0D4955BC37C2AB0C 853 | buildSettings 854 | 855 | ASSETCATALOG_COMPILER_APPICON_NAME 856 | AppIcon 857 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME 858 | LaunchImage 859 | GCC_PRECOMPILE_PREFIX_HEADER 860 | YES 861 | GCC_PREFIX_HEADER 862 | RealmJSONDemo/RealmJSONDemo-Prefix.pch 863 | INFOPLIST_FILE 864 | RealmJSONDemo/RealmJSONDemo-Info.plist 865 | PRODUCT_NAME 866 | $(TARGET_NAME) 867 | WRAPPER_EXTENSION 868 | app 869 | 870 | isa 871 | XCBuildConfiguration 872 | name 873 | Debug 874 | 875 | 34D5F9D21984ACD900BE1BBF 876 | 877 | baseConfigurationReference 878 | 970782E9E7A985538894C200 879 | buildSettings 880 | 881 | ASSETCATALOG_COMPILER_APPICON_NAME 882 | AppIcon 883 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME 884 | LaunchImage 885 | GCC_PRECOMPILE_PREFIX_HEADER 886 | YES 887 | GCC_PREFIX_HEADER 888 | RealmJSONDemo/RealmJSONDemo-Prefix.pch 889 | INFOPLIST_FILE 890 | RealmJSONDemo/RealmJSONDemo-Info.plist 891 | PRODUCT_NAME 892 | $(TARGET_NAME) 893 | WRAPPER_EXTENSION 894 | app 895 | 896 | isa 897 | XCBuildConfiguration 898 | name 899 | Release 900 | 901 | 34D5F9D31984ACD900BE1BBF 902 | 903 | buildConfigurations 904 | 905 | 34D5F9D41984ACD900BE1BBF 906 | 34D5F9D51984ACD900BE1BBF 907 | 908 | defaultConfigurationIsVisible 909 | 0 910 | defaultConfigurationName 911 | Release 912 | isa 913 | XCConfigurationList 914 | 915 | 34D5F9D41984ACD900BE1BBF 916 | 917 | buildSettings 918 | 919 | BUNDLE_LOADER 920 | $(BUILT_PRODUCTS_DIR)/RealmJSONDemo.app/RealmJSONDemo 921 | FRAMEWORK_SEARCH_PATHS 922 | 923 | $(SDKROOT)/Developer/Library/Frameworks 924 | $(inherited) 925 | $(DEVELOPER_FRAMEWORKS_DIR) 926 | 927 | GCC_PRECOMPILE_PREFIX_HEADER 928 | YES 929 | GCC_PREFIX_HEADER 930 | RealmJSONDemo/RealmJSONDemo-Prefix.pch 931 | GCC_PREPROCESSOR_DEFINITIONS 932 | 933 | DEBUG=1 934 | $(inherited) 935 | 936 | INFOPLIST_FILE 937 | RealmJSONDemoTests/RealmJSONDemoTests-Info.plist 938 | PRODUCT_NAME 939 | $(TARGET_NAME) 940 | TEST_HOST 941 | $(BUNDLE_LOADER) 942 | WRAPPER_EXTENSION 943 | xctest 944 | 945 | isa 946 | XCBuildConfiguration 947 | name 948 | Debug 949 | 950 | 34D5F9D51984ACD900BE1BBF 951 | 952 | buildSettings 953 | 954 | BUNDLE_LOADER 955 | $(BUILT_PRODUCTS_DIR)/RealmJSONDemo.app/RealmJSONDemo 956 | FRAMEWORK_SEARCH_PATHS 957 | 958 | $(SDKROOT)/Developer/Library/Frameworks 959 | $(inherited) 960 | $(DEVELOPER_FRAMEWORKS_DIR) 961 | 962 | GCC_PRECOMPILE_PREFIX_HEADER 963 | YES 964 | GCC_PREFIX_HEADER 965 | RealmJSONDemo/RealmJSONDemo-Prefix.pch 966 | INFOPLIST_FILE 967 | RealmJSONDemoTests/RealmJSONDemoTests-Info.plist 968 | PRODUCT_NAME 969 | $(TARGET_NAME) 970 | TEST_HOST 971 | $(BUNDLE_LOADER) 972 | WRAPPER_EXTENSION 973 | xctest 974 | 975 | isa 976 | XCBuildConfiguration 977 | name 978 | Release 979 | 980 | 34D5F9D61984AEE300BE1BBF 981 | 982 | fileEncoding 983 | 4 984 | isa 985 | PBXFileReference 986 | lastKnownFileType 987 | sourcecode.c.h 988 | path 989 | MCTableViewController.h 990 | sourceTree 991 | <group> 992 | 993 | 34D5F9D71984AEE300BE1BBF 994 | 995 | fileEncoding 996 | 4 997 | isa 998 | PBXFileReference 999 | lastKnownFileType 1000 | sourcecode.c.objc 1001 | path 1002 | MCTableViewController.m 1003 | sourceTree 1004 | <group> 1005 | 1006 | 34D5F9D81984AEE300BE1BBF 1007 | 1008 | fileRef 1009 | 34D5F9D71984AEE300BE1BBF 1010 | isa 1011 | PBXBuildFile 1012 | 1013 | 34D5F9D91984C84700BE1BBF 1014 | 1015 | fileEncoding 1016 | 4 1017 | isa 1018 | PBXFileReference 1019 | lastKnownFileType 1020 | sourcecode.c.h 1021 | path 1022 | MCEpisode.h 1023 | sourceTree 1024 | <group> 1025 | 1026 | 34D5F9DA1984C84700BE1BBF 1027 | 1028 | fileEncoding 1029 | 4 1030 | isa 1031 | PBXFileReference 1032 | lastKnownFileType 1033 | sourcecode.c.objc 1034 | path 1035 | MCEpisode.m 1036 | sourceTree 1037 | <group> 1038 | 1039 | 34D5F9DB1984C84700BE1BBF 1040 | 1041 | fileRef 1042 | 34D5F9DA1984C84700BE1BBF 1043 | isa 1044 | PBXBuildFile 1045 | 1046 | 81665805815743B5997ED31A 1047 | 1048 | fileRef 1049 | CCDF13328EF748B29194D191 1050 | isa 1051 | PBXBuildFile 1052 | 1053 | 90A24D59A82C40FDA4D09950 1054 | 1055 | buildActionMask 1056 | 2147483647 1057 | files 1058 | 1059 | inputPaths 1060 | 1061 | isa 1062 | PBXShellScriptBuildPhase 1063 | name 1064 | Check Pods Manifest.lock 1065 | outputPaths 1066 | 1067 | runOnlyForDeploymentPostprocessing 1068 | 0 1069 | shellPath 1070 | /bin/sh 1071 | shellScript 1072 | diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null 1073 | if [[ $? != 0 ]] ; then 1074 | cat << EOM 1075 | error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation. 1076 | EOM 1077 | exit 1 1078 | fi 1079 | 1080 | showEnvVarsInLog 1081 | 0 1082 | 1083 | 9290BFDD1548386FF432BF83 1084 | 1085 | children 1086 | 1087 | 1EF2988E0D4955BC37C2AB0C 1088 | 970782E9E7A985538894C200 1089 | 1090 | isa 1091 | PBXGroup 1092 | name 1093 | Pods 1094 | sourceTree 1095 | <group> 1096 | 1097 | 970782E9E7A985538894C200 1098 | 1099 | includeInIndex 1100 | 1 1101 | isa 1102 | PBXFileReference 1103 | lastKnownFileType 1104 | text.xcconfig 1105 | name 1106 | Pods.release.xcconfig 1107 | path 1108 | Pods/Target Support Files/Pods/Pods.release.xcconfig 1109 | sourceTree 1110 | <group> 1111 | 1112 | CCDF13328EF748B29194D191 1113 | 1114 | explicitFileType 1115 | archive.ar 1116 | includeInIndex 1117 | 0 1118 | isa 1119 | PBXFileReference 1120 | path 1121 | libPods.a 1122 | sourceTree 1123 | BUILT_PRODUCTS_DIR 1124 | 1125 | F0A39AD6E14A4087B2748160 1126 | 1127 | buildActionMask 1128 | 2147483647 1129 | files 1130 | 1131 | inputPaths 1132 | 1133 | isa 1134 | PBXShellScriptBuildPhase 1135 | name 1136 | Copy Pods Resources 1137 | outputPaths 1138 | 1139 | runOnlyForDeploymentPostprocessing 1140 | 0 1141 | shellPath 1142 | /bin/sh 1143 | shellScript 1144 | "${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh" 1145 | 1146 | showEnvVarsInLog 1147 | 0 1148 | 1149 | 1150 | rootObject 1151 | 34D5F9931984ACD800BE1BBF 1152 | 1153 | 1154 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 45 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/MCAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCAppDelegate.h 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MCAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/MCAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCAppDelegate.m 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import "MCAppDelegate.h" 10 | 11 | @implementation MCAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/MCEpisode.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCEpisode.h 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, MCEpisodeType) { 12 | MCEpisodeTypeFree = 0, 13 | MCEpisodeTypePaid 14 | }; 15 | 16 | @interface MCEpisode : RLMObject 17 | 18 | @property NSInteger episodeID; 19 | @property NSInteger episodeNumber; 20 | @property MCEpisodeType episodeType; 21 | 22 | @property NSString *title; 23 | @property NSString *subtitle; 24 | @property NSString *thumbnailURL; 25 | 26 | @property NSDate *publishedDate; 27 | 28 | @end 29 | 30 | // This protocol enables typed collections. i.e.: 31 | // RLMArray 32 | RLM_ARRAY_TYPE(MCEpisode) 33 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/MCEpisode.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCEpisode.m 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import "MCEpisode.h" 10 | #import "RLMObject+JSON.h" 11 | 12 | @implementation MCEpisode 13 | 14 | + (NSDictionary *)JSONInboundMappingDictionary { 15 | return @{ 16 | @"title": @"title", 17 | @"description": @"subtitle", 18 | @"id": @"episodeID", 19 | @"episode_number": @"episodeNumber", 20 | @"episode_type": @"episodeType", 21 | @"thumbnail_url": @"thumbnailURL", 22 | @"published_at": @"publishedDate", 23 | }; 24 | } 25 | 26 | + (NSDictionary *)JSONOutboundMappingDictionary { 27 | return @{ 28 | @"title": @"title", 29 | @"subtitle": @"episode.description", 30 | @"episodeID": @"id", 31 | @"episodeNumber": @"episode.number", 32 | @"publishedDate": @"published_at", 33 | }; 34 | } 35 | 36 | + (NSString *)primaryKey { 37 | return @"episodeID"; 38 | } 39 | 40 | + (NSValueTransformer *)episodeTypeJSONTransformer { 41 | return [MCJSONValueTransformer valueTransformerWithMappingDictionary:@{ 42 | @"free": @(MCEpisodeTypeFree), 43 | @"paid": @(MCEpisodeTypePaid) 44 | }]; 45 | } 46 | 47 | // Specify default values for properties 48 | 49 | + (NSDictionary *)defaultPropertyValues { 50 | return @{ @"publishedDate": [NSDate date] }; 51 | } 52 | 53 | // Specify properties to ignore (Realm won't persist these) 54 | 55 | //+ (NSArray *)ignoredProperties 56 | //{ 57 | // return @[]; 58 | //} 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/MCTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCTableViewController.h 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MCTableViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/MCTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCTableViewController.m 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import "MCTableViewController.h" 10 | #import "MCEpisode.h" 11 | 12 | #import 13 | #import 14 | 15 | #import 16 | #import 17 | 18 | @interface MCTableViewController () 19 | 20 | @property (nonatomic, strong) RLMResults *results; 21 | @property (nonatomic, strong) RLMNotificationToken *token; 22 | 23 | @end 24 | 25 | @implementation MCTableViewController 26 | 27 | #pragma mark - Methods 28 | 29 | - (IBAction)reloadData { 30 | AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 31 | [manager GET:@"https://www.nsscreencast.com/api/episodes.json" parameters:nil success: ^(AFHTTPRequestOperation *operation, id responseObject) { 32 | NSArray *array = responseObject[@"episodes"]; 33 | dispatch_async(dispatch_get_main_queue(), ^{ 34 | RLMRealm *realm = [RLMRealm defaultRealm]; 35 | 36 | [realm beginWriteTransaction]; 37 | NSArray *result = [MCEpisode createOrUpdateInRealm:realm withJSONArray:array]; 38 | [realm commitWriteTransaction]; 39 | 40 | NSLog(@"result: %@", result); 41 | }); 42 | } failure: ^(AFHTTPRequestOperation *operation, NSError *error) { 43 | NSLog(@"Error: %@", error); 44 | }]; 45 | } 46 | 47 | - (void)refreshData { 48 | self.results = [[MCEpisode allObjectsInRealm:[RLMRealm defaultRealm]] sortedResultsUsingProperty:@"publishedDate" ascending:NO]; 49 | [self.tableView reloadData]; 50 | } 51 | 52 | #pragma mark - UIViewController 53 | 54 | - (void)viewDidLoad { 55 | [super viewDidLoad]; 56 | 57 | self.token = [[RLMRealm defaultRealm] addNotificationBlock: ^(NSString *notification, RLMRealm *realm) { 58 | [self refreshData]; 59 | }]; 60 | [self refreshData]; 61 | [self reloadData]; 62 | } 63 | 64 | - (void)didReceiveMemoryWarning { 65 | [super didReceiveMemoryWarning]; 66 | // Dispose of any resources that can be recreated. 67 | } 68 | 69 | #pragma mark - UITableViewDataSource 70 | 71 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 72 | return 1; 73 | } 74 | 75 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 76 | return self.results.count; 77 | } 78 | 79 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 80 | static NSDateFormatter *dateFormatter = nil; 81 | if (!dateFormatter) { 82 | dateFormatter = [[NSDateFormatter alloc] init]; 83 | dateFormatter.dateStyle = NSDateFormatterMediumStyle; 84 | dateFormatter.timeStyle = NSDateFormatterShortStyle; 85 | } 86 | 87 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; 88 | MCEpisode *episode = self.results[indexPath.row]; 89 | 90 | cell.textLabel.text = episode.title; 91 | cell.detailTextLabel.text = [dateFormatter stringFromDate:episode.publishedDate]; 92 | cell.imageView.image = nil; 93 | cell.backgroundColor = episode.episodeType == MCEpisodeTypePaid ? [UIColor colorWithRed:0.996 green:0.839 blue:0.843 alpha:1]: nil; 94 | 95 | __weak UITableViewCell *weakCell = cell; 96 | [cell.imageView setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:episode.thumbnailURL]] placeholderImage:nil success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { 97 | weakCell.imageView.image = image; 98 | [weakCell setNeedsLayout]; 99 | } failure:nil]; 100 | 101 | return cell; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/RealmJSONDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.matthewcheok.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/RealmJSONDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "MCAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([MCAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemoTests/RealmJSONDemoTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.matthewcheok.${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 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemoTests/RealmJSONDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RealmJSONDemoTests.m 3 | // RealmJSONDemoTests 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RealmJSONDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation RealmJSONDemoTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Demo/RealmJSONDemoTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Matthew Cheok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Realm+JSON ![License MIT](https://go-shields.herokuapp.com/license-MIT-blue.png) 2 | ========== 3 | 4 | [![Badge w/ Version](https://cocoapod-badges.herokuapp.com/v/Realm+JSON/badge.png)](https://github.com/matthewcheok/Realm-JSON) 5 | [![Badge w/ Platform](https://cocoapod-badges.herokuapp.com/p/Realm+JSON/badge.svg)](https://github.com/matthewcheok/Realm-JSON) 6 | 7 | A concise [Mantle](https://github.com/Mantle/Mantle)-like way of working with [Realm](https://github.com/realm/realm-cocoa) and JSON. 8 | 9 | ## Breaking Change 10 | 11 | - Method `- deepCopy` replaces the previous functionality of `- shallowCopy`, which no longer maintains an object's primary key 12 | - Updated to use native primary key support in Realm 0.85.0 13 | - Update your code to use methods `-createOrUpdateInRealm:withJSONArray:` or `-createOrUpdateInRealm:withJSONDictionary:` 14 | - You must wrap these methods in a write transaction (between `[realm beginWriteTransaction];` and `[realm commitWriteTransaction];`) 15 | - These methods call `-createOrUpdateInRealm:withObject:` behind the scenes for performance. 16 | 17 | ## Installation 18 | 19 | Add the following to your [CocoaPods](http://cocoapods.org/) Podfile 20 | 21 | pod 'Realm+JSON', '~> 0.2' 22 | 23 | or clone as a git submodule, 24 | 25 | or just copy files in the ```Realm+JSON``` folder into your project. 26 | 27 | ## Using Realm+JSON 28 | 29 | Simply declare your model as normal: 30 | 31 | typedef NS_ENUM(NSInteger, MCEpisodeType) { 32 | MCEpisodeTypeFree = 0, 33 | MCEpisodeTypePaid 34 | }; 35 | 36 | @interface MCEpisode : RLMObject 37 | 38 | @property NSInteger episodeID; 39 | @property NSInteger episodeNumber; 40 | @property MCEpisodeType episodeType; 41 | 42 | @property NSString *title; 43 | @property NSString *subtitle; 44 | @property NSString *thumbnailURL; 45 | 46 | @property NSDate *publishedDate; 47 | 48 | @end 49 | 50 | RLM_ARRAY_TYPE(MCEpisode) 51 | 52 | Then pass the result of `NSJSONSerialization` or `AFNetworking` as follows: 53 | 54 | [MCEpisode createOrUpdateInRealm:[RLMRealm defaultRealm] withJSONArray:array]; 55 | 56 | or 57 | 58 | [MCEpisode createOrUpdateInRealm:[RLMRealm defaultRealm] withJSONDictionary:dictionary]; 59 | 60 | Use the `-JSONDictionary` method to get a JSON-ready dictionary for your network requests. 61 | 62 | ### Configuration 63 | 64 | You should specify the inbound and outbound JSON mapping on your `RLMObject` subclass like this: 65 | 66 | + (NSDictionary *)JSONInboundMappingDictionary { 67 | return @{ 68 | @"episode.title": @"title", 69 | @"episode.description": @"subtitle", 70 | @"episode.id": @"episodeID", 71 | @"episode.episode_number": @"episodeNumber", 72 | @"episode.episode_type": @"episodeType", 73 | @"episode.thumbnail_url": @"thumbnailURL", 74 | @"episode.published_at": @"publishedDate", 75 | }; 76 | } 77 | 78 | + (NSDictionary *)JSONOutboundMappingDictionary { 79 | return @{ 80 | @"title": @"title", 81 | @"subtitle": @"episode.description", 82 | @"episodeID": @"id", 83 | @"episodeNumber": @"episode.number", 84 | @"publishedDate": @"published_at", 85 | }; 86 | } 87 | 88 | JSON preprocessing can be done by implementing `jsonPreprocessing:` static method: 89 | 90 | ```ObjC 91 | - (NSDictionary *)jsonPreprocessing:(NSDictionary *)dictionary { 92 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:dictionary]; 93 | dict[@"releaseCount"] = @(0); 94 | return dict.copy; 95 | } 96 | ``` 97 | 98 | Leaving out either one of the above will result in a mapping that assumes camelCase for your properties which map to snake_case for the JSON equivalents. 99 | 100 | As you can do with Mantle, you can specify `NSValueTransformers` for your properties: 101 | 102 | + (NSValueTransformer *)episodeTypeJSONTransformer { 103 | return [MCJSONValueTransformer valueTransformerWithMappingDictionary:@{ 104 | @"free": @(MCEpisodeTypeFree), 105 | @"paid": @(MCEpisodeTypePaid) 106 | }]; 107 | } 108 | 109 | ## Working with background threads 110 | 111 | Realm requires that you use different `RLMRealm` objects when working across different threads. This means you shouldn't access the same `RLMObject` instances from different threads. To make this easier to work with, use `- primaryKeyValue` to retrieve the wrapped primary key value from an instance and query for it later using `+ objectInRealm:withPrimaryKeyValue:`. 112 | 113 | id primaryKeyValue = [episode primaryKeyValue]; 114 | dispatch_async(dispatch_get_main_queue(), ^{ 115 | MCEpisode *episode = [MCEpisode objectInRealm:[RLMRealm defaultRealm] withPrimaryKeyValue:primaryKeyValue]; 116 | 117 | // do something with episode here 118 | }); 119 | 120 | 121 | ## Working with (temporary) copies (RLMObject+Copying) 122 | 123 | If you need to display UI that may or may not change an object's properties, it is sometimes useful to work with an object not bound to a realm as a backing model object. When it is time to commit changes, the properties can be copied back to the stored model. 124 | 125 | Methods `- shallowCopy` and `- mergePropertiesFromObject:` are provided. The later of which needs to be performed in a realm transaction. 126 | 127 | #import 128 | 129 | MCEpisode *anotherEpisode = [episode shallowCopy]; 130 | anotherEpisode.title = @"New title"; 131 | 132 | // ... 133 | 134 | [episode performInTransaction:^{ 135 | [episode mergePropertiesFromObject:anotherEpisode]; 136 | }]; 137 | 138 | Additionally, method `- deepCopy` is provided. Unlike `- shallowCopy`, it maintains the object's primary key. 139 | 140 | ## License 141 | 142 | Realm+JSON is under the MIT license. 143 | -------------------------------------------------------------------------------- /Realm+JSON.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Realm+JSON' 3 | s.version = '0.2.16' 4 | s.ios.deployment_target = '7.0' 5 | s.osx.deployment_target = '10.9' 6 | s.watchos.deployment_target = '2.0' 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.summary = 'A concise Mantle-like way of working with Realm and JSON.' 9 | s.homepage = 'https://github.com/matthewcheok/Realm-JSON' 10 | s.author = { 'Matthew Cheok' => 'cheok.jz@gmail.com' } 11 | s.requires_arc = true 12 | s.source = { 13 | :git => 'https://github.com/matthewcheok/Realm-JSON.git', 14 | :branch => 'master', 15 | :tag => s.version.to_s 16 | } 17 | s.source_files = 'Realm+JSON/*.{h,m}' 18 | s.public_header_files = 'Realm+JSON/*.h' 19 | 20 | s.dependency 'Realm', '~> 1.0' 21 | end 22 | -------------------------------------------------------------------------------- /Realm+JSON/MCJSONDateTransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCJSONDateTransformer.h 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString* const MCJSONDateTimeTransformerName; 12 | extern NSString* const MCJSONDateTimeMillisecondTransformerName; 13 | extern NSString* const MCJSONDateOnlyTransformerName; 14 | 15 | typedef NS_ENUM(NSInteger, MCJSONDateTransformerStyle) { 16 | MCJSONDateTransformerStyleDateTime = 0, 17 | MCJSONDateTransformerStyleDateTimeMillisecond, 18 | MCJSONDateTransformerStyleDateOnly 19 | }; 20 | 21 | @interface MCJSONDateTransformer : NSValueTransformer 22 | 23 | + (instancetype)valueTransformerWithDateStyle:(MCJSONDateTransformerStyle)style; 24 | - (instancetype)initWithDateStyle:(MCJSONDateTransformerStyle)style; 25 | + (instancetype)valueTransformerWithDateFormat:(NSString *)dateFormat; 26 | - (instancetype)initWithDateFormat:(NSString *)dateFormat; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Realm+JSON/MCJSONDateTransformer.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCJSONDateTransformer.m 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import "MCJSONDateTransformer.h" 10 | 11 | NSString* const MCJSONDateTimeTransformerName = @"MCJSONDateTimeTransformerName"; 12 | NSString* const MCJSONDateTimeMillisecondTransformerName = @"MCJSONDateTimeMillisecondTransformerName"; 13 | NSString* const MCJSONDateOnlyTransformerName = @"MCJSONDateOnlyTransformerName"; 14 | static NSString *const kDateFormatDateTime = @"yyyy-MM-dd'T'HH:mm:ssZZZZZ"; 15 | static NSString *const kDateFormatDateTimeMillisecond = @"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"; 16 | static NSString *const kDateFormatDateOnly = @"yyyy-MM-dd"; 17 | 18 | @interface MCJSONDateTransformer () 19 | 20 | @property (nonatomic, strong) NSDateFormatter *formatter; 21 | 22 | @end 23 | 24 | @implementation MCJSONDateTransformer 25 | 26 | + (void)load { 27 | [NSValueTransformer setValueTransformer:[[self alloc] initWithDateStyle:MCJSONDateTransformerStyleDateTime] forName:MCJSONDateTimeTransformerName]; 28 | [NSValueTransformer setValueTransformer:[[self alloc] initWithDateStyle:MCJSONDateTransformerStyleDateTimeMillisecond] forName:MCJSONDateTimeMillisecondTransformerName]; 29 | [NSValueTransformer setValueTransformer:[[self alloc] initWithDateStyle:MCJSONDateTransformerStyleDateOnly] forName:MCJSONDateOnlyTransformerName]; 30 | } 31 | 32 | + (instancetype)valueTransformerWithDateStyle:(MCJSONDateTransformerStyle)style { 33 | return [[self alloc] initWithDateStyle:style]; 34 | } 35 | 36 | - (instancetype)initWithDateStyle:(MCJSONDateTransformerStyle)style { 37 | switch (style) { 38 | case MCJSONDateTransformerStyleDateOnly: 39 | self = [self initWithDateFormat:kDateFormatDateOnly]; 40 | break; 41 | case MCJSONDateTransformerStyleDateTimeMillisecond: 42 | self = [self initWithDateFormat:kDateFormatDateTimeMillisecond]; 43 | break; 44 | 45 | default: 46 | self = [self initWithDateFormat:kDateFormatDateTime]; 47 | break; 48 | } 49 | return self; 50 | } 51 | 52 | + (instancetype)valueTransformerWithDateFormat:(NSString *)dateFormat { 53 | return [[self alloc] initWithDateFormat:dateFormat]; 54 | } 55 | 56 | - (instancetype)initWithDateFormat:(NSString *)dateFormat { 57 | self = [super init]; 58 | if (self) { 59 | self.formatter = [[NSDateFormatter alloc] init]; 60 | self.formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 61 | self.formatter.dateFormat = dateFormat; 62 | } 63 | return self; 64 | } 65 | 66 | + (Class)transformedValueClass { 67 | return [NSDate class]; 68 | } 69 | 70 | + (BOOL)allowsReverseTransformation { 71 | return YES; 72 | } 73 | 74 | - (id)transformedValue:(id)value { 75 | if([value isKindOfClass:[NSNull class]])return nil; 76 | return [self.formatter dateFromString:value]; 77 | } 78 | 79 | - (id)reverseTransformedValue:(id)value { 80 | return [self.formatter stringFromDate:value]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /Realm+JSON/MCJSONNonNullStringTransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCJSONNonNullStringTransformer.h 3 | // Pods 4 | // 5 | // Created by Matthew Cheok on 23/5/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface MCJSONNonNullStringTransformer : NSValueTransformer 12 | 13 | + (instancetype)valueTransformer; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Realm+JSON/MCJSONNonNullStringTransformer.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCJSONNonNullStringTransformer.m 3 | // Pods 4 | // 5 | // Created by Matthew Cheok on 23/5/15. 6 | // 7 | // 8 | 9 | #import "MCJSONNonNullStringTransformer.h" 10 | 11 | @implementation MCJSONNonNullStringTransformer 12 | 13 | + (instancetype)valueTransformer { 14 | return [[self alloc] init]; 15 | } 16 | 17 | + (Class)transformedValueClass { 18 | return [NSString class]; 19 | } 20 | 21 | + (BOOL)allowsReverseTransformation { 22 | return YES; 23 | } 24 | 25 | - (id)transformedValue:(id)value { 26 | if (value && ![value isKindOfClass:[NSNull class]]) { 27 | return value; 28 | } 29 | else { 30 | return @""; 31 | } 32 | } 33 | 34 | - (id)reverseTransformedValue:(id)value { 35 | return value; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Realm+JSON/MCJSONPrimaryKeyTransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCJSONPrimaryKeyTransformer.h 3 | // RealmJSON 4 | // 5 | // Created by Anton Gaenko on 14.12.14. 6 | // Copyright (c) 2014 Anton Gaenko. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface MCJSONPrimaryKeyTransformer : NSValueTransformer 13 | 14 | // it should be RLMObject subclass 15 | + (instancetype)valueTransformerWithRealmClass:(Class)realmClass; 16 | - (instancetype)initWithRealmClass:(Class)realmClass; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Realm+JSON/MCJSONPrimaryKeyTransformer.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCJSONPrimaryKeyTransformer.m 3 | // RealmJSON 4 | // 5 | // Created by Anton Gaenko on 14.12.14. 6 | // Copyright (c) 2014 Anton Gaenko. All rights reserved. 7 | // 8 | 9 | #import "MCJSONPrimaryKeyTransformer.h" 10 | 11 | @interface MCJSONPrimaryKeyTransformer () 12 | 13 | @property (nonatomic, assign) Class realmClassToMap; 14 | 15 | @end 16 | 17 | @implementation MCJSONPrimaryKeyTransformer 18 | 19 | + (instancetype)valueTransformerWithRealmClass:(Class)realmClass { 20 | return [[self alloc] initWithRealmClass:realmClass]; 21 | } 22 | 23 | - (instancetype)initWithRealmClass:(Class)realmClass { 24 | self = [super init]; 25 | if (self) { 26 | // it should be RLMObject subclass 27 | BOOL isRlmObjectSubclass = [realmClass respondsToSelector:@selector(isSubclassOfClass:)] && 28 | [realmClass isSubclassOfClass:[RLMObject class]]; 29 | 30 | if (isRlmObjectSubclass) { 31 | self.realmClassToMap = realmClass; 32 | } 33 | 34 | } 35 | return self; 36 | } 37 | 38 | - (id)transformedValue:(NSString*)value { 39 | if (value && self.realmClassToMap) { 40 | return [self.realmClassToMap performSelector:@selector(objectForPrimaryKey:) withObject:value]; 41 | } 42 | 43 | return nil; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Realm+JSON/MCJSONValueTransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // MCJSONValueTransformer.h 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MCJSONValueTransformer : NSValueTransformer 12 | 13 | + (instancetype)valueTransformerWithMappingDictionary:(NSDictionary *)dictionary; 14 | - (instancetype)initWithMappingDictionary:(NSDictionary *)dictionary; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Realm+JSON/MCJSONValueTransformer.m: -------------------------------------------------------------------------------- 1 | // 2 | // MCJSONValueTransformer.m 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import "MCJSONValueTransformer.h" 10 | 11 | @interface MCJSONValueTransformer () 12 | 13 | @property (nonatomic, strong) NSDictionary *mappingDictionary; 14 | 15 | @end 16 | 17 | @implementation MCJSONValueTransformer 18 | 19 | + (instancetype)valueTransformerWithMappingDictionary:(NSDictionary *)dictionary { 20 | return [[self alloc] initWithMappingDictionary:dictionary]; 21 | } 22 | 23 | - (instancetype)initWithMappingDictionary:(NSDictionary *)dictionary { 24 | self = [super init]; 25 | if (self) { 26 | _mappingDictionary = dictionary; 27 | } 28 | return self; 29 | } 30 | 31 | + (BOOL)allowsReverseTransformation { 32 | return YES; 33 | } 34 | 35 | - (id)transformedValue:(id)value { 36 | return self.mappingDictionary[value]; 37 | } 38 | 39 | - (id)reverseTransformedValue:(id)value { 40 | return [[self.mappingDictionary allKeysForObject:value] firstObject]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Realm+JSON/RLMObject+Copying.h: -------------------------------------------------------------------------------- 1 | // 2 | // RLMObject+Copying.h 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 26/8/14. 6 | // Copyright (c) 2014 Getting Real. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RLMObject (Copying) 12 | 13 | - (instancetype)shallowCopy; 14 | - (instancetype)deepCopy; 15 | - (void)mergePropertiesFromObject:(id)object; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Realm+JSON/RLMObject+Copying.m: -------------------------------------------------------------------------------- 1 | // 2 | // RLMObject+Copying.m 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 26/8/14. 6 | // Copyright (c) 2014 Getting Real. All rights reserved. 7 | // 8 | 9 | #import "RLMObject+Copying.h" 10 | 11 | #import 12 | #import 13 | 14 | @interface RLMProperty (Copying_Internal) 15 | 16 | @property (nonatomic, assign) BOOL isPrimary; 17 | 18 | @end 19 | 20 | @implementation RLMObject (Copying) 21 | 22 | - (instancetype)shallowCopy { 23 | id object = [[NSClassFromString(self.objectSchema.className) alloc] init]; 24 | [object mergePropertiesFromObject:self]; 25 | 26 | return object; 27 | } 28 | 29 | - (void)mergePropertiesFromObject:(id)object { 30 | 31 | BOOL primaryKeyIsEmpty; 32 | id value; 33 | id selfValue; 34 | 35 | BOOL (^valuesAreEqual)(id, id) = ^BOOL(id value1, id value2) { 36 | return ([[NSString stringWithFormat:@"%@", value1] 37 | isEqualToString:[NSString stringWithFormat:@"%@", value2]]); 38 | }; 39 | 40 | for (RLMProperty *property in self.objectSchema.properties) { 41 | 42 | if (property.type != RLMPropertyTypeArray) { 43 | 44 | // asume data 45 | value = [object valueForKeyPath:property.name]; 46 | selfValue = [self valueForKeyPath:property.name]; 47 | 48 | primaryKeyIsEmpty = (property.isPrimary && 49 | !valuesAreEqual(value, selfValue) 50 | ); 51 | 52 | if (primaryKeyIsEmpty || !property.isPrimary) { 53 | [self setValue:value forKeyPath:property.name]; 54 | } 55 | 56 | } else { 57 | // asume array 58 | RLMArray *thisArray = [self valueForKeyPath:property.name]; 59 | RLMArray *thatArray = [object valueForKeyPath:property.name]; 60 | [thisArray addObjects:thatArray]; 61 | } 62 | } 63 | } 64 | 65 | 66 | - (instancetype)deepCopy { 67 | RLMObject *object = [[NSClassFromString(self.objectSchema.className) alloc] init]; 68 | 69 | for (RLMProperty *property in self.objectSchema.properties) { 70 | 71 | if (property.type == RLMPropertyTypeArray) { 72 | RLMArray *thisArray = [self valueForKeyPath:property.name]; 73 | RLMArray *newArray = [object valueForKeyPath:property.name]; 74 | 75 | for (RLMObject *currentObject in thisArray) { 76 | [newArray addObject:[currentObject deepCopy]]; 77 | } 78 | 79 | } 80 | else if (property.type == RLMPropertyTypeObject) { 81 | RLMObject *value = [self valueForKeyPath:property.name]; 82 | [object setValue:[value deepCopy] forKeyPath:property.name]; 83 | } 84 | else { 85 | id value = [self valueForKeyPath:property.name]; 86 | [object setValue:value forKeyPath:property.name]; 87 | } 88 | } 89 | 90 | return object; 91 | } 92 | 93 | 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Realm+JSON/RLMObject+JSON.h: -------------------------------------------------------------------------------- 1 | // 2 | // RLMObject+JSON.h 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MCJSONDateTransformer.h" 11 | #import "MCJSONValueTransformer.h" 12 | 13 | @interface RLMObject (JSON) 14 | 15 | + (NSArray *)createOrUpdateInRealm:(RLMRealm *)realm withJSONArray:(NSArray *)array; 16 | + (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withJSONDictionary:(NSDictionary *)dictionary; 17 | + (instancetype)objectInRealm:(RLMRealm *)realm withPrimaryKeyValue:(id)primaryKeyValue; 18 | 19 | - (instancetype)initWithJSONDictionary:(NSDictionary *)dictionary; 20 | - (NSDictionary *)JSONDictionary; 21 | 22 | - (id)primaryKeyValue; 23 | + (id)primaryKeyValueFromJSONDictionary:(NSDictionary *)dictionary; 24 | 25 | - (void)performInTransaction:(void (^)())transaction; 26 | - (void)removeFromRealm; 27 | 28 | @end 29 | 30 | @interface RLMArray (JSON) 31 | 32 | - (NSArray *)NSArray; 33 | - (NSArray *)JSONArray; 34 | 35 | @end -------------------------------------------------------------------------------- /Realm+JSON/RLMObject+JSON.m: -------------------------------------------------------------------------------- 1 | // 2 | // RLMObject+JSON.m 3 | // RealmJSONDemo 4 | // 5 | // Created by Matthew Cheok on 27/7/14. 6 | // Copyright (c) 2014 Matthew Cheok. All rights reserved. 7 | // 8 | 9 | #import "RLMObject+JSON.h" 10 | 11 | #import 12 | #import 13 | 14 | // RLMSchema private interface 15 | @interface RLMSchema () 16 | // class for string 17 | + (Class)classForString:(NSString *)className; 18 | @end 19 | 20 | #import 21 | 22 | static id MCValueFromInvocation(id object, SEL selector) { 23 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[object methodSignatureForSelector:selector]]; 24 | invocation.target = object; 25 | invocation.selector = selector; 26 | [invocation invoke]; 27 | 28 | __unsafe_unretained id result = nil; 29 | [invocation getReturnValue:&result]; 30 | 31 | return result; 32 | } 33 | 34 | static NSString *MCTypeStringFromPropertyKey(Class class, NSString *key) { 35 | const objc_property_t property = class_getProperty(class, [key UTF8String]); 36 | if (!property) { 37 | [NSException raise:NSInternalInconsistencyException format:@"Class %@ does not have property %@", class, key]; 38 | } 39 | const char *type = property_getAttributes(property); 40 | return [NSString stringWithUTF8String:type]; 41 | } 42 | 43 | @interface NSString (MCJSON) 44 | 45 | - (NSString *)snakeToCamelCase; 46 | - (NSString *)camelToSnakeCase; 47 | 48 | @end 49 | 50 | @interface RLMObject (JSON_Internal) 51 | 52 | // RLMObject private methods 53 | + (RLMObjectSchema *)sharedSchema; 54 | 55 | @end 56 | 57 | @implementation RLMObject (JSON) 58 | 59 | static NSInteger const kCreateBatchSize = 100; 60 | 61 | + (NSArray *)createOrUpdateInRealm:(RLMRealm *)realm withJSONArray:(NSArray *)array { 62 | NSInteger count = array.count; 63 | NSMutableArray *result = [NSMutableArray array]; 64 | 65 | for (NSInteger index=0; index*kCreateBatchSize 0) { 98 | return results.firstObject; 99 | } 100 | else { 101 | return nil; 102 | } 103 | } 104 | 105 | - (instancetype)initWithJSONDictionary:(NSDictionary *)dictionary { 106 | self = [self initWithValue:[[self class] mc_createObjectFromJSONDictionary:dictionary]]; 107 | if (self) { 108 | } 109 | return self; 110 | } 111 | 112 | - (NSDictionary *)JSONDictionary { 113 | return [self mc_createJSONDictionary]; 114 | } 115 | 116 | - (id)primaryKeyValue { 117 | NSString *primaryKey = [[self class] primaryKey]; 118 | NSAssert(primaryKey, @"No primary key on class %@", [self description]); 119 | 120 | return [self valueForKeyPath:primaryKey]; 121 | } 122 | 123 | + (id)primaryKeyValueFromJSONDictionary:(NSDictionary *)dictionary { 124 | NSString *primaryKey = [[self class] primaryKey]; 125 | if (!primaryKey) { 126 | return nil; 127 | } 128 | 129 | NSDictionary *inboundMapping = [self mc_inboundMapping]; 130 | NSString *primaryKeyPath = [[inboundMapping allKeysForObject:primaryKey] firstObject]; 131 | id primaryKeyValue = [dictionary valueForKeyPath:primaryKeyPath]; 132 | 133 | NSValueTransformer *transformer = [self mc_transformerForPropertyKey:primaryKey]; 134 | if (primaryKeyValue && transformer) { 135 | primaryKeyValue = [transformer transformedValue:primaryKeyValue]; 136 | } 137 | 138 | return primaryKeyValue; 139 | } 140 | 141 | - (void)performInTransaction:(void (^)())transaction { 142 | NSAssert(transaction != nil, @"No transaction block provided"); 143 | if (self.realm) { 144 | [self.realm transactionWithBlock:transaction]; 145 | } 146 | else { 147 | transaction(); 148 | } 149 | } 150 | 151 | - (void)removeFromRealm { 152 | [self performInTransaction: ^{ 153 | [self.realm deleteObject:self]; 154 | }]; 155 | } 156 | 157 | #pragma mark - Private 158 | 159 | + (id)mc_createObjectFromJSONDictionary:(NSDictionary *)origDict { 160 | NSMutableDictionary *result = [NSMutableDictionary dictionary]; 161 | NSDictionary *mapping = [[self class] mc_inboundMapping]; 162 | 163 | NSDictionary *dictionary; 164 | SEL preprocessingSel = NSSelectorFromString(@"preprocessedJSON:"); 165 | if ([self respondsToSelector:preprocessingSel]) { 166 | dictionary = [self performSelector:preprocessingSel withObject:origDict]; 167 | } else { 168 | dictionary = origDict; 169 | } 170 | 171 | for (NSString *dictionaryKeyPath in mapping) { 172 | NSString *objectKeyPath = mapping[dictionaryKeyPath]; 173 | 174 | id value = [dictionary valueForKeyPath:dictionaryKeyPath]; 175 | 176 | if (value) { 177 | Class propertyClass = [[self class] mc_classForPropertyKey:objectKeyPath]; 178 | 179 | NSValueTransformer *transformer = [[self class] mc_transformerForPropertyKey:objectKeyPath]; 180 | if (transformer) { 181 | value = [transformer transformedValue:value]; 182 | } 183 | else if ([propertyClass isSubclassOfClass:[RLMObject class]]) { 184 | if (!value || [value isEqual:[NSNull null]]) { 185 | continue; 186 | } 187 | 188 | if ([value isKindOfClass:[NSDictionary class]]) { 189 | value = [propertyClass mc_createObjectFromJSONDictionary:value]; 190 | } 191 | } 192 | else if ([propertyClass isSubclassOfClass:[RLMArray class]]) { 193 | RLMProperty *property = [self mc_propertyForPropertyKey:objectKeyPath]; 194 | Class elementClass = [RLMSchema classForString: property.objectClassName]; 195 | 196 | NSMutableArray *array = [NSMutableArray array]; 197 | for (id item in(NSArray*) value) { 198 | [array addObject:[elementClass mc_createObjectFromJSONDictionary:item]]; 199 | } 200 | value = [array copy]; 201 | } 202 | 203 | if ([objectKeyPath isEqualToString:@"self"]) { 204 | return value; 205 | } 206 | 207 | NSArray *keyPathComponents = [objectKeyPath componentsSeparatedByString:@"."]; 208 | id currentDictionary = result; 209 | for (NSString *component in keyPathComponents) { 210 | if ([currentDictionary valueForKey:component] == nil) { 211 | [currentDictionary setValue:[NSMutableDictionary dictionary] forKey:component]; 212 | } 213 | currentDictionary = [currentDictionary valueForKey:component]; 214 | } 215 | 216 | value = value ?: [NSNull null]; 217 | [result setValue:value forKeyPath:objectKeyPath]; 218 | } 219 | } 220 | 221 | return [result copy]; 222 | } 223 | 224 | - (id)mc_createJSONDictionary { 225 | NSMutableDictionary *result = [NSMutableDictionary dictionary]; 226 | NSDictionary *mapping = [[self class] mc_outboundMapping]; 227 | 228 | for (NSString *objectKeyPath in mapping) { 229 | NSString *dictionaryKeyPath = mapping[objectKeyPath]; 230 | 231 | id value = [self valueForKeyPath:objectKeyPath]; 232 | if (value) { 233 | Class propertyClass = [[self class] mc_classForPropertyKey:objectKeyPath]; 234 | 235 | NSValueTransformer *transformer = [[self class] mc_transformerForPropertyKey:objectKeyPath]; 236 | if (transformer) { 237 | value = [transformer reverseTransformedValue:value]; 238 | } 239 | else if ([propertyClass isSubclassOfClass:[RLMObject class]]) { 240 | value = [value mc_createJSONDictionary]; 241 | } 242 | else if ([propertyClass isSubclassOfClass:[RLMArray class]]) { 243 | NSMutableArray *array = [NSMutableArray array]; 244 | for (id item in(RLMArray *) value) { 245 | [array addObject:[item mc_createJSONDictionary]]; 246 | } 247 | value = [array copy]; 248 | } 249 | 250 | if ([dictionaryKeyPath isEqualToString:@"self"]) { 251 | return value; 252 | } 253 | 254 | NSArray *keyPathComponents = [dictionaryKeyPath componentsSeparatedByString:@"."]; 255 | id currentDictionary = result; 256 | for (NSString *component in keyPathComponents) { 257 | if ([currentDictionary valueForKey:component] == nil) { 258 | [currentDictionary setValue:[NSMutableDictionary dictionary] forKey:component]; 259 | } 260 | currentDictionary = [currentDictionary valueForKey:component]; 261 | } 262 | 263 | [result setValue:value forKeyPath:dictionaryKeyPath]; 264 | } else { 265 | [result setValue:[NSNull null] forKeyPath:dictionaryKeyPath]; 266 | } 267 | } 268 | 269 | return [result copy]; 270 | } 271 | 272 | #pragma mark - Properties 273 | 274 | + (NSDictionary *)mc_defaultInboundMapping { 275 | RLMObjectSchema *schema = [self sharedSchema]; 276 | 277 | NSMutableDictionary *result = [NSMutableDictionary dictionary]; 278 | for (RLMProperty *property in schema.properties) { 279 | result[[property.name camelToSnakeCase]] = property.name; 280 | 281 | } 282 | 283 | return [result copy]; 284 | } 285 | 286 | + (NSDictionary *)mc_defaultOutboundMapping { 287 | RLMObjectSchema *schema = [self sharedSchema]; 288 | 289 | NSMutableDictionary *result = [NSMutableDictionary dictionary]; 290 | for (RLMProperty *property in schema.properties) { 291 | result[property.name] = [property.name camelToSnakeCase]; 292 | 293 | } 294 | 295 | return [result copy]; 296 | } 297 | 298 | #pragma mark - Convenience Methods 299 | 300 | + (NSDictionary *)mc_inboundMapping { 301 | static NSMutableDictionary *mappingForClassName = nil; 302 | static dispatch_once_t onceToken; 303 | dispatch_once(&onceToken, ^{ 304 | mappingForClassName = [NSMutableDictionary dictionary]; 305 | }); 306 | @synchronized(mappingForClassName) { 307 | NSDictionary *mapping = mappingForClassName[[self className]]; 308 | if (!mapping) { 309 | SEL selector = NSSelectorFromString(@"JSONInboundMappingDictionary"); 310 | if ([self respondsToSelector:selector]) { 311 | mapping = MCValueFromInvocation(self, selector); 312 | } 313 | else { 314 | mapping = [self mc_defaultInboundMapping]; 315 | } 316 | mappingForClassName[[self className]] = mapping; 317 | } 318 | return mapping; 319 | } 320 | } 321 | 322 | + (NSDictionary *)mc_outboundMapping { 323 | static NSMutableDictionary *mappingForClassName = nil; 324 | static dispatch_once_t onceToken; 325 | dispatch_once(&onceToken, ^{ 326 | mappingForClassName = [NSMutableDictionary dictionary]; 327 | }); 328 | 329 | @synchronized(mappingForClassName) { 330 | NSDictionary *mapping = mappingForClassName[[self className]]; 331 | if (!mapping) { 332 | SEL selector = NSSelectorFromString(@"JSONOutboundMappingDictionary"); 333 | if ([self respondsToSelector:selector]) { 334 | mapping = MCValueFromInvocation(self, selector); 335 | } 336 | else { 337 | mapping = [self mc_defaultOutboundMapping]; 338 | } 339 | mappingForClassName[[self className]] = mapping; 340 | } 341 | return mapping; 342 | } 343 | } 344 | 345 | + (RLMProperty *)mc_propertyForPropertyKey:(NSString *)key { 346 | RLMObjectSchema *schema = [self sharedSchema]; 347 | for (RLMProperty *property in schema.properties) { 348 | if ([property.name isEqualToString:key]) { 349 | return property; 350 | } 351 | } 352 | return nil; 353 | } 354 | 355 | + (Class)mc_classForPropertyKey:(NSString *)key { 356 | NSString *attributes = MCTypeStringFromPropertyKey(self, key); 357 | if ([attributes hasPrefix:@"T@"]) { 358 | static NSCharacterSet *set = nil; 359 | static dispatch_once_t onceToken; 360 | dispatch_once(&onceToken, ^{ 361 | set = [NSCharacterSet characterSetWithCharactersInString:@"\"<"]; 362 | }); 363 | 364 | @synchronized(set) { 365 | NSString *string; 366 | NSScanner *scanner = [NSScanner scannerWithString:attributes]; 367 | scanner.charactersToBeSkipped = set; 368 | [scanner scanUpToCharactersFromSet:set intoString:NULL]; 369 | [scanner scanUpToCharactersFromSet:set intoString:&string]; 370 | return NSClassFromString(string); 371 | } 372 | } 373 | return nil; 374 | } 375 | 376 | + (NSValueTransformer *)mc_transformerForPropertyKey:(NSString *)key { 377 | Class propertyClass = [self mc_classForPropertyKey:key]; 378 | SEL selector = NSSelectorFromString([key stringByAppendingString:@"JSONTransformer"]); 379 | 380 | NSValueTransformer *transformer = nil; 381 | if ([self respondsToSelector:selector]) { 382 | transformer = MCValueFromInvocation(self, selector); 383 | } 384 | else if ([propertyClass isSubclassOfClass:[NSDate class]]) { 385 | transformer = [NSValueTransformer valueTransformerForName:MCJSONDateTimeTransformerName]; 386 | } 387 | 388 | return transformer; 389 | } 390 | 391 | @end 392 | 393 | @implementation NSString (MCJSON) 394 | 395 | - (NSString *)snakeToCamelCase { 396 | NSScanner *scanner = [NSScanner scannerWithString:self]; 397 | NSCharacterSet *underscoreSet = [NSCharacterSet characterSetWithCharactersInString:@"_"]; 398 | scanner.charactersToBeSkipped = underscoreSet; 399 | 400 | NSMutableString *result = [NSMutableString string]; 401 | NSString *buffer = nil; 402 | 403 | while (![scanner isAtEnd]) { 404 | BOOL atStartPosition = scanner.scanLocation == 0; 405 | [scanner scanUpToCharactersFromSet:underscoreSet intoString:&buffer]; 406 | [result appendString:atStartPosition ? buffer:[buffer capitalizedString]]; 407 | } 408 | 409 | return result; 410 | } 411 | 412 | - (NSString *)camelToSnakeCase { 413 | NSScanner *scanner = [NSScanner scannerWithString:self]; 414 | NSCharacterSet *uppercaseSet = [NSCharacterSet uppercaseLetterCharacterSet]; 415 | scanner.charactersToBeSkipped = uppercaseSet; 416 | 417 | NSMutableString *result = [NSMutableString string]; 418 | NSString *buffer = nil; 419 | 420 | while (![scanner isAtEnd]) { 421 | [scanner scanUpToCharactersFromSet:uppercaseSet intoString:&buffer]; 422 | [result appendString:[buffer lowercaseString]]; 423 | 424 | if (![scanner isAtEnd]) { 425 | [result appendString:@"_"]; 426 | [result appendString:[[self substringWithRange:NSMakeRange(scanner.scanLocation, 1)] lowercaseString]]; 427 | } 428 | } 429 | 430 | return result; 431 | } 432 | 433 | @end 434 | 435 | @implementation RLMArray (JSON) 436 | 437 | - (NSArray *)NSArray { 438 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count]; 439 | for (id object in self) { 440 | [array addObject:object]; 441 | } 442 | return [array copy]; 443 | } 444 | 445 | - (NSArray *)JSONArray { 446 | NSMutableArray *array = [NSMutableArray array]; 447 | for (RLMObject *object in self) { 448 | [array addObject:[object JSONDictionary]]; 449 | } 450 | return [array copy]; 451 | } 452 | 453 | @end 454 | --------------------------------------------------------------------------------