Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial Commit

App plays the Stream and Displays the current track. Twitter
Integration. No FB . very bad style lots of Ifs
  • Loading branch information...
commit 5b24ee6d7d73f2f6aa580445af4f6406db0ac040 1 parent faa52ce
Frederic Jacobs authored
Showing with 3,811 additions and 59 deletions.
  1. +69 −0 AudioStreamer.h
  2. +800 −0 AudioStreamer.m
  3. +78 −0 CocoaXMLParser.h
  4. +198 −0 CocoaXMLParser.m
  5. BIN  Default.png
  6. BIN  Default@2x.png
  7. +27 −0 DetailController.h
  8. +85 −0 DetailController.m
  9. +390 −0 MainWindow.xib
  10. +30 −0 SecondViewController.h
  11. +137 −0 SecondViewController.m
  12. +216 −0 SecondViewController.xib
  13. +24 −0 Show.h
  14. +18 −0 Show.m
  15. +70 −0 ShowRSSParser.h
  16. +97 −0 ShowRSSParser.m
  17. BIN  Tweet.png
  18. BIN  Tweet@2x.png
  19. BIN  UCLMainScreen.png
  20. BIN  UCLMainScreen@2x.png
  21. +166 −4 UCLRadio.xcodeproj/project.pbxproj
  22. +7 −0 UCLRadio.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  23. BIN  UCLRadio.xcodeproj/project.xcworkspace/xcuserdata/fredericjacobs.xcuserdatad/UserInterfaceState.xcuserstate
  24. +84 −0 UCLRadio.xcodeproj/xcuserdata/fredericjacobs.xcuserdatad/xcschemes/UCLRadio.xcscheme
  25. +22 −0 UCLRadio.xcodeproj/xcuserdata/fredericjacobs.xcuserdatad/xcschemes/xcschememanagement.plist
  26. +18 −2 UCLRadio/AppDelegate.h
  27. +75 −53 UCLRadio/AppDelegate.m
  28. +2 −0  UCLRadio/UCLRadio-Info.plist
  29. +415 −0 UCLRadio/UCLRadioViewController.xib
  30. +36 −0 UCLRadioViewController.h
  31. +747 −0 UCLRadioViewController.m
  32. BIN  playTabBar.png
  33. BIN  playTabBar@2x.png
  34. BIN  playbutton.png
  35. BIN  shows.png
  36. BIN  shows@2x.png
  37. BIN  stopbutton.png
View
69 AudioStreamer.h
@@ -0,0 +1,69 @@
+//
+// AudioStreamer.h
+// StreamingAudioPlayer
+//
+// Created by Matt Gallagher on 27/09/08.
+// Copyright 2008 Matt Gallagher. All rights reserved.
+//
+// Permission is given to use this source code file without charge in any
+// project, commercial or otherwise, entirely at your risk, with the condition
+// that any redistribution (in part or whole) of source code must retain
+// this copyright and permission notice. Attribution in compiled projects is
+// appreciated but not required.
+//
+
+#ifdef TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#else
+#import <Cocoa/Cocoa.h>
+#endif //TARGET_OS_IPHONE
+
+#include <pthread.h>
+#include <AudioToolbox/AudioToolbox.h>
+#import <AVFoundation/AVFoundation.h>
+
+
+
+#define kNumAQBufs 6 // number of audio queue buffers we allocate
+#define kAQBufSize 32 * 1024 // number of bytes in each audio queue buffer
+#define kAQMaxPacketDescs 512 // number of packet descriptions in our array
+
+@interface AudioStreamer : NSObject
+{
+ NSURL *url;
+ BOOL isPlaying;
+
+@public
+ AudioFileStreamID audioFileStream; // the audio file stream parser
+
+ AudioQueueRef audioQueue; // the audio queue
+ AudioQueueBufferRef audioQueueBuffer[kNumAQBufs]; // audio queue buffers
+
+ AudioStreamPacketDescription packetDescs[kAQMaxPacketDescs]; // packet descriptions for enqueuing audio
+
+ unsigned int fillBufferIndex; // the index of the audioQueueBuffer that is being filled
+ size_t bytesFilled; // how many bytes have been filled
+ size_t packetsFilled; // how many packets have been filled
+
+ bool inuse[kNumAQBufs]; // flags to indicate that a buffer is still in use
+ bool started; // flag to indicate that the queue has been started
+ bool failed; // flag to indicate an error occurred
+ bool finished; // flag to inidicate that termination is requested
+ // the audio queue is not necessarily complete until
+ // isPlaying is also false.
+ bool discontinuous; // flag to trigger bug-avoidance
+
+ pthread_mutex_t mutex; // a mutex to protect the inuse flags
+ pthread_cond_t cond; // a condition varable for handling the inuse flags
+
+ pthread_mutex_t mutex2; // a mutex to protect the AudioQueue buffer
+ CFReadStreamRef stream;
+}
+
+@property BOOL isPlaying;
+
+- (id)initWithURL:(NSURL *)newURL;
+- (void)start;
+- (void)stop;
+
+@end
View
800 AudioStreamer.m
@@ -0,0 +1,800 @@
+//
+// AudioStreamer.m
+// StreamingAudioPlayer
+//
+// Created by Matt Gallagher on 27/09/08.
+// Copyright 2008 Matt Gallagher. All rights reserved.
+//
+// Permission is given to use this source code file without charge in any
+// project, commercial or otherwise, entirely at your risk, with the condition
+// that any redistribution (in part or whole) of source code must retain
+// this copyright and permission notice. Attribution in compiled projects is
+// appreciated but not required.
+//
+
+#import "AudioStreamer.h"
+#ifdef TARGET_OS_IPHONE
+#import <CFNetwork/CFNetwork.h>
+#endif
+
+#define PRINTERROR(LABEL) printf("%s err %4.4s %d\n", LABEL, (char *)&err, (int)err)
+
+#pragma mark CFReadStream Callback Function Prototypes
+
+void ReadStreamCallBack(
+ CFReadStreamRef stream,
+ CFStreamEventType eventType,
+ void* dataIn);
+
+#pragma mark Audio Callback Function Prototypes
+
+void MyAudioQueueOutputCallback(void* inClientData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
+void MyAudioQueueIsRunningCallback(void *inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID);
+void MyPropertyListenerProc( void * inClientData,
+ AudioFileStreamID inAudioFileStream,
+ AudioFileStreamPropertyID inPropertyID,
+ UInt32 * ioFlags);
+void MyPacketsProc( void * inClientData,
+ UInt32 inNumberBytes,
+ UInt32 inNumberPackets,
+ const void * inInputData,
+ AudioStreamPacketDescription *inPacketDescriptions);
+OSStatus MyEnqueueBuffer(AudioStreamer* myData);
+
+#ifdef TARGET_OS_IPHONE
+void MyAudioSessionInterruptionListener(void *inClientData, UInt32 inInterruptionState);
+#endif
+
+#pragma mark Audio Callback Function Implementations
+
+//
+// MyPropertyListenerProc
+//
+// Receives notification when the AudioFileStream has audio packets to be
+// played. In response, this function creates the AudioQueue, getting it
+// ready to begin playback (playback won't begin until audio packets are
+// sent to the queue in MyEnqueueBuffer).
+//
+// This function is adapted from Apple's example in AudioFileStreamExample with
+// kAudioQueueProperty_IsRunning listening added.
+//
+void MyPropertyListenerProc( void * inClientData,
+ AudioFileStreamID inAudioFileStream,
+ AudioFileStreamPropertyID inPropertyID,
+ UInt32 * ioFlags)
+{
+ // this is called by audio file stream when it finds property values
+ AudioStreamer* myData = (AudioStreamer*)inClientData;
+ OSStatus err = noErr;
+
+ switch (inPropertyID) {
+ case kAudioFileStreamProperty_ReadyToProducePackets :
+ {
+ myData->discontinuous = true;
+
+ // the file stream parser is now ready to produce audio packets.
+ // get the stream format.
+ AudioStreamBasicDescription asbd;
+ UInt32 asbdSize = sizeof(asbd);
+ err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &asbdSize, &asbd);
+ if (err) { PRINTERROR("get kAudioFileStreamProperty_DataFormat"); myData->failed = true; break; }
+
+ // create the audio queue
+ err = AudioQueueNewOutput(&asbd, MyAudioQueueOutputCallback, myData, NULL, NULL, 0, &myData->audioQueue);
+ if (err) { PRINTERROR("AudioQueueNewOutput"); myData->failed = true; break; }
+
+ // listen to the "isRunning" property
+ err = AudioQueueAddPropertyListener(myData->audioQueue, kAudioQueueProperty_IsRunning, MyAudioQueueIsRunningCallback, myData);
+ if (err) { PRINTERROR("AudioQueueAddPropertyListener"); myData->failed = true; break; }
+
+ // allocate audio queue buffers
+ for (unsigned int i = 0; i < kNumAQBufs; ++i) {
+ err = AudioQueueAllocateBuffer(myData->audioQueue, kAQBufSize, &myData->audioQueueBuffer[i]);
+ if (err) { PRINTERROR("AudioQueueAllocateBuffer"); myData->failed = true; break; }
+ }
+
+ // get the cookie size
+ UInt32 cookieSize;
+ Boolean writable;
+ err = AudioFileStreamGetPropertyInfo(inAudioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable);
+ if (err) { PRINTERROR("info kAudioFileStreamProperty_MagicCookieData"); break; }
+
+ // get the cookie data
+ void* cookieData = calloc(1, cookieSize);
+ err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData);
+ if (err) { PRINTERROR("get kAudioFileStreamProperty_MagicCookieData"); free(cookieData); break; }
+
+ // set the cookie on the queue.
+ err = AudioQueueSetProperty(myData->audioQueue, kAudioQueueProperty_MagicCookie, cookieData, cookieSize);
+ free(cookieData);
+ if (err) { PRINTERROR("set kAudioQueueProperty_MagicCookie"); break; }
+ break;
+ }
+ }
+}
+
+//
+// MyPacketsProc
+//
+// When the AudioStream has packets to be played, this function gets an
+// idle audio buffer and copies the audio packets into it. The calls to
+// MyEnqueueBuffer won't return until there are buffers available (or the
+// playback has been stopped).
+//
+// This function is adapted from Apple's example in AudioFileStreamExample with
+// CBR functionality added.
+//
+void MyPacketsProc( void * inClientData,
+ UInt32 inNumberBytes,
+ UInt32 inNumberPackets,
+ const void * inInputData,
+ AudioStreamPacketDescription *inPacketDescriptions)
+{
+ // this is called by audio file stream when it finds packets of audio
+ AudioStreamer* myData = (AudioStreamer*)inClientData;
+
+ // we have successfully read the first packests from the audio stream, so
+ // clear the "discontinuous" flag
+ myData->discontinuous = false;
+
+ // the following code assumes we're streaming VBR data. for CBR data, the second branch is used.
+ if (inPacketDescriptions)
+ {
+ for (int i = 0; i < inNumberPackets; ++i) {
+ SInt64 packetOffset = inPacketDescriptions[i].mStartOffset;
+ SInt64 packetSize = inPacketDescriptions[i].mDataByteSize;
+
+ // If the audio was terminated before this point, then
+ // exit.
+ if (myData->finished)
+ {
+ return;
+ }
+
+ // if the space remaining in the buffer is not enough for this packet, then enqueue the buffer.
+ size_t bufSpaceRemaining = kAQBufSize - myData->bytesFilled;
+ if (bufSpaceRemaining < packetSize) {
+ MyEnqueueBuffer(myData);
+ }
+
+ pthread_mutex_lock(&myData->mutex2);
+
+ // If the audio was terminated while waiting for a buffer, then
+ // exit.
+ if (myData->finished)
+ {
+ pthread_mutex_unlock(&myData->mutex2);
+ return;
+ }
+
+ // copy data to the audio queue buffer
+ AudioQueueBufferRef fillBuf = myData->audioQueueBuffer[myData->fillBufferIndex];
+ memcpy((char*)fillBuf->mAudioData + myData->bytesFilled, (const char*)inInputData + packetOffset, packetSize);
+
+ pthread_mutex_unlock(&myData->mutex2);
+
+ // fill out packet description
+ myData->packetDescs[myData->packetsFilled] = inPacketDescriptions[i];
+ myData->packetDescs[myData->packetsFilled].mStartOffset = myData->bytesFilled;
+ // keep track of bytes filled and packets filled
+ myData->bytesFilled += packetSize;
+ myData->packetsFilled += 1;
+
+ // if that was the last free packet description, then enqueue the buffer.
+ size_t packetsDescsRemaining = kAQMaxPacketDescs - myData->packetsFilled;
+ if (packetsDescsRemaining == 0) {
+ MyEnqueueBuffer(myData);
+ }
+ }
+ }
+ else
+ {
+ size_t offset = 0;
+ while (inNumberBytes)
+ {
+ // if the space remaining in the buffer is not enough for this packet, then enqueue the buffer.
+ size_t bufSpaceRemaining = kAQBufSize - myData->bytesFilled;
+ if (bufSpaceRemaining < inNumberBytes) {
+ MyEnqueueBuffer(myData);
+ }
+
+ pthread_mutex_lock(&myData->mutex2);
+
+ // If the audio was terminated while waiting for a buffer, then
+ // exit.
+ if (myData->finished)
+ {
+ pthread_mutex_unlock(&myData->mutex2);
+ return;
+ }
+
+ // copy data to the audio queue buffer
+ AudioQueueBufferRef fillBuf = myData->audioQueueBuffer[myData->fillBufferIndex];
+ bufSpaceRemaining = kAQBufSize - myData->bytesFilled;
+ size_t copySize;
+ if (bufSpaceRemaining < inNumberBytes)
+ {
+ copySize = bufSpaceRemaining;
+ }
+ else
+ {
+ copySize = inNumberBytes;
+ }
+ memcpy((char*)fillBuf->mAudioData + myData->bytesFilled, (const char*)(inInputData + offset), copySize);
+
+ pthread_mutex_unlock(&myData->mutex2);
+
+ // keep track of bytes filled and packets filled
+ myData->bytesFilled += copySize;
+ myData->packetsFilled = 0;
+ inNumberBytes -= copySize;
+ offset += copySize;
+ }
+ }
+}
+
+//
+// MyEnqueueBuffer
+//
+// Called from MyPacketsProc and connectionDidFinishLoading to pass filled audio
+// bufffers (filled by MyPacketsProc) to the AudioQueue for playback. This
+// function does not return until a buffer is idle for further filling or
+// the AudioQueue is stopped.
+//
+// This function is adapted from Apple's example in AudioFileStreamExample with
+// CBR functionality added.
+//
+OSStatus MyEnqueueBuffer(AudioStreamer* myData)
+{
+ OSStatus err = noErr;
+ myData->inuse[myData->fillBufferIndex] = true; // set in use flag
+
+ // enqueue buffer
+ AudioQueueBufferRef fillBuf = myData->audioQueueBuffer[myData->fillBufferIndex];
+ fillBuf->mAudioDataByteSize = myData->bytesFilled;
+
+ if (myData->packetsFilled)
+ {
+ err = AudioQueueEnqueueBuffer(myData->audioQueue, fillBuf, myData->packetsFilled, myData->packetDescs);
+ }
+ else
+ {
+ err = AudioQueueEnqueueBuffer(myData->audioQueue, fillBuf, 0, NULL);
+ }
+
+ if (err) { PRINTERROR("AudioQueueEnqueueBuffer"); myData->failed = true; return err; }
+
+ if (!myData->started) { // start the queue if it has not been started already
+ err = AudioQueueStart(myData->audioQueue, NULL);
+ if (err) { PRINTERROR("AudioQueueStart"); myData->failed = true; return err; }
+ myData->started = true;
+ }
+
+ // go to next buffer
+ if (++myData->fillBufferIndex >= kNumAQBufs) myData->fillBufferIndex = 0;
+ myData->bytesFilled = 0; // reset bytes filled
+ myData->packetsFilled = 0; // reset packets filled
+
+ // wait until next buffer is not in use
+ pthread_mutex_lock(&myData->mutex);
+ while (myData->inuse[myData->fillBufferIndex] && !myData->finished)
+ {
+ pthread_cond_wait(&myData->cond, &myData->mutex);
+ }
+ pthread_mutex_unlock(&myData->mutex);
+
+ return err;
+}
+
+//
+// MyFindQueueBuffer
+//
+// Returns the index of the specified buffer in the audioQueueBuffer array.
+//
+// This function is unchanged from Apple's example in AudioFileStreamExample.
+//
+int MyFindQueueBuffer(AudioStreamer* myData, AudioQueueBufferRef inBuffer)
+{
+ for (unsigned int i = 0; i < kNumAQBufs; ++i) {
+ if (inBuffer == myData->audioQueueBuffer[i])
+ return i;
+ }
+ return -1;
+}
+
+//
+// MyAudioQueueOutputCallback
+//
+// Called from the AudioQueue when playback of specific buffers completes. This
+// function signals from the AudioQueue thread to the AudioStream thread that
+// the buffer is idle and available for copying data.
+//
+// This function is unchanged from Apple's example in AudioFileStreamExample.
+//
+void MyAudioQueueOutputCallback( void* inClientData,
+ AudioQueueRef inAQ,
+ AudioQueueBufferRef inBuffer)
+{
+ // this is called by the audio queue when it has finished decoding our data.
+ // The buffer is now free to be reused.
+ AudioStreamer* myData = (AudioStreamer*)inClientData;
+ unsigned int bufIndex = MyFindQueueBuffer(myData, inBuffer);
+
+ // signal waiting thread that the buffer is free.
+ pthread_mutex_lock(&myData->mutex);
+ myData->inuse[bufIndex] = false;
+ pthread_cond_signal(&myData->cond);
+ pthread_mutex_unlock(&myData->mutex);
+}
+
+//
+// MyAudioQueueIsRunningCallback
+//
+// Called from the AudioQueue when playback is started or stopped. This
+// information is used to toggle the observable "isPlaying" property and
+// set the "finished" flag.
+//
+void MyAudioQueueIsRunningCallback(void *inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID)
+{
+ AudioStreamer *myData = (AudioStreamer *)inUserData;
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (myData.isPlaying)
+ {
+ myData->finished = true;
+ myData.isPlaying = false;
+
+#ifdef TARGET_OS_IPHONE
+ AudioSessionSetActive(false);
+#endif
+ }
+ else
+ {
+ myData.isPlaying = true;
+ if (myData->finished)
+ {
+ myData.isPlaying = false;
+ }
+
+ //
+ // Note about this bug avoidance quirk:
+ //
+ // On cleanup of the AudioQueue thread, on rare occasions, there would
+ // be a crash in CFSetContainsValue as a CFRunLoopObserver was getting
+ // removed from the CFRunLoop.
+ //
+ // After lots of testing, it appeared that the audio thread was
+ // attempting to remove CFRunLoop observers from the CFRunLoop after the
+ // thread had already deallocated the run loop.
+ //
+ // By creating an NSRunLoop for the AudioQueue thread, it changes the
+ // thread destruction order and seems to avoid this crash bug -- or
+ // at least I haven't had it since (nasty hard to reproduce error!)
+ //
+ [NSRunLoop currentRunLoop];
+ }
+
+ [pool release];
+}
+
+#ifdef TARGET_OS_IPHONE
+//
+// MyAudioSessionInterruptionListener
+//
+// Invoked if the audio session is interrupted (like when the phone rings)
+//
+void MyAudioSessionInterruptionListener(void *inClientData, UInt32 inInterruptionState)
+{
+}
+#endif
+
+#pragma mark CFReadStream Callback Function Implementations
+
+//
+// ReadStreamCallBack
+//
+// This is the callback for the CFReadStream from the network connection. This
+// is where all network data is passed to the AudioFileStream.
+//
+// Invoked when an error occurs, the stream ends or we have data to read.
+//
+void ReadStreamCallBack
+(
+ CFReadStreamRef stream,
+ CFStreamEventType eventType,
+ void* dataIn
+)
+{
+ AudioStreamer *myData = (AudioStreamer *)dataIn;
+
+ if (eventType == kCFStreamEventErrorOccurred)
+ {
+ myData->failed = YES;
+ }
+ else if (eventType == kCFStreamEventEndEncountered)
+ {
+ if (myData->failed || myData->finished)
+ {
+ return;
+ }
+
+ //
+ // If there is a partially filled buffer, pass it to the AudioQueue for
+ // processing
+ //
+ if (myData->bytesFilled)
+ {
+ MyEnqueueBuffer(myData);
+ }
+
+ //
+ // If the AudioQueue started, then flush it (to make certain everything
+ // sent thus far will be processed) and subsequently stop the queue.
+ //
+ if (myData->started)
+ {
+ OSStatus err = AudioQueueFlush(myData->audioQueue);
+ if (err) { PRINTERROR("AudioQueueFlush"); return; }
+
+ err = AudioQueueStop(myData->audioQueue, false);
+ if (err) { PRINTERROR("AudioQueueStop"); return; }
+
+ CFReadStreamClose(stream);
+ CFRelease(stream);
+ myData->stream = nil;
+ }
+ else
+ {
+ //
+ // If we have reached the end of the file without starting, then we
+ // have failed to find any audio in the file. Abort.
+ //
+ myData->failed = YES;
+ }
+ }
+ else if (eventType == kCFStreamEventHasBytesAvailable)
+ {
+ if (myData->failed || myData->finished)
+ {
+ return;
+ }
+
+ //
+ // Read the bytes from the stream
+ //
+ UInt8 bytes[kAQBufSize];
+ CFIndex length = CFReadStreamRead(stream, bytes, kAQBufSize);
+
+ if (length == -1)
+ {
+ myData->failed = YES;
+ return;
+ }
+
+ //
+ // Parse the bytes read by sending them through the AudioFileStream
+ //
+ if (length > 0)
+ {
+ if (myData->discontinuous)
+ {
+ OSStatus err = AudioFileStreamParseBytes(myData->audioFileStream, length, bytes, kAudioFileStreamParseFlag_Discontinuity);
+ if (err) { PRINTERROR("AudioFileStreamParseBytes"); myData->failed = true;}
+ }
+ else
+ {
+ OSStatus err = AudioFileStreamParseBytes(myData->audioFileStream, length, bytes, 0);
+ if (err) { PRINTERROR("AudioFileStreamParseBytes"); myData->failed = true; }
+ }
+ }
+ }
+}
+
+@implementation AudioStreamer
+
+@synthesize isPlaying;
+
+//
+// initWithURL
+//
+// Init method for the object.
+//
+- (id)initWithURL:(NSURL *)newUrl
+{
+ self = [super init];
+ if (self != nil)
+ {
+ url = [newUrl retain];
+ }
+ return self;
+}
+
+//
+// dealloc
+//
+// Releases instance memory.
+//
+- (void)dealloc
+{
+ [url release];
+ [super dealloc];
+}
+
+//
+// startInternal
+//
+// This is the start method for the AudioStream thread. This thread is created
+// because it will be blocked when there are no audio buffers idle (and ready
+// to receive audio data).
+//
+// Activity in this thread:
+// - Creation and cleanup of all AudioFileStream and AudioQueue objects
+// - Receives data from the CFReadStream
+// - AudioFileStream processing
+// - Copying of data from AudioFileStream into audio buffers
+// - Stopping of the thread because of end-of-file
+// - Stopping due to error or failure
+//
+// Activity *not* in this thread:
+// - AudioQueue playback and notifications (happens in AudioQueue thread)
+// - Actual download of NSURLConnection data (NSURLConnection's thread)
+// - Creation of the AudioStreamer (other, likely "main" thread)
+// - Invocation of -start method (other, likely "main" thread)
+// - User/manual invocation of -stop (other, likely "main" thread)
+//
+// This method contains bits of the "main" function from Apple's example in
+// AudioFileStreamExample.
+//
+- (void)startInternal
+{
+ //
+ // Retains "self". This means that we can't be deleted while playback is
+ // using our buffers. It also means that releasing an AudioStreamer while
+ // it is playing won't stop playback. This is a bit weird but, oh well.
+ //
+ [self retain];
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+#ifdef TARGET_OS_IPHONE
+ //
+ // Set the audio session category so that we continue to play if the
+ // iPhone/iPod auto-locks.
+ //
+ AudioSessionInitialize (
+ NULL, // 'NULL' to use the default (main) run loop
+ NULL, // 'NULL' to use the default run loop mode
+ MyAudioSessionInterruptionListener, // a reference to your interruption callback
+ self // data to pass to your interruption listener callback
+ );
+ UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
+ AudioSessionSetProperty (
+ kAudioSessionProperty_AudioCategory,
+ sizeof (sessionCategory),
+ &sessionCategory
+ );
+ AudioSessionSetActive(true);
+#endif
+
+ //
+ // Attempt to guess the file type from the URL. Reading the MIME type
+ // from the CFReadStream would be a better approach since lots of
+ // URL's don't have the right extension.
+ //
+ // If you have a fixed file-type, you may want to hardcode this.
+ //
+ AudioFileTypeID fileTypeHint = kAudioFileMP3Type;
+ NSString *fileExtension = [[url path] pathExtension];
+ if ([fileExtension isEqual:@"mp3"])
+ {
+ fileTypeHint = kAudioFileMP3Type;
+ }
+ else if ([fileExtension isEqual:@"wav"])
+ {
+ fileTypeHint = kAudioFileWAVEType;
+ }
+ else if ([fileExtension isEqual:@"aifc"])
+ {
+ fileTypeHint = kAudioFileAIFCType;
+ }
+ else if ([fileExtension isEqual:@"aiff"])
+ {
+ fileTypeHint = kAudioFileAIFFType;
+ }
+ else if ([fileExtension isEqual:@"m4a"])
+ {
+ fileTypeHint = kAudioFileM4AType;
+ }
+ else if ([fileExtension isEqual:@"mp4"])
+ {
+ fileTypeHint = kAudioFileMPEG4Type;
+ }
+ else if ([fileExtension isEqual:@"caf"])
+ {
+ fileTypeHint = kAudioFileCAFType;
+ }
+ else if ([fileExtension isEqual:@"aac"])
+ {
+ fileTypeHint = kAudioFileAAC_ADTSType;
+ }
+
+ // initialize a mutex and condition so that we can block on buffers in use.
+ pthread_mutex_init(&mutex, NULL);
+ pthread_cond_init(&cond, NULL);
+ pthread_mutex_init(&mutex2, NULL);
+
+ // create an audio file stream parser
+ OSStatus err = AudioFileStreamOpen(self, MyPropertyListenerProc, MyPacketsProc,
+ fileTypeHint, &audioFileStream);
+ if (err) { PRINTERROR("AudioFileStreamOpen"); goto cleanup; }
+
+ //
+ // Create the GET request
+ //
+ CFHTTPMessageRef message= CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (CFURLRef)url, kCFHTTPVersion1_1);
+ stream = CFReadStreamCreateForHTTPRequest(NULL, message);
+ CFRelease(message);
+ if (!CFReadStreamOpen(stream))
+ {
+ CFRelease(stream);
+ goto cleanup;
+ }
+
+ //
+ // Set our callback function to receive the data
+ //
+ CFStreamClientContext context = {0, self, NULL, NULL, NULL};
+ CFReadStreamSetClient(
+ stream,
+ kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
+ ReadStreamCallBack,
+ &context);
+ CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
+
+ //
+ // Process the run loop until playback is finished or failed.
+ //
+ do
+ {
+ CFRunLoopRunInMode(
+ kCFRunLoopDefaultMode,
+ 0.25,
+ false);
+
+ if (failed)
+ {
+ [self stop];
+
+#ifdef TARGET_OS_IPHONE
+ UIAlertView *alert =
+ [[UIAlertView alloc]
+ initWithTitle:NSLocalizedStringFromTable(@"Audio Error", @"Errors", nil)
+ message:NSLocalizedStringFromTable(@"Attempt to play streaming audio failed.", @"Errors", nil)
+ delegate:self
+ cancelButtonTitle:@"OK"
+ otherButtonTitles: nil];
+ [alert
+ performSelector:@selector(show)
+ onThread:[NSThread mainThread]
+ withObject:nil
+ waitUntilDone:YES];
+ [alert release];
+#else
+ NSAlert *alert =
+ [NSAlert
+ alertWithMessageText:NSLocalizedString(@"Audio Error", @"")
+ defaultButton:NSLocalizedString(@"OK", @"")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:@"Attempt to play streaming audio failed."];
+ [alert
+ performSelector:@selector(runModal)
+ onThread:[NSThread mainThread]
+ withObject:nil waitUntilDone:NO];
+#endif
+
+ break;
+ }
+ } while (isPlaying || !finished);
+
+cleanup:
+
+ //
+ // Cleanup the read stream if it is still open
+ //
+ if (stream)
+ {
+ CFReadStreamClose(stream);
+ CFRelease(stream);
+ stream = nil;
+ }
+
+ //
+ // Close the audio file strea,
+ //
+ err = AudioFileStreamClose(audioFileStream);
+ if (err) { PRINTERROR("AudioFileStreamClose"); goto cleanup; }
+
+ //
+ // Dispose of the Audio Queue
+ //
+ if (started)
+ {
+ err = AudioQueueDispose(audioQueue, true);
+ if (err) { PRINTERROR("AudioQueueDispose"); goto cleanup; }
+ }
+
+ [pool release];
+ [self release];
+}
+
+//
+// start
+//
+// Calls startInternal in a new thread.
+//
+- (void)start{
+ [NSThread detachNewThreadSelector:@selector(startInternal) toTarget:self withObject:nil];
+}
+
+
+
+
+
+//
+// stop
+//
+// This method can be called to stop downloading/playback before it completes.
+// It is automatically called when an error occurs.
+//
+// If playback has not started before this method is called, it will toggle the
+// "isPlaying" property so that it is guaranteed to transition to true and
+// back to false
+//
+- (void)stop
+{
+ if (stream)
+ {
+ CFReadStreamClose(stream);
+ CFRelease(stream);
+ stream = nil;
+
+ if (finished)
+ {
+ return;
+ }
+
+ if (started)
+ {
+ //
+ // Set finished to true *before* we call stop. This is to handle our
+ // third thread...
+ // - This method is called from main (UI) thread
+ // - The AudioQueue thread (which owns the AudioQueue buffers nad
+ // will delete them as soon as we call AudioQueueStop)
+ // - URL connection thread is copying data from AudioStream to
+ // AudioQueue buffer
+ // We set this flag to tell the URL connection thread to stop
+ // copying.
+ //
+ pthread_mutex_lock(&mutex2);
+ finished = true;
+
+ OSStatus err = AudioQueueStop(audioQueue, true);
+ if (err) { PRINTERROR("AudioQueueStop"); }
+ pthread_mutex_unlock(&mutex2);
+
+ pthread_mutex_lock(&mutex);
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+ }
+ else
+ {
+ finished = true;
+ self.isPlaying = YES;
+ self.isPlaying = NO;
+ }
+ }
+}
+
+@end
View
78 CocoaXMLParser.h
@@ -0,0 +1,78 @@
+/*
+ File: CocoaXMLParser.h
+ Abstract: Subclass of ShowRSSParser that uses the Foundation framework's NSXMLParser for parsing the XML data.
+ Version: 1.3
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
+ Inc. ("Apple") in consideration of your agreement to the following
+ terms, and your use, installation, modification or redistribution of
+ this Apple software constitutes acceptance of these terms. If you do
+ not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc. may
+ be used to endorse or promote products derived from the Apple Software
+ without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or
+ implied, are granted by Apple herein, including but not limited to any
+ patent rights that may be infringed by your derivative works or by other
+ works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2010 Apple Inc. All Rights Reserved.
+
+*/
+
+#import <UIKit/UIKit.h>
+#import "ShowRSSParser.h"
+
+@class Show;
+
+@interface CocoaXMLParser : ShowRSSParser <NSXMLParserDelegate>
+{
+ NSMutableString *currentString;
+ Show *currentShow;
+ BOOL storingCharacters;
+ NSDateFormatter *parseFormatter;
+ NSMutableData *xmlData;
+ BOOL done;
+ NSURLConnection *rssConnection;
+ // The number of parsed Shows is tracked so that the autorelease pool for the parsing thread can be periodically
+ // emptied to keep the memory footprint under control.
+ NSUInteger countOfParsedShows;
+ NSAutoreleasePool *downloadAndParsePool;
+}
+
+@property (nonatomic, retain) NSMutableString *currentString;
+@property (nonatomic, retain) Show *currentShow;
+@property (nonatomic, retain) NSDateFormatter *parseFormatter;
+@property (nonatomic, retain) NSMutableData *xmlData;
+@property (nonatomic, retain) NSURLConnection *rssConnection;
+// The autorelease pool property is assign because autorelease pools cannot be retained.
+@property (nonatomic, assign) NSAutoreleasePool *downloadAndParsePool;
+
+- (void)downloadAndParse:(NSURL *)url;
+
+@end
View
198 CocoaXMLParser.m
@@ -0,0 +1,198 @@
+/*
+ File: CocoaXMLParser.m
+ Abstract: Subclass of ShowRSSParser that uses the Foundation framework's NSXMLParser for parsing the XML data.
+ Version: 1.3
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
+ Inc. ("Apple") in consideration of your agreement to the following
+ terms, and your use, installation, modification or redistribution of
+ this Apple software constitutes acceptance of these terms. If you do
+ not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc. may
+ be used to endorse or promote products derived from the Apple Software
+ without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or
+ implied, are granted by Apple herein, including but not limited to any
+ patent rights that may be infringed by your derivative works or by other
+ works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2010 Apple Inc. All Rights Reserved.
+
+*/
+
+#import "CocoaXMLParser.h"
+#import "Show.h"
+
+@implementation CocoaXMLParser
+
++ (NSString *)parserName {
+ return @"NSXMLParser";
+}
+
++ (XMLParserType)parserType {
+ return XMLParserTypeNSXMLParser;
+}
+
+@synthesize currentString, currentShow, parseFormatter, xmlData, rssConnection, downloadAndParsePool;
+
+- (void)downloadAndParse:(NSURL *)url {
+ self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
+ done = NO;
+ self.parseFormatter = [[[NSDateFormatter alloc] init] autorelease];
+ [parseFormatter setDateStyle:NSDateFormatterLongStyle];
+ [parseFormatter setTimeStyle:NSDateFormatterNoStyle];
+ // necessary because iTunes RSS feed is not localized, so if the device region has been set to other than US
+ // the date formatter must be set to US locale in order to parse the dates
+ [parseFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"US"] autorelease]];
+ self.xmlData = [NSMutableData data];
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
+ NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
+ // create the connection with the request and start loading the data
+ rssConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
+ [self performSelectorOnMainThread:@selector(downloadStarted) withObject:nil waitUntilDone:NO];
+ if (rssConnection != nil) {
+ do {
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
+ } while (!done);
+ }
+ self.rssConnection = nil;
+ self.parseFormatter = nil;
+ self.currentShow = nil;
+ [downloadAndParsePool release];
+ self.downloadAndParsePool = nil;
+}
+
+#pragma mark NSURLConnection Delegate methods
+
+/*
+ Disable caching so that each time we run this app we are starting with a clean slate. You may not want to do this in your application.
+ */
+- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
+ return nil;
+}
+
+// Forward errors to the delegate.
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+ done = YES;
+ [self performSelectorOnMainThread:@selector(parseError:) withObject:error waitUntilDone:NO];
+}
+
+// Called when a chunk of data has been downloaded.
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ // Append the downloaded chunk of data.
+ [xmlData appendData:data];
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ [self performSelectorOnMainThread:@selector(downloadEnded) withObject:nil waitUntilDone:NO];
+ NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
+ parser.delegate = self;
+ self.currentString = [NSMutableString string];
+ NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
+ [parser parse];
+ NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - start;
+ [self performSelectorOnMainThread:@selector(addToParseDuration:) withObject:[NSNumber numberWithDouble:duration] waitUntilDone:NO];
+ [self performSelectorOnMainThread:@selector(parseEnded) withObject:nil waitUntilDone:NO];
+ [parser release];
+ self.currentString = nil;
+ self.xmlData = nil;
+ // Set the condition which ends the run loop.
+ done = YES;
+}
+
+#pragma mark Parsing support methods
+
+static const NSUInteger kAutoreleasePoolPurgeFrequency = 20;
+
+- (void)finishedCurrentShow {
+ [self performSelectorOnMainThread:@selector(parsedShow:) withObject:currentShow waitUntilDone:NO];
+ // performSelectorOnMainThread: will retain the object until the selector has been performed
+ // setting the local reference to nil ensures that the local reference will be released
+ self.currentShow = nil;
+ countOfParsedShows++;
+ // Periodically purge the autorelease pool. The frequency of this action may need to be tuned according to the
+ // size of the objects being parsed. The goal is to keep the autorelease pool from growing too large, but
+ // taking this action too frequently would be wasteful and reduce performance.
+ if (countOfParsedShows == kAutoreleasePoolPurgeFrequency) {
+ [downloadAndParsePool release];
+ self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
+ countOfParsedShows = 0;
+ }
+}
+
+#pragma mark NSXMLParser Parsing Callbacks
+
+// Constants for the XML element names that will be considered during the parse.
+// Declaring these as static constants reduces the number of objects created during the run
+// and is less prone to programmer error.
+static NSString *kName_Item = @"item";
+static NSString *kName_Title = @"title";
+static NSString *kName_StartTime = @"startTime";
+static NSString *kName_EndTime = @"endTime";
+static NSString *kName_DayOfTheWeek = @"dayOfTheWeek";
+
+
+- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *) qualifiedName attributes:(NSDictionary *)attributeDict {
+ if ([elementName isEqualToString:kName_Item]) {
+ self.currentShow = [[[Show alloc] init] autorelease];
+ } else if ([elementName isEqualToString:kName_Title] || [elementName isEqualToString:kName_StartTime] || [elementName isEqualToString:kName_EndTime] || [elementName isEqualToString:kName_DayOfTheWeek]) {
+ [currentString setString:@""];
+ storingCharacters = YES;
+ }
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
+ if ([elementName isEqualToString:kName_Item]) {
+ [self finishedCurrentShow];
+ } else if ([elementName isEqualToString:kName_Title]) {
+ currentShow.name = currentString;
+ } else if ([elementName isEqualToString:kName_StartTime]) {
+ currentShow.startTime = currentString;
+ } else if ([elementName isEqualToString:kName_EndTime]) {
+ currentShow.endTime = currentString;
+ } else if ([elementName isEqualToString:kName_DayOfTheWeek]) {
+ currentShow.dayOfTheWeek = currentString;
+ }
+ storingCharacters = NO;
+}
+
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
+ if (storingCharacters) [currentString appendString:string];
+}
+
+/*
+ A production application should include robust error handling as part of its parsing implementation.
+ The specifics of how errors are handled depends on the application.
+ */
+- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
+ // Handle errors as appropriate for your application.
+}
+
+
+@end
View
BIN  Default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Default@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
27 DetailController.h
@@ -0,0 +1,27 @@
+// UCLRadio
+//
+// Created by Frederic Jacobs on 23/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+@class Show;
+
+@interface DetailController : UITableViewController {
+ NSDateFormatter *dateFormatter;
+ Show *show;
+ Show *showToSubscribe;
+ AppDelegate *appDelegate;
+}
+
+@property (nonatomic, retain) Show *show;
+@property (nonatomic, readonly, retain) NSDateFormatter *dateFormatter;
+@property (nonatomic, readwrite, retain) Show *showToSubscribe;
+@property (nonatomic, retain) AppDelegate *appDelegate;
+
+
+
+@end
View
85 DetailController.m
@@ -0,0 +1,85 @@
+// UCLRadio
+//
+// Created by Frederic Jacobs on 23/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+
+#import "DetailController.h"
+#import "Show.h"
+
+
+@implementation DetailController
+
+@synthesize show, dateFormatter, showToSubscribe, appDelegate;
+
+
+- (void)dealloc {
+ [dateFormatter release];
+ [show release];
+ [super dealloc];
+}
+
+- (NSDateFormatter *)dateFormatter {
+ if (dateFormatter == nil) {
+ dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
+ [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
+ }
+ return dateFormatter;
+}
+
+// When the view appears, update the title and table contents.
+- (void)viewWillAppear:(BOOL)animated {
+ appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ self.title = show.name;
+ [self.tableView reloadData];
+}
+
+- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section {
+ return 4;
+}
+
+
+
+- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ [appDelegate addShow:show];
+}
+
+- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ static NSString *kCellIdentifier = @"ShowDetailCell";
+ UITableViewCell *cell = (UITableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:kCellIdentifier] autorelease];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ }
+ switch (indexPath.row) {
+ case 0: {
+ cell.textLabel.text = NSLocalizedString(@"Starts at ", @"album label");
+ cell.detailTextLabel.text = show.startTime;
+ } break;
+ case 1: {
+ cell.textLabel.text = NSLocalizedString(@"artist", @"artist label");
+ cell.detailTextLabel.text = show.endTime;
+ } break;
+ case 2: {
+ cell.textLabel.text = NSLocalizedString(@"category", @"category label");
+ cell.detailTextLabel.text = show.dayOfTheWeek;
+ } break;
+ case 3: {
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellIdentifier];
+ cell.textLabel.text = @"Subscribe";
+ cell.textLabel.textAlignment = UITextAlignmentCenter;
+ }
+ }
+ return cell;
+}
+
+
+
+- (NSString *)tableView:(UITableView *)tv titleForHeaderInSection:(NSInteger)section {
+ return NSLocalizedString(@"Show details:", @"Show details label");
+}
+
+
+@end
View
390 MainWindow.xib
@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">528</int>
+ <string key="IBDocument.SystemVersion">11C74</string>
+ <string key="IBDocument.InterfaceBuilderVersion">1938</string>
+ <string key="IBDocument.AppKitVersion">1138.23</string>
+ <string key="IBDocument.HIToolboxVersion">567.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="NS.object.0">933</string>
+ </object>
+ <object class="NSArray" key="IBDocument.IntegratedClassDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>IBProxyObject</string>
+ <string>IBUITabBarItem</string>
+ <string>IBUIViewController</string>
+ <string>IBUICustomObject</string>
+ <string>IBUITabBarController</string>
+ <string>IBUIWindow</string>
+ <string>IBUITabBar</string>
+ <string>IBUINavigationController</string>
+ <string>IBUINavigationBar</string>
+ <string>IBUINavigationItem</string>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+ <integer value="1" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBProxyObject" id="841351856">
+ <string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBProxyObject" id="427554174">
+ <string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUICustomObject" id="1013181473">
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUITabBarController" id="170853733">
+ <object class="IBUISimulatedTabBarMetrics" key="IBUISimulatedBottomBarMetrics"/>
+ <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+ <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+ <int key="IBUIInterfaceOrientation">1</int>
+ <int key="interfaceOrientation">1</int>
+ </object>
+ <bool key="IBUIDefinesPresentationContext">YES</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIHorizontal">NO</bool>
+ <object class="IBUINavigationController" key="IBUISelectedViewController" id="102085146">
+ <object class="IBUITabBarItem" key="IBUITabBarItem" id="371369640">
+ <string key="IBUITitle">Shows</string>
+ <object class="NSCustomResource" key="IBUIImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">shows.png</string>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <reference key="IBUIParentViewController" ref="170853733"/>
+ <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+ <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+ <int key="IBUIInterfaceOrientation">1</int>
+ <int key="interfaceOrientation">1</int>
+ </object>
+ <bool key="IBUIDefinesPresentationContext">YES</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIHorizontal">NO</bool>
+ <object class="IBUINavigationBar" key="IBUINavigationBar" id="77114169">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrameSize">{0, 0}</string>
+ <string key="NSReuseIdentifierKey">_NS:451</string>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <bool key="IBUIMultipleTouchEnabled">YES</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="NSMutableArray" key="IBUIViewControllers">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBUIViewController" id="165672799">
+ <object class="IBUITabBarItem" key="IBUITabBarItem" id="640094539">
+ <string key="IBUITitle">Shows</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUINavigationItem" key="IBUINavigationItem" id="636803895">
+ <string key="IBUITitle">Item</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <reference key="IBUIParentViewController" ref="102085146"/>
+ <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+ <int key="IBUIInterfaceOrientation">1</int>
+ <int key="interfaceOrientation">1</int>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIHorizontal">NO</bool>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBUIViewControllers">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBUIViewController" id="62223111">
+ <object class="IBUITabBarItem" key="IBUITabBarItem" id="609211178">
+ <string key="IBUITitle">Now Playing</string>
+ <object class="NSCustomResource" key="IBUIImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">playTabBar.png</string>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <reference key="IBUIParentViewController" ref="170853733"/>
+ <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+ <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+ <int key="IBUIInterfaceOrientation">1</int>
+ <int key="interfaceOrientation">1</int>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIHorizontal">NO</bool>
+ </object>
+ <reference ref="102085146"/>
+ </object>
+ <object class="IBUITabBar" key="IBUITabBar" id="73766809">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">266</int>
+ <string key="NSFrame">{{0, 431}, {320, 49}}</string>
+ <reference key="NSSuperview"/>
+ <reference key="NSNextKeyView"/>
+ <string key="NSReuseIdentifierKey">_NS:473</string>
+ <object class="NSColor" key="IBUIBackgroundColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MCAwAA</bytes>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ </object>
+ <object class="IBUIWindow" id="117978783">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrameSize">{320, 480}</string>
+ <reference key="NSSuperview"/>
+ <object class="NSColor" key="IBUIBackgroundColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MSAxIDEAA</bytes>
+ </object>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClearsContextBeforeDrawing">NO</bool>
+ <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="841351856"/>
+ <reference key="destination" ref="1013181473"/>
+ </object>
+ <int key="connectionID">27</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">rootViewController</string>
+ <reference key="source" ref="117978783"/>
+ <reference key="destination" ref="170853733"/>
+ </object>
+ <int key="connectionID">37</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">viewController</string>
+ <reference key="source" ref="1013181473"/>
+ <reference key="destination" ref="62223111"/>
+ </object>
+ <int key="connectionID">48</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">secondViewController</string>
+ <reference key="source" ref="1013181473"/>
+ <reference key="destination" ref="165672799"/>
+ </object>
+ <int key="connectionID">49</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="957960031">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="841351856"/>
+ <reference key="parent" ref="957960031"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="427554174"/>
+ <reference key="parent" ref="957960031"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">12</int>
+ <reference key="object" ref="117978783"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="parent" ref="957960031"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">26</int>
+ <reference key="object" ref="1013181473"/>
+ <reference key="parent" ref="957960031"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">28</int>
+ <reference key="object" ref="170853733"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="73766809"/>
+ <reference ref="62223111"/>
+ <reference ref="102085146"/>
+ </object>
+ <reference key="parent" ref="957960031"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="73766809"/>
+ <reference key="parent" ref="170853733"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">34</int>
+ <reference key="object" ref="62223111"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="609211178"/>
+ </object>
+ <reference key="parent" ref="170853733"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">35</int>
+ <reference key="object" ref="609211178"/>
+ <reference key="parent" ref="62223111"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">40</int>
+ <reference key="object" ref="102085146"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="77114169"/>
+ <reference ref="371369640"/>
+ <reference ref="165672799"/>
+ </object>
+ <reference key="parent" ref="170853733"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">41</int>
+ <reference key="object" ref="77114169"/>
+ <reference key="parent" ref="102085146"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">44</int>
+ <reference key="object" ref="371369640"/>
+ <reference key="parent" ref="102085146"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">31</int>
+ <reference key="object" ref="165672799"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="640094539"/>
+ <reference ref="636803895"/>
+ </object>
+ <reference key="parent" ref="102085146"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">32</int>
+ <reference key="object" ref="640094539"/>
+ <reference key="parent" ref="165672799"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">45</int>
+ <reference key="object" ref="636803895"/>
+ <reference key="parent" ref="165672799"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.CustomClassName</string>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.CustomClassName</string>
+ <string>-2.IBPluginDependency</string>
+ <string>12.IBPluginDependency</string>
+ <string>26.CustomClassName</string>
+ <string>26.IBPluginDependency</string>
+ <string>28.IBPluginDependency</string>
+ <string>29.IBPluginDependency</string>
+ <string>31.CustomClassName</string>
+ <string>31.IBPluginDependency</string>
+ <string>32.IBPluginDependency</string>
+ <string>34.CustomClassName</string>
+ <string>34.IBPluginDependency</string>
+ <string>35.IBPluginDependency</string>
+ <string>40.IBPluginDependency</string>
+ <string>41.IBPluginDependency</string>
+ <string>44.IBPluginDependency</string>
+ <string>45.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>UIApplication</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>UIResponder</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>AppDelegate</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>SecondViewController</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>UCLRadioViewController</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="957960031"/>
+ <reference key="dict.values" ref="957960031"/>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="957960031"/>
+ <reference key="dict.values" ref="957960031"/>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">49</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes"/>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
+ <integer value="528" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>playTabBar.png</string>
+ <string>shows.png</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>{26, 26}</string>
+ <string>{26, 26}</string>
+ </object>
+ </object>
+ <string key="IBCocoaTouchPluginVersion">933</string>
+ </data>
+</archive>
View
30 SecondViewController.h
@@ -0,0 +1,30 @@
+//
+// SecondViewController.h
+// UCLRadio
+//
+// Created by Frederic Jacobs on 23/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "ShowRSSParser.h"
+
+@class DetailController;
+
+@interface SecondViewController : UITableViewController<ShowRSSParserDelegate>{
+ IBOutlet UITableView *allShows;
+ ShowRSSParser *myParser;
+ NSMutableArray *shows;
+ DetailController *detailController;
+
+}
+
+@property (nonatomic, retain) NSMutableArray *shows;
+@property (nonatomic, retain) ShowRSSParser *myParser;
+@property (nonatomic, retain, readonly) DetailController *detailController;
+
+- (id) init;
+
+
+@end
+
View
137 SecondViewController.m
@@ -0,0 +1,137 @@
+//
+// SecondViewController.m
+// UCLRadio
+//
+// Created by Frederic Jacobs on 23/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "SecondViewController.h"
+#import "CocoaXMLParser.h"
+#import "DetailController.h"
+
+@implementation SecondViewController
+
+@synthesize myParser, shows;
+
+
+
+- (id) init{
+ self = [super initWithStyle:UITableViewStyleGrouped];
+ return self;
+}
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)allShows{
+
+ return 1;
+
+}
+
+- (DetailController *)detailController {
+ if (detailController == nil) {
+ detailController = [[DetailController alloc] initWithStyle:UITableViewStyleGrouped];
+ }
+ return detailController;
+}
+
+
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
+ if (self) {
+ // Custom initialization
+ }
+ return self;
+}
+
+- (void)didReceiveMemoryWarning
+{
+ // Releases the view if it doesn't have a superview.
+ [super didReceiveMemoryWarning];
+
+ // Release any cached data, images, etc that aren't in use.
+}
+
+#pragma mark - View lifecycle
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+ if (shows == nil) {
+ self.shows = [NSMutableArray array];
+ } else {
+ [shows removeAllObjects];
+ [self.tableView reloadData];
+ }
+
+ self.myParser = [[[CocoaXMLParser alloc] init] autorelease];
+
+ myParser.delegate = self;
+ [myParser start];
+
+
+ // Do any additional setup after loading the view from its nib
+}
+
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return [shows count];
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ static NSString *kCellIdentifier = @"MyCell";
+ UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellIdentifier] autorelease];
+ cell.textLabel.font = [UIFont boldSystemFontOfSize:14.0];
+ cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+ }
+ cell.textLabel.text = [[shows objectAtIndex:indexPath.row] name];
+ return cell;
+
+
+}
+
+
+- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ self.detailController.show = [shows objectAtIndex:indexPath.row];
+ [self.navigationController pushViewController:self.detailController animated:YES];
+
+}
+
+
+
+
+- (void)viewDidUnload
+{
+ [super viewDidUnload];
+ // Release any retained subviews of the main view.
+ // e.g. self.myOutlet = nil;
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+ // Return YES for supported orientations
+ return (interfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+- (void)parserDidEndParsingData:(ShowRSSParser *)parser {
+ self.title = [NSString stringWithFormat:NSLocalizedString(@"All %d Shows", @"Top Songs format"), [shows count]];
+ [self.tableView reloadData];
+ self.navigationItem.rightBarButtonItem.enabled = YES;
+ self.myParser = nil;
+}
+
+- (void)parser:(ShowRSSParser *)parser didParseShows:(NSArray *)parsedShows{
+ [shows addObjectsFromArray:parsedShows];
+ // Three scroll view properties are checked to keep the user interface smooth during parse. When new objects are delivered by the parser, the table view is reloaded to display them. If the table is reloaded while the user is scrolling, this can result in eratic behavior. dragging, tracking, and decelerating can be checked for this purpose. When the parser finishes, reloadData will be called in parserDidEndParsingData:, guaranteeing that all data will ultimately be displayed even if reloadData is not called in this method because of user interaction.
+ if (!self.tableView.dragging && !self.tableView.tracking && !self.tableView.decelerating) {
+ self.title = [NSString stringWithFormat:NSLocalizedString(@"All %d Shows", @"Top Songs format"), [shows count]];
+ [self.tableView reloadData];
+ }
+}
+
+
+
+
+@end
View
216 SecondViewController.xib
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
+ <data>
+ <int key="IBDocument.SystemTarget">1280</int>
+ <string key="IBDocument.SystemVersion">11C74</string>
+ <string key="IBDocument.InterfaceBuilderVersion">1938</string>
+ <string key="IBDocument.AppKitVersion">1138.23</string>
+ <string key="IBDocument.HIToolboxVersion">567.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="NS.object.0">933</string>
+ </object>
+ <array key="IBDocument.IntegratedClassDependencies">
+ <string>IBUINavigationItem</string>
+ <string>IBUIViewController</string>
+ <string>IBUITableView</string>
+ <string>IBUINavigationBar</string>
+ <string>IBUINavigationController</string>
+ <string>IBProxyObject</string>
+ </array>
+ <array key="IBDocument.PluginDependencies">
+ <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </array>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+ <integer value="1" key="NS.object.0"/>
+ </object>
+ <array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <object class="IBProxyObject" id="372490531">
+ <string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBProxyObject" id="975951072">
+ <string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <object class="IBUINavigationController" id="85656930">
+ <object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+ <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+ <int key="IBUIInterfaceOrientation">1</int>
+ <int key="interfaceOrientation">1</int>
+ </object>
+ <bool key="IBUIDefinesPresentationContext">YES</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIHorizontal">NO</bool>
+ <object class="IBUINavigationBar" key="IBUINavigationBar" id="485072175">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrameSize">{0, 0}</string>
+ <string key="NSReuseIdentifierKey">_NS:451</string>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <bool key="IBUIMultipleTouchEnabled">YES</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <array class="NSMutableArray" key="IBUIViewControllers">
+ <object class="IBUIViewController" id="848449117">
+ <object class="IBUITableView" key="IBUIView" id="173939279">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrame">{{0, 64}, {320, 416}}</string>
+ <reference key="NSSuperview"/>
+ <reference key="NSNextKeyView"/>
+ <string key="NSReuseIdentifierKey">_NS:418</string>
+ <object class="NSColor" key="IBUIBackgroundColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIAlwaysBounceVertical">YES</bool>
+ <int key="IBUISeparatorStyle">1</int>
+ <int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
+ <bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
+ <float key="IBUIRowHeight">44</float>
+ <float key="IBUISectionHeaderHeight">22</float>
+ <float key="IBUISectionFooterHeight">22</float>
+ </object>
+ <object class="IBUINavigationItem" key="IBUINavigationItem" id="93248673">
+ <string key="IBUITitle">All The Shows</string>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ </object>
+ <reference key="IBUIParentViewController" ref="85656930"/>
+ <object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+ <int key="IBUIInterfaceOrientation">1</int>
+ <int key="interfaceOrientation">1</int>
+ </object>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIHorizontal">NO</bool>
+ </object>
+ </array>
+ </object>
+ </array>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <array class="NSMutableArray" key="connectionRecords">
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="85656930"/>
+ <reference key="destination" ref="372490531"/>
+ </object>
+ <int key="connectionID">26</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="485072175"/>
+ <reference key="destination" ref="848449117"/>
+ </object>
+ <int key="connectionID">20</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">titleView</string>
+ <reference key="source" ref="93248673"/>
+ <reference key="destination" ref="485072175"/>
+ </object>
+ <int key="connectionID">24</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">dataSource</string>
+ <reference key="source" ref="173939279"/>
+ <reference key="destination" ref="372490531"/>
+ </object>
+ <int key="connectionID">22</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="173939279"/>
+ <reference key="destination" ref="372490531"/>
+ </object>
+ <int key="connectionID">23</int>
+ </object>
+ </array>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <array key="orderedObjects">
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <array key="object" id="0"/>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="372490531"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="975951072"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="85656930"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="485072175"/>
+ <reference ref="848449117"/>
+ </array>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="485072175"/>
+ <reference key="parent" ref="85656930"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">15</int>
+ <reference key="object" ref="848449117"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="93248673"/>
+ <reference ref="173939279"/>
+ </array>
+ <reference key="parent" ref="85656930"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">16</int>
+ <reference key="object" ref="93248673"/>
+ <reference key="parent" ref="848449117"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">18</int>
+ <reference key="object" ref="173939279"/>
+ <reference key="parent" ref="848449117"/>
+ </object>
+ </array>
+ </object>
+ <dictionary class="NSMutableDictionary" key="flattenedProperties">
+ <string key="-1.CustomClassName">SecondViewController</string>
+ <string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="-2.CustomClassName">UIResponder</string>
+ <string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="13.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="14.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="15.CustomClassName">SecondViewController</string>
+ <string key="15.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="16.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="18.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
+ <nil key="activeLocalization"/>
+ <dictionary class="NSMutableDictionary" key="localizations"/>
+ <nil key="sourceID"/>
+ <int key="maxID">26</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes"/>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <string key="IBCocoaTouchPluginVersion">933</string>
+ </data>
+</archive>
View
24 Show.h
@@ -0,0 +1,24 @@
+//
+// Show.h
+// UCLRadio
+//
+// Created by Frederic Jacobs on 23/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface Show : NSObject {
+ NSString *name;
+ NSString *dayOfTheWeek;
+ NSString *startTime;
+ NSString *endTime;
+}
+
+@property (nonatomic, copy) NSString *name;
+@property (nonatomic, copy) NSString *dayOfTheWeek;
+@property (nonatomic, copy) NSString *startTime;
+@property (nonatomic, copy) NSString *endTime;
+
+
+@end
View
18 Show.m
@@ -0,0 +1,18 @@
+//
+// Show.m
+// UCLRadio
+//
+// Created by Frederic Jacobs on 23/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "Show.h"
+
+@implementation Show
+
+@synthesize name, endTime, startTime,dayOfTheWeek;
+
+
+
+
+@end
View
70 ShowRSSParser.h
@@ -0,0 +1,70 @@
+// UCLRadio
+//
+// Created by Frederic Jacobs on 23/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef enum {
+ XMLParserTypeAbstract = -1,
+ XMLParserTypeNSXMLParser = 0,
+ XMLParserTypeLibXMLParser
+} XMLParserType;
+
+@class ShowRSSParser, Show;
+
+// Protocol for the parser to communicate with its delegate.
+@protocol ShowRSSParserDelegate <NSObject>
+
+@optional
+// Called by the parser when parsing is finished.
+- (void)parserDidEndParsingData:(ShowRSSParser *)parser;
+// Called by the parser in the case of an error.
+- (void)parser:(ShowRSSParser *)parser didFailWithError:(NSError *)error;
+// Called by the parser when one or more Shows have been parsed. This method may be called multiple times.
+- (void)parser:(ShowRSSParser *)parser didParseShows:(NSArray *)parsedShows;
+
+@end
+
+
+@interface ShowRSSParser : NSObject {
+ id <ShowRSSParserDelegate> delegate;
+ NSMutableArray *parsedShows;
+ // This time interval is used to measure the overall time the parser takes to download and parse XML.
+ NSTimeInterval startTimeReference;
+ NSTimeInterval downloadStartTimeReference;
+ double parseDuration;
+ double downloadDuration;
+ double totalDuration;
+}
+
+@property (nonatomic, assign) id <ShowRSSParserDelegate> delegate;
+@property (nonatomic, retain) NSMutableArray *parsedShows;
+@property NSTimeInterval startTimeReference;
+@property NSTimeInterval downloadStartTimeReference;
+@property double parseDuration;
+@property double downloadDuration;
+@property double totalDuration;
+
++ (NSString *)parserName;
++ (XMLParserType)parserType;
+
+- (void)start;
+
+// Subclasses must implement this method. It will be invoked on a secondary thread to keep the application responsive.
+// Although NSURLConnection is inherently asynchronous, the parsing can be quite CPU intensive on the device, so
+// the user interface can be kept responsive by moving that work off the main thread. This does create additional
+// complexity, as any code which interacts with the UI must then do so in a thread-safe manner.
+- (void)downloadAndParse:(NSURL *)url;
+
+// Subclasses should invoke these methods and let the superclass manage communication with the delegate.
+// Each of these methods must be invoked on the main thread.
+- (void)downloadStarted;
+- (void)downloadEnded;
+- (void)parseEnded;
+- (void)parsedShow:(Show *)Show;
+- (void)parseError:(NSError *)error;
+- (void)addToParseDuration:(NSNumber *)duration;
+
+@end
View
97 ShowRSSParser.m
@@ -0,0 +1,97 @@
+// UCLRadio
+//
+// Created by Frederic Jacobs on 23/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+
+
+#import "ShowRSSParser.h"
+#import "Show.h"
+
+static NSUInteger kCountForNotification = 10;
+
+@implementation ShowRSSParser
+
+@synthesize delegate, parsedShows, startTimeReference, downloadStartTimeReference, parseDuration, downloadDuration, totalDuration;
+
++ (NSString *)parserName {
+ NSAssert((self != [ShowRSSParser class]), @"Class method parserName not valid for abstract base class ShowRSSParser");
+ return @"Base Class";
+}
+
++ (XMLParserType)parserType {
+ NSAssert((self != [ShowRSSParser class]), @"Class method parserType not valid for abstract base class ShowRSSParser");
+ return XMLParserTypeAbstract;
+}
+
+- (void)start {
+ self.startTimeReference = [NSDate timeIntervalSinceReferenceDate];
+ [[NSURLCache sharedURLCache] removeAllCachedResponses];
+ self.parsedShows = [NSMutableArray array];
+ NSURL *url = [NSURL URLWithString:@"http://www.fredericjacobs.com/rss.xml"];
+ [NSThread detachNewThreadSelector:@selector(downloadAndParse:) toTarget:self withObject:url];
+}
+
+- (void)dealloc {
+ [parsedShows release];
+ [super dealloc];
+}
+
+- (void)downloadAndParse:(NSURL *)url {
+ NSAssert([self isMemberOfClass:[ShowRSSParser class]] == NO, @"Object is of abstract base class ShowRSSParser");
+}
+
+- (void)downloadStarted {
+ NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ self.downloadStartTimeReference = [NSDate timeIntervalSinceReferenceDate];
+ [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
+}
+
+- (void)downloadEnded {
+ NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - self.downloadStartTimeReference;
+ downloadDuration += duration;
+ [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
+}
+
+- (void)parseEnded {
+ NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didParseShows:)] && [parsedShows count] > 0) {
+ [self.delegate parser:self didParseShows:parsedShows];
+ }
+ [self.parsedShows removeAllObjects];
+ if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parserDidEndParsingData:)]) {
+ [self.delegate parserDidEndParsingData:self];
+ }
+ NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - self.startTimeReference;
+ totalDuration = duration;
+ NSLog(@"Completed");
+
+
+}
+
+- (void)parsedShow:(Show *)Show {
+ NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ [self.parsedShows addObject:Show];
+ if (self.parsedShows.count > kCountForNotification) {
+ if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didParseShows:)]) {
+ [self.delegate parser:self didParseShows:parsedShows];
+ }
+ [self.parsedShows removeAllObjects];
+ }
+}
+
+- (void)parseError:(NSError *)error {
+ NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didFailWithError:)]) {
+ [self.delegate parser:self didFailWithError:error];
+ }
+}
+
+- (void)addToParseDuration:(NSNumber *)duration {
+ NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
+ parseDuration += [duration doubleValue];
+}
+
+@end
View
BIN  Tweet.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Tweet@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  UCLMainScreen.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  UCLMainScreen@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
170 UCLRadio.xcodeproj/project.pbxproj
@@ -13,6 +13,34 @@
F4522451147E88BB000A94F5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F452244F147E88BB000A94F5 /* InfoPlist.strings */; };
F4522453147E88BB000A94F5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F4522452147E88BB000A94F5 /* main.m */; };
F4522457147E88BB000A94F5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F4522456147E88BB000A94F5 /* AppDelegate.m */; };
+ F4522464147E88F5000A94F5 /* AudioStreamer.m in Sources */ = {isa = PBXBuildFile; fileRef = F452245F147E88F5000A94F5 /* AudioStreamer.m */; };
+ F4522466147E88F5000A94F5 /* UCLRadioViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4522463147E88F5000A94F5 /* UCLRadioViewController.m */; };
+ F4522474147E8932000A94F5 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = F4522468147E8932000A94F5 /* Default.png */; };
+ F4522475147E8932000A94F5 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F4522469147E8932000A94F5 /* Default@2x.png */; };
+ F4522476147E8932000A94F5 /* playbutton.png in Resources */ = {isa = PBXBuildFile; fileRef = F452246A147E8932000A94F5 /* playbutton.png */; };
+ F4522477147E8932000A94F5 /* playTabBar.png in Resources */ = {isa = PBXBuildFile; fileRef = F452246B147E8932000A94F5 /* playTabBar.png */; };
+ F4522478147E8932000A94F5 /* playTabBar@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F452246C147E8932000A94F5 /* playTabBar@2x.png */; };
+ F4522479147E8932000A94F5 /* shows.png in Resources */ = {isa = PBXBuildFile; fileRef = F452246D147E8932000A94F5 /* shows.png */; };
+ F452247A147E8932000A94F5 /* shows@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F452246E147E8932000A94F5 /* shows@2x.png */; };
+ F452247B147E8932000A94F5 /* stopbutton.png in Resources */ = {isa = PBXBuildFile; fileRef = F452246F147E8932000A94F5 /* stopbutton.png */; };
+ F452247C147E8932000A94F5 /* Tweet.png in Resources */ = {isa = PBXBuildFile; fileRef = F4522470147E8932000A94F5 /* Tweet.png */; };
+ F452247D147E8932000A94F5 /* Tweet@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F4522471147E8932000A94F5 /* Tweet@2x.png */; };
+ F452247E147E8932000A94F5 /* UCLMainScreen.png in Resources */ = {isa = PBXBuildFile; fileRef = F4522472147E8932000A94F5 /* UCLMainScreen.png */; };
+ F452247F147E8932000A94F5 /* UCLMainScreen@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F4522473147E8932000A94F5 /* UCLMainScreen@2x.png */; };
+ F4522483147E895A000A94F5 /* CocoaXMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = F4522482147E895A000A94F5 /* CocoaXMLParser.m */; };
+ F4522489147E8983000A94F5 /* Show.m in Sources */ = {isa = PBXBuildFile; fileRef = F4522486147E8983000A94F5 /* Show.m */; };
+ F452248A147E8983000A94F5 /* ShowRSSParser.m in Sources */ = {isa = PBXBuildFile; fileRef = F4522488147E8983000A94F5 /* ShowRSSParser.m */; };
+ F452248F147E89BF000A94F5 /* SecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F452248D147E89BF000A94F5 /* SecondViewController.m */; };
+ F4522490147E89BF000A94F5 /* SecondViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F452248E147E89BF000A94F5 /* SecondViewController.xib */; };
+ F4522493147E8A11000A94F5 /* DetailController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4522492147E8A11000A94F5 /* DetailController.m */; };
+ F4522495147E8B46000A94F5 /* UCLRadioViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F4522494147E8B46000A94F5 /* UCLRadioViewController.xib */; };
+ F4522497147E8B59000A94F5 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = F4522496147E8B59000A94F5 /* MainWindow.xib */; };
+ F45224B6147E90CA000A94F5 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F45224B3147E90A9000A94F5 /* CFNetwork.framework */; };
+ F45224B7147E90CA000A94F5 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F45224B1147E909D000A94F5 /* QuartzCore.framework */; };
+ F45224BA147E90DF000A94F5 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F45224B8147E90D6000A94F5 /* MediaPlayer.framework */; };
+ F45224CB147E9329000A94F5 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F45224C9147E927E000A94F5 /* AudioToolbox.framework */; };
+ F45224CC147E936D000A94F5 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F45224A5147E8F75000A94F5 /* AVFoundation.framework */; };
+ F45224CF147E9386000A94F5 /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F45224CD147E937C000A94F5 /* Twitter.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -20,12 +48,47 @@
F4522446147E88BB000A94F5 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
F4522448147E88BB000A94F5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
F452244A147E88BB000A94F5 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
- F452244E147E88BB000A94F5 /* UCLRadio-Info.plist */ = {isa = PBXFileReference; path = "UCLRadio-Info.plist"; sourceTree = "<group>"; };
+ F452244E147E88BB000A94F5 /* UCLRadio-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UCLRadio-Info.plist"; sourceTree = "<group>"; };
F4522450147E88BB000A94F5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
F4522452147E88BB000A94F5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
- F4522454147E88BB000A94F5 /* UCLRadio-Prefix.pch */ = {isa = PBXFileReference; path = "UCLRadio-Prefix.pch"; sourceTree = "<group>"; };
- F4522455147E88BB000A94F5 /* AppDelegate.h */ = {isa = PBXFileReference; path = AppDelegate.h; sourceTree = "<group>"; };
+ F4522454147E88BB000A94F5 /* UCLRadio-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UCLRadio-Prefix.pch"; sourceTree = "<group>"; };
+ F4522455147E88BB000A94F5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
F4522456147E88BB000A94F5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ F452245E147E88F5000A94F5 /* AudioStreamer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioStreamer.h; sourceTree = "<group>"; };
+ F452245F147E88F5000A94F5 /* AudioStreamer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioStreamer.m; sourceTree = "<group>"; };
+ F4522462147E88F5000A94F5 /* UCLRadioViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UCLRadioViewController.h; sourceTree = "<group>"; };
+ F4522463147E88F5000A94F5 /* UCLRadioViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UCLRadioViewController.m; sourceTree = "<group>"; };
+ F4522468147E8932000A94F5 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
+ F4522469147E8932000A94F5 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = "<group>"; };
+ F452246A147E8932000A94F5 /* playbutton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = playbutton.png; sourceTree = "<group>"; };
+ F452246B147E8932000A94F5 /* playTabBar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = playTabBar.png; sourceTree = "<group>"; };
+ F452246C147E8932000A94F5 /* playTabBar@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "playTabBar@2x.png"; sourceTree = "<group>"; };
+ F452246D147E8932000A94F5 /* shows.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = shows.png; sourceTree = "<group>"; };
+ F452246E147E8932000A94F5 /* shows@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shows@2x.png"; sourceTree = "<group>"; };
+ F452246F147E8932000A94F5 /* stopbutton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = stopbutton.png; sourceTree = "<group>"; };
+ F4522470147E8932000A94F5 /* Tweet.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Tweet.png; sourceTree = "<group>"; };
+ F4522471147E8932000A94F5 /* Tweet@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Tweet@2x.png"; sourceTree = "<group>"; };
+ F4522472147E8932000A94F5 /* UCLMainScreen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UCLMainScreen.png; sourceTree = "<group>"; };
+ F4522473147E8932000A94F5 /* UCLMainScreen@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "UCLMainScreen@2x.png"; sourceTree = "<group>"; };
+ F4522481147E895A000A94F5 /* CocoaXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocoaXMLParser.h; sourceTree = "<group>"; };
+ F4522482147E895A000A94F5 /* CocoaXMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocoaXMLParser.m; sourceTree = "<group>"; };
+ F4522485147E8983000A94F5 /* Show.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Show.h; sourceTree = "<group>"; };
+ F4522486147E8983000A94F5 /* Show.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Show.m; sourceTree = "<group>"; };
+ F4522487147E8983000A94F5 /* ShowRSSParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowRSSParser.h; sourceTree = "<group>"; };
+ F4522488147E8983000A94F5 /* ShowRSSParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShowRSSParser.m; sourceTree = "<group>"; };
+ F452248C147E89BF000A94F5 /* SecondViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecondViewController.h; sourceTree = "<group>"; };
+ F452248D147E89BF000A94F5 /* SecondViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecondViewController.m; sourceTree = "<group>"; };
+ F452248E147E89BF000A94F5 /* SecondViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SecondViewController.xib; sourceTree = "<group>"; };
+ F4522491147E8A11000A94F5 /* DetailController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetailController.h; sourceTree = "<group>"; };
+ F4522492147E8A11000A94F5 /* DetailController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DetailController.m; sourceTree = "<group>"; };
+ F4522494147E8B46000A94F5 /* UCLRadioViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = UCLRadioViewController.xib; path = UCLRadio/UCLRadioViewController.xib; sourceTree = "<group>"; };
+ F4522496147E8B59000A94F5 /* MainWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = "<group>"; };
+ F45224A5147E8F75000A94F5 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
+ F45224B1147E909D000A94F5 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+ F45224B3147E90A9000A94F5 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
+ F45224B8147E90D6000A94F5 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
+ F45224C9147E927E000A94F5 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
+ F45224CD147E937C000A94F5 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -33,9 +96,15 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- F4522447147E88BB000A94F5 /* UIKit.framework in Frameworks */,
+ F45224CF147E9386000A94F5 /* Twitter.framework in Frameworks */,
+ F45224CC147E936D000A94F5 /* AVFoundation.framework in Frameworks */,
F4522449147E88BB000A94F5 /* Foundation.framework in Frameworks */,
+ F4522447147E88BB000A94F5 /* UIKit.framework in Frameworks */,
F452244B147E88BB000A94F5 /* CoreGraphics.framework in Frameworks */,
+ F45224B7147E90CA000A94F5 /* QuartzCore.framework in Frameworks */,
+ F45224B6147E90CA000A94F5 /* CFNetwork.framework in Frameworks */,
+ F45224CB147E9329000A94F5 /* AudioToolbox.framework in Frameworks */,
+ F45224BA147E90DF000A94F5 /* MediaPlayer.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -45,6 +114,10 @@