The client I'm building is using Reactive Cocoa with Octokit and so far it has been going very well. However now I'm at a point where I want to fetch a collection of repositories and am having trouble wrapping my head around doing this the "RAC way"
// 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");
}];
I originally assumed that -subscribeNext: would pass an NSArray, but now understand that it sends the message every "next" object returned, which in this case is an OCTRepository.
Now I could do something like this:
NSMutableArray *repos = [NSMutableArray array];
// most of that code above
subscribeNext:^(OCTRepository *fetchedRepo) {
[repos addObject:fetchedRepo];
}
// the rest of the code above
Sure, this works, but it doesn't seem to follow the functional principles that RAC enables. I'm really trying to stick to conventions here. Any light on capabilities of RAC/Octokit are greatly appreciated!
It largely depends on what you want to do with the repositories afterward. It seems like you want to do something once you have all the repositories, so I'll set up an example that does that.
// Watch for the client to change
RAC(self.repositories) = [[[[[RACAbleWithStart([GHDataStore sharedStore], client)
// Ignore clients that aren't authenticated
filter:^ BOOL (OCTClient *client) {
return client != nil && client.authenticated;
}]
// For each client, execute the block. Returns a signal that sends a signal
// to fetch the user repositories whenever a new client comes in. A signal of
// of signals is often used to do some work in response to some other work.
// Often times, you'd want to use `-flattenMap:`, but we're using `-map:` with
// `-switchToLatest` so the resultant signal will only send repositories for
// the most recent client.
map:^(OCTClient *client) {
// -collect will send a single value--an NSArray with all of the values
// that were send on the original signal.
return [[client fetchUserRepositories] collect];
}]
// Switch to the latest signal that was returned from the map block.
switchToLatest]
// Execute a block when an error occurs, but don't alter the values sent on
// the original signal.
doError:^(NSError *error) {
NSLog(#"Error fetching repos: %#",error.localizedDescription);
}]
deliverOn:RACScheduler.mainThreadScheduler];
Now self.repositories will change (and fire a KVO notification) whenever the repositories are updated from the client.
A couple things to note about this:
It's best to avoid subscribeNext: whenever possible. Using it steps outside of the functional paradigm (as do doNext: and doError:, but they're also helpful tools at times). In general, you want to think about how you can transform the signal into something that does what you want.
If you want to chain one or more pieces of work together, you often want to use flattenMap:. More generally, you want to start thinking about signals of signals--signals that send other signals that represent the other work.
You often want to wait as long as possible to move work back to the main thread.
When thinking through a problem, it's sometimes valuable to start by writing out each individual signal to think about a) what you have, b) what you want, and c) how to get from one to the other.
EDIT: Updated to address #JustinSpahrSummers' comment below.
There is a -collect operator that should do exactly what you're looking for.
// Collect all receiver's `next`s into a NSArray. nil values will be converted
// to NSNull.
//
// This corresponds to the `ToArray` method in Rx.
//
// Returns a signal which sends a single NSArray when the receiver completes
// successfully.
- (RACSignal *)collect;
Related
In my OSX application I'm using code below to show preview from camera.
[[self session] beginConfiguration];
NSError *error = nil;
AVCaptureDeviceInput *newVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
if (captureDevice != nil) {
[[self session] removeInput: [self videoDeviceInput]];
if([[self session] canAddInput: newVideoDeviceInput]) {
[[self session] addInput:newVideoDeviceInput];
[self setVideoDeviceInput:newVideoDeviceInput];
} else {
DLog(#"WTF?");
}
}
[[self session] commitConfiguration];
Yet, I need to detect the exact time when the preview from the camera becomes available.
In other words I'm trying to detect the same moment like in Facetime under OSX, where animation starts once the camera provides the preview.
What is the best way to achieve this?
I know this question is really old, but I stumbled upon it too when I was looking for this same question, and I have found answers so here goes.
For starters, AVFoundation is too high level, you'll need to drop down to a lower level, CoreMediaIO. There's not a lot of documentation on this, but basically you need to perform a couple queries.
To do this, we'll use a combination of calls. First, CMIOObjectGetPropertyDataSize lets us get the size of the data we'll query for next, which we can then use when we call CMIOObjectGetPropertyData. To set up the get property data size call, we need to start at the top, using this property address:
var opa = CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(kCMIOHardwarePropertyDevices),
mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementMaster)
)
Next, we'll set up some variables to keep the data we'll need:
var (dataSize, dataUsed) = (UInt32(0), UInt32(0))
var result = CMIOObjectGetPropertyDataSize(CMIOObjectID(kCMIOObjectSystemObject), &opa, 0, nil, &dataSize)
var devices: UnsafeMutableRawPointer? = nil
From this point on, we'll need to wait until we get some data out, so let's busy loop:
repeat {
if devices != nil {
free(devices)
devices = nil
}
devices = malloc(Int(dataSize))
result = CMIOObjectGetPropertyData(CMIOObjectID(kCMIOObjectSystemObject), &opa, 0, nil, dataSize, &dataUsed, devices);
} while result == OSStatus(kCMIOHardwareBadPropertySizeError)
Once we get past this point in our execution, devices will point to potentially many devices. We need to loop through them, somewhat like this:
if let devices = devices {
for offset in stride(from: 0, to: dataSize, by: MemoryLayout<CMIOObjectID>.size) {
let current = devices.advanced(by: Int(offset)).assumingMemoryBound(to: CMIOObjectID.self)
// current.pointee is your object ID you will want to keep track of somehow
}
}
Finally, clean up devices
free(devices)
Now at this point, you'll want to use that object ID you saved above to make another query. We need a new property address:
var CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(kCMIODevicePropertyDeviceIsRunningSomewhere),
mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeWildcard),
mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementWildcard)
)
This tells CoreMediaIO that we want to know if the device is currently running somewhere (read: in any app), wildcarding the rest of the fields. Next we get to the meat of the query, camera below corresponds to the ID you saved before:
var (dataSize, dataUsed) = (UInt32(0), UInt32(0))
var result = CMIOObjectGetPropertyDataSize(camera, &opa, 0, nil, &dataSize)
if result == OSStatus(kCMIOHardwareNoError) {
if let data = malloc(Int(dataSize)) {
result = CMIOObjectGetPropertyData(camera, &opa, 0, nil, dataSize, &dataUsed, data)
let on = data.assumingMemoryBound(to: UInt8.self)
// on.pointee != 0 means that it's in use somewhere, 0 means not in use anywhere
}
}
With the above code samples you should have enough to test whether or not the camera is in use. You only need to get the device once (the first part of the answer); the check for if it's in use however, you'll have to do at any time you want this information. As an extra exercise, consider playing with CMIOObjectAddPropertyListenerBlock to be notified on event changes for the in use property address we used above.
While this answer is nearly 3 years too late for the OP, I hope it helps someone in the future. Examples here are given with Swift 3.0.
The previous answer from the user jer is definitely the correct answer, but I just wanted to add one additional important information.
If a listener block is registered with CMIOObjectAddPropertyListenerBlock, the current run loop must be run, otherwise no event will be received and the listener block will never fire.
I am currently writing a program to help me control complex lights installations. The idea is I tell the program to start a preset, then the app has three options (depending on the preset type)
1) the lights go to one position (so only one group of data sent when the preset starts)
2) the lights follows a mathematical equation (ex: sinus with a timer to make smooth circles)
3) the lights respond to a flow of data (ex midi controller)
So I decided to go with an object I call the AppBrain, that receive data from the controllers and the templates, but also is able to send processed data to the lights.
Now, I come from non-native programming, and I kinda have trust issues concerning working with a lot of processing, events and timing; as well as troubles with understanding 100% the Cocoa logic.
This is where the actual question starts, sorry
What I want to do, would be when I load the preset, I parse it to prepare the timer/data receive event so it doesn't have to go trough every option for 100 lights 100 times per second.
To explain more deeply, here's how I would do it in Javascript (crappy pseudo code, of course)
var lightsFunctions = {};
function prepareTemplate(theTemplate){
//Let's assume here the template is just an array, and I won't show all the processing
switch(theTemplate.typeOfTemplate){
case "simpledata":
sendAllDataTooLights(); // Simple here
break;
case "periodic":
for(light in theTemplate.lights){
switch(light.typeOfEquation){
case "sin":
lightsFunctions[light.id] = doTheSinus; // doTheSinus being an existing function
break;
case "cos":
...
}
}
function onFrame(){
for(light in lightsFunctions){
lightsFunctions[light]();
}
}
var theTimer = setTimeout(onFrame, theTemplate.delay);
break;
case "controller":
//do the same pre-processing without the timer, to know which function to execute for which light
break;
}
}
}
So, my idea is to store the processing function I need in an NSArray, so I don't need to test on each frame the type and loose some time/CPU.
I don't know if I'm clear, or if my idea is possible/the good way to go. I'm mostly looking for algorithm ideas, and if you have some code that might direct me in the good direction... (I know of PerformSelector, but I don't know if it is the best for this situation.
Thanks;
I_
First of all, don't spend time optimizing what you don't know is a performance problem. 100 iterations of the type is nothing in the native world, even on the weaker mobile CPUs.
Now, to your problem. I take it you are writing some kind of configuration / DSL to specify the light control sequences. One way of doing it is to store blocks in your NSArray. A block is the equivalent of a function object in JavaScript. So for example:
typedef void (^LightFunction)(void);
- (NSArray*) parseProgram ... {
NSMutableArray* result = [NSMutableArray array];
if(...) {
LightFunction simpledata = ^{ sendDataToLights(); };
[result addObject:simpleData];
} else if(...) {
Light* light = [self getSomeLight:...];
LightFunction periodic = ^{
// Note how you can access the local scope of the outside function.
// Make sure you use automatic reference counting for this.
[light doSomethingWithParam:someParam];
};
[result addObject:periodic];
}
return result;
}
...
NSArray* program = [self parseProgram:...];
// To run your program
for(LightFunction func in program) {
func();
}
I would like some opinions on a design that i have. My app is starting to send an asynchronous request to get some JSON data and if that goes well i get to my callback method and then start some other asynchronous request for some data then in another callback i go on and on and i feel my code is getting to be like spaghetti code, i jump from one callback method to a request to another callback then request and so on, any ideas of an better design ?
Let me show some example code (not the real code but shows my point) :
-(void)startApp {
//make an request and use callBackMethod to parse data
}
-(void)callBackMethod {
//parse data and check it, if ok go make next request and use CallBackMethod1
}
-(void)callBackMethod1 {
//parse data and check it, if ok go make next request and use CallBackMethod2
}
-(void)callBackMethod2 {
//parse data and check it, if ok go make next request and use CallBackMethod3
}
-(void)callBackMethod3 {
//parse data
}
You get the point. It's a mess after my opinion and i want to refactor the code to something more readable and easier to maintain. I heard about blocks. Not sure how/if that might make my design better.
donnib
Your asynchronous request design is fine, although you should use more descriptive method names. The example here is not really spaghetti code, it's just you're doing something somewhat complicated and the code structure necessarily reflects that.
An implementation using blocks would look something vaguely like this:
- (void)startApp {
[self makeRequest:... completionHandler:^(NSData *data){
// Parse data and check it, as in callBackMethod
if (ok) [self makeRequest:... completionHandler:^(NSData *data){
// Parse data and check it, as in callBackMethod1
if (ok) [self makeRequest:... completionHandler:^(NSData *data){
// Parse data and check it, as in callBackMethod2
if (ok) [self makeRequest:... completionHandler:^(NSData *data){
// Parse data, as in callBackMethod3
}];
}];
}];
}];
}
Depending on the situation, that may or may not be easier to follow. Another alternative, if you control the web service you're querying and the "check" is amenable, is to have the web service do all the checking on its end and return all the data in one response instead of requiring all this back-and-forth.
everybody.
I want to understand, how i shoud procceed situations when an asynchronous method has "didFinish:#selector(SEL)" parameter.
My code example is:
//
// Authentication check
- ( void )authenticationSuccess: ( GDataServiceTicket* ) ticket
authenticatedWithError: ( NSError* ) error {
if ( error == nil )
{
NSLog( #"authentication success" );
}
else
{
NSLog( #"authentication error" );
}
}
//
- ( void ) fetchFeedOfSpreadsheets {
//create and authenticate to a google spreadsheet service
if ( !(mService) )
{
GDataServiceGoogleSpreadsheet *service = [self spreadsheetService];
[mService autorelease];
mService = [service retain];
}
// check autentication success ( invoke "authenticationSuccess" method for debug success & error )
[mService authenticateWithDelegate: self
didAuthenticateSelector:#selector(authenticationSuccess:
authenticatedWithError:) ];
// HERE I WANT TO MAKE A PAUSE AND WHAIT THE RESULT, EITHER I AUTHENTICATED OR NOT
// AND MAKE AN "IF" STATEMENT TO CONTINTUE WORKING ON SERVER, OR RETURN ERROR
//fetch retrieves the feed of spreadsheets entries
NSURL *feedURL = [ NSURL URLWithString: kGDataGoogleSpreadsheetsPrivateFullFeed ];
GDataServiceTicket *ticket;
ticket = [mService fetchFeedWithURL: feedURL
delegate: self
didFinishSelector: #selector(spreadsheetsTicket:finishedWithFeed:
error: ) ];
// HERE I WANT TO WAIT SECOND TIME. I WANT "spreadsheetsTicket:
// finishedWithFeed:error:" TO PROCCEED ERROR AND PUT A FEED IN SOME NSARRAY OBJECT
// AND AFTER THAT I WANT TO WORK WITH THAT NSARRAY RIGHT HERE
}
I's clear, that i can push the code i want into the end of "authenticationSuccess" method section, but it's also clear, that it's a wrong a way to solve the proble. There a number of situations like this, where i call an asynchronous method with a selector parameter, and i want to find a solution providing me a flexible code writing.
Thanks in advance.
It's a standard practice in Objective-C to put the code to be executed after the authentication in the authenticationSucess: method. You might not like it, but that is life.
Many people had the same complaint as you, so
on iOS 4 and later, there's something called blocks which allow you to write the code to be executed after the authentication in the method which initiates the authentication, as in
[mService authenticateAndExecute:^{
code to be executed when successfully authenticated ;
} whenError:^{
code to be executed when authentication failed;
} ];
But in this case you need to modify the API, which is possible by using categories. See this blog post by Mike Ash. He has many other posts on blocks on the same blog, which are also very instructive.
If you're going to use a library that works asynchronously (and therefore doesn't block your UI), you should have a good reason for trying to force it to work synchronously.
You should be checking for an authentication error at the end of your authenticationSuccess:authenticatedWithError: method, and calling the next request from there if there's a success. Similarly, in your spreadsheetsTicket:finishedWithFeed:error: check for an error, and continuing processing if there isn't one. It might be a better design to do that continued work in a separate method, but that's up to you.
Is there a specific reason you want to use the GData API in a synchronous fashion?
I've got two dojo.dnd.Sources with items. Whenever an item is dropped I need to persist the new order of the items in the Sources using an xhr.
Is there an dojo event or topic that is fired after an dnd operation has (successfully) finished? What would be the best way to use it?
Probably I don't understand the problem in all details but I don't see why you need to process events or topics. The best way to record changes is to intercept updating methods on relevant sources. Specifically you need to intercept insertNodes() for drops or any other additions.
Simple example (pseudo-code):
var source1, source2;
// ...
// initialize sources
// populate sources
// ...
function getAllItems(source){
var items = source.getAllNodes().map(function(node){
return source.getItem(node.id);
});
return items;
}
function dumpSource(source){
var items = getAllItems(source);
// XHR items here to your server
}
function recordChange(){
// now we know that some change has occured
// it could be a drop or some programmatic updates
// we don't really care
dumpSource(source1);
dumpSource(source2);
}
dojo.connect(source1, "insertNodes", recordChanges);
dojo.connect(source2, "insertNodes", recordChanges);
// now any drop or other change will trigger recordChanges()
// after the change has occurred.
You can try to be smart about that and send some diff information instead of a whole list, but it is up to you to generate it — you have everything you need for that.
You can use dojo.subscribe to do something when a drop is finished like so:
dojo.subscribe("/dnd/drop", function(source, nodes, copy, target) {
// do your magic here
});
There's examples of using subscribe on the dojotoolkit tests site. More info about dojo publish and subscribe too.
Alternately, you could connect to the onDndDrop method.
var source = new dojo.dnd.Source( ... );
dojo.connect( source, "onDndDrop", function( source, nodes, copy, target ) {
// make magic happen here
});
connect methods are called at the end so the items will be there at that point.
I'm keeping this note for dojo Tree folks just like me who would run in to this problem. Solutions given here was not quite worked well in my situation. I was using a dijit.tree.dndSource with Dojo tree and subscribing to "/dnd/drop" allows me to capture the event even though at that point my underlying data store hadn't been updated with latest changes. So I tried waiting as Wienczny explains, that doesn't solve the problem completely as I can't rely on a timeout to do the waiting job. Time taken for store update could be vary, i.e. shorter or very long depends on how complex your data structure is. I found the solution with overriding the onDndDrop method of the dndController. Simply you can specify the onDndDrop : on your tree initialization. One thing I found odd though you can not hitch this method, you will get weird behavior during dnd.
Tree
this._tree = new MapConfigTree({
checkAcceptance: this.dndAccept,
onDndDrop: this.onDndDrop,
betweenThreshold:5,
method
onDndDrop : function(source, nodes, copy, target){
if(source.dropPosition === 'Over' && (target.targetAnchor.item.type[0] == 'Test layer')) {
this.inherited(arguments);
// do your bit here
} else {
this.onDndCancel();
}
}