Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Real World ReactiveCocoa ❤️
Search
R. Peres
April 24, 2015
Technology
260
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Real World ReactiveCocoa ❤️
R. Peres
April 24, 2015
More Decks by R. Peres
See All by R. Peres
diff objetive-c swift
ruiaaperes
0
120
Need For Deliveries
ruiaaperes
0
81
Other Decks in Technology
See All in Technology
Claude の新機能を、 出た日に触って社内に届けるまで
rfdnxbro
1
180
Code for Security プロジェクト紹介 2026年10月
yu23ki14
0
110
二歩目のフロントエンド・ウェブアクセシビリティ
camiha
2
630
しろおびから始める!ログ活用の第一歩
shumei_ito
0
950
How to Design Configuration for Rails Applications
willnet
1
810
その Valkey 使いこなしていますか?
onk
PRO
5
1.5k
Tofu-Controller lightning talk @ Containerday 2026
gionn
0
110
パスキーの自動テストはどこまでいけるのか
teamlab
PRO
0
250
SAENM ~Jupyterで残すPoCの試行錯誤、未来の自分と仲間を助ける記録!~
terapyon
0
110
V8のArray.prototype.flatを最大約20倍速くするまでと、巨大OSSへの大規模コミットの道のり
riyaamemiya
0
290
Webの常識が通じないHTMLメールのダークモード
teamlab
PRO
0
130
コンテナ運用におけるネットワーク・コンテナコストの削減方法/How to Reduce Network and Container Costs in Container Operations
masakiokuda
1
230
Featured
See All Featured
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
1k
Utilizing Notion as your number one productivity tool
mfonobong
4
620
[RailsConf 2023] Rails as a piece of cake
palkan
59
7.1k
Code Reviewing Like a Champion
maltzj
529
40k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
エンジニアに許された特別な時間の終わり
watany
109
250k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
1k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
2
950
[SF Ruby Conf 2025] Rails X
palkan
3
1.5k
Color Theory Basics | Prateek | Gurzu
gurzu
1
490
The Invisible Side of Design
smashingmag
301
52k
Transcript
ReactiveCocoa @RuiAAPeres Real World
101 Signals
[RACObserve(self, usernameTextField) subscribeNext:^(NSString *newName) { NSLog(@"%@", newName); }]; Subscribers
Higher Order functions [[RACObserve(self, usernameTextField) filter:^(NSString *newName) { return [newName
hasPrefix:@"j"]; }] subscribeNext:^(NSString *newName) { NSLog(@"%@", newName); }];
Bind + Derivation RAC(self, loginButton.enabled) = [RACSignal combineLatest: combinedSignals reduce:^(NSString
*p, NSString *u) { return @(p.length > 0 && u.length > 0); }]; NSArray *combinedSignals = @[RACObserve(self, passwordTextField), RACObserve(self, usernameTextField)];
Composition [[[loginUserSignal flattenMap:^(User *user) { // Return a signal that
loads cached messages for the user. return [client loadCachedMessagesForUser:user]; }] flattenMap:^(NSArray *messages) { // Return a signal that fetches any remaining messages. return [client fetchMessagesAfterMessage:messages.lastObject]; }] subscribeNext:^(NSArray *newMessages) { NSLog(@"New messages: %@", newMessages); } completed:^{ NSLog(@"Fetched all messages."); }];
“The power of these abstractions are very hard to convey
to someone who has never had the privilege of learning them” Bartosz Milewski
None
ViewModel DataSource Storage
ViewModel
@interface RPNotificationsViewModel : NSObject @property(nonatomic,strong,readonly)RACCommand *notificationsCommand; @property(nonatomic,strong,readonly)RACSignal *errorMessageSignal; @property(nonatomic,strong,readonly)RACSignal *notificationsSignal;
@end
DataSource
@interface RPNotificationsDataSource : NSObject @property(nonatomic,strong,readonly)RACSignal *dataSourceChanged; - (instancetype)initWithNotificationsSignal:(RACSignal *)signal; -
(NSString *)titleForSection:(NSUInteger)index; @end
self.dataSourceChanged = [signal map:^id(NSArray *notifications) { NSArray *normalizedNotifications = //
special sauce return normalizedNotifications; }];
@[OCTNotification1, OCTNotification2, OCTNotification3…]; Map from an Array to a NSOrderedSet
fold @[“RuiPeres/Octify, iOS-Goodies/iOS-Goodies, ReactiveCocoa/ReactiveCocoa];
@“ReactiveCocoa/ReactiveCocoa” @“RuiAAPeres/Octify” @“iOS-Goodies/iOS-Goodies” [[signal map:^id(NSArray *notifications) { }]; @property(nonatomic,strong)NSOrderedSet *sections;
return [NSOrderedSet orderedSetWithArray: ]; RAC(self,sections) = [notifications.rac_sequence foldLeftWithStart:[NSMutableOrderedSet orderedSet] reduce:^id(NSMutableOrderedSet *accumulator, OCTResponse *response) { OCTNotification *notification = response.parsedResult; [accumulator addObject:[notification ownerAndLogin]]; return accumulator; }].array
Storage
@interface RPNotificationsStorage : NSObject + (RACSignal *)storeNotifications:(RACSignal *)signal; + (RACSignal
*)storedNotifications; @end
+ (RACSignal *)storeNotifications:(RACSignal *)signal { return [signal doNext:^(NSArray *notifications) {
// Store the notifications }]; }
+ (RACSignal *)storedNotifications { return [RACSignal createSignal: ^RACDisposable *(id<RACSubscriber> subscriber)
{ NSError *error = // if something fails NSArray *notifications = // get notifications if (error) { [subscriber sendError:[NSError storedError]]; } else { [subscriber sendNext:notifications]; [subscriber sendCompleted]; } return nil; }]; }
The okay way
VM subscribed by VC DS UI ST
[self.notificationsViewModel.notificationsSignal subscribeNext:^(NSArray *notifications){ // 1. Store the notifications // 2.
Update the Data Source // 3. Update the UI }];
“We can do better”
VM DS Observed + Bind Network -> DataSource
notifications = self.notificationsViewModel.notificationsSignal; dataSource = [[RPNotificationsDataSource alloc] initWithNotificationsSignal:notifications];
VM ST injected side effect DS Observed + Bind Network
+ Storage -> DataSource
notifications = self.notificationsViewModel.notificationsSignal; persistence = [RPNotificationsPersistence storeNotifications:notifications]; dataSource = [[RPNotificationsDataSource
alloc] initWithNotificationsSignal:persistence];
ST DS Observed + Bind Storage -> DataSource
persistence = [RPNotificationsPersistence storedNotifications]; dataSource = [[RPNotificationsDataSource alloc] initWithNotificationsSignal:persistence];
DS UI Observed + Bind Observed + Bind UI
RAC(self,nyanCat.hidden) = [self.dataSource.dataSourceChanged map:^id(NSArray *notifications) { return [notifications count] ==
0?@YES:@NO; }];
RAC([UIApplication sharedApplication],applicationIconBadgeNumber) = [[self.dataSource.dataSourceChanged filter:^BOOL(id value) { return [RPPreferences preference:RPBadgeCountPreference];
}] map:^id(NSArray *notifications) { return @([notifications count]); }];
VM ST side effect DS Observed + Bind UI Observed
+ Bind Observed + Bind UI
None
RACSequence *handlers = @[ @"twitter://user?screen_name=%@", @"tweetbot://%@/user_profile/%@", @"twitterrific:///profile?screen_name=%@", @“https://twitter.com/%@"].rac_sequence; return [[[[handlers
map:^id(NSString *value) { return [NSString stringWithFormat:value,screenName,screenName]; }] map:^id(NSString *value) { return [NSURL URLWithString:value]; }] filter:^BOOL(NSURL *value) { return [[UIApplication sharedApplication] canOpenURL:value]; }] head];
Final thoughts
Thanks! @RuiAAPeres