我仍然总是围绕着RAC和FRP - 现在正在努力弄清楚如何实现我通常不得不在其他地方使用的模式.
假设我正在制作一个flashcard应用程序,主屏幕是我的卡片组列表.这个应用程序使用网络服务器的状态作为事实的来源.我不希望每次显示屏幕时都从服务器重新获取这个甲板列表 - 这太好了,我可以在带有重播主题的多播信号中使用延迟网络请求来有效地记住该列表.
我有两种方法可以通过从服务器重新获取来刷新此列表,这对我来说很复杂.我希望能够在应用程序中发生任何数量的事情时使这个"缓存"列表无效(例如,用户导航到其他屏幕并执行某些操作会使主屏幕上的套牌列表过期或应用程序刚刚被重新唤醒,所以我们可以猜测它可能已经过时而且是安全的),以便下次用户返回到主屏幕时,它一开始不会显示任何内容(而不是显示旧列表,因为它知道它是由于用户的操作而过时)并将重新获取列表,一旦下载就显示它.我怎样才能最优雅地处理这种"无效"状态(希望没有实际状态)?
我还希望能够在超时时使"缓存"列表到期 - 基本上,套牌列表信号将给出缓存列表,直到有足够的时间过去,此时它会在提供数据之前懒洋洋地发出网络请求.
我有几个关于如何实现这两件事的想法,但它们似乎有点复杂.希望得到一些指导或指向一些示例项目的方向.
我可以看到处理这个问题的一个简单方法是拥有一个必要的服务层,并强制处理缓存和缓存失效,并使用广播事件使缓存无效并从缓存返回或生成网络请求以填充缓存时反应层试图访问数据.如果不先了解这种方法的反应方式,我宁愿不遵从这种方法.
谢谢!
我在RxSwift中学习了示例代码.在文件GithubSignupViewModel1.swift中,validatedUsername的定义是:
validatedUsername = input.username //the username is a textfiled.rx_text
.flatMapLatest { username -> Observable<ValidationResult> in
print("-------->1:")
return validationService.validateUsername(username)
.observeOn(MainScheduler.instance)
.catchErrorJustReturn(.Failed(message: "Error contacting server"))
}
.shareReplay(1)
Run Code Online (Sandbox Code Playgroud)
validateUsername方法最终被称为以下方法:
func usernameAvailable(username: String) -> Observable<Bool> {
// this is ofc just mock, but good enough
print("-------->2:")
let URL = NSURL(string: "https://github.com/\(username.URLEscaped)")!
let request = NSURLRequest(URL: URL)
return self.URLSession.rx_response(request)
.map { (maybeData, response) in
print("-------->3:")
return response.statusCode == 404
}
.catchErrorJustReturn(false)
}
Run Code Online (Sandbox Code Playgroud)
这是我的困惑:
每当我在用户名文本字段中快速输入一个字符时,消息--------> 1:,--------> 2:显示,以及稍后的消息-------- > 3:显示,但只显示一个--------> 3:消息.
当我输入较慢的字符时,消息--------> 1:,--------> 2:,--------> 3:连续显示.
但是当我将flatMapLatest更改为flatMap时,我输入了多少个字符,我将得到相同数量的--------> 3:message. …
尝试遵循ReactiveCocoa 的最佳实践,每小时更新我的UI.这就是我所拥有的:
NSDateComponents *components = [[[NSCalendar sharedCalendar] calendar] components:NSMinuteCalendarUnit fromDate:[NSDate date]];
// Generalization, I know (not every hour has 60 minutes, but bear with me).
NSInteger minutesToNextHour = 60 - components.minute;
RACSubject *updateEventSignal = [RACSubject subject];
[updateEventSignal subscribeNext:^(NSDate *now) {
// Update some UI
}];
[[[RACSignal interval:(60 * minutesToNextHour)] take:1] subscribeNext:^(id x) {
[updateEventSignal sendNext:x];
[[RACSignal interval:3600] subscribeNext:^(id x) {
[updateEventSignal sendNext:x];
}];
}];
Run Code Online (Sandbox Code Playgroud)
这有一些明显的缺陷:手动订阅和发送,只是"感觉不对劲".关于如何使这更"反应"的任何想法?
我已经阅读了文档,通过他们精彩的Playground示例,搜索了SO,并达到了我的google-fu的范围,但我不能为我的生活包围我如何使用ReactiveSwift.
鉴于以下......
class SomeModel {
var mapType: MKMapType = .standard
var selectedAnnotation: MKAnnotation?
var annotations = [MKAnnotation]()
var enableRouteButton = false
// The rest of the implementation...
}
class SomeViewController: UIViewController {
let model: SomeModel
let mapView = MKMapView(frame: .zero) // It's position is set elsewhere
@IBOutlet var routeButton: UIBarButtonItem?
init(model: SomeModel) {
self.model = model
super.init(nibName: nil, bundle: nil)
}
// The rest of the implementation...
}
Run Code Online (Sandbox Code Playgroud)
....如何使用ReactiveSwift初始化SomeViewController值SomeModel,然后SomeViewController在SomeModel …
我正在构建的客户端正在使用Reactive Cocoa和Octokit,到目前为止它已经进展顺利.但是现在我正处于一个我想要获取存储库集合的地步,并且无法绕过这样的"RAC方式".
// fire this when an authenticated client is set
[[RACAbleWithStart([GHDataStore sharedStore], client)
filter:^BOOL (OCTClient *client) {
return client != nil && client.authenticated;
}]
subscribeNext:^(OCTClient *client) {
[[[client fetchUserRepositories] deliverOn:RACScheduler.mainThreadScheduler]
subscribeNext:^(OCTRepository *fetchedRepo) {
NSLog(@" Received new repo: %@",fetchedRepo.name);
}
error:^(NSError *error) {
NSLog(@"Error fetching repos: %@",error.localizedDescription);
}];
} completed:^{
NSLog(@"Completed fetching repos");
}];
Run Code Online (Sandbox Code Playgroud)
我最初假设-subscribeNext:会传递一个NSArray,但现在明白它会返回每个返回的"next"对象的消息,在这种情况下是一个OCTRepository.
现在我可以这样做:
NSMutableArray *repos = [NSMutableArray array];
// most of that code above
subscribeNext:^(OCTRepository …Run Code Online (Sandbox Code Playgroud) 我正在构建一个演示应用程序,并尽可能地尝试符合ReactiveCocoa 设计模式.以下是该应用的功能:
所以顺序是1)更新位置2)合并所有3个天气提取.我已经构建了一个WeatherManager暴露天气对象,位置信息和手动更新方法的单例.该单例符合CLLocationManagerDelegate协议.位置代码非常基本,所以我将其遗漏.唯一真正感兴趣的是:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
// omitting accuracy & cache checking
CLLocation *location = [locations lastObject];
self.currentLocation = location;
[self.locationManager stopUpdatingLocation];
}
Run Code Online (Sandbox Code Playgroud)
获取天气条件非常相似,所以我构建了一个方法来生成RACSignal从URL获取JSON.
- (RACSignal *)fetchJSONFromURL:(NSURL *)url {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (! error) {
NSError *jsonError = nil;
id json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
if (! …Run Code Online (Sandbox Code Playgroud) design-patterns functional-programming objective-c ios reactive-cocoa
我正在研究一些通过websockets与远程API交互的代码.我的数据层负责建立和监控websocket连接.它还包含应用程序可用于排队要发送的websocket消息的方法.应用程序代码不应该负责检查websocket连接的状态,即"即发即忘".
理想情况下,我希望数据层的功能如下:
self.isConnected == NO)的连接时,消息在内部缓冲.self.isConnected == YES)时,立即发送缓冲的消息,并立即发送任何后续消息.这是我能够提出的:
#import "RACSignal+Buffering.h"
@implementation RACSignal (Buffering)
- (RACSignal*)bufferWithSignal:(RACSignal*)shouldBuffer
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
NSMutableArray* bufferedValues = [[NSMutableArray alloc] init];
__block BOOL buffering = NO;
void (^bufferHandler)() = ^{
if (!buffering)
{
for (id val in bufferedValues)
{
[subscriber sendNext:val];
}
[bufferedValues removeAllObjects];
}
};
RACDisposable* bufferDisposable = [shouldBuffer subscribeNext:^(NSNumber* shouldBuffer) {
buffering = shouldBuffer.boolValue;
bufferHandler();
}];
if (bufferDisposable)
{
[disposable …Run Code Online (Sandbox Code Playgroud) 我最近一直在阅读ReactiveCocoa v3,而我正在努力设置基本的东西.我已经阅读了变更日志,测试,少数SO问题和Colin Eberhardt关于这个主题的文章.但是,我仍然缺少基本绑定的示例.
假设我有一个提供当天菜单的应用程序.该应用程序正在使用RAC3和MVVM模式.
型号(菜单)
该模型有一个简单的方法来获取今天的菜单.至于现在,这不做任何网络请求,它基本上只是创建一个模型对象.该mainCourse物业是一个String.
class func fetchTodaysMenu() -> SignalProducer<Menu, NoError> {
return SignalProducer {
sink, dispoable in
let newMenu = Menu()
newMenu.mainCourse = "Some meat"
sendNext(sink, newMenu)
sendCompleted(sink)
}
}
Run Code Online (Sandbox Code Playgroud)
ViewModel(MenuViewModel)
视图模型公开了不同的String变量,让视图控制器显示菜单.让我们只添加一个属性来显示主菜.
var mainCourse = MutableProperty("")
Run Code Online (Sandbox Code Playgroud)
我们为这个属性添加一个绑定:
self.mainCourse <~ Menu.fetchTodaysMenu()
|> map { menu in
return menu.mainCourse!
}
Run Code Online (Sandbox Code Playgroud)
ViewController(MenuViewController)
最后但同样重要的是,我想在视图中介绍这个主要课程.我会UILabel为此添加一个.
var headline = UILabel()
Run Code Online (Sandbox Code Playgroud)
最后我想text通过观察我的视图模型来设置该UILabel 的属性.像这样的东西:
self.headline.text <~ viewModel.headline.producer
Run Code Online (Sandbox Code Playgroud)
遗憾的是,这不起作用.
问题
fetchTodaysMenu()返回一个SignalProducer<Menu, NoError> …我有一个实体数组,我想对实体执行异步操作.应该链接操作并以与阵列中的实体相同的顺序运行.我是RAC的新手.如何在RAC中做到这一点?
有人可以帮我这个.
我有以下内容 public enum
public enum OfferViewRow {
case Candidates
case Expiration
case Description
case Timing
case Money
case Payment
}
Run Code Online (Sandbox Code Playgroud)
以下mutableProperty:
private let rows = MutableProperty<[OfferViewRow]>([OfferViewRow]())
Run Code Online (Sandbox Code Playgroud)
在我的init文件中,我使用一些reactiveCocoa来设置我的MutableProperty:
rows <~ application.producer
.map { response in
if response?.application.status == .Applied {
return [.Candidates, .Description, .Timing, .Money, .Payment]
} else {
return [.Candidates, .Expiration, .Description, .Timing, .Money, .Payment]
}
}
Run Code Online (Sandbox Code Playgroud)
但现在问题是,当我试图在我的行中获取枚举的值时,它会抛出错误.请看下面的代码.
func cellViewModelForRowAtIndexPath(indexPath: NSIndexPath) -> ViewModel {
guard
let row = rows.value[indexPath.row],
let response = self.application.value
else {
fatalError("")
}
switch row …Run Code Online (Sandbox Code Playgroud)