Permalink
Browse files

Added Context

  • Loading branch information...
1 parent f6f199a commit fa19bf6b2023c1cc754103d5247d0647b23bf9c9 @cneuwirt committed Feb 21, 2014
Showing with 3,512 additions and 38 deletions.
  1. +140 −8 Miruken.xcodeproj/project.pbxproj
  2. +1 −1 Miruken/Callbacks/MKCallbackHandler+Buffer.h
  3. +1 −1 Miruken/Callbacks/MKCallbackHandler+Buffer.m
  4. +1 −1 Miruken/Callbacks/{Callbacks.h → MirukenCallbacks.h}
  5. +10 −0 Miruken/Cocoa/MirukenCocoa.h
  6. +35 −0 Miruken/Cocoa/UINavigationController_ContextualMixin.h
  7. +170 −0 Miruken/Cocoa/UINavigationController_ContextualMixin.m
  8. +37 −0 Miruken/Cocoa/UIViewController_ContextualMixin.h
  9. +111 −0 Miruken/Cocoa/UIViewController_ContextualMixin.m
  10. +1 −1 Miruken/Concurrency/{Concurrency.h → MirukenConcurrency.h}
  11. +16 −0 Miruken/Context/MKCallbackHandler+Context.h
  12. +24 −0 Miruken/Context/MKCallbackHandler+Context.m
  13. +15 −0 Miruken/Context/MKCallbackHandler+Track.h
  14. +47 −0 Miruken/Context/MKCallbackHandler+Track.m
  15. +25 −0 Miruken/Context/MKContext+Subscribe.h
  16. +34 −0 Miruken/Context/MKContext+Subscribe.m
  17. +31 −0 Miruken/Context/MKContext+Traversal.h
  18. +159 −0 Miruken/Context/MKContext+Traversal.m
  19. +95 −0 Miruken/Context/MKContext.h
  20. +314 −0 Miruken/Context/MKContext.m
  21. +25 −0 Miruken/Context/MKContextObserver.h
  22. +79 −0 Miruken/Context/MKContextObserver.m
  23. +57 −0 Miruken/Context/MKContextual.h
  24. +171 −0 Miruken/Context/MKContextual.m
  25. +27 −0 Miruken/Context/MKContextualHelper.h
  26. +83 −0 Miruken/Context/MKContextualHelper.m
  27. +16 −0 Miruken/Context/MirukenContext.h
  28. +63 −0 Miruken/MKCollectionExtensions.h
  29. +167 −0 Miruken/MKCollectionExtensions.m
  30. +33 −0 Miruken/MKDirtyMixin.h
  31. +97 −0 Miruken/MKDirtyMixin.m
  32. +20 −0 Miruken/MKTraversal.h
  33. +68 −0 Miruken/MKTraversal.m
  34. +41 −0 Miruken/MKTraversing.h
  35. +35 −0 Miruken/MKTraversingMixin.h
  36. +157 −0 Miruken/MKTraversingMixin.m
  37. +25 −0 Miruken/MKWeakCell.h
  38. +112 −0 Miruken/MKWeakCell.m
  39. +13 −5 Miruken/Miruken.h
  40. +0 −13 Miruken/Miruken.m
  41. +1 −1 MirukenTests/Callbacks/ApplicationCallbackHandler.h
  42. +1 −1 MirukenTests/Callbacks/ConfigurationCallbackHandler.h
  43. +1 −1 MirukenTests/Callbacks/ConfigurationTagCallbackHandler.h
  44. +1 −1 MirukenTests/Callbacks/MKCallbackHandlerTests.m
  45. +1 −1 MirukenTests/Callbacks/ResourcesCallbackHandler.h
  46. +1 −1 MirukenTests/Concurrency/MKBufferedPromiseTests.m
  47. +1 −1 MirukenTests/Concurrency/MKConcurrencyTests.m
  48. +1 −1 MirukenTests/Concurrency/MKDeferredTests.m
  49. +839 −0 MirukenTests/Context/MKContextTests.m
  50. +17 −0 MirukenTests/Context/SomeContextualObject.h
  51. +30 −0 MirukenTests/Context/SomeContextualObject.m
  52. +21 −0 MirukenTests/Context/SomeViewController.h
  53. +41 −0 MirukenTests/Context/SomeViewController.m

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -1,5 +1,5 @@
//
-// CallbackHandler+Buffer.h
+// MKCallbackHandler+Buffer.h
// Miruken
//
// Created by Craig Neuwirt on 4/5/13.
@@ -1,5 +1,5 @@
//
-// CallbackHandler+Buffer.m
+// MKCallbackHandler+Buffer.m
// Miruken
//
// Created by Craig Neuwirt on 4/5/13.
@@ -1,5 +1,5 @@
//
-// Callbacks.h
+// MirukenCallbacks.h
// Miruken
//
// Created by Craig Neuwirt on 2/16/14.
@@ -0,0 +1,10 @@
+//
+// MirukenCocoa.h
+// Miruken
+//
+// Created by Craig Neuwirt on 2/21/14.
+// Copyright (c) 2014 Craig Neuwirt. All rights reserved.
+//
+
+#import "UIViewController_ContextualMixin.h"
+#import "UINavigationController_ContextualMixin.h"
@@ -0,0 +1,35 @@
+//
+// UINavigationController_ContextualMixin.h
+// Miruken
+//
+// Created by Craig Neuwirt on 10/12/12.
+// Copyright (c) 2014 Craig Neuwirt. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "MKContextual.h"
+
+/**
+ This class is an mix-in that expands the contextual behavior for
+ UINavigationController's such that pushes and pops create and destroy
+ child contexts, respectively. It can be enabled by
+ [UINavigationController mixinFrom:UINavigationController_ContextualMixin.class];
+ */
+
+@interface UINavigationController_ContextualMixin : UINavigationController <MKContextual>
+
+@end
+
+
+/**
+ This category enhances the UINavigationController with contextual extensions.
+ */
+
+@interface UINavigationController (UINavigationController_Contextual)
+
+- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
+ pushedContext:(MKContextAction)pushedContext;
+
+- (UIViewController *)popViewControllerAnimated:(BOOL)animated poppedContext:(MKContextAction)poppedContext;
+
+@end
@@ -0,0 +1,170 @@
+//
+// UINavigationController_ContextualMixin.m
+// Miruken
+//
+// Created by Craig Neuwirt on 10/12/12.
+// Copyright (c) 2014 Craig Neuwirt. All rights reserved.
+//
+
+#import "UINavigationController_ContextualMixin.h"
+#import "UIViewController_ContextualMixin.h"
+#import "MKContextualHelper.h"
+#import "MKContextObserver.h"
+#import "EXTScope.h"
+
+@implementation UINavigationController_ContextualMixin
+
+- (void)contextChanged:(MKContext *)context
+{
+ if (context == nil)
+ return;
+
+ id owner = self;
+ for (UIViewController *viewController in self.viewControllers)
+ {
+ [self bindChildContextAndPopOnEnd:viewController owner:owner];
+ owner = viewController;
+ }
+}
+
+#pragma mark - Swizzled methods
+
+- (id)swizzleContextual_initWithRootViewController:(UIViewController *)rootViewController
+{
+ [self bindChildContextAndPopOnEnd:rootViewController owner:self];
+ return [self swizzleContextual_initWithRootViewController:rootViewController];
+}
+
+- (void)swizzleContextual_setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated
+{
+ id owner = self;
+ for (UIViewController *viewController in viewControllers)
+ {
+ [self bindChildContextAndPopOnEnd:viewController owner:owner];
+ owner = viewController;
+ }
+ [self swizzleContextual_setViewControllers:viewControllers animated:animated];
+}
+
+- (void)swizzleContextual_pushViewController:(UIViewController *)viewController animated:(BOOL)animated
+{
+ id owner = self.topViewController ? self.topViewController : self;
+
+ [self bindChildContextAndPopOnEnd:viewController owner:owner];
+ [self swizzleContextual_pushViewController:viewController animated:animated];
+}
+
+- (UIViewController *)swizzleContextual_popViewControllerAnimated:(BOOL)animated
+{
+ // The following check for the popped controller against self is ONLY
+ // needed to accomodate a bug in MonkeyTalk in which calling
+ // popViewController on a UINavigationController that has a single child
+ // returns the owning navigatorinstead of nil.
+
+ NSUInteger count = [[self viewControllers] count];
+ UIViewController *topController = [self topViewController];
+ id viewController = [self swizzleContextual_popViewControllerAnimated:animated];
+
+ if (count > 1 && viewController == self)
+ viewController = topController;
+
+ if (viewController)
+ [MKContextualHelper endContextBoundTo:viewController];
+ return viewController;
+}
+
+- (NSArray *)swizzleContextual_popToViewController:(UIViewController *)viewController animated:(BOOL)animated
+{
+ NSArray *popped = [self swizzleContextual_popToViewController:viewController animated:animated];
+ for (id poppedViewController in popped)
+ [MKContextualHelper endContextBoundTo:poppedViewController];
+ return popped;
+}
+
+- (NSArray *)swizzleContextual_popToRootViewControllerAnimated:(BOOL)animated
+{
+ NSArray *popped = [self swizzleContextual_popToRootViewControllerAnimated:animated];
+ for (id poppedViewController in popped)
+ [MKContextualHelper endContextBoundTo:poppedViewController];
+ return popped;
+}
+
+- (MKContext *)bindChildContextAndPopOnEnd:(UIViewController *)viewController owner:(id)owner
+{
+ // This gesture recognizer performs a pop without a push back
+
+ self.interactivePopGestureRecognizer.enabled = NO;
+
+ MKContext *childContext = [MKContextualHelper bindChildContextFrom:owner toChild:viewController];
+
+ if (childContext)
+ {
+ BOOL navBarHidden = self.navigationBarHidden;
+
+ @weakify(self, viewController);
+ [childContext subscribe:[MKContextObserver contextDidEnd:^(MKContext *context)
+ {
+ @strongify(self, viewController);
+
+ // Ensure the parent is active to prevent nested UINavigationController
+ // pops which is not supported.
+
+ if (context.parent && context.parent.state != MKContextStateActive)
+ return;
+
+ [self setNavigationBarHidden:navBarHidden animated:YES];
+
+ if ([self.viewControllers containsObject:viewController])
+ {
+ [self popToViewController:viewController animated:NO];
+
+ // The following check for the popped controller against self is ONLY
+ // needed to accomodate a bug in MonkeyTalk in which calling
+ // popViewController on a UINavigationController that has a single child
+ // returns the owning navigator instead of nil.
+
+ NSUInteger count = [self.viewControllers count];
+ UIViewController *poppedController = [self popViewControllerAnimated:YES];
+ if (count == 1 && (poppedController == nil || poppedController == self))
+ [self.context end];
+ }
+ }] retain:YES];
+ }
+
+ return childContext;
+}
+
+@end
+
+#pragma mark - UINavigationController_Contextual methods
+
+@implementation UINavigationController (UINavigationController_Contextual)
+
+- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
+ pushedContext:(MKContextAction)pushedContext
+{
+ if (pushedContext)
+ {
+ id owner = self.topViewController ? self.topViewController : self;
+
+ MKContext *childContext =
+ [MKContextualHelper bindChildContextFrom:owner toChild:viewController];
+
+ if (childContext)
+ pushedContext(childContext);
+ }
+ [self pushViewController:viewController animated:animated];
+}
+
+- (UIViewController *)popViewControllerAnimated:(BOOL)animated poppedContext:(MKContextAction)poppedContext
+{
+ if (poppedContext && self.viewControllers.count > 1)
+ {
+ MKContext *childContext = [MKContextualHelper contextBoundTo:self.topViewController];
+ if (childContext)
+ poppedContext(childContext);
+ }
+ return [self popViewControllerAnimated:animated];
+}
+
+@end
@@ -0,0 +1,37 @@
+//
+// UIViewController_ContextualMixin.h
+// Miruken
+//
+// Created by Craig Neuwirt on 10/16/12.
+// Copyright (c) 2014 Craig Neuwirt. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "MKContextual.h"
+
+/**
+ This class is an mix-in that expands the contextual behavior for
+ UIViewController's such that presentViewController and dismissViewController
+ will create and destroy child contexts, respectively. It can be enabled by
+ [UIViewController mixinFrom:UIViewController_ContextualMixin.class];
+ */
+
+@interface UIViewController_ContextualMixin : UIViewController <MKContextual>
+
+@end
+
+
+/**
+ This category enhances the UIViewController with contextual extensions.
+ */
+
+@interface UIViewController (UIViewController_Contextual)
+
+- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)animated
+ completion:(void (^)(void))completion presentedContext:(MKContextAction)presentedContext;
+
+
+- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
+ dismissedContext:(MKContextAction)dismissedContext;
+
+@end
@@ -0,0 +1,111 @@
+//
+// UIViewController_ContextualMixin.m
+// Miruken
+//
+// Created by Craig Neuwirt on 10/16/12.
+// Copyright (c) 2014 Craig Neuwirt. All rights reserved.
+//
+
+#import "UIViewController_ContextualMixin.h"
+#import "MKContextualHelper.h"
+#import "MKContextObserver.h"
+#import "EXTScope.h"
+
+@implementation UIViewController_ContextualMixin
+
+#pragma mark - Modal methods
+
+- (void)swizzleContextual_presentViewController:(UIViewController *)viewControllerToPresent
+ animated:(BOOL)flag completion:(void (^)(void))completion
+{
+ MKContext *childContext =
+ [MKContextualHelper bindChildContextFrom:self toChild:viewControllerToPresent];
+
+ if (childContext)
+ {
+ @weakify(self, viewControllerToPresent);
+ [childContext subscribe:[MKContextObserver contextDidEnd:^(id<MKContext> ctx) {
+ @strongify(self, viewControllerToPresent);
+ if (self.presentedViewController == viewControllerToPresent
+ && [self.presentedViewController isBeingDismissed] == NO)
+ [self dismissViewControllerAnimated:YES completion:nil];
+ }] retain:YES];
+ }
+
+ [self swizzleContextual_presentViewController:viewControllerToPresent
+ animated:flag completion:completion];
+}
+
+- (void)swizzleContextual_dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
+{
+ UIViewController *presentedViewController = self.presentedViewController;
+ if ([presentedViewController isBeingDismissed] == NO)
+ [self swizzleContextual_dismissViewControllerAnimated:flag completion:completion];
+ if (presentedViewController != nil)
+ [MKContextualHelper endContextBoundTo:presentedViewController];
+}
+
+#pragma mark - Container methods
+
+- (void)swizzleContextual_addChildViewController:(UIViewController *)childController
+{
+ [MKContextualHelper bindChildContextFrom:self toChild:childController];
+ [self swizzleContextual_addChildViewController:childController];
+}
+
+- (void)swizzleContextual_removeFromParentViewController
+{
+ [MKContextualHelper endContextBoundTo:self];
+ [self swizzleContextual_removeFromParentViewController];
+}
+
+- (void)swizzleContextual_prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
+{
+ // Container Views are realized by emebedded segues that ultimately get added
+ // as child controllers. Unfortunately, this lifecycle results in the contained
+ // view controller's viewDidLoad method being called before addChildViewController.
+ // This prevents access to the child context. To overcome this, we bind the
+ // context in the prepareForSegue method which occurs before viewDidLoad.
+
+ if ([NSStringFromClass(segue.class) isEqualToString:@"UIStoryboardEmbedSegue"])
+ [MKContextualHelper bindChildContextFrom:self toChild:segue.destinationViewController];
+
+ [self swizzleContextual_prepareForSegue:segue sender:sender];
+}
+@end
+
+#pragma mark - UIViewController_Contextual methods
+
+@implementation UIViewController (UIViewController_Contextual)
+
+- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)animated
+ completion:(void (^)(void))completion presentedContext:(MKContextAction)presentedContext
+{
+ if (presentedContext)
+ {
+ MKContext *childContext =
+ [MKContextualHelper bindChildContextFrom:self toChild:viewControllerToPresent];
+
+ if (childContext)
+ presentedContext(childContext);
+ }
+ [self presentViewController:viewControllerToPresent animated:animated completion:completion];
+}
+
+- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
+ dismissedContext:(MKContextAction)dismissedContext
+{
+ if (dismissedContext)
+ {
+ UIViewController *presentedViewController = self.presentedViewController;
+ if (presentedViewController)
+ {
+ MKContext *childContext = [MKContextualHelper contextBoundTo:presentedViewController];
+ if (childContext)
+ dismissedContext(childContext);
+ }
+ }
+ [self dismissViewControllerAnimated:animated completion:completion];
+}
+
+@end
@@ -1,5 +1,5 @@
//
-// Concurrency.h
+// MirukenConcurrency.h
// Miruken
//
// Created by Craig Neuwirt on 2/9/14.
Oops, something went wrong.

0 comments on commit fa19bf6

Please sign in to comment.