Joe Conway - Big Nerd Ranch Tue, 19 Oct 2021 17:46:56 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 iOS Auto Layout: Fun Facts and Tips https://bignerdranch.com/blog/ios-auto-layout-fun-facts-and-tips/ https://bignerdranch.com/blog/ios-auto-layout-fun-facts-and-tips/#respond Sun, 10 Mar 2013 22:14:51 +0000 https://nerdranchighq.wpengine.com/blog/ios-auto-layout-fun-facts-and-tips/

Editor’s note: This post originally appeared on the the blog of Joe Conway, co-author of iOS Programming:The Big Nerd Ranch Guide.

The post iOS Auto Layout: Fun Facts and Tips appeared first on Big Nerd Ranch.

]]>

Editor’s note: This post originally appeared on the the blog of Joe Conway, co-author of iOS Programming:The Big Nerd Ranch Guide.

Have you ever stared at the tracking information for your new MacBook, hitting refresh every five minutes? It is infuriating. But the fact that a MacBook can come off the assembly line somewhere in China and end up at my doorstep a few days later is a serious work of mathematical art.

Consider all of the issues in optimizing the pickup and delivery of your next MacBook: a company like FedEx has to factor in the number of trucks they have, the amount of goods that can fit on those trucks, the cost of gas, the distance from their nearest distribution center to your doorstep and so on. If they were to just shoot from the hip and deliver things in whatever order their manager thinks is best, they would run out of money or your shiny laptop wouldn’t show up. Instead, they have to rely on a field of math called linear programming to determine the way they operate.

Linear programming (which has nothing to do at all with programming in the way we think of it, but instead, is a synonym for linear optimization for math folks) is used to solve equations for which there are a lot of variables that depend on each other. FedEx needs to minimize some of these variables (like the amount they spend on gas) and maximize some others (like how many items can be delivered in a day).

Because there are so many variables involved and each of their ranges is so vast, a human being couldn’t find the optimum value for each of the variables to minimize their costs and maximize their production, but a linear objective function can. A human being can set up some constraints for each variable, like a truck can travel up to 200 miles in a day and gas costs $3.50 today. Each of these constraints is a simple linear equation, like travel < = 200.0 and gas = 3.50. A linear objective function is a combination of all of these equations solved at once that finds the optimum value for each unsolved variable.

What does this have to do with autolayout? The autolayout system is one big linear objective function. Each NSLayoutConstraint you create is just one linear equation in a linear objective function. For example, one constraint could be view.left = anotherView.right + 10 and another could be view.width >= 20.

When a view is being laid out, the linear objective function is evaluated to determine a minimum and maximum value for each part of a view’s frame. A valid set of constraints for autolayout is when every variable has a maximum equal to its minimum; that is, there is only one solution to satisfy every constraint in a view hierarchy. (If there is more than one solution to the function, you have an ambiguous layout, if there isn’t a solution, you have conflicting constraints and you get an exception.)

Because of the way linear objective functions are evaluated, there isn’t an order that the constraints are computed in. So if that was how you were approaching constraints, get that out of your head: an equation like view.top = anotherView.bottom + 10 doesn’t mean that you find the value of anotherView’s bottom first and add 10 to get view’s top. It is just one more equation that has to be satisfied for your layout to work; both of these variables can change depending on the other constraints.

Of course, the math behind all of this is really complicated. The implementation of autolayout is less complicated, but let’s not pretend it is easy at first. So, here is what I recommend:

Use autolayout only when you need to

Autoresizing masks aren’t deprecated or obsolete. Just like Core Data didn’t make archiving obsolete nor did blocks make delegation useless, you will still use autoresizing masks. Many iOS applications—while they may have pretty artwork—are pretty simple, layout-wise. You have a table, it has some cells, there is some stuff in the cells, there are some buttons above it and maybe a duck. Well, probably not a duck, but do you really need autolayout to keep the table view on the bottom of the screen and the buttons as a header on top? Does a cell with two text fields need autolayout to keep them aligned with each other? Will rotating the device, putting into a navigation controller or running it on an iPhone 5 really change the layout of this view? No. So don’t overkill it with autolayout.

The keyboard shortcut you need to know about

Manipulating the layout of a user interface in an XIB is, let’s face it, a cruel joke. Once a view is dropped into an XIB, you get all kinds of constraints that make sense for the position of that view where you dropped it—but not where you eventually want it. On top of that, Xcode enforces the constraints that apply to your views when manipulating them in the XIB—got a label that is pinned to match the size of the button below it? Xcode may not let you drag the resizing controls for that label.

Unless you hold the command key. The most useful but under-documented feature of autolayout is holding the command key while manipulating views in an XIB file. While holding the command key, Xcode lets you break any constraint applied to a view while resizing it.

Stop thinking about frames

When laying out an interface, we typically think about the frame of a view. Stop. The frame of a view is an absolute position and size. That doesn’t make sense when a view should lay itself out relative to the rest of the screen. Want to think about the size of a view? Think about its intrinsic content size. Want to think about the position of a view? Think about its offset from other views in the interface. A button doesn’t have a frame of {20, 40, 200, 40}, no, it is 10 points below a label, centered on the Y axis of its superview and wants to fit its title comfortably.

Change constants, not constraints

One of the more useful (and difficult) features of autolayout is the ability to make dynamic user interfaces. If your model changes and a new button needs to move into place, don’t delete the current constraint and create a new one. Finding constraints at runtime is not straightforward, but modifying the constant of a constraint is very simple. If a button needs to slide in from the right side of the screen, create a constraint where initially button.left = superview.left + 400. When it comes time to slide it in, change the constant so that the equation is button.left = superview.left + 200. As an added bonus, animation comes for free:

    [[self buttonEdgeConstraint] setConstant:200];
    [UIView animateWithDuration:0.5 animations:^{
    [[self button] layoutIfNeeded];
    }];

Understand the limitations and bugs

Right now, setting up constraints in an XIB file for a UITableViewCell doesn’t do what you expect it to do: each constraint thinks that the superview is the instance of UITableViewCell, not the cell’s contentView. This means that you will never get what you expect. Instead, turn off autolayout for a UITableViewCell XIB and either set up constraints programmatically or use autoresizing masks.

When modifying an XIB, Xcode won’t let you set up constraints that are invalid (for the most part). That means if you have a constraint you don’t want, you may have to add another constraint before you can delete it.

The thickness of the constraint line in the XIB is your key: a thin blue line is a required constraint, whereas the thicker blue line is a user constraint. Required constraints can’t be deleted, otherwise the layout would become invalid. When a set of constraints over-qualifies a view’s frame, the constraints that aren’t necessary automatically become user constraints that can be deleted. For example, if you plop a text field onto the screen, it may set up a left and right edge constraint. If you don’t want the right edge constraint, pin the width of the text field—this makes the right edge and width constraints redundant. Then you can delete the right edge constraint and the width constraint will become required.

The post iOS Auto Layout: Fun Facts and Tips appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/ios-auto-layout-fun-facts-and-tips/feed/ 0
Xcode Templates https://bignerdranch.com/blog/xcode-templates/ https://bignerdranch.com/blog/xcode-templates/#respond Thu, 13 Jan 2011 07:44:30 +0000 https://nerdranchighq.wpengine.com/blog/xcode-templates/

Update: It appears Apple has done a lot of these things, so thanks!

The post Xcode Templates appeared first on Big Nerd Ranch.

]]>

Update: It appears Apple has done a lot of these things, so thanks!

If you follow me on Twitter, you’ve probably seen my disapproval of the templates that ship with Xcode. Disapproval is a nice way of saying, “The templates are really bad.” So, I have to ask for a favor from everyone. Please submit a bug report to Apple and ask them to change their approach on templates. You can link to this article, copy and paste its contents, or write your own thoughts.

Now, before I point out the bad, I’d like to give my opinion on what a good template should do:

  1. A template should minimize keystrokes by adding code you always need and never code you will usually delete.

  2. A template can provide simple boiler-plate code for beginning programmers so they can add substance to existing form, without sacrificing rule 1.

  3. A template should not make any design or stylistic choices for developers.

I think we can all agree on the first two rules. The third rule is subjective: what is the best design? What is the best style? There isn’t a right answer. Therefore, a template should omit any code that potentially interferes with rule 3. When it doubt, leave it out. Here are two of the worst offenders of these rules:

1. Creating a universal window-based application gives you an abstract superclass for the app delegate, two concrete subclasses of that superclass and two XIB files.

This violates rule 3. There is an alternative design for universal applications that only requires a single application delegate and a single XIB file.

To be honest, this template is very bad. You do not need two MainWindow XIB files. You can have a single XIB file with a window and an app delegate. Check the box that says “Full Screen at Launch.” With this flag set, no matter what device you are on, the window is the right size. You don’t need three app delegates either, here’s an example of a single app delegate that works on both devices:
`

- (BOOL)application:(UIApplication *)app
  didFinishLaunchingWithOptions:(NSDictionary *)opts
{
    UIViewController *rootMaster = ...;
    if([[UIDevice currentDevice] userInterfaceIdiom] ==
              UIUserInterfaceIdiomPad)
    {
        UISplitViewController *svc = ...;
        ...
        [window setRootViewController:svc];
    }
    else
    {
        UINavigationController *nvc = ...;
        ...
        [window setRootViewController:nvc];
    }
    [window makeKeyAndVisible];
    return YES;
}

`

Not only do you not need the multiple app delegate/XIB monster, I would argue you don’t want it either. Why? Where you once had 3 files to maintain (AppDelegate.h, AppDelegate.m, MainWindow.xib), you now have eight. Instead of one class to write your code in, you have three. Adding complexity like this when you can make a very simple if statement in places where the behavior differs seems like a better choice, right?

2. The UITableViewController has code completion tags in it.

This breaks rule 1. Why? When I create a table view controller subclass, there are plenty of things I need to do before I write the data source methods. In other words, I need to write out the model objects that will be presented by the view object.

The code completion placeholders prevent building the application from being built while doing this. Thus, I must replace those placeholders with “return 0;” right away so I can check my work on those model objects as I write. This is extra work that could easily be avoided by just putting a return 0 in there in the first place.

Now, for rule 2: the errors generated by not replacing the completion handlers are not easy for a beginner to figure out. This hurts them more than helps: they have to type code there anyway, let them figure it out without introducing more issues to fix.

These are the two huge ones. There are other smaller ones that I will just list. (Some of these were introduced in [redacted], so my pretext is, “If you’re thinking about or have already done this, don’t do it…”)

  1. Don’t put a label that says “My Universal View Controller” on the window in MainWindow.xib. That doesn’t even deserve my rationale.

  2. Ivars are by default protected. Placing a @private at the top of every interface ivar block effectively makes the default private. Developers should opt-in to non-default behavior, not have to opt-out.

  3. The app delegate doesn’t need to expose the window as a property; there are other ways of getting the window. I find that using the app delegate as an object dispenser to be very poor design – if someone chooses another design, they can add the property on top of an existing ivar.

  4. The code doesn’t need to be chalked with comments. Experienced developers just delete it. For beginners, Apple has a fantastic documentation viewer and great content. Beginners need to learn to use that documentation instead of relying on commented cookie cutter code, or else they will never be able to expand their knowledge.

  5. If you’re going to add applicationWillTerminate: as a stub to the app delegate, applicationDidEnterBackground: better be in there, too.

  6. Every method that could potentially be overridden or implemented does not need to be stubbed in the templates. viewWillAppear:, viewDidAppear:, the rest of the app delegate methods, etc. We know how to write code, we’ll implement those if we need them. Put the necessary stuff in there.

Overall, I wish they would keep it simple. If a developer wants to customize their own templates, then there should be tools for doing that. (In Xcode 3, this is almost simple. In [redacted] 4, it is very difficult. I spent two hours trying to make you guys some better templates; it did not go well. However, the form of those templates leaves me hopeful that there may be a tool in the future…)

This may seem like a bit much for something so small. And you’re right, it is a small amount of time each developer spends correcting the issues. But it still is an amount of time that could be used more productively.

However, I have an ulterior motive. The templates have caused lots of issues for our students and readers. So much so that the next edition of the book will most likely not use anything but the NSObject template. This is either a lot of extra typing or a lot of explanation to the reader for something that they shouldn’t really worry about. (Imagine if, everytime we told you to create a new class, we had to preface it with a 1/2 page of text showing where to delete code from.)

The templates have become cumbersome instead of helpful. Let’s fix that. Here is my compromise: give us some very simple templates that follow these rules in one category of the left hand table, and some “learning” or highly bloated templates in another category in the left hand table. Everyone is happy!

(You can submit a bug report by going here, logging in with your Apple ID and selecting New Problem. Then, fill out the Problem Report Title to be Xcode Templates. Product: Developers Tools. Version/Build Number: 4. Classification: Enhancement. In the details, just fill out the Summary: with a link to this blog post and a quick summary of the issue.)

The post Xcode Templates appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/xcode-templates/feed/ 0
Core Animation Target-Action Layers https://bignerdranch.com/blog/core-animation-target-action-layers/ https://bignerdranch.com/blog/core-animation-target-action-layers/#respond Mon, 07 Dec 2009 20:43:59 +0000 https://nerdranchighq.wpengine.com/blog/core-animation-target-action-layers/

When you write a Core Animation heavy application, you spend a lot of time implementing code that executes when an animation finishes. Typically, the view controller whose view contains the animating layers implements animationDidStop:finished: and does a series of checks to see which animation finished. This method becomes difficult to manage as the number of animations it handles increases. You must also set the delegate of each animation object and tell them not to remove themselves when finished. You end up writing a lot of code over and over again.

The post Core Animation Target-Action Layers appeared first on Big Nerd Ranch.

]]>

When you write a Core Animation heavy application, you spend a lot of time implementing code that executes when an animation finishes. Typically, the view controller whose view contains the animating layers implements animationDidStop:finished: and does a series of checks to see which animation finished. This method becomes difficult to manage as the number of animations it handles increases. You must also set the delegate of each animation object and tell them not to remove themselves when finished. You end up writing a lot of code over and over again.

It would be simpler to have animations work more like UIControls. A layer would have a set of target-action pairs that would be triggered when an animation it is running completes. That way, you could easily chain animations or have code executed only after an animation completes.

How would you achieve this functionality? You would subclass CALayer. This layer subclass would have a list of target-action pairs for animation keys. (Note: Not animation key paths, but rather the name you assign to an animation when it is added to a layer.) Here is the code for that subclass:

BNRActionLayer.h:
`

#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>

@interface BNRActionLayer : CALayer {
    NSMutableDictionary *targetActionPairs;
}
- (void)addTarget:(id)t action:(SEL)a forKey:(NSString *)k;
- (void)removeTarget:(id)t action:(SEL)a forKey:(NSString *)k;
- (NSArray *)actionsForTarget:(id)t forKey:(NSString *)k;
@end

`

BNRActionLayer.m:
`

#import "BNRActionLayer.h"

// Declare a private class to keep track of target-action pairs
@interface BNRActionLayerTargetActionPair : NSObject
{
    id target;
    SEL action;
}
@property (nonatomic, assign) id target;
@property (nonatomic, assign) SEL action;
@end

@implementation BNRActionLayerTargetActionPair
@synthesize target, action;
@end

@interface BNRActionLayer (Private)
- (NSMutableArray *)pairsForKey:(NSString *)k;
@end

@implementation BNRActionLayer
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
    for(NSString *observedKey in targetActionPairs) {
        if([self animationForKey:observedKey] == theAnimation) {
            NSArray *pairs = [self pairsForKey:observedKey];
            [self removeAnimationForKey:observedKey];
            for(BNRActionLayerTargetActionPair *pair in pairs) {
                if(flag)
                    [[pair target] performSelector:[pair action] 
                                        withObject:self];
            }
        }
    }
}
- (void)addAnimation:(CAAnimation *)theAnimation forKey:(NSString *)key
{
    NSArray *targetActionsForThisKey = [targetActionPairs objectForKey:key];
    if([targetActionsForThisKey count] > 0) {
        [theAnimation setRemovedOnCompletion:NO];
        [theAnimation setDelegate:self];
    } 		
    [super addAnimation:theAnimation forKey:key];
}
- (void)addTarget:(id)t action:(SEL)a forKey:(NSString *)k
{
    if(!targetActionPairs)
        targetActionPairs = [[NSMutableDictionary alloc] init];

    NSMutableArray *pairsForKey = [self pairsForKey:k];
    if(!pairsForKey) {
        pairsForKey = [NSMutableArray array];
        [targetActionPairs setObject:pairsForKey forKey:k];
    }
	
    for(BNRActionLayerTargetActionPair *pair in pairsForKey) {
        if([pair target] == t && [pair action] == a)
        return;
    }
	
    BNRActionLayerTargetActionPair *newPair = 
        [[BNRActionLayerTargetActionPair alloc] init];
    [newPair setTarget:t];
    [newPair setAction:a];
    [pairsForKey addObject:newPair];
    [newPair release];
}
- (void)removeTarget:(id)t action:(SEL)a forKey:(NSString *)k
{
    NSMutableArray *pairsForKey = [self pairsForKey:k];
    if(!pairsForKey)
		return;
		
    BNRActionLayerTargetActionPair *removablePair = nil;
    for(BNRActionLayerTargetActionPair *pair in pairsForKey) {
        if([pair target] == t && [pair action] == a) {
            removablePair = pair;
            break;
        }
    }	
    [pairsForKey removeObject:removablePair];
}
- (NSMutableArray *)pairsForKey:(NSString *)k
{
    return [targetActionPairs objectForKey:k];
}
- (NSArray *)actionsForTarget:(id)t forKey:(NSString *)k
{
    NSMutableArray *list = [NSMutableArray array];
    NSMutableArray *pairsForKey = [self pairsForKey:k];
    for(BNRActionLayerTargetActionPair *pair in pairsForKey) {
        if([pair target] == t)
            [list addObject:NSStringFromSelector([pair action])];
    }
    return [NSArray arrayWithArray:list];
}
- (void)dealloc
{
    [targetActionPairs release];
    [super dealloc];
}
@end

`

How do you use this class? In your view subclass, make sure the type of layer it uses is of type BNRActionLayer (if you are using an explicit layer, you would simply create an instance of BNRActionLayer):
`

@implementation MyView
+ (Class)layerClass
{
    return [BNRActionLayer class];
}
@end

`

When you create an instance of MyView, you can add target-action pairs to it.
`

- (void)applicationDidFinishLaunching:(UIApplication *)app
{
    MyView *v = [[[MyView alloc] initWithFrame:someRect] autorelease];
    [window addSubview:v];
    [(BNRActionLayer *)[v layer] addTarget:self 
                                    action:@selector(viewDidFadeIn:) 
                                    forKey:@"Fade In"];

    [window makeKeyAndVisible];
}

`

Of course, you then need to implement viewDidFadeIn: to do something. Let’s pretend you are fading a view so it can become touchable:
`

- (void)viewDidFadeIn:(BNRActionLayer *)layer
{
    // We can operate on the layer here, do some controllery stuff, and we
    // can also get the this layers owning view. An implicit layer's delegate
    // is always its view (on the iPhone).
    MyView *v = [layer delegate];
    [v setUserInteractionEnabled:YES];
}

`

So, how would you create an animation that will trigger this message to be sent when it finishes? The same way as you would normally create an animation:
`

- (void)activateView:(MyView *)v
{
    CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@"opacity"];
    [a setToValue:[NSNumber numberWithFloat:1]];
    [a setDuration:1];
    
    // The key here matches the key the target-action pair was added for
    [[v layer] addAnimation:a forKey:@"Fade In"];
}

`

Cool, huh? Disclaimer: I’ve been using this code for a few days and it hasn’t given me any problems. However, that doesn’t mean it is perfect. This was definitely a wake-up-at-3am-from-a-programming-dream-and-write-code type deal. (I dream about programming. I’m weird.) If you have any suggestions for improving the code or find a problem, by all means, please don’t hesitate to comment.

Furthermore, if your understanding of layers is a bit fuzzy (for example, you did a double-take when I said that a layer’s delegate is always its view), be sure to keep an eye out for the Big Nerd Ranch iPhone book, written by Aaron Hillegass and myself, due early next year.

The post Core Animation Target-Action Layers appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/core-animation-target-action-layers/feed/ 0
iPhone Review Process https://bignerdranch.com/blog/iphone-review-process/ https://bignerdranch.com/blog/iphone-review-process/#respond Sun, 15 Nov 2009 18:41:26 +0000 https://nerdranchighq.wpengine.com/blog/iphone-review-process/

(Disclaimer: These are my (Joe Conway) opinions. Not Aaron Hillegass’ or any of the rest of the Big Nerd Ranch staff.)

The post iPhone Review Process appeared first on Big Nerd Ranch.

]]>

(Disclaimer: These are my (Joe Conway) opinions. Not Aaron Hillegass’ or any of the rest of the Big Nerd Ranch staff.)

It shouldn’t come as a surprise to the programming community that many developers are upset at Apple’s review process for iPhone applications. In fact, typing “rejected” into my Google bar shows “rejected iphone apps” as a suggestion.

When a high-profile iPhone application gets rejected and the story hits the blogs, there is an uproar: “Apple sucks!” “Switch to Droid!” “It’s all about the money for Apple!” This kind of attitude simply makes me laugh. But, the collective conscious that is the internet is right about one thing, it is all about the money. But not just for Apple, for any business.

A business is a group of employees that provides a service or a product. The money earned from the sale of said service or product is used to pay the employees, cover facility costs, electricity bills, health insurance, buy cleaning products for the office, the list goes on. The revenue generated by the service or product must match or exceed the cost to produce. If the revenue falls short of the cost, employees lose their jobs. A person without a job cannot provide for their family. Therefore, profitable businesses are essential to our survival.

What does this have to do with App Store rejections? Everything. Apple is providing a product (the iPhone) and a service (the App Store) to end-users. Their goal is to earn more money than it costs to create their products and services. To do this, they must survive in a highly competitive market that is growing at an amazing rate. How do you survive in a market like that? By differienating yourself from the competition.

Apple differientiates themselves from competitors by having a consistent and reliable experience. This is why the iPhone is successful. The misunderstanding is who that consistent and reliable experience is for. It is not for developers, it is for consumers. What the nay-saying high-profile developer folk in Silicon Valley have forgotten is that there is a world that exists outside of Northern California. This world is filled with people who don’t understand technology, but still use it everyday. The majority of iPhone users are part of this world – they expect things to “just work.”

The iPhone review process makes sure that these users are satisfied with their product. Now, Facebook developer Joe Hewitt argues that “we have our own product managers and quality assurance testers, and we are liable to our users.” What Mr. Hewitt fails to realize is that he is working for a half-a-billion-dollar corporation with the resources to create high quality applications and test them thoroughly. (Side note: In case you haven’t seen the Facebook iPhone App, do yourself a favor and download it.)

Most iPhone developers are not this well-financed or experienced. When an inexperienced developer writes an application, not only is the application more prone to crashing or not working as intended, the inexperienced developer doesn’t know how to test for these problems properly. If Apple let every application in to the App Store as-is, I would wager that an incredible amount of applications would crash, freeze or generally give the user a bad experience.

The majority of the blame for a poor user experience would fall on Apple. And this is what angry developers are failing to realize: the typical user of an iPhone does not differentiate between Apple and the companies that write software for the iPhone. If a user purchases applications, and the applications crash or don’t work, they are going to be upset at the iPhone, not at the companies or individuals that wrote the applications.

Consider what might happen if Apple allowed Flash web apps to be executed in Mobile Safari. We all know Flash is slow and it crashes Desktop Safari like clockwork. If Flash web apps were allowed on Mobile Safari, Mobile Safari would be slow and it would crash. The end user would not say, “I’m never going to that website again!” They would go to their blog and write, “I like my iPhone, but the web browser is really unstable. I bought this phone to browse the web. I’m switching to (insert next smartphone here).” This would be bad for Apple.

As an iPhone developer, what is bad for Apple and its iPhone is bad for you. If lots of applications typically crashed or did not work, the end user would suffer. If the end user suffers, the iPhone sells less units. If the iPhone sells less units, you, as a developer, make less money because your available market is smaller. The review process is making you money and helping you to be a better programmer. These are good things. (And, without the review process, there would also be even more crap on the App Store.)

Joe Hewitt also argues that “any bug that Apple finds after their two week delay would have been found by users on day one, and fixed on day two.” Yes, maybe by a quality development team working on a high-profile application. However, a less experienced developer team (or developer, singular) may not know how to fix that bug. They may not have a website for users to submit that bug. They may not even know that bug exists because their small userbase has yet to encounter it. It is not in the best interest of iPhone users, iPhone developers or Apple to have unstable applications on the App Store.

Besides all of this, there is still the issue of security. One may argue that the world wide web doesn’t have a review process and it does just fine. I imagine the guys making millions off security software for Windows OS would agree. The iPhone could be another platform they can sell their software on! Could you imagine having to have Norton Anti-Virus on your iPhone? (You could also argue that sandboxed applications make the iPhone safe. Someone will always find a way to bypass security measures. The review process helps to prevent that.)

So, what is the point? You should concentrate on doing the hard work required to excel. You should read the documentation, learn proper memory management and learn better programming practices that ensure your application is stable. Occasionally, you might get thrown a rejection curveball by Apple. You should work through the problem instead of giving up and threatening to switch platforms.

The post iPhone Review Process appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/iphone-review-process/feed/ 0
Dot-notation Syntax https://bignerdranch.com/blog/dot-notation-syntax/ https://bignerdranch.com/blog/dot-notation-syntax/#respond Thu, 06 Aug 2009 02:44:51 +0000 https://nerdranchighq.wpengine.com/blog/dot-notation-syntax/

When I teach, I always make sure to mention the dot-notation addition to Objective-C 2.0. Then, I make sure to tell the students never to use it ever, ever, ever again. But why? Why this seemingly irrational hatred of dot-notation? Is this a style choice and us “bracketeers” are being hard-headed? The answer is no, we are not being hard-headed, we are keeping our code consistent and maintaining readability.

The post Dot-notation Syntax appeared first on Big Nerd Ranch.

]]>

When I teach, I always make sure to mention the dot-notation addition to Objective-C 2.0. Then, I make sure to tell the students never to use it ever, ever, ever again. But why? Why this seemingly irrational hatred of dot-notation? Is this a style choice and us “bracketeers” are being hard-headed? The answer is no, we are not being hard-headed, we are keeping our code consistent and maintaining readability.

The Intent of Code

The naming conventions used in Cocoa and Cocoa Touch are clear and straightforward. When we look at well written Mac or iPhone code, we can tell exactly what is going on by glancing at it. That’s the power that Objective-C gives us. If I want an object to take a drink of water while doing a cartwheel, I send it the message:

[obj takeDrinkOfLiquid:water whileDoingCartwheel:YES];

We know exactly what that means. There is no room for interpretation. Likewise, we know exactly what this line of code does:

a = b + 5;

This adds b and the value 5 together and assigns the result to the variable a. We get these operators, = and +, from the C programming language. We have a lot more operators in C, like ***, **/, , % and &. And we know what all of those operators mean. When we see them, we say stuff like, “That’s a division!” or “That’s a subtraction!” Except we don’t say that. We don’t even think about that. We instantly know.

Another operator that is commonly used in C is the . operator. The dot operator accesses a member of a structure. It goes to an address in memory (the address of the structure object) plus an offset. That’s all it does. We instantly know what it means.

Sometimes, an operator may have another meaning depending on the context. For example, the *** operator could be dereferencing a pointer or it could be doing a multiplication. However, the context this operator is being used in allows us to easily discern which operation is occuring. The dereferencing *** is a unary operator and the multiplication ***** is binary operator. When we look at code, we know which one is being used.

   `int a = *ptr; // Definitely a dereference!
    int c = a * b; // Definitely a multiplication!
    int c = a * (*b); // Definitely both!`

In Objective-C, the square brackets ([]) were added for message sending. While the square brackets had previously only been used for indexing an array, the intent of the brackets can be easily determined by the context.

    `[object message]; // Definitely a message send!
    int a = variable[index]; // Definitely indexing!`

With that, we can say that we definitely know what the operators in Objective-C do with a quick look.

Then comes Objective-C 2.0…

With Objective-C 2.0, we get dot-notation. Many programmers new to Objective-C like dot-notation, it reminds them of other languages that they are more proficient in. Except for dot notation doesn’t do the same thing in Objective-C as it does in other languages.

Dot-notation in Objective-C invokes an instance method. Code in a method definition is executed when this dot is used. This code could be simple and it could just return a value. That doesn’t cause much of a problem. However, what if that method is a lot beefier:

 - (int)value
    {
        NSData *valueWrapper = [NSURLConnection sendSynchronousRequest:...];
        if(valueWrapper)
        {
            const int *v = [valueWrapper bytes];
            return *v;
        }
        return -1;
    }

That method could go wrong in a number of ways. When we see the invocation of that method with dot-notation, all we see is:

`int x = foo.value;`

What does that mean? Are we getting the value field out of the structure object foo? Are we executing a simple method that returns a value? Or, in this case, are we creating a network connection, pulling a value from a web server, turning that data in to an integer and then returning it?

It is ambiguous. With dot-notation, we don’t know exactly what this code means at first glance. When we see this code:

`int x = [foo value];`

Our first glance tells us we are definitely sending a message. That clues us in that more work is being done, not just a simple memory offset and an assignment.

What is more confusing, especially to Objective-C newcomers, is invoking a setter method with dot-notation.

`foo.object = otherObject;`

In C or C++, this is a simple assignment. In Objective-C, this could easily be creating a copy of otherObject or retaining otherObject. It is not immediately apparent from that line of code. The difficulty of understanding reference counting as a beginner gets amplified by code like this. Our code takes longer to read. We have to dig around to determine what is actually happening in this line of code.

Same context, same operator, different operation. We don’t know what this operator does anymore.

Return Values

Here is another example:

`someView.frame.size.width = 30;` 

Very straightforward, we’re setting the width of a view’s frame to 30 pixels.

Actually, no. We are not. We’re generating a compiler error. The value returned by a dot-notation invocation is not an lvalue. We can’t assign it anything. However, if someView is a structure, that line of code will work as intended.

`someView.frame.size.width = 30; // This is valid code!`

We lose consistency in our code using the dot operator. An operator in the same context, one is valid code, one is not. (It’s also a good thing that first line generates a compiler error, otherwise, we’d only be changing the copy of someView’s frame, not the instance variable of someView.)

Now, that is confusing.

But I’m used to C#, C++ or Java

I sometimes hear that dot-notation looks like home. You aren’t at home. You’re working with another language.

If one language solved all problems, everyone would use that language. Each language and API is a tool and it has an intended use. Using dot-notation in Objective-C would be akin to pounding nails in with a screwdriver. Sure, it works most of the time, but it isn’t the best use of a screwdriver or the best way to hammer nails. Eventually, you might smash your own hand.

Users of dot-notation are typically new to Objective-C. This is most likely the intention of dot-notation; a more familiar syntax for programmers coming from other languages. However, you would be better off familiarizing yourself with Objective-C then pretending it is another language. You will spend less time confused and more time solving a bigger problem.

Apple uses it in their sample code, though!

That’s right, they do. Now, despite what the kool-aid is making you think, Apple does occasionally get something wrong. Their sample code has always been hastily crafted to get developers to use their platform. It is not always the best example of how to use a language.

It is my belief, after teaching roughly 300 students Objective-C, that dot-notation is confusing. It hinders the main goal of software development: writing maintainable, effective, efficient, easy to read and bug-free code.

In the end, there is wrong way and a right way to write Objective-C code. I will not say that using dot-notation is the wrong way. It is an acceptable way to write Objective-C code. I will, however, say there is a better way to write your code. The better way does not include dot-notation.

The post Dot-notation Syntax appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/dot-notation-syntax/feed/ 0
Good iPhone Practices https://bignerdranch.com/blog/good-iphone-practices/ https://bignerdranch.com/blog/good-iphone-practices/#respond Tue, 17 Mar 2009 11:31:33 +0000 https://nerdranchighq.wpengine.com/blog/good-iphone-practices/

The iPhone SDK has now been around long enough where we can start to pick out good practices in using some of the more “fuzzy” areas. There are two small, but important, practices that can make your life much easier.

The post Good iPhone Practices appeared first on Big Nerd Ranch.

]]>

The iPhone SDK has now been around long enough where we can start to pick out good practices in using some of the more “fuzzy” areas. There are two small, but important, practices that can make your life much easier.

###
Buttons in UITableViewCells

Sometimes, you will want to have a button or some sort of control in a UITableViewCell subclass. You could have many rows, and each one has a button, and that button should have a different result depending on what row it is in.

In sticking with the MVC paradigm, how do you accomplish this? You have to be able to set the target-action pair for these buttons and you also have to decide which row’s button was pressed.

Well, you don’t even have to look at the UITableViewCell, you are really only concerned with the button. A UIButton (or any UIControl) is a subclass of UIView. Every UIView has an integer instance variable called tag. You can use this tag to specify the row the button is currently in.

  1. When a UITableViewCell is created (and only when it is created), you add a target-action pair from you UIButton to your UIViewController subclass to call the method you want.
  2. When a UITableViewCell’s data is prepared, you set the tag of its button to the row that cell is occupying.
  3. When the action message of your UIButton is sent to your UIViewController subclass, you simply grab the tag of the sender to determine what row it is.

Your UITableViewCell subclass interface should look like this:
`

@interface ButtonCell : UITableViewCell
{
    UIButton *button;
}
@property (readonly) UIButton *button;
@end

`

And your cell retrieval method should look like this:
`

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ButtonCell *aCell = (ButtonCell *)[tableView 
            dequeueReusableCellWithIdentifier:@"ButtonCell"];
    
    if(!aCell)
    {
        aCell = [[[ButtonCell alloc] initWithFrame:CGRectZero
                                   reuseIdentifier:@"ButtonCell"]
                                        autorelease];
        [[aCell button] addTarget:self 
                           action:@selector(buttonPressed:)
                 forControlEvents:UIControlEventTouchUpInside];
    }
    
    [[aCell button] setTag:[indexPath row]];
    
    return aCell;
}


And finally, your action message should do this:

- (void)buttonPressed:(id)sender
{
    int rowOfButton = [sender tag];
    [[internalData objectAtIndex:rowOfButton] performFunStuff];
}

`

All there is to it.

###
Instantiating UIViewController subclasses

XIB files are confusing. Chaining UIViewController XIB files and creating instances of them in another XIB file is even more confusing. And quite pointless. There is a much better way to do it.

First, you have to decide if you want to use a XIB file or not. The only reason to use a XIB file is if you have a complicated user interface that needs to be set up via Interface Builder. Otherwise, if you have a single view like a UITableView, UIImageView or UIView, simply implement loadView.

Now, regardless of how you are creating the interface, all UIViewController subclasses (save the standard ones, like UITableViewController, UINavigationController and UITabBarController) should be instantiated in code and sent an init message.
`

ViewControllerSubclass *vcs = [[ViewControllerSubclass alloc] init];

`

You are probably wondering, “Uhh… if I just send it init, how do I load its view from a XIB?” Good question. You will be overriding the init method of your UIViewController subclass (and overriding the superclasses designated initializer).
`

- (id)init
{
    return [super initWithNibName:@"ViewControllerSubclass" bundle:nil];
}
- (id)initWithNibName:(NSString *)name bundle:(NSBundle *)bundle
{
   return [self init];
}

`

How does this work out in code? Let’s pretend you have two UIViewController subclasses, RootViewController and DetailViewController. They are both intended to be part of a UINavigationController.

In your application delegate, you should be doing this:
`

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    RootViewController *rootViewController = 
        [[RootViewController alloc] init];
    UINavigationController *navController = [[UINavigationController alloc] 
            initWithRootViewController:rootViewController];
    
    [window addSubview:[navController view]];
    [window makeKeyAndVisible];
}

`

In RootViewController’s implementation, you can do a little something like this:
`

- (void)viewDidLoad
{
    if(!detailViewController)
        detailViewController = [[DetailViewController alloc] init];
}

`

Therefore, none of your other classes need to know anything about any other UIViewController subclass. They just know if they want an instance of it – with a fully configured user interface – they can just send it init.

Easy enough.

The post Good iPhone Practices appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/good-iphone-practices/feed/ 0