2

I started Objective-C programming a couple weeks ago, so my understanding of how all these pieces fit together & the order they're all happening is still confusing to me. I'm trying to make a JSON API call to one of my apps using NSURLSession. That all works flawlessly, but I want to update a label with a piece of the data that's returned & anytime I look at/try to update the label, I get null.

Some of the SO posts I've found that are similar to my problem include: this, this, this, and this. Coming from the world of Ruby on Rails, I haven't had to deal with async concepts at all, but I know I'm close.

Here's the relevant snippet of code in question:

        if (!jsonError) {
            NSDictionary *skillBuildData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

            dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"%@:", skillBuildNameLabel.text);  // should say "Build Name" but returns null
                    NSLog(@"%@", skillBuildData[@"name"]);    // correctly prints the result
                    NSLog(@"%@:", skillBuildNameLabel.text);  // should have contents of skillBuildData[@"name"] but returns null
                    skillBuildNameLabel.text = skillBuildData[@"name"]; // obviously results in null, but I don't know why.
            });
        }

EDIT:

Not sure if it's relevant, but here's the bulk of my ViewController.h to give you an idea of the outlets & actions in this very simple app. One button, one method, the IBOutlet that links the button & JSON call method, and a label:

@interface ViewController : UIViewController {
    IBOutlet UILabel *skillBuildNameLabel;
    IBOutlet UIButton *getSkillBuildDataButton;
}

- (void)getSkillBuildDataById:(int) skillBuildId;

- (IBAction)buttonPressed;

It seems like I'm very close, I just can't see the link I'm missing. Thank you so much in advance!

EDIT 2:

Check out Ben Kreeger's comment to the response I marked as the answer. I didn't connect the actual label in my storyboard to the outlet I created in my ViewController.h. I had no idea you could drag the line from the element in a storyboard to an actual line of code. That was the missing piece. Looks like I have a lot more to learn about Xcode & Objective-C. Thanks to all who helped me out!

Community
  • 1
  • 1
Kyle Carlson
  • 7,967
  • 5
  • 35
  • 43
  • 1
    Your code snippets are too narrowly-scoped to tell quite what's going on. Your JSON data callback snippet: where is it located, in `ViewController.m`? What happens when you `NSLog(@"%@", skillBuildNameLabel);`? You're doing a good thing by popping into the main thread to make those changes, but I still can't get a good enough idea of what's going on in that first snippet. – Ben Kreeger Jan 06 '14 at 03:13
  • @BenKreeger: It's in a deeply nested set of error checks in the `completionHandler` (callback?) of the method that makes the API call originally. I'm just not sure why the code can't seem to speak with the label. – Kyle Carlson Jan 06 '14 at 06:22
  • 1
    What happens when you `NSLog(@"%@", skillBuildNameLabel);` inside your callback? – Ben Kreeger Jan 06 '14 at 12:54

2 Answers2

2

You may have more luck declaring your IBOutlets as properties (@property) instead of as instance variables (see this answer for why weak instead of strong).

@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UILabel *skillBuildNameLabel;
@property (weak, nonatomic) IBOutlet UIButton *getSkillBuildDataButton;

...

@end

Then you'll be able to reference them as self.skillBuildNameLabel and self.getSkillBuildDataButton in your implementation.

Beware that this self.x notation inside of a callback like that may lead to what's called a retain cycle. If this is the case, Xcode will warn you about this. Here's a bit on retain cycles for you.

Footnote: I rarely ever see (and never write) this syntax anymore for declaring instance variables.

@interface ViewController : UIViewController {
    IBOutlet UILabel *skillBuildNameLabel;
    IBOutlet UIButton *getSkillBuildDataButton;
}

Use properties instead.

Community
  • 1
  • 1
Ben Kreeger
  • 6,355
  • 2
  • 38
  • 53
  • Thanks for the links & suggestions as properties rather than outlets. I suppose it makes some sense to do that, but I just assumed that was given by default since the label belonged to the ViewController (now as properties as per your suggestion). I tried logging `self.skillBuildNameLabel` in my callback, above it, and below it, and they all returned `null`. Do I have to actually name the label somewhere like I did back in my VB days? How does the code know that label in my storyboard IS the label I want to update? – Kyle Carlson Jan 06 '14 at 16:49
  • 1
    You'll have to [connect the outlet using Interface Builder](http://stackoverflow.com/a/19197517/194869); it's a ctrl-drag from your `UILabel` in your storyboard into the proper line of code. It's best performed when the Assistant Editor is open. Instead of creating a new outlet, you can drag the ctrl-drag line right onto the outlet you've already created. – Ben Kreeger Jan 06 '14 at 17:09
  • 1
    SUCCESS!!! I had no idea you could drag a UI element to a line of code, nor did I know you had to. I knew there was clearly a link missing, it just seemed weird that the ViewController didn't know about the label. Thanks so much. Marking your answer correct. – Kyle Carlson Jan 06 '14 at 17:30
1

You are doing the logging before setting the text. move

skillBuildNameLabel.text = skillBuildData[@"name"];

to the top of the async block, above the NSLog statements.

Nick
  • 2,361
  • 16
  • 27
  • Thanks, but that didn't change anything. All the NSLog commands that related to my label are still null even though I've supplied my label with default text. I still would have thought `skillBuildLabel.text' would have showed its default `Build Name` text. – Kyle Carlson Jan 05 '14 at 20:16
  • Did you initialize the UILabel? ie. is skillBuildNameLabel = nil when that block is executed? – Nick Jan 06 '14 at 15:30
  • I dropped it on my storyboard & created an outlet for it, but am I supposed to initialize something else? I remember back in my Visual Basic days you just named the label/UI element when you put it on the page. Couldn't see anywhere to do that in Xcode. – Kyle Carlson Jan 06 '14 at 16:29
  • I would put a breakpoint there and confirm the label is not nil. Typically, when allowing storyboard to generate the code for you, the label's declaration should look like what you see in Ben Kreeger's answer. If it is hooked up wrong, you can re-create the label in storyboard, or add it programatically. – Nick Jan 06 '14 at 16:45
  • I've brokepointed (yes, just made that up) and `NSLogged` inside, outside, above & below my callback and the label itself always returned null. I just don't understand why the `ViewController` can't see the label. Do I have to connect it to something from my storyboard? – Kyle Carlson Jan 06 '14 at 16:53
  • You should post your ViewController's complete .m file as well as the code that presents that view controller. – Nick Jan 06 '14 at 16:58