Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction of CXCKeyValueObserver - KVO and Unit Testing

cockscomb
February 15, 2014

Introduction of CXCKeyValueObserver - KVO and Unit Testing

My presentation @ Cocoa Kansai #54, about CXCKeyValueObserver (https://github.com/cockscomb/CXCKeyValueObserver)

cockscomb

February 15, 2014
Tweet

More Decks by cockscomb

Other Decks in Programming

Transcript

  1. ͸ͯͳϒϩά

    View full-size slide

  2. CXCKeyValueObserver

    View full-size slide

  3. Key Value Observation

    View full-size slide

  4. Key Value Observation
    • Key Value CodingͰΦϒδΣΫτͷঢ়ଶΛ؂ࢹ
    • @property͸KVCͷنଇʹै͏ͨΊ؂ࢹͰ͖Δ
    • Modelͷঢ়ଶΛ؂ࢹͯ͠ViewΛߋ৽ͳͲͰศར

    View full-size slide

  5. - (void)viewDidLoad
    {
    [super viewDidLoad];
    [self.someObject addObserver:self
    forKeyPath:@"example"
    options:NSKeyValueObservingOptionNew
    context:NULL];
    }
    !
    - (void)dealloc
    {
    [self.someObject removeObserver:self forKeyPath:@"example"];
    }
    !
    - (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context
    {
    if (object == self.someObject) {
    if ([keyPath isEqualToString:@"example"]) {
    // do something
    }
    }
    }

    View full-size slide

  6. - (void)viewDidLoad
    {
    [super viewDidLoad];
    [self.someObject addObserver:self
    forKeyPath:@"example"
    options:NSKeyValueObservingOptionNew
    context:NULL];
    }
    !
    - (void)dealloc
    {
    [self.someObject removeObserver:self forKeyPath:@"example"];
    }
    !
    - (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context
    {
    if (object == self.someObject) {
    if ([keyPath isEqualToString:@"example"]) {
    // do something
    }
    }
    }
    1

    View full-size slide

  7. - (void)viewDidLoad
    {
    [super viewDidLoad];
    [self.someObject addObserver:self
    forKeyPath:@"example"
    options:NSKeyValueObservingOptionNew
    context:NULL];
    }
    !
    - (void)dealloc
    {
    [self.someObject removeObserver:self forKeyPath:@"example"];
    }
    !
    - (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context
    {
    if (object == self.someObject) {
    if ([keyPath isEqualToString:@"example"]) {
    // do something
    }
    }
    }
    1
    2

    View full-size slide

  8. - (void)viewDidLoad
    {
    [super viewDidLoad];
    [self.someObject addObserver:self
    forKeyPath:@"example"
    options:NSKeyValueObservingOptionNew
    context:NULL];
    }
    !
    - (void)dealloc
    {
    [self.someObject removeObserver:self forKeyPath:@"example"];
    }
    !
    - (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context
    {
    if (object == self.someObject) {
    if ([keyPath isEqualToString:@"example"]) {
    // do something
    }
    }
    }
    1
    3
    2

    View full-size slide

  9. - (void)viewDidLoad
    {
    [super viewDidLoad];
    [self.someObject addObserver:self
    forKeyPath:@"example"
    options:NSKeyValueObservingOptionNew
    context:NULL];
    }
    !
    - (void)dealloc
    {
    [self.someObject removeObserver:self forKeyPath:@"example"];
    }
    !
    - (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context
    {
    if (object == self.someObject) {
    if ([keyPath isEqualToString:@"example"]) {
    // do something
    }
    }
    }

    View full-size slide

  10. • observeValueForKeyPath:ofObject:change:context:

    ͱ͍͏ϝιουʹશͯهड़͠ͳ͚Ε͹ͳΒͳ͍
    • removeObserver:forKeyPath:

    Λݺͼ๨Εͯ͸͍͚ͳ͍
    • ͩΜͩΜෳࡶʹͳ͍ͬͯ͘
    • ෳ਺ਓ։ൃʹ޲͔ͳ͍આ

    View full-size slide

  11. CXCKeyValueObserver

    View full-size slide

  12. - (void)viewDidLoad
    {
    [super viewDidLoad];
    _observer = [[CXCKeyValueObserver alloc]
    initWithObservee:self.someObject
    forKeyPath:@"example"
    options:NSKeyValueObservingOptionNew
    block:^(id observee, NSString *keyPath, NSDictionary *change) {
    // do something
    }];
    }

    View full-size slide

  13. • BlockΛ༻͍ͯએݴతʹهड़
    • observer΁ͷڧࢀর͕ͳ͘ͳΔͱ͖ࣗಈతʹ

    removeObserver:forKeyPath:
    • ϦʔζφϒϧͳΠϯλʔϑΣʔε

    View full-size slide

  14. https://github.com/cockscomb/CXCKeyValueObserver

    View full-size slide

  15. CXCKeyValueObserver
    • MIT License
    • CocoaPodsରԠ
    • ୯ମςετ

    View full-size slide

  16. The Unit Testing of
    Objective-C
    • https://speakerdeck.com/cockscomb/the-
    unit-testing-of-objective-c
    • Kiwi
    • Nocilla

    View full-size slide

  17. Unit Testing Tools
    • XCTest
    • (BDDϥΠϒϥϦ)
    • ඇಉظςετϥΠϒϥϦ
    • ΧελϜϚονϟ
    • Mock/StubϥΠϒϥϦ
    • ωοτϫʔΫstubϥΠϒϥϦ

    View full-size slide

  18. BDD
    • ϥΠϒϥϦ
    • Kiwi
    • Specta
    • Behavior Driven Development

    View full-size slide

  19. ඇಉظςετ
    • ϥΠϒϥϦ
    • Kiwi
    • Specta
    • TRVSMonitor
    • TKRGuard
    • ಉظతʹॲཧ͞Εͳ͍ͱ͖ͷςετ

    View full-size slide

  20. ΧελϜϚονϟ
    • ϥΠϒϥϦ
    • Kiwi
    • OCHamcrest
    • Expecta
    • ศརͳAssert

    View full-size slide

  21. Mock/Stub
    • ϥΠϒϥϦ
    • Kiwi
    • OCMock
    • ςετ͍ͨ͠ΦϒδΣΫτͷؔ࿈ΦϒδΣΫτ
    Λٖࣅతʹ࡞Γग़ͯ͠ςετΛ؆ུԽ

    View full-size slide

  22. ωοτϫʔΫStub
    • ϥΠϒϥϦ
    • Nocilla
    • OHHTTPStubs
    • ωοτϫʔΫͷঢ়ଶ΍ϨεϙϯεΛٖࣅతʹ࡞
    Γग़ͯ͠ωοτϫʔΫपΓͷςετΛ؆ུԽ

    View full-size slide

  23. • BDD
    • Kiwi
    • Specta
    • ඇಉظςετ
    • Kiwi
    • Specta
    • TRVSMonitor
    • TKRGuard
    !
    • ΧελϜϚονϟ
    • Kiwi
    • OCHamcrest
    • Expecta
    • Mock/Stub
    • Kiwi
    • OCMock
    • ωοτϫʔΫstub
    • Nocilla
    • OHHTTPStubs

    View full-size slide

  24. CXCKeyValueObserver
    • Specta
    • Expecta
    • OCMock

    View full-size slide

  25. @interface CXCKeyValueObserverTestsObject : NSObject
    @property (nonatomic, strong) id exampleProperty;
    @end
    @implementation CXCKeyValueObserverTestsObject
    @end
    !
    SpecBegin(CXCKeyValueObserver)
    describe(@"KeyValueObserver", ^{
    !
    __block CXCKeyValueObservingBlock block;
    __block id gotObservee;
    __block NSString *gotKeyPath;
    __block NSDictionary *gotChange;
    __block CXCKeyValueObserverTestsObject *observee;
    __block NSString *keyPath;
    !
    beforeEach(^{
    block = ^(id _observee, NSString *_keyPath, NSDictionary *_change) {
    gotObservee = _observee;
    gotKeyPath = _keyPath;
    gotChange = _change;
    };
    gotObservee = nil;
    gotKeyPath = nil;
    gotChange = nil;
    observee = [[CXCKeyValueObserverTestsObject alloc] init];
    keyPath = NSStringFromSelector(@selector(exampleProperty));
    });
    !
    ...
    });
    SpecEnd

    View full-size slide

  26. it(@"should call its block when the observing property changed", ^{
    __unused CXCKeyValueObserver *observer = [[CXCKeyValueObserver alloc]
    initWithObservee:observee
    forKeyPath:keyPath
    options:0
    block:block];
    !
    observee.exampleProperty = @"exampleNewValue";
    !
    expect(gotObservee).to.equal(observee);
    expect(gotKeyPath).to.beIdenticalTo(keyPath);
    expect(gotChange).notTo.beNil();
    });

    View full-size slide

  27. it(@"should call removeObserver:forKayPath:context: when deallocated", ^{
    id mock = [OCMockObject niceMockForClass:
    [CXCKeyValueObserverTestsObject class]];
    [[mock expect] removeObserver:[OCMArg any]
    forKeyPath:keyPath
    context:[OCMArg anyPointer]];
    !
    @autoreleasepool {
    CXCKeyValueObserver *observer = [[CXCKeyValueObserver alloc]
    initWithObservee:mock
    forKeyPath:keyPath
    options:0
    block:block];
    !
    observer = nil;
    }
    !
    [mock verify];
    });

    View full-size slide

  28. CXCKeyValueObserver
    • Specta
    • Expecta
    • OCMock

    View full-size slide

  29. ࡶஊίʔφʔ

    View full-size slide

  30. description
    • descriptionॻ͍͓͖͍ͯͨ
    • ඞཁʹԠͯ͡debugDescription
    • AppCodeศར

    View full-size slide

  31. description
    - (NSString *)description
    {
    NSMutableString *description = [NSMutableString
    stringWithFormat:@"<%@: ", NSStringFromClass([self class])];
    [description appendFormat:@"self.observee=%@", self.observee];
    [description appendFormat:@", self.keyPath=%@", self.keyPath];
    [description appendFormat:@", self.options=%lu", (unsigned long)self.options];
    [description appendFormat:@", self.block=%p", self.block];
    [description appendString:@">"];
    return description;
    }

    View full-size slide

  32. AppCode
    • JetBrains IDE
    • ௒ߴػೳ

    View full-size slide

  33. AppCode
    • ݡ͍ΦʔτίϯϓϦʔτ
    • ϦϑΥʔϚοτ
    • ίʔυͷੜ੒
    • ϦϑΝΫλϦϯά
    • ίʔυͷղੳ

    View full-size slide

  34. github.com/cockscomb/CXCKeyValueObserver

    View full-size slide

  35. DPDLTDPNCIBUFOBCMPHDPN

    View full-size slide