1

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?

Emil
  • 7,220
  • 17
  • 76
  • 135
  • 1
    To answer your other question, look at class_dump. – Richard J. Ross III Jul 27 '12 at 20:52
  • 2
    So you want to listen to a variable outside of your control (in the system's, i.e. Apple's compiled code), not something which source code you have access? I doubt NSNotification will help you, unless there _is_ some private, system wide notification that the OS is already broadcasting. I wonder if KVO is any good in this case? (Total KVO noob here) – Nicolas Miari Jul 27 '12 at 21:04
  • Until you mentioned the specifics, it sounded like you were trying to crack somebody else's binary ;) – Nicolas Miari Jul 27 '12 at 21:05
  • key-value observing is what you want, no notification center hacking. –  Jul 27 '12 at 21:13
  • @ranReloaded, he's trying to listen for changes in WiFi signal strength. He knows that such information is obviously available in the status bar, since it displays it. Thus, he's trying to hook a listener to the SpringBoard (Apple's binary) class that has that data. You're right, though. KVO is useless here, too. – Nate Jul 28 '12 at 09:30

5 Answers5

3

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

  • 1
    Look for a KVO wrapper like http://www.mikeash.com/pyblog/friday-qa-2012-03-02-key-value-observing-done-right-take-2.html to make it more approachable... – Kendall Helmstetter Gelner Jul 27 '12 at 21:58
  • @H2CO3 This looks like what I need, but I'm not sure how to implement it. If the class is SBStatusBarDataManager, and I want to read the int gsmSignalStrengthRaw (which is in a struct), how would I go about that? I've been reading through the page, it's just a bit daunting. – LibertasMens Jul 28 '12 at 01:58
  • 1
    The problem with this (also with the notifications mentioned in the other answers) is that you can only observe the value if the class lets you -- not everything is KVO-compliant. @LibertasMens – jscs Jul 28 '12 at 07:42
  • @LibertasMens are you having trouble getting its class object? Use objc_getClass() then. –  Jul 28 '12 at 08:14
  • As @JoshCaswell says, not everything is KVO-compliant. If you look at that data in [SBStatusBarDataManager.h](https://github.com/peterhajas/SpringBoard-4.x-Headers/blob/master/SBStatusBarDataManager.h), you'll see that Apple just coded it as a plain nested struct. So while this is nice to know, it doesn't solve his **specific** problem. – Nate Jul 28 '12 at 09:27
  • @JoshCaswell I can use [Logos](http://iphonedevwiki.net/index.php/Logos) to hook into the class. – LibertasMens Jul 28 '12 at 20:35
  • @Nate If I used the hook that I mentioned and get access to the _class_, does that give me access to data in the _struct_? – LibertasMens Jul 28 '12 at 20:35
  • @LibertasMens, you would need an *instance* of that class (specifically the shared instance used in SpringBoard), but **yes** ... that would give you access. See the answer I posted with code. – Nate Jul 29 '12 at 09:02
  • A downvote again. Please ***stop serial downvoting me whoever does it!*** –  Jul 29 '12 at 21:07
2

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

Community
  • 1
  • 1
Nate
  • 31,017
  • 13
  • 83
  • 207
  • Man, I really got in over my head on this one. haha Not sure why I expected using a totally new language would be easier... So using the code that John Calsbeek provided, is that a generic function that I can copy, or do I put some of my data into it? It doesn't seem to like Ivar. `'Ivar' was not declared in this scope` EDIT: My apologies that I'm so helpless on this. – LibertasMens Jul 29 '12 at 20:14
  • @LibertasMens, No problem, but **yes**, you are now well into advanced topics in Objective-C. This is something that's not particularly well-understood, as evidenced by the fact that, quite frankly, all the other answers are just wrong (or they didn't bother to notice that the class whose data you want access to is not under your control). I recommend using my version of `instanceVariableForObject:andKey:`, above, rather than the original version from John Calsbeek. His version is only useful for getting at ivars (aka "member variables") within `self` (aka "this" in other languages). – Nate Jul 29 '12 at 20:38
  • The other benefit of using my code, above, is that I point out that you have to `#import `. That's probably why your compiler doesn't find the `Ivar` type. – Nate Jul 29 '12 at 20:41
  • I'm getting two declaration problems, one being `'SBStatusBarDataManager' does not name a type` and the other `'self' was not declared in this scope`. [Here](http://pastebin.com/wv1gbiz2) is my current code, would you mind taking a look? I probably just misunderstood something. – LibertasMens Jul 29 '12 at 21:31
  • @LibertasMens, are you familiar with the C language? You need to `#include` headers for the types inside them to be visible. In Objective-C, you also have `#import`, which is a similar construct. In any case, you would need to `#import "SBStatusBarDataManager.h"` for the compiler to be able to know about `SBStatusBarDataManager`. You also didn't take my advice about using my version of `instanceVariableForObject:andKey:`. How new are you to Objective-C? It might be worthwhile to build a few hello world programs first. You're trying to jump right into very advanced hacking ... – Nate Jul 29 '12 at 21:37
  • Sorry, it looks like you have **both** versions of the instanceVariable* method in your code. In any case, you have orphan code that appears to exist outside the scope of any **class** or **method**. That's going to cause you problems with the `self` reference. Again, it looks like you need to build some simple programs with Objective-C first. – Nate Jul 29 '12 at 21:40
  • Ah, can't believe I missed that. I had actually put the the `#import SBStatusBarDataManager.h" into the code _after_ I put it in pastebin. Sorry for the mixup. Apparently the wrong SDK installed and couldn't find the file, which is why I didn't import it. Silly mistake on my part. – LibertasMens Jul 29 '12 at 23:52
1

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?

Community
  • 1
  • 1
Glavid
  • 1,071
  • 9
  • 19
  • Not directly applicable to this problem (but good general information). He's trying to listen for changes in data in an iOS (SpringBoard) class that he doesn't control. – Nate Jul 28 '12 at 09:21
1

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"); 
 }
random
  • 8,568
  • 12
  • 50
  • 85
  • 1
    Not directly applicable to this problem (but good general information). He's trying to listen for changes in data in an iOS (SpringBoard) class that he doesn't control. – Nate Jul 28 '12 at 09:21
0

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)

Community
  • 1
  • 1
Vincent Mimoun-Prat
  • 28,208
  • 16
  • 81
  • 124
  • 1
    Not directly applicable to this problem (but good general information). He's trying to listen for changes in data in an iOS (SpringBoard) class that he doesn't control and was not coded to be KVO compliant. – Nate Jul 28 '12 at 09:20