Looking for the simplest possible remoteIO example

I have an app (iPhone / iPad) that currently uses miniaudio and I'd to transition to a pure CoreAudio solution, and I cannot for the life of me get it to work.

I want to set up a remoteIO with microphone and speaker callbacks, so that I get a callback when the microphone (USB microphone at 384kHz, mono) has samples for me, and I get a callback when the speakers (49kHz, stereo) need more samples. It should be insanely simple. It's Objective C as I have never got round to Swift and can't see it happening this late in life, but that shouldn't change things.

So if anyone can tell me what I'm doing wrong here it would be GREATLY appreciated. My playbackCallback is never being fired, only the recordCallback.

`-(void) launchRemoteIOandSleep { NSError *error; [ [AVAudioSession sharedInstance] setPreferredIOBufferDuration:(1024.0f / 48000.0f) error:&error ];

OSStatus status;
AudioComponentInstance audioUnit;

// set up callback structures - different sanity clauses to identify in breakpoints
renderCallBackHandle sanityClause666;
sanityClause666.remoteIO = audioUnit;
sanityClause666.sanityCheck666 = 666;

renderCallBackHandle sanityClause667;
sanityClause667.remoteIO = audioUnit;
sanityClause667.sanityCheck666 = 667;

// set up audio formats
AudioStreamBasicDescription audioFormatInput;
FillOutASBDForLPCM(audioFormatInput,384000.0,1,16,16,0,0,0);

AudioStreamBasicDescription audioFormatOutput;
FillOutASBDForLPCM(audioFormatOutput,48000.0,2,16,16,0,0,0);

// set up callback structs
AURenderCallbackStruct      callbackStructRender;
callbackStructRender.inputProc       = playbackCallback;
callbackStructRender.inputProcRefCon = &sanityClause666;

AURenderCallbackStruct      callbackStructRecord;
callbackStructRecord.inputProc       = recordCallback;
callbackStructRecord.inputProcRefCon = &sanityClause667;

// grab remoteIO
AudioComponentDescription   desc;
desc.componentType         = kAudioUnitType_Output;
desc.componentSubType      = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags        = 0;
desc.componentFlagsMask    = 0;

AudioComponent component = AudioComponentFindNext(NULL, &desc);

// Get audio unit
status = AudioComponentInstanceNew(component, &audioUnit);
checkStatus(status);

// Enable IO for both recording and playback
// this enables the OUTPUT side of the OUTPUT bus which is the speaker (I thnk ... )
UInt32 flag = 1;
status = AudioUnitSetProperty(audioUnit,
                              kAudioOutputUnitProperty_EnableIO,
                              kAudioUnitScope_Output,
                              kOutputBus,
                              &flag,
                              sizeof(flag));
checkStatus(status);
// this enables the INPUT side of the INPUT bus which is the mic (I thnk ... )
flag = 1;
status = AudioUnitSetProperty(audioUnit,
                              kAudioOutputUnitProperty_EnableIO,
                              kAudioUnitScope_Input,
                              kInputBus,
                              &flag,
                              sizeof(flag));
checkStatus(status);

// Apply format - INPUT bus of OUTPUT SCOPE which is my samples into remoteIO
status = AudioUnitSetProperty(audioUnit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Output,
                              kInputBus,
                              &audioFormatOutput,
                              sizeof(audioFormatOutput));
checkStatus(status);

// Apply format - OUTPUT bus of INPUT SCOPE which is where I pick up my samples from mic
status = AudioUnitSetProperty(audioUnit,
                              kAudioUnitProperty_StreamFormat,
                              kAudioUnitScope_Input,
                              kOutputBus,
                              &audioFormatInput,
                              sizeof(audioFormatInput));
checkStatus(status);

//  set output callback
status = AudioUnitSetProperty(audioUnit,
                              kAudioUnitProperty_SetRenderCallback,
                              kAudioUnitScope_Output,
                              kInputBus,
                              &callbackStructRender,
                              sizeof(callbackStructRender));
checkStatus(status);

// Set input callback
status = AudioUnitSetProperty(audioUnit,
                              kAudioOutputUnitProperty_SetInputCallback,
                              kAudioUnitScope_Input,
                              kOutputBus,
                              &callbackStructRecord,
                              sizeof(callbackStructRecord));
checkStatus(status);

// Disable buffer allocation for the recorder
flag = 0;
status = AudioUnitSetProperty(audioUnit,
                              kAudioUnitProperty_ShouldAllocateBuffer,
                              kAudioUnitScope_Input,
                              kInputBus,
                              &flag,
                              sizeof(flag));

// Initialise
status = AudioUnitInitialize(audioUnit);
checkStatus(status);

status = AudioOutputUnitStart(audioUnit);
checkStatus(status);

[ self waitForAudioStabilisation ];

while (1) sleep(2);

}`

This hs now transitioned into "I have now made it work but I have no idea how or why, and help would still be very much appreciated". So if anyone has hands-on remoteIO experience and can walk me through the crazy space of buses and scopes, which frankly all seem to be ***-backwards, I'd be very grateful.

Looking for the simplest possible remoteIO example
 
 
Q