appjam 0.1.8.8 → 0.1.8.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +22 -62
- data/lib/appjam/generators/gist.yml +1 -1
- data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.pbxproj +1166 -482
- data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.xcworkspace/xcuserdata/eiffel.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/EiffelApplication-Prefix.pch.tt +26 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/AwesomeMenu.h +52 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/AwesomeMenu.m +412 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/AwesomeMenuItem.h +44 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/AwesomeMenuItem.m +97 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/bg-addbutton-highlighted.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/bg-addbutton-highlighted@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/bg-addbutton.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/bg-addbutton@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/bg-menuitem-highlighted.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/bg-menuitem-highlighted@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/bg-menuitem.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/bg-menuitem@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/icon-plus-highlighted.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/icon-plus-highlighted@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/icon-plus.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/icon-plus@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/icon-star.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/AwesomeMenu/Images/icon-star@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/CMPopTipView/CMPopTipView.h +167 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/CMPopTipView/CMPopTipView.m +639 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/FXLabel/FXLabel.h +97 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/FXLabel/FXLabel.m +1080 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MBProgressHUD/MBProgressHUD.h +451 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/MBProgressHUD/MBProgressHUD.m +865 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/FacingView.h +42 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/FacingView.m +56 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/FoldView.h +90 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/FoldView.m +316 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/MultiFoldView.h +104 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/MultiFoldView.m +516 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldConstants.h +49 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldNavigationController.h +18 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldNavigationController.m +88 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldResources.bundle/swipe_guide_left.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldResources.bundle/swipe_guide_left@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldResources.bundle/swipe_guide_right.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldResources.bundle/swipe_guide_right@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldSwipeHintView.h +25 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldSwipeHintView.m +76 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldView.h +131 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/PaperFoldView.m +941 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/Resources/swipe_guide.psd +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/Resources/swipe_guide2.psd +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/ShadowView.h +46 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/ShadowView.m +82 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/TouchThroughUIView.h +39 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/TouchThroughUIView.m +72 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/UIView+Screenshot.h +42 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/PaperFold/UIView+Screenshot.m +100 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/NSBubbleData.h +34 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/NSBubbleData.m +140 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleHeaderTableViewCell.h +17 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleHeaderTableViewCell.m +57 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleTableView.h +30 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleTableView.m +233 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleTableViewCell.h +19 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleTableViewCell.m +122 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleTableViewDataSource.h +24 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleTypingTableViewCell.h +20 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/UIBubbleTypingTableViewCell.m +56 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/bubbleMine.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/bubbleMine@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/bubbleSomeone.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/bubbleSomeone@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/missingAvatar.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/missingAvatar@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/typingMine.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/typingMine@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/typingSomeone.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIBubbleTableView/images/typingSomeone@2x.png +0 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIGlossyButton/UIGlossyButton.h +124 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIGlossyButton/UIGlossyButton.m +545 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIGlossyButton/UIView+LayerEffects.h +22 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/components/UIGlossyButton/UIView+LayerEffects.m +32 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFIncrementalStore/AFIncrementalStore.h +344 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFIncrementalStore/AFIncrementalStore.m +848 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFIncrementalStore/AFRESTClient.h +135 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFIncrementalStore/AFRESTClient.m +441 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/InflectorKit/NSString+InflectorKit.h +40 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/InflectorKit/NSString+InflectorKit.m +36 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/InflectorKit/TTTStringInflector.h +76 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/InflectorKit/TTTStringInflector.m +270 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/MKAnnotationView+WebCache.h +95 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/MKAnnotationView+WebCache.m +78 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDImageCache.h +144 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDImageCache.m +311 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageCompat.h +40 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageCompat.m +54 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageDecoder.h +18 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageDecoder.m +70 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageDownloader.h +102 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageDownloader.m +226 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageDownloaderOperation.h +25 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageDownloaderOperation.m +339 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageManager.h +167 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageManager.m +244 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImageOperation.h +15 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImagePrefetcher.h +58 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/SDWebImagePrefetcher.m +127 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/UIButton+WebCache.h +173 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/UIButton+WebCache.m +129 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/UIImageView+WebCache.h +140 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/SDWebImage/UIImageView+WebCache.m +84 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CPAnimationSequence/CPAnimationProgram.h +28 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CPAnimationSequence/CPAnimationSequence.h +28 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CPAnimationSequence/CPAnimationStep.h +49 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CPAnimationSequence/private/CPAnimationProgram.m +100 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CPAnimationSequence/private/CPAnimationSequence.m +85 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CPAnimationSequence/private/CPAnimationStep.m +122 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaAsyncSocket/GCDAsyncSocket.h +1074 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaAsyncSocket/GCDAsyncSocket.m +7430 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Categories/DDData.h +14 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Categories/DDData.m +158 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Categories/DDNumber.h +12 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Categories/DDNumber.m +88 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Categories/DDRange.h +56 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Categories/DDRange.m +104 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPAuthenticationRequest.h +45 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPAuthenticationRequest.m +195 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPConnection.h +119 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPConnection.m +2708 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPLogging.h +136 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPMessage.h +48 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPMessage.m +113 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPResponse.h +149 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPServer.h +205 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/HTTPServer.m +772 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.h +65 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.m +529 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.h +33 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.m +86 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.h +23 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.m +211 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.h +75 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.m +405 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPDataResponse.h +13 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPDataResponse.m +79 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.h +52 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.m +292 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.h +9 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.m +38 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPFileResponse.h +25 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPFileResponse.m +237 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.h +12 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.m +73 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/WebSocket.h +105 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Core/WebSocket.m +791 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.h +7 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.m +160 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.h +11 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.m +372 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.h +7 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.m +49 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.h +8 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.m +69 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/hpple/TFHpple.h +54 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/hpple/TFHpple.m +102 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/hpple/TFHppleElement.h +103 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/hpple/TFHppleElement.m +200 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/hpple/XPathQuery.h +10 -0
- data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/hpple/XPathQuery.m +197 -0
- data/lib/appjam/version.rb +1 -1
- metadata +164 -3
@@ -0,0 +1,22 @@
|
|
1
|
+
//
|
2
|
+
// UIView+ExtraAnimation.h
|
3
|
+
//
|
4
|
+
// Created by Water Lou on 15/09/2010.
|
5
|
+
// Copyright 2010 First Water Tech Ltd. All rights reserved.
|
6
|
+
//
|
7
|
+
// many visual effects and animation effects create using CoreAnimation and CALayer
|
8
|
+
//
|
9
|
+
|
10
|
+
#import <Foundation/Foundation.h>
|
11
|
+
|
12
|
+
@interface UIView(LayerEffects)
|
13
|
+
|
14
|
+
// set round corner
|
15
|
+
- (void) setCornerRadius : (CGFloat) radius;
|
16
|
+
// set inner border
|
17
|
+
- (void) setBorder : (UIColor *) color width : (CGFloat) width;
|
18
|
+
// set the shadow
|
19
|
+
// Example: [view setShadow:[UIColor blackColor] opacity:0.5 offset:CGSizeMake(1.0, 1.0) blueRadius:3.0];
|
20
|
+
- (void) setShadow : (UIColor *)color opacity:(CGFloat)opacity offset:(CGSize) offset blurRadius:(CGFloat)blurRadius;
|
21
|
+
|
22
|
+
@end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
//
|
2
|
+
// UIView+LayerEffects.m
|
3
|
+
//
|
4
|
+
// Created by Water Lou on 15/09/2010.
|
5
|
+
// Copyright 2010 First Water Tech Ltd. All rights reserved.
|
6
|
+
//
|
7
|
+
|
8
|
+
#import <QuartzCore/QuartzCore.h>
|
9
|
+
#import "UIView+LayerEffects.h"
|
10
|
+
|
11
|
+
@implementation UIView(LayerEffects)
|
12
|
+
|
13
|
+
/* simple setting using the layer */
|
14
|
+
- (void) setCornerRadius : (CGFloat) radius {
|
15
|
+
self.layer.cornerRadius = radius;
|
16
|
+
}
|
17
|
+
|
18
|
+
- (void) setBorder : (UIColor *) color width : (CGFloat) width {
|
19
|
+
self.layer.borderColor = [color CGColor];
|
20
|
+
self.layer.borderWidth = width;
|
21
|
+
}
|
22
|
+
|
23
|
+
- (void) setShadow : (UIColor *)color opacity:(CGFloat)opacity offset:(CGSize)offset blurRadius:(CGFloat)blurRadius {
|
24
|
+
CALayer *l = self.layer;
|
25
|
+
l.shadowColor = [color CGColor];
|
26
|
+
l.shadowOpacity = opacity;
|
27
|
+
l.shadowOffset = offset;
|
28
|
+
l.shadowRadius = blurRadius;
|
29
|
+
}
|
30
|
+
|
31
|
+
|
32
|
+
@end
|
@@ -0,0 +1,344 @@
|
|
1
|
+
// AFIncrementalStore.h
|
2
|
+
//
|
3
|
+
// Copyright (c) 2012 Mattt Thompson (http://mattt.me)
|
4
|
+
//
|
5
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
// of this software and associated documentation files (the "Software"), to deal
|
7
|
+
// in the Software without restriction, including without limitation the rights
|
8
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
// copies of the Software, and to permit persons to whom the Software is
|
10
|
+
// furnished to do so, subject to the following conditions:
|
11
|
+
//
|
12
|
+
// The above copyright notice and this permission notice shall be included in
|
13
|
+
// all 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,
|
17
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
// THE SOFTWARE.
|
22
|
+
|
23
|
+
#import <CoreData/CoreData.h>
|
24
|
+
#import "AFNetworking.h"
|
25
|
+
|
26
|
+
@protocol AFIncrementalStoreHTTPClient;
|
27
|
+
|
28
|
+
/**
|
29
|
+
`AFIncrementalStore` is an abstract subclass of `NSIncrementalStore`, designed to allow you to load and save data incrementally to and from a one or more web services.
|
30
|
+
|
31
|
+
## Subclassing Notes
|
32
|
+
|
33
|
+
### Methods to Override
|
34
|
+
|
35
|
+
In a subclass of `AFIncrementalStore`, you _must_ override the following methods to provide behavior appropriate for your store:
|
36
|
+
|
37
|
+
- `+type`
|
38
|
+
- `+model`
|
39
|
+
|
40
|
+
Additionally, all `NSPersistentStore` subclasses, and thus all `AFIncrementalStore` subclasses must do `NSPersistentStoreCoordinator +registerStoreClass:forStoreType:` in order to be created by `NSPersistentStoreCoordinator -addPersistentStoreWithType:configuration:URL:options:error:`. It is recommended that subclasses register themselves in their own `+initialize` method.
|
41
|
+
|
42
|
+
Optionally, `AFIncrementalStore` subclasses can override the following methods:
|
43
|
+
|
44
|
+
- `-executeFetchRequest:withContext:error:`
|
45
|
+
- `-executeSaveChangesRequest:withContext:error:`
|
46
|
+
|
47
|
+
### Methods Not To Be Overridden
|
48
|
+
|
49
|
+
Subclasses should not override `-executeRequest:withContext:error`. Instead, override `-executeFetchRequest:withContext:error:` or `-executeSaveChangesRequest:withContext:error:`, which are called by `-executeRequest:withContext:error` depending on the type of persistent store request.
|
50
|
+
*/
|
51
|
+
@interface AFIncrementalStore : NSIncrementalStore
|
52
|
+
|
53
|
+
///---------------------------------------------
|
54
|
+
/// @name Accessing Incremental Store Properties
|
55
|
+
///---------------------------------------------
|
56
|
+
|
57
|
+
/**
|
58
|
+
The HTTP client used to manage requests and responses with the associated web services.
|
59
|
+
*/
|
60
|
+
@property (nonatomic, strong) AFHTTPClient <AFIncrementalStoreHTTPClient> *HTTPClient;
|
61
|
+
|
62
|
+
/**
|
63
|
+
The persistent store coordinator used to persist data from the associated web serivices locally.
|
64
|
+
|
65
|
+
@discussion Rather than persist values directly, `AFIncrementalStore` manages and proxies through a persistent store coordinator.
|
66
|
+
*/
|
67
|
+
@property (readonly) NSPersistentStoreCoordinator *backingPersistentStoreCoordinator;
|
68
|
+
|
69
|
+
///-----------------------
|
70
|
+
/// @name Required Methods
|
71
|
+
///-----------------------
|
72
|
+
|
73
|
+
/**
|
74
|
+
Returns the string used as the `NSStoreTypeKey` value by the application's persistent store coordinator.
|
75
|
+
|
76
|
+
@return The string used to describe the type of the store.
|
77
|
+
*/
|
78
|
+
+ (NSString *)type;
|
79
|
+
|
80
|
+
/**
|
81
|
+
Returns the managed object model used by the store.
|
82
|
+
|
83
|
+
@return The managed object model used by the store
|
84
|
+
*/
|
85
|
+
+ (NSManagedObjectModel *)model;
|
86
|
+
|
87
|
+
///-----------------------
|
88
|
+
/// @name Optional Methods
|
89
|
+
///-----------------------
|
90
|
+
|
91
|
+
/**
|
92
|
+
|
93
|
+
*/
|
94
|
+
- (id)executeFetchRequest:(NSFetchRequest *)fetchRequest
|
95
|
+
withContext:(NSManagedObjectContext *)context
|
96
|
+
error:(NSError *__autoreleasing *)error;
|
97
|
+
|
98
|
+
/**
|
99
|
+
|
100
|
+
*/
|
101
|
+
- (id)executeSaveChangesRequest:(NSSaveChangesRequest *)saveChangesRequest
|
102
|
+
withContext:(NSManagedObjectContext *)context
|
103
|
+
error:(NSError *__autoreleasing *)error;
|
104
|
+
|
105
|
+
@end
|
106
|
+
|
107
|
+
#pragma mark -
|
108
|
+
|
109
|
+
/**
|
110
|
+
The `AFIncrementalStoreHTTPClient` protocol defines the methods used by the HTTP client to interract with the associated web services of an `AFIncrementalStore`.
|
111
|
+
*/
|
112
|
+
@protocol AFIncrementalStoreHTTPClient <NSObject>
|
113
|
+
|
114
|
+
///-----------------------
|
115
|
+
/// @name Required Methods
|
116
|
+
///-----------------------
|
117
|
+
|
118
|
+
/**
|
119
|
+
Returns an `NSDictionary` or an `NSArray` of `NSDictionaries` containing the representations of the resources found in a response object.
|
120
|
+
|
121
|
+
@discussion For example, if `GET /users` returned an `NSDictionary` with an array of users keyed on `"users"`, this method would return the keyed array. Conversely, if `GET /users/123` returned a dictionary with all of the atributes of the requested user, this method would simply return that dictionary.
|
122
|
+
|
123
|
+
@param entity The entity represented
|
124
|
+
@param responseObject The response object returned from the server.
|
125
|
+
|
126
|
+
@return An `NSDictionary` with the representation or an `NSArray` of `NSDictionaries` containing the resource representations.
|
127
|
+
*/
|
128
|
+
- (id)representationOrArrayOfRepresentationsOfEntity:(NSEntityDescription *)entity
|
129
|
+
fromResponseObject:(id)responseObject;
|
130
|
+
|
131
|
+
/**
|
132
|
+
Returns an `NSDictionary` containing the representations of associated objects found within the representation of a response object, keyed by their relationship name.
|
133
|
+
|
134
|
+
@discussion For example, if `GET /albums/123` returned the representation of an album, including the tracks as sub-entities, keyed under `"tracks"`, this method would return a dictionary with an array of representations for those objects, keyed under the name of the relationship used in the model (which is likely also to be `"tracks"`). Likewise, if an album also contained a representation of its artist, that dictionary would contain a dictionary representation of that artist, keyed under the name of the relationship used in the model (which is likely also to be `"artist"`).
|
135
|
+
|
136
|
+
@param representation The resource representation.
|
137
|
+
@param entity The entity for the representation.
|
138
|
+
@param response The HTTP response for the resource request.
|
139
|
+
|
140
|
+
@return An `NSDictionary` containing representations of relationships, keyed by relationship name.
|
141
|
+
*/
|
142
|
+
- (NSDictionary *)representationsForRelationshipsFromRepresentation:(NSDictionary *)representation
|
143
|
+
ofEntity:(NSEntityDescription *)entity
|
144
|
+
fromResponse:(NSHTTPURLResponse *)response;
|
145
|
+
|
146
|
+
/**
|
147
|
+
Returns the resource identifier for the resource whose representation of an entity came from the specified HTTP response. A resource identifier is a string that uniquely identifies a particular resource among all resource types. If new attributes come back for an existing resource identifier, the managed object associated with that resource identifier will be updated, rather than a new object being created.
|
148
|
+
|
149
|
+
@discussion For example, if `GET /posts` returns a collection of posts, the resource identifier for any particular one might be its URL-safe "slug" or parameter string, or perhaps its numeric id. For example: `/posts/123` might be a resource identifier for a particular post.
|
150
|
+
|
151
|
+
@param representation The resource representation.
|
152
|
+
@param entity The entity for the representation.
|
153
|
+
@param response The HTTP response for the resource request.
|
154
|
+
|
155
|
+
@return An `NSString` resource identifier for the resource.
|
156
|
+
*/
|
157
|
+
- (NSString *)resourceIdentifierForRepresentation:(NSDictionary *)representation
|
158
|
+
ofEntity:(NSEntityDescription *)entity
|
159
|
+
fromResponse:(NSHTTPURLResponse *)response;
|
160
|
+
|
161
|
+
/**
|
162
|
+
Returns the attributes for the managed object corresponding to the representation of an entity from the specified response. This method is used to get the attributes of the managed object from its representation returned in `-representationOrArrayOfRepresentationsFromResponseObject` or `representationsForRelationshipsFromRepresentation:ofEntity:fromResponse:`.
|
163
|
+
|
164
|
+
@discussion For example, if the representation returned from `GET /products/123` had a `description` field that corresponded with the `productDescription` attribute in its Core Data model, this method would set the value of the `productDescription` key in the returned dictionary to the value of the `description` field in representation.
|
165
|
+
|
166
|
+
@param representation The resource representation.
|
167
|
+
@param entity The entity for the representation.
|
168
|
+
@param response The HTTP response for the resource request.
|
169
|
+
|
170
|
+
@return An `NSDictionary` containing the attributes for a managed object.
|
171
|
+
*/
|
172
|
+
- (NSDictionary *)attributesForRepresentation:(NSDictionary *)representation
|
173
|
+
ofEntity:(NSEntityDescription *)entity
|
174
|
+
fromResponse:(NSHTTPURLResponse *)response;
|
175
|
+
|
176
|
+
/**
|
177
|
+
Returns a URL request object for the specified fetch request within a particular managed object context.
|
178
|
+
|
179
|
+
@discussion For example, if the fetch request specified the `User` entity, this method might return an `NSURLRequest` with `GET /users` if the web service was RESTful, `POST /endpoint?method=users.getAll` for an RPC-style system, or a request with an XML envelope body for a SOAP webservice.
|
180
|
+
|
181
|
+
@param fetchRequest The fetch request to translate into a URL request.
|
182
|
+
@param context The managed object context executing the fetch request.
|
183
|
+
|
184
|
+
@return An `NSURLRequest` object corresponding to the specified fetch request.
|
185
|
+
*/
|
186
|
+
- (NSMutableURLRequest *)requestForFetchRequest:(NSFetchRequest *)fetchRequest
|
187
|
+
withContext:(NSManagedObjectContext *)context;
|
188
|
+
|
189
|
+
/**
|
190
|
+
Returns a URL request object with a given HTTP method for a particular managed object. This method is used in `AFIncrementalStore -newValuesForObjectWithID:withContext:error`.
|
191
|
+
|
192
|
+
@discussion For example, if a `User` managed object were to be refreshed, this method might return a `GET /users/123` request.
|
193
|
+
|
194
|
+
@param method The HTTP method of the request.
|
195
|
+
@param objectID The object ID for the specified managed object.
|
196
|
+
@param context The managed object context for the managed object.
|
197
|
+
|
198
|
+
@return An `NSURLRequest` object with the provided HTTP method for the resource corresponding to the managed object.
|
199
|
+
*/
|
200
|
+
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
|
201
|
+
pathForObjectWithID:(NSManagedObjectID *)objectID
|
202
|
+
withContext:(NSManagedObjectContext *)context;
|
203
|
+
|
204
|
+
/**
|
205
|
+
Returns a URL request object with a given HTTP method for a particular relationship of a given managed object. This method is used in `AFIncrementalStore -newValueForRelationship:forObjectWithID:withContext:error:`.
|
206
|
+
|
207
|
+
@discussion For example, if a `Department` managed object was attempting to fulfill a fault on the `employees` relationship, this method might return `GET /departments/sales/employees`.
|
208
|
+
|
209
|
+
@param method The HTTP method of the request.
|
210
|
+
@param relationship The relationship of the specifified managed object
|
211
|
+
@param objectID The object ID for the specified managed object.
|
212
|
+
@param context The managed object context for the managed object.
|
213
|
+
|
214
|
+
@return An `NSURLRequest` object with the provided HTTP method for the resource or resoures corresponding to the relationship of the managed object.
|
215
|
+
|
216
|
+
*/
|
217
|
+
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
|
218
|
+
pathForRelationship:(NSRelationshipDescription *)relationship
|
219
|
+
forObjectWithID:(NSManagedObjectID *)objectID
|
220
|
+
withContext:(NSManagedObjectContext *)context;
|
221
|
+
|
222
|
+
@optional
|
223
|
+
|
224
|
+
///-----------------------
|
225
|
+
/// @name Optional Methods
|
226
|
+
///-----------------------
|
227
|
+
|
228
|
+
/**
|
229
|
+
Returns the attributes representation of an entity from the specified managed object. This method is used to get the attributes of the representation from its managed object.
|
230
|
+
|
231
|
+
@discussion For example, if the representation sent to `POST /products` or `PUT /products/123` had a `description` field that corresponded with the `productDescription` attribute in its Core Data model, this method would set the value of the `productDescription` field to the value of the `description` key in representation/dictionary.
|
232
|
+
|
233
|
+
@param attributes The resource representation.
|
234
|
+
@param managedObject The `NSManagedObject` for the representation.
|
235
|
+
|
236
|
+
@return An `NSDictionary` containing the attributes for a representation, based on the given managed object.
|
237
|
+
*/
|
238
|
+
- (NSDictionary *)representationOfAttributes:(NSDictionary *)attributes
|
239
|
+
ofManagedObject:(NSManagedObject *)managedObject;
|
240
|
+
|
241
|
+
/**
|
242
|
+
|
243
|
+
*/
|
244
|
+
- (NSMutableURLRequest *)requestForInsertedObject:(NSManagedObject *)insertedObject;
|
245
|
+
|
246
|
+
/**
|
247
|
+
|
248
|
+
*/
|
249
|
+
- (NSMutableURLRequest *)requestForUpdatedObject:(NSManagedObject *)updatedObject;
|
250
|
+
|
251
|
+
/**
|
252
|
+
|
253
|
+
*/
|
254
|
+
- (NSMutableURLRequest *)requestForDeletedObject:(NSManagedObject *)deletedObject;
|
255
|
+
|
256
|
+
/**
|
257
|
+
Returns whether the client should fetch remote attribute values for a particular managed object. This method is consulted when a managed object faults on an attribute, and will call `-requestWithMethod:pathForObjectWithID:withContext:` if `YES`.
|
258
|
+
|
259
|
+
@param objectID The object ID for the specified managed object.
|
260
|
+
@param context The managed object context for the managed object.
|
261
|
+
|
262
|
+
@return `YES` if an HTTP request should be made, otherwise `NO.
|
263
|
+
*/
|
264
|
+
- (BOOL)shouldFetchRemoteAttributeValuesForObjectWithID:(NSManagedObjectID *)objectID
|
265
|
+
inManagedObjectContext:(NSManagedObjectContext *)context;
|
266
|
+
|
267
|
+
/**
|
268
|
+
Returns whether the client should fetch remote relationship values for a particular managed object. This method is consulted when a managed object faults on a particular relationship, and will call `-requestWithMethod:pathForRelationship:forObjectWithID:withContext:` if `YES`.
|
269
|
+
|
270
|
+
@param relationship The relationship of the specifified managed object
|
271
|
+
@param objectID The object ID for the specified managed object.
|
272
|
+
@param context The managed object context for the managed object.
|
273
|
+
|
274
|
+
@return `YES` if an HTTP request should be made, otherwise `NO.
|
275
|
+
*/
|
276
|
+
- (BOOL)shouldFetchRemoteValuesForRelationship:(NSRelationshipDescription *)relationship
|
277
|
+
forObjectWithID:(NSManagedObjectID *)objectID
|
278
|
+
inManagedObjectContext:(NSManagedObjectContext *)context;
|
279
|
+
|
280
|
+
@end
|
281
|
+
|
282
|
+
///----------------
|
283
|
+
/// @name Functions
|
284
|
+
///----------------
|
285
|
+
|
286
|
+
/**
|
287
|
+
There is a bug in Core Data wherein managed object IDs whose reference object is a string beginning with a digit will incorrectly strip any subsequent non-numeric characters from the reference object. This breaks any functionality related to URI representations of the managed object ID, and likely other methods as well. For example, an object ID with a reference object of @"123ABC" would generate one with a URI represenation `coredata://store-UUID/Entity/123`, rather than the expected `coredata://store-UUID/Entity/123ABC`. As a fix, rather than resource identifiers being used directly as reference objects, they are prepended with a non-numeric constant first.
|
288
|
+
|
289
|
+
Thus, in order to get the resource identifier of a managed object's reference object, you must use the function `AFResourceIdentifierFromReferenceObject()`.
|
290
|
+
|
291
|
+
See https://github.com/AFNetworking/AFIncrementalStore/issues/82 for more details.
|
292
|
+
*/
|
293
|
+
extern NSString * AFReferenceObjectFromResourceIdentifier(NSString *resourceIdentifier);
|
294
|
+
|
295
|
+
extern NSString * AFResourceIdentifierFromReferenceObject(id referenceObject);
|
296
|
+
|
297
|
+
///----------------
|
298
|
+
/// @name Constants
|
299
|
+
///----------------
|
300
|
+
|
301
|
+
/**
|
302
|
+
The name of the exception called when `AFIncrementalStore` or a subclass is attempted to be used, without implementing one of the required methods.
|
303
|
+
*/
|
304
|
+
extern NSString * const AFIncrementalStoreUnimplementedMethodException;
|
305
|
+
|
306
|
+
///--------------------
|
307
|
+
/// @name Notifications
|
308
|
+
///--------------------
|
309
|
+
|
310
|
+
/**
|
311
|
+
Posted before an HTTP request operation starts.
|
312
|
+
The object is the managed object context of the request.
|
313
|
+
The notification `userInfo` contains the finished request operation, keyed at `AFIncrementalStoreRequestOperationKey`, as well as the associated persistent store request, if applicable, keyed at `AFIncrementalStorePersistentStoreRequestKey`.
|
314
|
+
*/
|
315
|
+
extern NSString * const AFIncrementalStoreContextWillFetchRemoteValues;
|
316
|
+
|
317
|
+
/**
|
318
|
+
|
319
|
+
*/
|
320
|
+
extern NSString * const AFIncrementalStoreContextWillSaveRemoteValues;
|
321
|
+
|
322
|
+
/**
|
323
|
+
Posted after an HTTP request operation finishes.
|
324
|
+
The object is the managed object context of the request.
|
325
|
+
The notification `userInfo` contains the finished request operation, keyed at `AFIncrementalStoreRequestOperationKey`, as well as the associated persistent store request, if applicable, keyed at `AFIncrementalStorePersistentStoreRequestKey`.
|
326
|
+
*/
|
327
|
+
extern NSString * const AFIncrementalStoreContextDidFetchRemoteValues;
|
328
|
+
|
329
|
+
/**
|
330
|
+
|
331
|
+
*/
|
332
|
+
extern NSString * const AFIncrementalStoreContextDidSaveRemoteValues;
|
333
|
+
|
334
|
+
/**
|
335
|
+
A key in the `userInfo` dictionary in a `AFIncrementalStoreContextWillFetchRemoteValues` or `AFIncrementalStoreContextDidFetchRemoteValues` notification.
|
336
|
+
The corresponding value is an `AFHTTPRequestOperation` object representing the associated request. */
|
337
|
+
extern NSString * const AFIncrementalStoreRequestOperationKey;
|
338
|
+
|
339
|
+
/**
|
340
|
+
A key in the `userInfo` dictionary in a `AFIncrementalStoreContextWillFetchRemoteValues` or `AFIncrementalStoreContextDidFetchRemoteValues` notification.
|
341
|
+
The corresponding value is an `NSPersistentStoreRequest` object representing the associated fetch or save request. */
|
342
|
+
extern NSString * const AFIncrementalStorePersistentStoreRequestKey;
|
343
|
+
|
344
|
+
extern NSString * const AFIncrementalStoreFetchedObjectsKey;
|
@@ -0,0 +1,848 @@
|
|
1
|
+
// AFIncrementalStore.m
|
2
|
+
//
|
3
|
+
// Copyright (c) 2012 Mattt Thompson (http://mattt.me)
|
4
|
+
//
|
5
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
// of this software and associated documentation files (the "Software"), to deal
|
7
|
+
// in the Software without restriction, including without limitation the rights
|
8
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
// copies of the Software, and to permit persons to whom the Software is
|
10
|
+
// furnished to do so, subject to the following conditions:
|
11
|
+
//
|
12
|
+
// The above copyright notice and this permission notice shall be included in
|
13
|
+
// all 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,
|
17
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
// THE SOFTWARE.
|
22
|
+
|
23
|
+
#import "AFIncrementalStore.h"
|
24
|
+
#import "AFHTTPClient.h"
|
25
|
+
#import <objc/runtime.h>
|
26
|
+
|
27
|
+
NSString * const AFIncrementalStoreUnimplementedMethodException = @"com.alamofire.incremental-store.exceptions.unimplemented-method";
|
28
|
+
|
29
|
+
NSString * const AFIncrementalStoreContextWillFetchRemoteValues = @"AFIncrementalStoreContextWillFetchRemoteValues";
|
30
|
+
NSString * const AFIncrementalStoreContextWillSaveRemoteValues = @"AFIncrementalStoreContextWillSaveRemoteValues";
|
31
|
+
NSString * const AFIncrementalStoreContextDidFetchRemoteValues = @"AFIncrementalStoreContextDidFetchRemoteValues";
|
32
|
+
NSString * const AFIncrementalStoreContextDidSaveRemoteValues = @"AFIncrementalStoreContextDidSaveRemoteValues";
|
33
|
+
NSString * const AFIncrementalStoreRequestOperationKey = @"AFIncrementalStoreRequestOperation";
|
34
|
+
NSString * const AFIncrementalStorePersistentStoreRequestKey = @"AFIncrementalStorePersistentStoreRequest";
|
35
|
+
NSString * const AFIncrementalStoreFetchedObjectsKey = @"AFIncrementalStoreFetchedObjectsKey";
|
36
|
+
|
37
|
+
static char kAFResourceIdentifierObjectKey;
|
38
|
+
|
39
|
+
static NSString * const kAFIncrementalStoreResourceIdentifierAttributeName = @"__af_resourceIdentifier";
|
40
|
+
static NSString * const kAFIncrementalStoreLastModifiedAttributeName = @"__af_lastModified";
|
41
|
+
|
42
|
+
static NSString * const kAFReferenceObjectPrefix = @"__af_";
|
43
|
+
|
44
|
+
inline NSString * AFReferenceObjectFromResourceIdentifier(NSString *resourceIdentifier) {
|
45
|
+
if (!resourceIdentifier) {
|
46
|
+
return nil;
|
47
|
+
}
|
48
|
+
|
49
|
+
return [kAFReferenceObjectPrefix stringByAppendingString:resourceIdentifier];
|
50
|
+
}
|
51
|
+
|
52
|
+
inline NSString * AFResourceIdentifierFromReferenceObject(id referenceObject) {
|
53
|
+
if (!referenceObject) {
|
54
|
+
return nil;
|
55
|
+
}
|
56
|
+
|
57
|
+
NSString *string = [referenceObject description];
|
58
|
+
return [string hasPrefix:kAFReferenceObjectPrefix] ? [string substringFromIndex:[kAFReferenceObjectPrefix length]] : string;
|
59
|
+
}
|
60
|
+
|
61
|
+
@interface NSManagedObject (_AFIncrementalStore)
|
62
|
+
@property (readwrite, nonatomic, copy, setter = af_setResourceIdentifier:) NSString *af_resourceIdentifier;
|
63
|
+
@end
|
64
|
+
|
65
|
+
@implementation NSManagedObject (_AFIncrementalStore)
|
66
|
+
@dynamic af_resourceIdentifier;
|
67
|
+
|
68
|
+
- (NSString *)af_resourceIdentifier {
|
69
|
+
NSString *identifier = (NSString *)objc_getAssociatedObject(self, &kAFResourceIdentifierObjectKey);
|
70
|
+
|
71
|
+
if (!identifier) {
|
72
|
+
if ([self.objectID.persistentStore isKindOfClass:[AFIncrementalStore class]]) {
|
73
|
+
id referenceObject = [(AFIncrementalStore *)self.objectID.persistentStore referenceObjectForObjectID:self.objectID];
|
74
|
+
if ([referenceObject isKindOfClass:[NSString class]]) {
|
75
|
+
return AFResourceIdentifierFromReferenceObject(referenceObject);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
return identifier;
|
81
|
+
}
|
82
|
+
|
83
|
+
- (void)af_setResourceIdentifier:(NSString *)resourceIdentifier {
|
84
|
+
objc_setAssociatedObject(self, &kAFResourceIdentifierObjectKey, resourceIdentifier, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
85
|
+
}
|
86
|
+
|
87
|
+
@end
|
88
|
+
|
89
|
+
#pragma mark -
|
90
|
+
|
91
|
+
@implementation AFIncrementalStore {
|
92
|
+
@private
|
93
|
+
NSCache *_backingObjectIDByObjectID;
|
94
|
+
NSMutableDictionary *_registeredObjectIDsByEntityNameAndNestedResourceIdentifier;
|
95
|
+
NSPersistentStoreCoordinator *_backingPersistentStoreCoordinator;
|
96
|
+
NSManagedObjectContext *_backingManagedObjectContext;
|
97
|
+
}
|
98
|
+
@synthesize HTTPClient = _HTTPClient;
|
99
|
+
@synthesize backingPersistentStoreCoordinator = _backingPersistentStoreCoordinator;
|
100
|
+
|
101
|
+
+ (NSString *)type {
|
102
|
+
@throw([NSException exceptionWithName:AFIncrementalStoreUnimplementedMethodException reason:NSLocalizedString(@"Unimplemented method: +type. Must be overridden in a subclass", nil) userInfo:nil]);
|
103
|
+
}
|
104
|
+
|
105
|
+
+ (NSManagedObjectModel *)model {
|
106
|
+
@throw([NSException exceptionWithName:AFIncrementalStoreUnimplementedMethodException reason:NSLocalizedString(@"Unimplemented method: +model. Must be overridden in a subclass", nil) userInfo:nil]);
|
107
|
+
}
|
108
|
+
|
109
|
+
#pragma mark -
|
110
|
+
|
111
|
+
- (void)notifyManagedObjectContext:(NSManagedObjectContext *)context
|
112
|
+
aboutRequestOperation:(AFHTTPRequestOperation *)operation
|
113
|
+
forFetchRequest:(NSFetchRequest *)fetchRequest
|
114
|
+
fetchedObjects:(NSArray *)fetchedObjects
|
115
|
+
{
|
116
|
+
NSString *notificationName = [operation isFinished] ? AFIncrementalStoreContextDidFetchRemoteValues : AFIncrementalStoreContextWillFetchRemoteValues;
|
117
|
+
|
118
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
119
|
+
[userInfo setObject:operation forKey:AFIncrementalStoreRequestOperationKey];
|
120
|
+
[userInfo setObject:fetchRequest forKey:AFIncrementalStorePersistentStoreRequestKey];
|
121
|
+
if ([operation isFinished] && fetchedObjects) {
|
122
|
+
[userInfo setObject:fetchedObjects forKey:AFIncrementalStoreFetchedObjectsKey];
|
123
|
+
}
|
124
|
+
|
125
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:context userInfo:userInfo];
|
126
|
+
}
|
127
|
+
|
128
|
+
- (void)notifyManagedObjectContext:(NSManagedObjectContext *)context
|
129
|
+
aboutRequestOperations:(NSArray *)operations
|
130
|
+
forSaveChangesRequest:(NSSaveChangesRequest *)saveChangesRequest
|
131
|
+
{
|
132
|
+
NSString *notificationName = [[operations lastObject] isFinished] ? AFIncrementalStoreContextDidSaveRemoteValues : AFIncrementalStoreContextWillSaveRemoteValues;
|
133
|
+
|
134
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
135
|
+
[userInfo setObject:operations forKey:AFIncrementalStoreRequestOperationKey];
|
136
|
+
[userInfo setObject:saveChangesRequest forKey:AFIncrementalStorePersistentStoreRequestKey];
|
137
|
+
|
138
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:context userInfo:userInfo];
|
139
|
+
}
|
140
|
+
|
141
|
+
#pragma mark -
|
142
|
+
|
143
|
+
- (NSManagedObjectContext *)backingManagedObjectContext {
|
144
|
+
if (!_backingManagedObjectContext) {
|
145
|
+
_backingManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
146
|
+
_backingManagedObjectContext.persistentStoreCoordinator = _backingPersistentStoreCoordinator;
|
147
|
+
_backingManagedObjectContext.retainsRegisteredObjects = YES;
|
148
|
+
}
|
149
|
+
|
150
|
+
return _backingManagedObjectContext;
|
151
|
+
}
|
152
|
+
|
153
|
+
- (NSManagedObjectID *)objectIDForEntity:(NSEntityDescription *)entity
|
154
|
+
withResourceIdentifier:(NSString *)resourceIdentifier
|
155
|
+
{
|
156
|
+
if (!resourceIdentifier) {
|
157
|
+
return nil;
|
158
|
+
}
|
159
|
+
|
160
|
+
NSManagedObjectID *objectID = nil;
|
161
|
+
NSMutableDictionary *objectIDsByResourceIdentifier = [_registeredObjectIDsByEntityNameAndNestedResourceIdentifier objectForKey:entity.name];
|
162
|
+
if (objectIDsByResourceIdentifier) {
|
163
|
+
objectID = [objectIDsByResourceIdentifier objectForKey:resourceIdentifier];
|
164
|
+
}
|
165
|
+
|
166
|
+
if (!objectID) {
|
167
|
+
objectID = [self newObjectIDForEntity:entity referenceObject:AFReferenceObjectFromResourceIdentifier(resourceIdentifier)];
|
168
|
+
}
|
169
|
+
|
170
|
+
NSParameterAssert([objectID.entity.name isEqualToString:entity.name]);
|
171
|
+
|
172
|
+
return objectID;
|
173
|
+
}
|
174
|
+
|
175
|
+
- (NSManagedObjectID *)objectIDForBackingObjectForEntity:(NSEntityDescription *)entity
|
176
|
+
withResourceIdentifier:(NSString *)resourceIdentifier
|
177
|
+
{
|
178
|
+
if (!resourceIdentifier) {
|
179
|
+
return nil;
|
180
|
+
}
|
181
|
+
|
182
|
+
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[entity name]];
|
183
|
+
fetchRequest.resultType = NSManagedObjectIDResultType;
|
184
|
+
fetchRequest.fetchLimit = 1;
|
185
|
+
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"%K = %@", kAFIncrementalStoreResourceIdentifierAttributeName, resourceIdentifier];
|
186
|
+
|
187
|
+
__block NSArray *results = nil;
|
188
|
+
__block NSError *error = nil;
|
189
|
+
|
190
|
+
[self.backingPersistentStoreCoordinator lock];
|
191
|
+
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
|
192
|
+
[backingContext performBlockAndWait:^{
|
193
|
+
results = [backingContext executeFetchRequest:fetchRequest error:&error];
|
194
|
+
}];
|
195
|
+
[self.backingPersistentStoreCoordinator unlock];
|
196
|
+
|
197
|
+
if (error) {
|
198
|
+
NSLog(@"Error: %@", error);
|
199
|
+
return nil;
|
200
|
+
}
|
201
|
+
|
202
|
+
return [results lastObject];
|
203
|
+
}
|
204
|
+
|
205
|
+
- (void)updateBackingObject:(NSManagedObject *)backingObject
|
206
|
+
withAttributeAndRelationshipValuesFromManagedObject:(NSManagedObject *)managedObject
|
207
|
+
{
|
208
|
+
for (NSRelationshipDescription *relationship in [managedObject.entity.relationshipsByName allValues]) {
|
209
|
+
id relationshipValue = [managedObject valueForKey:relationship.name];
|
210
|
+
if (!relationshipValue) {
|
211
|
+
continue;
|
212
|
+
}
|
213
|
+
|
214
|
+
if ([relationship isToMany]) {
|
215
|
+
id mutableBackingRelationshipValue = nil;
|
216
|
+
if ([relationship isOrdered]) {
|
217
|
+
mutableBackingRelationshipValue = [NSMutableOrderedSet orderedSetWithCapacity:[relationshipValue count]];
|
218
|
+
} else {
|
219
|
+
mutableBackingRelationshipValue = [NSMutableSet setWithCapacity:[relationshipValue count]];
|
220
|
+
}
|
221
|
+
|
222
|
+
for (NSManagedObject *relationshipManagedObject in relationshipValue) {
|
223
|
+
if (![[relationshipManagedObject objectID] isTemporaryID]) {
|
224
|
+
NSManagedObjectID *backingRelationshipObjectID = [self objectIDForBackingObjectForEntity:relationship.destinationEntity withResourceIdentifier:AFResourceIdentifierFromReferenceObject([self referenceObjectForObjectID:relationshipManagedObject.objectID])];
|
225
|
+
if (backingRelationshipObjectID) {
|
226
|
+
NSManagedObject *backingRelationshipObject = [backingObject.managedObjectContext existingObjectWithID:backingRelationshipObjectID error:nil];
|
227
|
+
if (backingRelationshipObject) {
|
228
|
+
[mutableBackingRelationshipValue addObject:backingRelationshipObject];
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
[backingObject setValue:mutableBackingRelationshipValue forKey:relationship.name];
|
235
|
+
} else {
|
236
|
+
if (![[relationshipValue objectID] isTemporaryID]) {
|
237
|
+
NSManagedObjectID *backingRelationshipObjectID = [self objectIDForBackingObjectForEntity:relationship.destinationEntity withResourceIdentifier:AFResourceIdentifierFromReferenceObject([self referenceObjectForObjectID:[relationshipValue objectID]])];
|
238
|
+
if (backingRelationshipObjectID) {
|
239
|
+
NSManagedObject *backingRelationshipObject = [backingObject.managedObjectContext existingObjectWithID:backingRelationshipObjectID error:nil];
|
240
|
+
[backingObject setValue:backingRelationshipObject forKey:relationship.name];
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
[backingObject setValuesForKeysWithDictionary:[managedObject dictionaryWithValuesForKeys:[managedObject.entity.attributesByName allKeys]]];
|
247
|
+
}
|
248
|
+
|
249
|
+
#pragma mark -
|
250
|
+
|
251
|
+
- (BOOL)insertOrUpdateObjectsFromRepresentations:(id)representationOrArrayOfRepresentations
|
252
|
+
ofEntity:(NSEntityDescription *)entity
|
253
|
+
fromResponse:(NSHTTPURLResponse *)response
|
254
|
+
withContext:(NSManagedObjectContext *)context
|
255
|
+
error:(NSError *__autoreleasing *)error
|
256
|
+
completionBlock:(void (^)(NSArray *managedObjects, NSArray *backingObjects))completionBlock
|
257
|
+
{
|
258
|
+
NSParameterAssert([representationOrArrayOfRepresentations isKindOfClass:[NSArray class]] || [representationOrArrayOfRepresentations isKindOfClass:[NSDictionary class]]);
|
259
|
+
|
260
|
+
if ([representationOrArrayOfRepresentations count] == 0) {
|
261
|
+
if (completionBlock) {
|
262
|
+
completionBlock([NSArray array], [NSArray array]);
|
263
|
+
}
|
264
|
+
|
265
|
+
return NO;
|
266
|
+
}
|
267
|
+
|
268
|
+
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
|
269
|
+
NSString *lastModified = [[response allHeaderFields] valueForKey:@"Last-Modified"];
|
270
|
+
|
271
|
+
NSArray *representations = nil;
|
272
|
+
if ([representationOrArrayOfRepresentations isKindOfClass:[NSArray class]]) {
|
273
|
+
representations = representationOrArrayOfRepresentations;
|
274
|
+
} else if ([representationOrArrayOfRepresentations isKindOfClass:[NSDictionary class]]) {
|
275
|
+
representations = [NSArray arrayWithObject:representationOrArrayOfRepresentations];
|
276
|
+
}
|
277
|
+
|
278
|
+
NSUInteger numberOfRepresentations = [representations count];
|
279
|
+
NSMutableArray *mutableManagedObjects = [NSMutableArray arrayWithCapacity:numberOfRepresentations];
|
280
|
+
NSMutableArray *mutableBackingObjects = [NSMutableArray arrayWithCapacity:numberOfRepresentations];
|
281
|
+
|
282
|
+
for (NSDictionary *representation in representations) {
|
283
|
+
NSString *resourceIdentifier = [self.HTTPClient resourceIdentifierForRepresentation:representation ofEntity:entity fromResponse:response];
|
284
|
+
NSDictionary *attributes = [self.HTTPClient attributesForRepresentation:representation ofEntity:entity fromResponse:response];
|
285
|
+
|
286
|
+
__block NSManagedObject *managedObject = nil;
|
287
|
+
[context performBlockAndWait:^{
|
288
|
+
managedObject = [context existingObjectWithID:[self objectIDForEntity:entity withResourceIdentifier:resourceIdentifier] error:nil];
|
289
|
+
}];
|
290
|
+
|
291
|
+
[managedObject setValuesForKeysWithDictionary:attributes];
|
292
|
+
|
293
|
+
NSManagedObjectID *backingObjectID = [self objectIDForBackingObjectForEntity:entity withResourceIdentifier:resourceIdentifier];
|
294
|
+
__block NSManagedObject *backingObject = nil;
|
295
|
+
[backingContext performBlockAndWait:^{
|
296
|
+
if (backingObjectID) {
|
297
|
+
backingObject = [backingContext existingObjectWithID:backingObjectID error:nil];
|
298
|
+
} else {
|
299
|
+
backingObject = [NSEntityDescription insertNewObjectForEntityForName:entity.name inManagedObjectContext:backingContext];
|
300
|
+
[backingObject.managedObjectContext obtainPermanentIDsForObjects:[NSArray arrayWithObject:backingObject] error:nil];
|
301
|
+
}
|
302
|
+
}];
|
303
|
+
[backingObject setValue:resourceIdentifier forKey:kAFIncrementalStoreResourceIdentifierAttributeName];
|
304
|
+
[backingObject setValue:lastModified forKey:kAFIncrementalStoreLastModifiedAttributeName];
|
305
|
+
[backingObject setValuesForKeysWithDictionary:attributes];
|
306
|
+
|
307
|
+
if (!backingObjectID) {
|
308
|
+
[context insertObject:managedObject];
|
309
|
+
}
|
310
|
+
|
311
|
+
NSDictionary *relationshipRepresentations = [self.HTTPClient representationsForRelationshipsFromRepresentation:representation ofEntity:entity fromResponse:response];
|
312
|
+
for (NSString *relationshipName in relationshipRepresentations) {
|
313
|
+
NSRelationshipDescription *relationship = [[entity relationshipsByName] valueForKey:relationshipName];
|
314
|
+
id relationshipRepresentation = [relationshipRepresentations objectForKey:relationshipName];
|
315
|
+
if (!relationship || (relationship.isOptional && (!relationshipRepresentation || [relationshipRepresentation isEqual:[NSNull null]]))) {
|
316
|
+
continue;
|
317
|
+
}
|
318
|
+
|
319
|
+
if (!relationshipRepresentation || [relationshipRepresentation isEqual:[NSNull null]] || [relationshipRepresentation count] == 0) {
|
320
|
+
[managedObject setValue:nil forKey:relationshipName];
|
321
|
+
[backingObject setValue:nil forKey:relationshipName];
|
322
|
+
continue;
|
323
|
+
}
|
324
|
+
|
325
|
+
[self insertOrUpdateObjectsFromRepresentations:relationshipRepresentation ofEntity:relationship.destinationEntity fromResponse:response withContext:context error:error completionBlock:^(NSArray *managedObjects, NSArray *backingObjects) {
|
326
|
+
if ([relationship isToMany]) {
|
327
|
+
if ([relationship isOrdered]) {
|
328
|
+
[managedObject setValue:[NSOrderedSet orderedSetWithArray:managedObjects] forKey:relationship.name];
|
329
|
+
[backingObject setValue:[NSOrderedSet orderedSetWithArray:backingObjects] forKey:relationship.name];
|
330
|
+
} else {
|
331
|
+
[managedObject setValue:[NSSet setWithArray:managedObjects] forKey:relationship.name];
|
332
|
+
[backingObject setValue:[NSSet setWithArray:backingObjects] forKey:relationship.name];
|
333
|
+
}
|
334
|
+
} else {
|
335
|
+
[managedObject setValue:[managedObjects lastObject] forKey:relationship.name];
|
336
|
+
[backingObject setValue:[backingObjects lastObject] forKey:relationship.name];
|
337
|
+
}
|
338
|
+
}];
|
339
|
+
}
|
340
|
+
|
341
|
+
[mutableManagedObjects addObject:managedObject];
|
342
|
+
[mutableBackingObjects addObject:backingObject];
|
343
|
+
}
|
344
|
+
|
345
|
+
if (completionBlock) {
|
346
|
+
completionBlock(mutableManagedObjects, mutableBackingObjects);
|
347
|
+
}
|
348
|
+
|
349
|
+
return YES;
|
350
|
+
}
|
351
|
+
|
352
|
+
- (id)executeFetchRequest:(NSFetchRequest *)fetchRequest
|
353
|
+
withContext:(NSManagedObjectContext *)context
|
354
|
+
error:(NSError *__autoreleasing *)error
|
355
|
+
{
|
356
|
+
NSURLRequest *request = [self.HTTPClient requestForFetchRequest:fetchRequest withContext:context];
|
357
|
+
if ([request URL]) {
|
358
|
+
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
|
359
|
+
id representationOrArrayOfRepresentations = [self.HTTPClient representationOrArrayOfRepresentationsOfEntity:fetchRequest.entity fromResponseObject:responseObject];
|
360
|
+
|
361
|
+
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
362
|
+
childContext.parentContext = context;
|
363
|
+
childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
364
|
+
|
365
|
+
[childContext performBlockAndWait:^{
|
366
|
+
[self insertOrUpdateObjectsFromRepresentations:representationOrArrayOfRepresentations ofEntity:fetchRequest.entity fromResponse:operation.response withContext:childContext error:error completionBlock:^(NSArray *managedObjects, NSArray *backingObjects) {
|
367
|
+
|
368
|
+
NSSet *childObjects = [childContext registeredObjects];
|
369
|
+
|
370
|
+
if (![[self backingManagedObjectContext] save:error] || ![childContext save:error]) {
|
371
|
+
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[*error localizedFailureReason] userInfo:[NSDictionary dictionaryWithObject:*error forKey:NSUnderlyingErrorKey]];
|
372
|
+
}
|
373
|
+
|
374
|
+
for (NSManagedObject *childObject in childObjects) {
|
375
|
+
NSManagedObject *parentObject = [context objectWithID:childObject.objectID];
|
376
|
+
[parentObject.managedObjectContext refreshObject:parentObject mergeChanges:NO];
|
377
|
+
}
|
378
|
+
|
379
|
+
[context performBlockAndWait:^{
|
380
|
+
for (NSManagedObject *childObject in childObjects) {
|
381
|
+
NSManagedObject *parentObject = [context objectWithID:childObject.objectID];
|
382
|
+
[parentObject willChangeValueForKey:@"self"];
|
383
|
+
[context refreshObject:parentObject mergeChanges:NO];
|
384
|
+
[parentObject didChangeValueForKey:@"self"];
|
385
|
+
}
|
386
|
+
}];
|
387
|
+
|
388
|
+
NSMutableArray *fetchedObjects = [[NSMutableArray alloc] initWithCapacity:[managedObjects count]];
|
389
|
+
for (NSManagedObject *managedObject in managedObjects) {
|
390
|
+
NSManagedObject *fetchedObject = [context existingObjectWithID:managedObject.objectID error:NULL];
|
391
|
+
if (fetchedObject) {
|
392
|
+
[fetchedObjects addObject:fetchedObject];
|
393
|
+
}
|
394
|
+
}
|
395
|
+
[fetchedObjects sortUsingDescriptors:fetchRequest.sortDescriptors];
|
396
|
+
|
397
|
+
[self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest fetchedObjects:fetchedObjects];
|
398
|
+
}];
|
399
|
+
}];
|
400
|
+
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
|
401
|
+
NSLog(@"Error: %@", error);
|
402
|
+
[self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest fetchedObjects:nil];
|
403
|
+
}];
|
404
|
+
|
405
|
+
[self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest fetchedObjects:nil];
|
406
|
+
[self.HTTPClient enqueueHTTPRequestOperation:operation];
|
407
|
+
}
|
408
|
+
|
409
|
+
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
|
410
|
+
NSFetchRequest *backingFetchRequest = [fetchRequest copy];
|
411
|
+
backingFetchRequest.entity = [NSEntityDescription entityForName:fetchRequest.entityName inManagedObjectContext:backingContext];
|
412
|
+
|
413
|
+
switch (fetchRequest.resultType) {
|
414
|
+
case NSManagedObjectResultType: {
|
415
|
+
backingFetchRequest.resultType = NSDictionaryResultType;
|
416
|
+
backingFetchRequest.propertiesToFetch = [NSArray arrayWithObject:kAFIncrementalStoreResourceIdentifierAttributeName];
|
417
|
+
NSArray *results = [backingContext executeFetchRequest:backingFetchRequest error:error];
|
418
|
+
|
419
|
+
NSMutableArray *mutableObjects = [NSMutableArray arrayWithCapacity:[results count]];
|
420
|
+
for (NSString *resourceIdentifier in [results valueForKeyPath:kAFIncrementalStoreResourceIdentifierAttributeName]) {
|
421
|
+
NSManagedObjectID *objectID = [self objectIDForEntity:fetchRequest.entity withResourceIdentifier:resourceIdentifier];
|
422
|
+
NSManagedObject *object = [context objectWithID:objectID];
|
423
|
+
object.af_resourceIdentifier = resourceIdentifier;
|
424
|
+
[mutableObjects addObject:object];
|
425
|
+
}
|
426
|
+
|
427
|
+
return mutableObjects;
|
428
|
+
}
|
429
|
+
case NSManagedObjectIDResultType: {
|
430
|
+
NSArray *backingObjectIDs = [backingContext executeFetchRequest:backingFetchRequest error:error];
|
431
|
+
NSMutableArray *managedObjectIDs = [NSMutableArray arrayWithCapacity:[backingObjectIDs count]];
|
432
|
+
|
433
|
+
for (NSManagedObjectID *backingObjectID in backingObjectIDs) {
|
434
|
+
NSManagedObject *backingObject = [backingContext objectWithID:backingObjectID];
|
435
|
+
NSString *resourceID = [backingObject valueForKey:kAFIncrementalStoreResourceIdentifierAttributeName];
|
436
|
+
[managedObjectIDs addObject:[self objectIDForEntity:fetchRequest.entity withResourceIdentifier:resourceID]];
|
437
|
+
}
|
438
|
+
|
439
|
+
return managedObjectIDs;
|
440
|
+
}
|
441
|
+
case NSDictionaryResultType:
|
442
|
+
case NSCountResultType:
|
443
|
+
return [backingContext executeFetchRequest:backingFetchRequest error:error];
|
444
|
+
default:
|
445
|
+
return nil;
|
446
|
+
}
|
447
|
+
}
|
448
|
+
|
449
|
+
- (id)executeSaveChangesRequest:(NSSaveChangesRequest *)saveChangesRequest
|
450
|
+
withContext:(NSManagedObjectContext *)context
|
451
|
+
error:(NSError *__autoreleasing *)error
|
452
|
+
{
|
453
|
+
NSMutableArray *mutableOperations = [NSMutableArray array];
|
454
|
+
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
|
455
|
+
|
456
|
+
if ([self.HTTPClient respondsToSelector:@selector(requestForInsertedObject:)]) {
|
457
|
+
for (NSManagedObject *insertedObject in [saveChangesRequest insertedObjects]) {
|
458
|
+
NSURLRequest *request = [self.HTTPClient requestForInsertedObject:insertedObject];
|
459
|
+
if (!request) {
|
460
|
+
[backingContext performBlockAndWait:^{
|
461
|
+
CFUUIDRef UUID = CFUUIDCreate(NULL);
|
462
|
+
NSString *resourceIdentifier = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, UUID);
|
463
|
+
CFRelease(UUID);
|
464
|
+
|
465
|
+
NSManagedObject *backingObject = [NSEntityDescription insertNewObjectForEntityForName:insertedObject.entity.name inManagedObjectContext:backingContext];
|
466
|
+
[backingObject.managedObjectContext obtainPermanentIDsForObjects:[NSArray arrayWithObject:backingObject] error:nil];
|
467
|
+
[backingObject setValue:resourceIdentifier forKey:kAFIncrementalStoreResourceIdentifierAttributeName];
|
468
|
+
[self updateBackingObject:backingObject withAttributeAndRelationshipValuesFromManagedObject:insertedObject];
|
469
|
+
[backingContext save:nil];
|
470
|
+
}];
|
471
|
+
|
472
|
+
[insertedObject willChangeValueForKey:@"objectID"];
|
473
|
+
[context obtainPermanentIDsForObjects:[NSArray arrayWithObject:insertedObject] error:nil];
|
474
|
+
[insertedObject didChangeValueForKey:@"objectID"];
|
475
|
+
continue;
|
476
|
+
}
|
477
|
+
|
478
|
+
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
|
479
|
+
NSString *resourceIdentifier = [self.HTTPClient resourceIdentifierForRepresentation:responseObject ofEntity:[insertedObject entity] fromResponse:operation.response];
|
480
|
+
NSManagedObjectID *objectID = [self objectIDForEntity:[insertedObject entity] withResourceIdentifier:resourceIdentifier];
|
481
|
+
insertedObject.af_resourceIdentifier = resourceIdentifier;
|
482
|
+
[insertedObject setValuesForKeysWithDictionary:[self.HTTPClient attributesForRepresentation:responseObject ofEntity:insertedObject.entity fromResponse:operation.response]];
|
483
|
+
|
484
|
+
[backingContext performBlockAndWait:^{
|
485
|
+
__block NSManagedObject *backingObject = nil;
|
486
|
+
if (objectID) {
|
487
|
+
[backingContext performBlockAndWait:^{
|
488
|
+
backingObject = [backingContext existingObjectWithID:objectID error:nil];
|
489
|
+
}];
|
490
|
+
}
|
491
|
+
|
492
|
+
if (!backingObject) {
|
493
|
+
backingObject = [NSEntityDescription insertNewObjectForEntityForName:insertedObject.entity.name inManagedObjectContext:backingContext];
|
494
|
+
[backingObject.managedObjectContext obtainPermanentIDsForObjects:[NSArray arrayWithObject:backingObject] error:nil];
|
495
|
+
}
|
496
|
+
|
497
|
+
[backingObject setValue:resourceIdentifier forKey:kAFIncrementalStoreResourceIdentifierAttributeName];
|
498
|
+
[self updateBackingObject:backingObject withAttributeAndRelationshipValuesFromManagedObject:insertedObject];
|
499
|
+
[backingContext save:nil];
|
500
|
+
}];
|
501
|
+
|
502
|
+
[insertedObject willChangeValueForKey:@"objectID"];
|
503
|
+
[context obtainPermanentIDsForObjects:[NSArray arrayWithObject:insertedObject] error:nil];
|
504
|
+
[insertedObject didChangeValueForKey:@"objectID"];
|
505
|
+
|
506
|
+
[context refreshObject:insertedObject mergeChanges:NO];
|
507
|
+
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
|
508
|
+
NSLog(@"Insert Error: %@", error);
|
509
|
+
}];
|
510
|
+
|
511
|
+
[mutableOperations addObject:operation];
|
512
|
+
}
|
513
|
+
}
|
514
|
+
|
515
|
+
if ([self.HTTPClient respondsToSelector:@selector(requestForUpdatedObject:)]) {
|
516
|
+
for (NSManagedObject *updatedObject in [saveChangesRequest updatedObjects]) {
|
517
|
+
NSManagedObjectID *backingObjectID = [self objectIDForBackingObjectForEntity:[updatedObject entity] withResourceIdentifier:AFResourceIdentifierFromReferenceObject([self referenceObjectForObjectID:updatedObject.objectID])];
|
518
|
+
|
519
|
+
NSURLRequest *request = [self.HTTPClient requestForUpdatedObject:updatedObject];
|
520
|
+
if (!request) {
|
521
|
+
[backingContext performBlockAndWait:^{
|
522
|
+
NSManagedObject *backingObject = [backingContext existingObjectWithID:backingObjectID error:nil];
|
523
|
+
[self updateBackingObject:backingObject withAttributeAndRelationshipValuesFromManagedObject:updatedObject];
|
524
|
+
[backingContext save:nil];
|
525
|
+
}];
|
526
|
+
continue;
|
527
|
+
}
|
528
|
+
|
529
|
+
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
|
530
|
+
[updatedObject setValuesForKeysWithDictionary:[self.HTTPClient attributesForRepresentation:responseObject ofEntity:updatedObject.entity fromResponse:operation.response]];
|
531
|
+
|
532
|
+
[backingContext performBlockAndWait:^{
|
533
|
+
NSManagedObject *backingObject = [backingContext existingObjectWithID:backingObjectID error:nil];
|
534
|
+
[self updateBackingObject:backingObject withAttributeAndRelationshipValuesFromManagedObject:updatedObject];
|
535
|
+
[backingContext save:nil];
|
536
|
+
}];
|
537
|
+
|
538
|
+
[context refreshObject:updatedObject mergeChanges:NO];
|
539
|
+
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
|
540
|
+
NSLog(@"Update Error: %@", error);
|
541
|
+
[context refreshObject:updatedObject mergeChanges:NO];
|
542
|
+
}];
|
543
|
+
|
544
|
+
[mutableOperations addObject:operation];
|
545
|
+
}
|
546
|
+
}
|
547
|
+
|
548
|
+
if ([self.HTTPClient respondsToSelector:@selector(requestForDeletedObject:)]) {
|
549
|
+
for (NSManagedObject *deletedObject in [saveChangesRequest deletedObjects]) {
|
550
|
+
NSManagedObjectID *backingObjectID = [self objectIDForBackingObjectForEntity:[deletedObject entity] withResourceIdentifier:AFResourceIdentifierFromReferenceObject([self referenceObjectForObjectID:deletedObject.objectID])];
|
551
|
+
|
552
|
+
NSURLRequest *request = [self.HTTPClient requestForDeletedObject:deletedObject];
|
553
|
+
if (!request) {
|
554
|
+
[backingContext performBlockAndWait:^{
|
555
|
+
NSManagedObject *backingObject = [backingContext existingObjectWithID:backingObjectID error:nil];
|
556
|
+
[backingContext deleteObject:backingObject];
|
557
|
+
[backingContext save:nil];
|
558
|
+
}];
|
559
|
+
continue;
|
560
|
+
}
|
561
|
+
|
562
|
+
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
|
563
|
+
[backingContext performBlockAndWait:^{
|
564
|
+
NSManagedObject *backingObject = [backingContext existingObjectWithID:backingObjectID error:nil];
|
565
|
+
[backingContext deleteObject:backingObject];
|
566
|
+
[backingContext save:nil];
|
567
|
+
}];
|
568
|
+
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
|
569
|
+
NSLog(@"Delete Error: %@", error);
|
570
|
+
}];
|
571
|
+
|
572
|
+
[mutableOperations addObject:operation];
|
573
|
+
}
|
574
|
+
}
|
575
|
+
|
576
|
+
// NSManagedObjectContext removes object references from an NSSaveChangesRequest as each object is saved, so create a copy of the original in order to send useful information in AFIncrementalStoreContextDidSaveRemoteValues notification.
|
577
|
+
NSSaveChangesRequest *saveChangesRequestCopy = [[NSSaveChangesRequest alloc] initWithInsertedObjects:[saveChangesRequest.insertedObjects copy] updatedObjects:[saveChangesRequest.updatedObjects copy] deletedObjects:[saveChangesRequest.deletedObjects copy] lockedObjects:[saveChangesRequest.lockedObjects copy]];
|
578
|
+
|
579
|
+
[self notifyManagedObjectContext:context aboutRequestOperations:mutableOperations forSaveChangesRequest:saveChangesRequestCopy];
|
580
|
+
|
581
|
+
[self.HTTPClient enqueueBatchOfHTTPRequestOperations:mutableOperations progressBlock:nil completionBlock:^(NSArray *operations) {
|
582
|
+
[self notifyManagedObjectContext:context aboutRequestOperations:operations forSaveChangesRequest:saveChangesRequestCopy];
|
583
|
+
}];
|
584
|
+
|
585
|
+
return [NSArray array];
|
586
|
+
}
|
587
|
+
|
588
|
+
#pragma mark - NSIncrementalStore
|
589
|
+
|
590
|
+
- (BOOL)loadMetadata:(NSError *__autoreleasing *)error {
|
591
|
+
if (!_backingObjectIDByObjectID) {
|
592
|
+
NSMutableDictionary *mutableMetadata = [NSMutableDictionary dictionary];
|
593
|
+
[mutableMetadata setValue:[[NSProcessInfo processInfo] globallyUniqueString] forKey:NSStoreUUIDKey];
|
594
|
+
[mutableMetadata setValue:NSStringFromClass([self class]) forKey:NSStoreTypeKey];
|
595
|
+
[self setMetadata:mutableMetadata];
|
596
|
+
|
597
|
+
_backingObjectIDByObjectID = [[NSCache alloc] init];
|
598
|
+
_registeredObjectIDsByEntityNameAndNestedResourceIdentifier = [[NSMutableDictionary alloc] init];
|
599
|
+
|
600
|
+
NSManagedObjectModel *model = [self.persistentStoreCoordinator.managedObjectModel copy];
|
601
|
+
for (NSEntityDescription *entity in model.entities) {
|
602
|
+
// Don't add properties for sub-entities, as they already exist in the super-entity
|
603
|
+
if ([entity superentity]) {
|
604
|
+
continue;
|
605
|
+
}
|
606
|
+
|
607
|
+
NSAttributeDescription *resourceIdentifierProperty = [[NSAttributeDescription alloc] init];
|
608
|
+
[resourceIdentifierProperty setName:kAFIncrementalStoreResourceIdentifierAttributeName];
|
609
|
+
[resourceIdentifierProperty setAttributeType:NSStringAttributeType];
|
610
|
+
[resourceIdentifierProperty setIndexed:YES];
|
611
|
+
|
612
|
+
NSAttributeDescription *lastModifiedProperty = [[NSAttributeDescription alloc] init];
|
613
|
+
[lastModifiedProperty setName:kAFIncrementalStoreLastModifiedAttributeName];
|
614
|
+
[lastModifiedProperty setAttributeType:NSStringAttributeType];
|
615
|
+
[lastModifiedProperty setIndexed:NO];
|
616
|
+
|
617
|
+
[entity setProperties:[entity.properties arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:resourceIdentifierProperty, lastModifiedProperty, nil]]];
|
618
|
+
}
|
619
|
+
|
620
|
+
_backingPersistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
621
|
+
|
622
|
+
return YES;
|
623
|
+
} else {
|
624
|
+
return NO;
|
625
|
+
}
|
626
|
+
}
|
627
|
+
|
628
|
+
- (NSArray *)obtainPermanentIDsForObjects:(NSArray *)array error:(NSError **)error {
|
629
|
+
NSMutableArray *mutablePermanentIDs = [NSMutableArray arrayWithCapacity:[array count]];
|
630
|
+
for (NSManagedObject *managedObject in array) {
|
631
|
+
NSManagedObjectID *managedObjectID = managedObject.objectID;
|
632
|
+
if ([managedObjectID isTemporaryID] && managedObject.af_resourceIdentifier) {
|
633
|
+
NSManagedObjectID *objectID = [self objectIDForEntity:managedObject.entity withResourceIdentifier:managedObject.af_resourceIdentifier];
|
634
|
+
[mutablePermanentIDs addObject:objectID];
|
635
|
+
} else {
|
636
|
+
[mutablePermanentIDs addObject:managedObjectID];
|
637
|
+
}
|
638
|
+
}
|
639
|
+
|
640
|
+
return mutablePermanentIDs;
|
641
|
+
}
|
642
|
+
|
643
|
+
- (id)executeRequest:(NSPersistentStoreRequest *)persistentStoreRequest
|
644
|
+
withContext:(NSManagedObjectContext *)context
|
645
|
+
error:(NSError *__autoreleasing *)error
|
646
|
+
{
|
647
|
+
if (persistentStoreRequest.requestType == NSFetchRequestType) {
|
648
|
+
return [self executeFetchRequest:(NSFetchRequest *)persistentStoreRequest withContext:context error:error];
|
649
|
+
} else if (persistentStoreRequest.requestType == NSSaveRequestType) {
|
650
|
+
return [self executeSaveChangesRequest:(NSSaveChangesRequest *)persistentStoreRequest withContext:context error:error];
|
651
|
+
} else {
|
652
|
+
NSMutableDictionary *mutableUserInfo = [NSMutableDictionary dictionary];
|
653
|
+
[mutableUserInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Unsupported NSFetchRequestResultType, %d", nil), persistentStoreRequest.requestType] forKey:NSLocalizedDescriptionKey];
|
654
|
+
if (error) {
|
655
|
+
*error = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:0 userInfo:mutableUserInfo];
|
656
|
+
}
|
657
|
+
|
658
|
+
return nil;
|
659
|
+
}
|
660
|
+
}
|
661
|
+
|
662
|
+
- (NSIncrementalStoreNode *)newValuesForObjectWithID:(NSManagedObjectID *)objectID
|
663
|
+
withContext:(NSManagedObjectContext *)context
|
664
|
+
error:(NSError *__autoreleasing *)error
|
665
|
+
{
|
666
|
+
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[[objectID entity] name]];
|
667
|
+
fetchRequest.resultType = NSDictionaryResultType;
|
668
|
+
fetchRequest.fetchLimit = 1;
|
669
|
+
fetchRequest.includesSubentities = NO;
|
670
|
+
|
671
|
+
NSArray *attributes = [[[NSEntityDescription entityForName:fetchRequest.entityName inManagedObjectContext:context] attributesByName] allValues];
|
672
|
+
NSArray *intransientAttributes = [attributes filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"isTransient == NO"]];
|
673
|
+
fetchRequest.propertiesToFetch = [[intransientAttributes valueForKeyPath:@"name"] arrayByAddingObject:kAFIncrementalStoreLastModifiedAttributeName];
|
674
|
+
|
675
|
+
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"%K = %@", kAFIncrementalStoreResourceIdentifierAttributeName, AFResourceIdentifierFromReferenceObject([self referenceObjectForObjectID:objectID])];
|
676
|
+
|
677
|
+
__block NSArray *results;
|
678
|
+
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
|
679
|
+
[backingContext performBlockAndWait:^{
|
680
|
+
results = [backingContext executeFetchRequest:fetchRequest error:error];
|
681
|
+
}];
|
682
|
+
|
683
|
+
NSDictionary *attributeValues = [results lastObject] ?: [NSDictionary dictionary];
|
684
|
+
NSIncrementalStoreNode *node = [[NSIncrementalStoreNode alloc] initWithObjectID:objectID withValues:attributeValues version:1];
|
685
|
+
|
686
|
+
if ([self.HTTPClient respondsToSelector:@selector(shouldFetchRemoteAttributeValuesForObjectWithID:inManagedObjectContext:)] && [self.HTTPClient shouldFetchRemoteAttributeValuesForObjectWithID:objectID inManagedObjectContext:context]) {
|
687
|
+
if (attributeValues) {
|
688
|
+
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
689
|
+
childContext.parentContext = context;
|
690
|
+
childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
691
|
+
|
692
|
+
NSMutableURLRequest *request = [self.HTTPClient requestWithMethod:@"GET" pathForObjectWithID:objectID withContext:context];
|
693
|
+
NSString *lastModified = [attributeValues objectForKey:kAFIncrementalStoreLastModifiedAttributeName];
|
694
|
+
if (lastModified) {
|
695
|
+
[request setValue:lastModified forHTTPHeaderField:@"Last-Modified"];
|
696
|
+
}
|
697
|
+
|
698
|
+
if ([request URL]) {
|
699
|
+
if ([attributeValues valueForKey:kAFIncrementalStoreLastModifiedAttributeName]) {
|
700
|
+
[request setValue:[[attributeValues valueForKey:kAFIncrementalStoreLastModifiedAttributeName] description] forHTTPHeaderField:@"If-Modified-Since"];
|
701
|
+
}
|
702
|
+
|
703
|
+
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, NSDictionary *representation) {
|
704
|
+
NSManagedObject *managedObject = [childContext existingObjectWithID:objectID error:error];
|
705
|
+
|
706
|
+
NSMutableDictionary *mutableAttributeValues = [attributeValues mutableCopy];
|
707
|
+
[mutableAttributeValues addEntriesFromDictionary:[self.HTTPClient attributesForRepresentation:representation ofEntity:managedObject.entity fromResponse:operation.response]];
|
708
|
+
[mutableAttributeValues removeObjectForKey:kAFIncrementalStoreLastModifiedAttributeName];
|
709
|
+
[managedObject setValuesForKeysWithDictionary:mutableAttributeValues];
|
710
|
+
|
711
|
+
NSManagedObjectID *backingObjectID = [self objectIDForBackingObjectForEntity:[objectID entity] withResourceIdentifier:AFResourceIdentifierFromReferenceObject([self referenceObjectForObjectID:objectID])];
|
712
|
+
NSManagedObject *backingObject = [[self backingManagedObjectContext] existingObjectWithID:backingObjectID error:nil];
|
713
|
+
[backingObject setValuesForKeysWithDictionary:mutableAttributeValues];
|
714
|
+
|
715
|
+
NSString *lastModified = [[operation.response allHeaderFields] valueForKey:@"Last-Modified"];
|
716
|
+
if (lastModified) {
|
717
|
+
[backingObject setValue:lastModified forKey:kAFIncrementalStoreLastModifiedAttributeName];
|
718
|
+
}
|
719
|
+
|
720
|
+
id observer = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:childContext queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
721
|
+
[context mergeChangesFromContextDidSaveNotification:note];
|
722
|
+
}];
|
723
|
+
|
724
|
+
[childContext performBlockAndWait:^{
|
725
|
+
if (![[self backingManagedObjectContext] save:error] || ![childContext save:error]) {
|
726
|
+
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[*error localizedFailureReason] userInfo:[NSDictionary dictionaryWithObject:*error forKey:NSUnderlyingErrorKey]];
|
727
|
+
}
|
728
|
+
}];
|
729
|
+
|
730
|
+
[[NSNotificationCenter defaultCenter] removeObserver:observer];
|
731
|
+
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
|
732
|
+
NSLog(@"Error: %@, %@", operation, error);
|
733
|
+
}];
|
734
|
+
|
735
|
+
[self.HTTPClient enqueueHTTPRequestOperation:operation];
|
736
|
+
}
|
737
|
+
}
|
738
|
+
}
|
739
|
+
|
740
|
+
return node;
|
741
|
+
}
|
742
|
+
|
743
|
+
- (id)newValueForRelationship:(NSRelationshipDescription *)relationship
|
744
|
+
forObjectWithID:(NSManagedObjectID *)objectID
|
745
|
+
withContext:(NSManagedObjectContext *)context
|
746
|
+
error:(NSError *__autoreleasing *)error
|
747
|
+
{
|
748
|
+
if ([self.HTTPClient respondsToSelector:@selector(shouldFetchRemoteValuesForRelationship:forObjectWithID:inManagedObjectContext:)] && [self.HTTPClient shouldFetchRemoteValuesForRelationship:relationship forObjectWithID:objectID inManagedObjectContext:context]) {
|
749
|
+
NSURLRequest *request = [self.HTTPClient requestWithMethod:@"GET" pathForRelationship:relationship forObjectWithID:objectID withContext:context];
|
750
|
+
|
751
|
+
if ([request URL] && ![[context existingObjectWithID:objectID error:nil] hasChanges]) {
|
752
|
+
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
753
|
+
childContext.parentContext = context;
|
754
|
+
childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
755
|
+
|
756
|
+
AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
|
757
|
+
id representationOrArrayOfRepresentations = [self.HTTPClient representationOrArrayOfRepresentationsOfEntity:relationship.entity fromResponseObject:responseObject];
|
758
|
+
|
759
|
+
[childContext performBlockAndWait:^{
|
760
|
+
[self insertOrUpdateObjectsFromRepresentations:representationOrArrayOfRepresentations ofEntity:relationship.destinationEntity fromResponse:operation.response withContext:childContext error:error completionBlock:^(NSArray *managedObjects, NSArray *backingObjects) {
|
761
|
+
NSManagedObject *managedObject = [childContext objectWithID:objectID];
|
762
|
+
NSManagedObject *backingObject = [[self backingManagedObjectContext] existingObjectWithID:objectID error:nil];
|
763
|
+
|
764
|
+
if ([relationship isToMany]) {
|
765
|
+
if ([relationship isOrdered]) {
|
766
|
+
[managedObject setValue:[NSOrderedSet orderedSetWithArray:managedObjects] forKey:relationship.name];
|
767
|
+
[backingObject setValue:[NSOrderedSet orderedSetWithArray:backingObjects] forKey:relationship.name];
|
768
|
+
} else {
|
769
|
+
[managedObject setValue:[NSSet setWithArray:managedObjects] forKey:relationship.name];
|
770
|
+
[backingObject setValue:[NSSet setWithArray:backingObjects] forKey:relationship.name];
|
771
|
+
}
|
772
|
+
} else {
|
773
|
+
[managedObject setValue:[managedObjects lastObject] forKey:relationship.name];
|
774
|
+
[backingObject setValue:[backingObjects lastObject] forKey:relationship.name];
|
775
|
+
}
|
776
|
+
|
777
|
+
id observer = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:childContext queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
778
|
+
[context mergeChangesFromContextDidSaveNotification:note];
|
779
|
+
}];
|
780
|
+
|
781
|
+
if (![[self backingManagedObjectContext] save:error] || ![childContext save:error]) {
|
782
|
+
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[*error localizedFailureReason] userInfo:[NSDictionary dictionaryWithObject:*error forKey:NSUnderlyingErrorKey]];
|
783
|
+
}
|
784
|
+
|
785
|
+
[[NSNotificationCenter defaultCenter] removeObserver:observer];
|
786
|
+
}];
|
787
|
+
}];
|
788
|
+
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
|
789
|
+
NSLog(@"Error: %@, %@", operation, error);
|
790
|
+
}];
|
791
|
+
|
792
|
+
[self.HTTPClient enqueueHTTPRequestOperation:operation];
|
793
|
+
}
|
794
|
+
}
|
795
|
+
|
796
|
+
NSManagedObjectID *backingObjectID = [self objectIDForBackingObjectForEntity:[objectID entity] withResourceIdentifier:AFResourceIdentifierFromReferenceObject([self referenceObjectForObjectID:objectID])];
|
797
|
+
NSManagedObject *backingObject = (backingObjectID == nil) ? nil : [[self backingManagedObjectContext] existingObjectWithID:backingObjectID error:nil];
|
798
|
+
|
799
|
+
if (backingObject && ![backingObject hasChanges]) {
|
800
|
+
id backingRelationshipObject = [backingObject valueForKeyPath:relationship.name];
|
801
|
+
if ([relationship isToMany]) {
|
802
|
+
NSMutableArray *mutableObjects = [NSMutableArray arrayWithCapacity:[backingRelationshipObject count]];
|
803
|
+
for (NSString *resourceIdentifier in [backingRelationshipObject valueForKeyPath:kAFIncrementalStoreResourceIdentifierAttributeName]) {
|
804
|
+
NSManagedObjectID *objectID = [self objectIDForEntity:relationship.destinationEntity withResourceIdentifier:resourceIdentifier];
|
805
|
+
[mutableObjects addObject:objectID];
|
806
|
+
}
|
807
|
+
|
808
|
+
return mutableObjects;
|
809
|
+
} else {
|
810
|
+
NSString *resourceIdentifier = [backingRelationshipObject valueForKeyPath:kAFIncrementalStoreResourceIdentifierAttributeName];
|
811
|
+
NSManagedObjectID *objectID = [self objectIDForEntity:relationship.destinationEntity withResourceIdentifier:resourceIdentifier];
|
812
|
+
|
813
|
+
return objectID ?: [NSNull null];
|
814
|
+
}
|
815
|
+
} else {
|
816
|
+
if ([relationship isToMany]) {
|
817
|
+
return [NSArray array];
|
818
|
+
} else {
|
819
|
+
return [NSNull null];
|
820
|
+
}
|
821
|
+
}
|
822
|
+
}
|
823
|
+
|
824
|
+
- (void)managedObjectContextDidRegisterObjectsWithIDs:(NSArray *)objectIDs {
|
825
|
+
[super managedObjectContextDidRegisterObjectsWithIDs:objectIDs];
|
826
|
+
|
827
|
+
for (NSManagedObjectID *objectID in objectIDs) {
|
828
|
+
id referenceObject = [self referenceObjectForObjectID:objectID];
|
829
|
+
if (!referenceObject) {
|
830
|
+
continue;
|
831
|
+
}
|
832
|
+
|
833
|
+
NSMutableDictionary *objectIDsByResourceIdentifier = [_registeredObjectIDsByEntityNameAndNestedResourceIdentifier objectForKey:objectID.entity.name] ?: [NSMutableDictionary dictionary];
|
834
|
+
[objectIDsByResourceIdentifier setObject:objectID forKey:AFResourceIdentifierFromReferenceObject(referenceObject)];
|
835
|
+
|
836
|
+
[_registeredObjectIDsByEntityNameAndNestedResourceIdentifier setObject:objectIDsByResourceIdentifier forKey:objectID.entity.name];
|
837
|
+
}
|
838
|
+
}
|
839
|
+
|
840
|
+
- (void)managedObjectContextDidUnregisterObjectsWithIDs:(NSArray *)objectIDs {
|
841
|
+
[super managedObjectContextDidUnregisterObjectsWithIDs:objectIDs];
|
842
|
+
|
843
|
+
for (NSManagedObjectID *objectID in objectIDs) {
|
844
|
+
[[_registeredObjectIDsByEntityNameAndNestedResourceIdentifier objectForKey:objectID.entity.name] removeObjectForKey:AFResourceIdentifierFromReferenceObject([self referenceObjectForObjectID:objectID])];
|
845
|
+
}
|
846
|
+
}
|
847
|
+
|
848
|
+
@end
|