LibertasMens
LibertasMens

Reputation: 31

Listening to a variable in a different file?

As I said, brand new to Objective-C. I have a few years of experience in programming, though, and I'm a quick learner.

General Issue: I want to listen to a variable inside of a different file, then perform a certain action in response to changes to that variable.

Specific Issue: I want to modify the way that the iPhone displays WiFi signal. Currently it has 4 states, but I want to make it have 8 states. My hope is to somehow listen to (or periodically read from) the variable wifiSignalStrengthRaw in SBStatusBarDataManager.h and then display it.

Question: What's the best way to go about reading, or listening to, that variable?

EDIT: Secondary Question: Is there a way to analyze code already compiled in Obj-C?

Upvotes: 1

Views: 1233

Answers (5)

Nate
Nate

Reputation: 31045

Key Value Observing is a great feature, but it doesn't help you (directly) with this particular problem. For those not familiar with the class he's referring to, it's a private class within the iOS SpringBoard itself. So, he doesn't have control over how it publishes the data he's interested in.

You can't observe just any old data. It has to be coded to be Key Value Observing (KVO) compliant. If you look at the SBStatusBarDataManager.h (this is the iOS 4 version ... generate the one you need with class-dump) ... you'll see that it's not coded that way :(

But, you could use some dynamic Objective-C runtime feature to get at the data anyway. See here on getting at private/protected instance variables directly.

Then, just locally declare a struct to match what's in the springboard header, and do this:

// this was coded to match the iOS 5.0 header, but of course, this may
// change with each iOS version
typedef struct {
    char itemIsEnabled[23];
    char timeString[64];
    int gsmSignalStrengthRaw;
    int gsmSignalStrengthBars;
    char serviceString[100];
    char serviceCrossfadeString[100];
    char serviceImages[3][100];
    char operatorDirectory[1024];
    unsigned int serviceContentType;
    int wifiSignalStrengthRaw;
    int wifiSignalStrengthBars;
    unsigned int dataNetworkType;
    int batteryCapacity;
    unsigned int batteryState;
    char notChargingString[150];
    int bluetoothBatteryCapacity;
    int thermalColor;
    unsigned int thermalSunlightMode:1;
    unsigned int slowActivity:1;
    unsigned int syncActivity:1;
    char activityDisplayId[256];
    unsigned int bluetoothConnected:1;
    unsigned int displayRawGSMSignal:1;
    unsigned int displayRawWifiSignal:1;
} SbStatusBarDataType;

a helper to retrieve ivars by name:

#import <objc/runtime.h>

- (void *) instanceVariableForObject: (id)obj andKey: (NSString *)key {
    if (key != nil) {
        Ivar ivar = object_getInstanceVariable(obj, [key UTF8String], NULL);
        if (ivar) {
            return (void *)((char *)obj + ivar_getOffset(ivar));
        }
    }
    return NULL;
}

and finally, get the data like so:

// get an instance to the data manager this way, or however you're 
//  doing it via Mobile Substrate
SBStatusBarDataManager* mgr = [SBStatusBarDataManager sharedDataManager];
SbStatusBarDataType data = *(SbStatusBarDataType*)[self instanceVariableForObject: mgr andKey: @"_data"];
int signalStrength = data.wifiSignalStrengthRaw;

You could then just repeatedly query this data, at some interval that you consider fast enough.

Otherwise, try looking at the methods in SBStatusBarDataManager.h. It looks like some of them might conceivably be called at the precise time that signal strength changes. If you hook those methods, you might be able to push a notification that the data has changed, so that you don't have to constantly poll for the data.

For example:

- (void)_dataChanged;
- (void)_updateSignalStrengthItem;
- (void)_signalStrengthChange;

all look like good candidates for hooking, if you're trying to determine when there's been a change to WiFi signal strength. But, I have no experience with those, and it'll be trial-and-error on your part. Good luck!

A couple references pertaining to your Secondary question:

class-dump

Apple docs on Obj-C runtime APIs

Upvotes: 2

user529758
user529758

Reputation:

What you really want is Key-Value Observing. No need to hack around with NSNotificationcenter, etc...

Upvotes: 3

random
random

Reputation: 8608

Check out NSNotificationCenter. This will let you add listeners to variables, when the variable changes it post to notification center. You can then "hear" those changes in any class.

Apple doc description of NotificationCenter:

An NSNotificationCenter object (or simply, notification center) provides a mechanism for broadcasting information within a program. An NSNotificationCenter object is essentially a notification dispatch table.

Quick example:

Add a listener

 [[NSNotificationCenter defaultCenter] addObserver:self 
                                          selector:@selector(showMainMenu:) 
                                              name:@"loginComplete" object:nil];

Post a notification to loginComplete

[[NSNotificationCenter defaultCenter] postNotificationName:@"loginComplete" object:nil];

Hear the notification.

- (void)showMainMenu:(NSNotification *)note {
     NSLog(@"Received Notification - Someone seems to have logged in"); 
 }

Upvotes: 1

Glavid
Glavid

Reputation: 1081

Use a NSNotificationCenter to send values to/from different classes. Here is a good example of using the NSNotifications Send and receive messages through NSNotificationCenter in Objective-C?

Upvotes: 1

Vincent Mimoun-Prat
Vincent Mimoun-Prat

Reputation: 28541

If you are the one making the changes to the variable you want to listen, you should use the observer pattern.

Here is a tutorial to get you started: Patterns in Objective-C: Observer Pattern

And also have a look at that question on SO: What's the nicest way to do observer/observable in objective-c (iphone version)

Upvotes: 0

Related Questions