Adam Preble - Big Nerd Ranch Tue, 19 Oct 2021 17:46:33 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 WWDC 2014: Big Nerd Ranch and Swift https://bignerdranch.com/blog/wwdc-2014-big-nerd-ranch-and-swift/ https://bignerdranch.com/blog/wwdc-2014-big-nerd-ranch-and-swift/#respond Mon, 02 Jun 2014 10:10:22 +0000 https://nerdranchighq.wpengine.com/blog/wwdc-2014-big-nerd-ranch-and-swift/

Editor’s note: We have since published the first edition of our Swift programming guide, currently an Amazon best seller. Our iOS and Cocoa bootcamps have also been updated to include Swift programming.

The post WWDC 2014: Big Nerd Ranch and Swift appeared first on Big Nerd Ranch.

]]>

Editor’s note: We have since published the first edition of our Swift programming guide, currently an Amazon best seller. Our iOS and Cocoa bootcamps have also been updated to include Swift programming.

I have a knack for attending WWDC on years that are especially great for developers. Apple introduced blocks in 2009, ARC in 2011, and now in 2014, the new Swift programming language. Each of these technologies have dramatically changed the way that we write software for Apple platforms. Swift is the most momentous of all.

Introducing Swift

For Big Nerd Ranch, Swift is huge news. Being nerds, we love new technology like this, but it also means that we have a lot of work to do! Not unlike you, we watch WWDC carefully to stay on top of our app development work for our clients, but we’re also dedicated to keeping our courses up to date. A new language is a big deal.

After the keynote, it was time for lunch: sandwiches and ethernet awaited, where we could download the Xcode 6 beta and Apple’s Swift book. We immediately set to work on better understanding this new language. Interestingly, I found that we all approached this somewhat differently: some of us began poring over the book; I started at the other end and began porting exercises from the Cocoa book to Swift.

Programming in Swift has largely been strangely comfortable, punctuated by moments of sheer bafflement, followed by a little study in the book and finally getting back on track. The comfort comes from being able to use the familiar Cocoa/Cocoa Touch APIs, a great advantage to experienced developers. The bafflement is a natural consequence of such a new, rich language. I hesitate to make the comparison, but Swift’s appearance is superficially reminiscent of JavaScript—but you will quickly realize that it is significantly more powerful and very carefully thought out.

New features with Swift

One of the more exciting features that Swift enables is the playground with its timeline assistant, deeply reminiscent of the ideas former Apple designer Bret Victor illustrated in his talk, Inventing on Principle. Like Bret’s ideas, Swift’s playground provides a tight feedback loop and encourages experimentation. As a co-author of our Cocoa Programming for Mac OS X, my head is swimming with possibilities of using playgrounds in the book and in our bootcamps as a learning tool. Beginners will get a lot out of streamlining the run-check-fix cycle.

Swift in Big Nerd Ranch bootcamps

We’re working right now on preparing our bootcamps for Swift. In fact, starting next week, we’ll be teaching an introduction to Swift in all of our iOS and Cocoa bootcamps. In our courses, we teach a mixture of language, frameworks and design patterns/best practices. The frameworks and best practices haven’t changed that much, so you’ll find that just as valuable as they’ve always been, but by giving you a head start on Swift, you’ll be prepared for the language shift when Swift, Xcode 6, Yosemite and iOS 8 are released.

Editor’s note: Adam will be teaching our Cocoa I class, with the Swift introduction, next month.

The post WWDC 2014: Big Nerd Ranch and Swift appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/wwdc-2014-big-nerd-ranch-and-swift/feed/ 0
Cocoa UI Preservation Y'all https://bignerdranch.com/blog/cocoa-ui-preservation-yall/ https://bignerdranch.com/blog/cocoa-ui-preservation-yall/#respond Wed, 30 Jan 2013 21:14:11 +0000 https://nerdranchighq.wpengine.com/blog/cocoa-ui-preservation-yall/

Mac OS X Lion introduced sudden termination, an opt-in feature for Cocoa apps that allows the system to terminate apps that aren’t in use and transparently re-launch them when the user brings them back to the foreground. This allows the system to keep resource usage as low as possible, but it also puts an additional burden on the developer to ensure that when the application is re-launched, it looks just like it did when the user last saw it. You’ve probably seen this in document-based applications that remember which documents were open when they last ran.

The post Cocoa UI Preservation Y'all appeared first on Big Nerd Ranch.

]]>

Mac OS X Lion introduced sudden termination, an opt-in feature for Cocoa apps that allows the system to terminate apps that aren’t in use and transparently re-launch them when the user brings them back to the foreground. This allows the system to keep resource usage as low as possible, but it also puts an additional burden on the developer to ensure that when the application is re-launched, it looks just like it did when the user last saw it. You’ve probably seen this in document-based applications that remember which documents were open when they last ran.

Fortunately the work of implementing this has been minimized by another Lion feature, Cocoa UI Preservation, which remembers the state of restorable windows so they can be recreated at launch. UI preservation takes care of the unpleasantness of this sort of meticulous work by recording and restoring the state of the windows and providing us with hooks to store our own data.

Let’s look at how we might implement UI preservation in our own project. Consider a typical Cocoa application with a preferences window. The AppDelegate handles showing the PreferencesWindowController:

    - (PreferencesWindowController *)preferencesWindowController
    {
        if (!_preferencesWindowController)
        {
            _preferencesWindowController =
                [[PreferencesWindowController alloc] init];
        }
        return _preferencesWindowController;
    }
    - (IBAction)showPreferences:(id)sender
    {
        [[self preferencesWindowController] showWindow:nil];
    }

Let’s implement UI preservation for the preferences window in our application. Cocoa automatically persists state information for all restorable windows in an application. So first we must set the preferences window to be restorable, as well as tell it which class knows how to restore it (more on that in a moment).

    - (PreferencesWindowController *)preferencesWindowController
    {
        if (!_preferencesWindowController)
        {
            _preferencesWindowController =
                [[PreferencesWindowController alloc] init];
            _preferencesWindowController.window.restorable = YES;
            _preferencesWindowController.window.restorationClass = [self class];
            _preferencesWindowController.window.identifier = @"preferences";
        }
        return _preferencesWindowController;
    }

We can turn on Restorable and set the Identifier in Interface Builder, but as of Xcode 4.5.2, there isn’t a control for setting the restoration class. Thus it feels much cleaner to do it all in code. And although we use a string literal for the identifier in this blog entry, you’ll want to use a string constant in your own code.

Setting the restorable boolean is straightforward enough, but what about this restoration class? Cocoa will store this class along with the window’s properties, such as its frame. When it comes time to restore the window when our application starts, Cocoa will ask the restoration class to do this by sending it the message +restoreWindowWithIdentifier:state:completionHandler:. The identifier parameter will match the identifier we had set on the window. Let’s implement this method in the AppDelegate:

    + (void)restoreWindowWithIdentifier:(NSString *)identifier
                                  state:(NSCoder *)state
                      completionHandler:(void (^)(NSWindow *, NSError *))completionHandler
    {
        NSWindow *window = nil;
        if ([identifier isEqualToString:@"preferences"])
        {
            AppDelegate *appDelegate = [NSApp delegate];
            window = [[appDelegate preferencesWindowController] window];
        }
        completionHandler(window, nil);
    }

This method is responsible for creating the window that corresponds to the given identifier and passing that NSWindow instance to the completion handler block. By not requiring that the developer return the window from this method, restoring the window may be delayed until an appropriate time in the future without blocking the rest of the application startup.

Note that by lazily creating our window controller via a read-only property, it becomes quite simple to restore this window, as well as to ensure that its restoration properties are configured.

Let’s review. In order for Cocoa to be able to restore a window:

  • The window must be marked restorable.

  • The window must have an identifier.

  • The window must have a restoration class.

  • Given an identifier, the restoration class must be able to recreate the corresponding window.

In this example, our restoration class is the application delegate, but it won’t always be. A good rule of thumb is that the restoration class will be the same class that keeps a strong reference to that window’s controller. You will probably need some mechanism to find the appropriate instance of this class from within the +restoreWindowWithIdentifier:state:completionHandler: class method. In this case we use the -[NSApplication delegate] property.

Not Just Frames

We’ve seen how to restore windows to their former position and size on the screen, but Cocoa actually restores more than this. For example, the window’s first responder is stored, as is the selected tab of an NSTabView. Data is not saved, however. So if we want to preserve the user’s input in a text field, or the position of a slider, we will need to make other arrangements. In the old days we might have used NSUserDefaults, but UI preservation gives us a much more convenient mechanism, as we shall see.

Suppose our preferences window has several panes. The window controller might have a property, selectedPaneIndex. Its setter would look something like this:

    - (void)setSelectedPaneIndex:(NSUInteger)index
    {
        NSViewController *paneViewController =
            [self paneViewControllerForIndex:index];
        [[self window] setContentView:[paneViewController view]];
        _selectedPaneIndex = index;
    }

We’d like to ensure that the pane the user had last selected is shown when the window is restored. By implementing +restorableStateKeyPaths on the PreferencesWindowController we can easily cue Cocoa to save this state information along with the window:

    + (NSArray *)restorableStateKeyPaths
    {
        return @[ @"selectedPaneIndex" ];
    }

The class must be KVC- and KVO-compliant for the key paths returned by this method, as Cocoa observes the key paths for changes and saves the state as needed. Once the window has been passed to the completion block by the restoration class (shown in the first section), Cocoa will use KVC to restore the state.

If you prefer to have more control over which values are stored, and how they are restored, you can implement -encodeRestorableStateWithCoder: and -restoreStateWithCoder:. These methods will look very familiar if you’ve used Cocoa archiving. They can be implemented on the window itself, as well as a view, window controller or document. Similarly, the window’s delegate can implement -window:willEncodeRestorableState: and -window:didDecodeRestorableState:.

Let’s look at a case where we might want to use the aforementioned methods. +restorableStateKeyPaths depends on our setter doing the work of reflecting the state in the UI, but this isn’t always appropriate. Suppose we have a property, text, for which this is the case. Here’s how we might implement such a pattern:

    - (void)encodeRestorableStateWithCoder:(NSCoder *)coder
    {
        [coder encodeObject:self.text forKey:@"text"];
    }
    - (void)restoreStateWithCoder:(NSCoder *)coder
    {
        self.text = [coder decodeObjectForKey:@"text"];
        [self updateForChangedText];
    }

We need one more bit of code, however, and that is to tell the UI preservation system when some state has changed which should be persisted. We use the method -invalidateRestorableState to do this:

    - (void)setText:(NSString *)text
    {
        _text = text;
        [self invalidateRestorableState];
    }

The above step isn’t necessary when using +restorableStateKeyPaths because KVO handles it for us. Keep in mind that you don’t have to choose between +restorableStateKeyPaths and the archiving methods; they can be used together.

Not Everything Need Be Preserved

There are some pitfalls to watch out for with UI preservation. Because Restorable is checked in the Interface Builder editor by default, you may find that if you have run an app once and then later resize the main window in Interface Builder, the application will appear to have ignored your changes. What’s happening is that UI preservation is remembering the window size from last time it ran and overwriting the window size from the XIB.

There are a few ways to address this:

  • Quit the application with Command-Option-Q (Quit and Close All Windows), which causes the application to forget its UI preservation information.

  • Edit the Run action in Xcode’s Scheme Editor and check “Launch application without state restoration” in the Options tab. Just remember to turn it off later.

  • An application’s state restoration data is stored in the user’s library, under ~/Library/Saved Application State/. You can delete the app’s folder in order to reset it. This is effectively the same as “Quit and Close All Windows”.

It’s worth noting that UI preservation means more opportunities for tricky bugs, given that there is now additional state information associated with the application.

Back to the App

Lion was announced with the phrase “Back to the Mac.” UI preservation is a behavior that has long been thought of as important for iOS apps, which is becoming more important for Mac apps, particularly alongside sudden termination. If you write iOS apps, you’ll be happy to know that iOS has APIs that are quite similar to those we have discussed today for Cocoa. (It is somewhat ironic, however, that they weren’t available until iOS 6.)

For more on UI preservation, as well as links to the various APIs discussed here, check out the Mac App Programming Guide, under the heading “Support the Key Runtime Behaviors in Your Apps.” If you’re curious about state restoration on iOS, you’ll want to refer to the very detailed coverage in the iOS App Programming Guide. If you’re new to archiving, check out the Archives and Serializations Programming Guide, Chapter 10 in our Cocoa book or Chapter 14 in our iOS book.

The post Cocoa UI Preservation Y'all appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/cocoa-ui-preservation-yall/feed/ 0
An Objective-C Literal Subtlety https://bignerdranch.com/blog/an-objective-c-literal-subtlety/ https://bignerdranch.com/blog/an-objective-c-literal-subtlety/#respond Wed, 28 Nov 2012 17:32:29 +0000 https://nerdranchighq.wpengine.com/blog/an-objective-c-literal-subtlety/

Update: In the time since this blog post was published, the compiler behavior demonstrated has been remedied in the version of clang accompanying Xcode 4.6. For a BOOL generateMipmaps, @(generateMipmaps) now evaluates to kCFBooleanTrue or kCFBooleanFalse.

The post An Objective-C Literal Subtlety appeared first on Big Nerd Ranch.

]]>

Update: In the time since this blog post was published, the compiler behavior demonstrated has been remedied in the version of clang accompanying Xcode 4.6. For a BOOL generateMipmaps, @(generateMipmaps) now evaluates to kCFBooleanTrue or kCFBooleanFalse.

Earlier this year, Apple introduced new syntax for Objective-C literals—perhaps you read Mark’s early coverage of it here on the blog (Part 1Part 2). This new syntax is a great improvement for Cocoa and iOS developers: suddenly our code can be much less verbose, while remaining expressive. There are some sharp edges, however. Consider @YES, which is shorthand for [NSNumber numberWithBool:YES]. There’s also @42 and so forth, but what if we want to store the value of an expression in an NSNumber? The boxed expression helps with that: @(1 + 3). We also need to use the boxed expression for variables. For example: @(answer). There’s a subtlety to this last case, however. I was taking our OpenGL class recently and was using GLKit’s excellent GLKTextureLoader to, well, load a texture:

options = @{};
tex = [GLKTextureLoader textureWithContentsOfFile:path
                                          options:options
                                            error:&error];

Then I decided I might want to generate mipmaps for my texture.

BOOL generateMipmaps = YES;
options = @{ GLKTextureLoaderGenerateMipmaps : @(generateMipmaps) };
tex = [GLKTextureLoader textureWithContentsOfFile:path
                                          options:options
                                            error:NULL];

Alas, the mipmaps were nowhere to be found. After entirely too much searching for problems loading mipmaps, I started poking at my assumptions. On a whim I tried simplifying my options dictionary:

options = @{ GLKTextureLoaderGenerateMipmaps : @YES };

Suddenly, I had mipmaps, but more importantly I learned that

@(YES) has a different result from @YES. Using po (Class)[obj class] I was able to find that @(YES) has a class of _NSCFNumber, while @YES is an __NSCFBoolean. So why is -textureWithContentsOfFile:options:error: interpreting my __NSCFNumber for that key as NO? According to the documentation, the value for the key GLKTextureLoaderGenerateMipmaps is “an NSNumber object that specifies a boolean value”. NSNumbers simply hold a scalar, right? And [[NSNumber numberWithInt:YES] boolValue] == [[NSNumber numberWithBool:YES] boolValue]. Why didn’t -textureWithContentsOfFile:options:error: agree? I reasoned that I could take advantage of Objective-C’s message passing and log all messages sent to my NSNumber instance. I pulled out dtrace and wrote a script – the subject of another blog post – and found, to my surprise, that -textureWithContentsOfFile:options:error: wasn’t sending _any messages to my NSNumber! After conferring with my fellow nerds we found a probable explanation: the NSNumber’s value is likely being checked using something like the following:

NSNumber *generateMipmaps =
    [options objectForKey:GLKTextureLoaderGenerateMipmaps];
if (generateMipmaps == kCFBooleanTrue)
{
    // generate those mipmaps
}

That would explain why no messages are sent to our number object, and why this method displays a distinction between

[NSNumber numberWithInt:YES] and [NSNumber numberWithBool:YES]. As an optimization, the latter always returns the same pointer (kCFBooleanTrue). The same is true for the NO counterpart – it always returns kCFBooleanFalse. I would argue that this method should use -boolValue to check the options dictionary values, but I would also argue that this is something the compiler should be handling when it translates the literal syntax into an object. I’ve filed radars on both of these issues this with Apple (rdar://12761147 and rdar://12761621). Enjoy the new literal syntax with care, and watch out for these sorts of pitfalls in your Objective-C work. If you’re a consumer of APIs, be aware of this slippery behavior when mixing @() with YES and NO. If you’re writing an API, the extra -boolValue won’t cost that much, and you’ll save yourself emails from confused users.

The post An Objective-C Literal Subtlety appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/an-objective-c-literal-subtlety/feed/ 0