development

AccountsDemo: a hacky solution to the "Setting TCC failed" error accessing Twitter accounts

I recently blogged about some critical bugs in OS X that Apple hasn't fixed, including getting a "Setting TCC failed" error when trying to access Twitter accounts (and other kinds of accounts).

Again, the problem is that calling -[ACAccountStore requestAccessToAccountsWithType:options:completion:] on OS X 10.11 for a new app will ask the user for permission then always give the error "Setting TCC failed." (Reported as Radar #23114308 for any Apple people reading this.)

While this bug remains, I came across a solution in Apple's developer forums. It's a hack, and probably won't work in sandboxed apps, but does work for non-sandboxed apps.

Basically, if that error is received, we can edit the "~/Library/Accounts/Accounts3.sqlite" database directly via the sqlite3 command line tool.

I've put together an open-source demo app to show this technique, called AccountsDemo.

When the error is detected, it calls a method to send the SQL:

            [self sendSQL:[NSString stringWithFormat:@"insert into ZAUTHORIZATION (Z_ENT, Z_OPT, ZACCOUNTTYPE, ZBUNDLEID, ZGRANTEDPERMISSIONS) values (5, 1, (select Z_PK from ZACCOUNTTYPE where ZIDENTIFIER = 'com.apple.twitter'), '%@', '')", [NSBundle mainBundle].bundleIdentifier] completionHandler:^{
                NSLog(@"Workaround completed");  // log
               
                [self loadAccounts:sender];
            }];

(Sorry Swifties; I wrote the demo project a while ago for my original Radar bug report, so it's in Objective-C.)

The -sendSQL: method simply uses NSTask to execute the sqlite3 command on the database:

- (void)sendSQL:(NSString *)sql completionHandler:(void (^)(void))handler {
    // Based on code from https://forums.developer.apple.com/message/69921
    NSURL *libraryURL = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
    NSURL *databaseURL = [libraryURL URLByAppendingPathComponent:@"Accounts/Accounts3.sqlite"];
    NSString *command = [NSString stringWithFormat:@"sqlite3 %@ \"%@\"", databaseURL.path, sql];
   
    NSTask *task = [NSTask new];
   
    task.launchPath = @"/bin/sh";
    task.arguments = @[@"-c", command];
   
    if (handler) {
        task.terminationHandler = ^(NSTask *localTask) {
            handler();
        };
    };
   
    [task launch];
}

The AccountsDemo sample project includes a button to load the Twitter accounts, a checkbox to use the above hack, and a button to remove the app from the accounts database (so you can re-try it). Try clicking the load button with the checkbox unchecked, to confirm that you get the error, then check the box and try loading again, and it should work.

You can get the project via the Dejal GitHub page.

Apple's neglected OS

I've been developing Mac apps for nearly 25 years under the Dejal banner. It's a great platform, and an ever-improving OS.

Except when it isn't.

Sometimes, I encounter serious issues that make me think that it isn't getting the attention it deserves from Apple. Sure, each year they add lots of useful new features, and hundreds of APIs... but there are often huge fundamentally broken problems that don't get addressed.

I just encountered another mysterious bug that Apple says has no workaround, so I thought I'd take a moment to mention some that particularly bother me. Again, I really like Macs and Mac OS X, and primarily develop for it (along with iOS and watchOS), so I wish these issues would be fixed. I have, of course, filed Radar bug reports for all of these.

Here are my top three, all of which Apple has said there is no workaround, but are causing serious issues for lots of people. Please feel most welcome to file duplicates of these if they bother you too.

#22446403: SMLoginItemSetEnabled often launches an agent outside the app bundle

This one is particularly annoying for my Time Out break reminder app, as the Mac App Store edition is sandboxed, so using SMLoginItemSetEnabled is (I believe) the only supported way to launch a helper.

SMLoginItemSetEnabled is documented to:

Enable a helper application located in the main application bundle's Contents/Library/LoginItems directory.

But it often doesn't work as expected. It will often launch another copy of the agent elsewhere on the Mac, e.g. in the build or DerivedData folder, or in another copy of the app, etc.

This is a serious issue, particularly for developers, but potentially also for users who like to keep multiple versions (in case the want to revert). Launching an agent from an older version of the app leads to incorrect behavior.

I previously mentioned this issue on Stack Overflow.

Steps to Reproduce:
1. Have more than one copy of an app containing a login item.
2. Call SMLoginItemSetEnabled for the login item.

Expected Results:
The login item contained within the app bundle (at Contents/Library/LoginItems) will be launched.

Actual Results:
Sometimes a login item from another copy of the app (or in the build folder) will be launched.

#23114308: "Setting TCC failed" error accessing accounts

This one affects my Simon server monitoring tool, and prevented including a Twitter action in Time Out.

There is a widespread bug in 10.11 that gives this error when attempting to access accounts, e.g. with code such as this:


ACAccountStore *accountStore = [ACAccountStore new];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error) ...

I get the error:


Error Domain=com.apple.accounts Code=1 "Setting TCC failed." UserInfo={NSLocalizedDescription=Setting TCC failed.}

See a developer forum thread that discusses it, and includes a sample project.

This is a serious issue that is preventing existing apps and new apps from accessing Twitter and other services.

Steps to Reproduce:
Run the project in that forum post.

Expected Results:
Access is granted.

Actual Results:
"Setting TCC failed" error.

#25691051: Pasteboard stops working for some customers

This is one I encountered recently, and reported today. It affects some Time Out customers, fortunately fairly rare, but annoying.

Some users have reported that after some time their pasteboards stop working: copy/paste and dragging. The only solution is to restart the Mac.

I haven't experienced this myself, but I have had a few reports, and at least one of the users has offered to help test any fixes.

When the issue occurs, the following line appears in the Console (for pretty much every running app):


CFPasteboardCreate(CFAllocatorRef, CFStringRef) : Lock timeout

I can't think of anything I do in the app that could cause a pasteboard issue; it doesn't do much in related areas.

This issue isn't unique to my apps; a Google search reveals other examples, e.g.

Steps to Reproduce:
I can't reproduce, but have a customer who can help.

Expected Results:
Pasteboard keeps working.

Actual Results:
Pasteboard stops working, with the above-mentioned error.

Surely we can do better

It's often been said, especially in recent years, but maybe Apple just needs to slow down. Instead of spreading themselves too thin with major annual updates, not to mention new OS platforms, maybe everyone would be better served with a slower pace. Perhaps fewer user features and API changes, and more focus on improving the quality of what's already there.

Fundamental things like reliably launching helpers, accessing Twitter accounts, and a functioning clipboard should not be low priorities. They should be urgent priorities that get fixed before adding new features.

Please, Apple.

Developer: NSButton drawing issue

A post for developers, though also related to Time Out 2.

One of the features of Time Out 2 is a sidebar listing the breaks and options, as you could see (blurred somewhat) in my previous post . The sidebar includes a couple of buttons that appear when you hover the pointer over a row, enabling you to manually start or pause that break.

A vexing issue I had, though, was that the button didn't draw correctly. If you look closely at this screenshot, you may notice that the background of the text and the remainder of the button content don't match — there's a visible outline around the text:

That is rather ugly. I'm not sure exactly what causes it, but presumably it's related to being in a sidebar list, and perhaps an issue with title-less windows (I've seen some other issues, too).

Regardless, I wanted to fix it. After a bit of experimentation, I found a simple solution: a subclass of NSButtonCell to override the -drawTitle:withFrameinView: method, which as you might imagine is responsible for drawing the title text. The override simply invokes the superclass, and returns the full button rect instead of the rect of the text itself:


@interface DejalButtonCell : NSButtonCell

@end

@implementation DejalButtonCell

- (NSRect)drawTitle:(NSAttributedString *)title withFrame:(NSRect)frame inView:(NSView *)controlView;
{
    [super drawTitle:title withFrame:frame inView:controlView];
    return frame;
}

@end

That seems to do the trick:

Filed Radar #22491410.

DejalIntervalPicker: a custom Mac control similar to NSDatePicker, but for time intervals or ranges

Another open source project for developers.

DejalIntervalPicker is a custom Mac control similar to NSDatePicker, but for time intervals or ranges.

This project was started several years ago for Time Out 2, but got put aside while I worked on contract projects. I've just finished it off and released it for others to enjoy. It will be making an appearance in Time Out 2 as of the next alpha release.

Requirements

  • OS X 10.10 or later recommended, but should work back to 10.7.
  • Objective-C language.
  • ARC.
  • Dependency: the DejalObject project.

Features

  • A custom control with an amount or amount range, units, and stepper.
  • Like NSDatePicker, editing components separately, with a stepper.
  • Can set minimum and maximum amounts.
  • Can get/set the interval as a DejalInterval, as individual values, or as a NSTimeInterval.
  • Can have either a single amount or a range of amounts.
  • Can optionally filter the range to ensure the first amount is smaller (or equal to) the second one, or vice versa.
  • Can control which units to include.
  • Can navigate between components via Tab and Shift-Tab and left/right arrow keys, or clicking.
  • Can type amounts just like in the date picker, and units with auto-completion.
  • Can increment and decrement amounts and units via up/down arrow keys, +/- keys, or the stepper.
  • Can increment/decrement in steps of 5 via Shift/Option/Ctrl and up/down arrow keys, or Page Up/Down.
  • Can go to the first/last valid values via Home/End.
  • Can display a drop-down menu of suggested legal amounts or units via the spacebar or clicking on the selected value.
  • Supports regular, small and mini sizes.
  • Supports properties, key-value coding, and bindings.
  • Supports IB_DESIGNABLE and IBInspectable, so the picker can be configured in IB.
  • A demo project is included.

Usage

  1. Include the DejalIntervalPicker.h and DejalIntervalPicker.m files in your project. Also include at least DejalObject.h, DejalObject.m, DejalInterval.h and DejalInterval.m from the DejalObject project.
  2. In Interface Builder for your xib or storyboard, drag a custom view to your view or window.
  3. In the Identity inspector, change the Custom Class of the view to DejalIntervalPicker.
  4. In the Size inspector, add a Placeholder Intrinsic Size of 150 width and 22 height if using Auto Layout, or set the view to that size for auto-resizing.
  5. In the Attributes, configure the desired attributes like the using range, initial amounts, and which units to include.
  6. In your controller, you can also configure the picker via methods like usingRange, includeForever, firstAmount, and others; see the demo project for examples.
  7. Populate the picker value by setting the amount(s) and units, or setting the interval from a DejalInterval instance.
  8. Get the picker value via the same properties: either amounts(s) and units directly, or the interval instance.
  9. See DejalInterval for several useful methods and properties, e.g. to get a string representation of the interval.

You can get the code and more information from the Dejal Open Source page.

DejalObject: an abstract data model class that can represent subclasses as dictionary or JSON data

DejalObject is an abstract data model class that can represent subclasses as dictionary or JSON data for saving to disk or over the network.

Included are DejalColor, DejalDate and DejalInterval concrete subclasses.

They work on both OS X and iOS.

Features

  • DejalObject: This is an abstract subclass of NSObject that adds methods to represent the receiver as a dictionary or JSON data, load default values, track changes, enumerate an array of DejalObject instances, and more.
  • DejalColor: A concrete subclass of DejalObject to represent a color (for OS X or iOS), enabling it to be stored in a DejalObject subclass.
  • DejalDate: Another concrete subclass to represent a date, primarily so it can automatically be represented as JSON.
  • DejalInterval: A subclass to represent a time interval or a range of intervals, including an amount and units, with methods to represent the interval or range in various ways, including as human-readable strings (see also the DejalIntervalPicker project for OS X).

A demo project is included, showing a subclass of DejalObject to store various data types.

Usage

Include at least DejalObject.h and DejalObject.m in your project. Include the DejalColor, DejalDate and/or DejalInterval files if those are needed.

Add a new class that inherits from DejalObject.

In the header, you only need to define properties to store, e.g.:

@interface Demo : DejalObject

@property (nonatomic, strong) NSString *text;  
@property (nonatomic) NSInteger number;  
@property (nonatomic, strong) DejalColor *label;  
@property (nonatomic, strong) DejalDate *when;

@end

In the implementation, override -initWithCoder: and -encodeWithCoder: if you need to support coding (secure coding is supported in DejalObject).

Override -loadDefaultValues to populate default values to each of the properties. A version number can be assigned (via DejalObject’s version property) to enable upgrading later, e.g.:

- (void)loadDefaultValues;  
{
    [super loadDefaultValues];

    self.version = DemoVersion;  
    self.text = @"Foo";  
    self.number = 12345;  
    self.label = [DejalColor colorWithColor:[NSColor blueColor]];  
    self.when = [DejalDate dateWithNow];  
}

Finally, override -savedKeys to indicate which properties should be automatically included in the dictionary or JSON representation, e.g.:

- (NSArray *)savedKeys;  
{
    return [[super savedKeys] arrayByAddingObjectsFromArray:@[@"text", @"number", @"label", @"when"]];  
}

A DejalObject subclass can be represented as a NSDictionary simply by invoking the dictionary property on it, or as JSON via the json property. That can then be saved to disk or the user defaults, or passed over the network.

Those properties can also be set, or a new instance can be created via +objectWithDictionary: or +objectWithJSON:, or an instance with default values via +object.

DejalObject instances automatically track changes, with a hasChanges BOOL property indicating that something has changed (so may need to be saved). There is also a hasAnyChanges property that recursively enumerates the properties and any other DejalObject instances in them, e.g. if the color is changed in the above example.

After saving, you should invoke -clearChanges to reset the change flag.

You can get the code and more information from the Dejal Open Source page.

DejaUIKitCategories: an open source collection of UIKit-level categories for iOS

The final batch of open source categories: DejalUIKitCategories.

DejalUIKitCategories is a collection of categories for UIKit on iOS, to add useful methods to classes like UIBarButtonItem, UIColor, UIView, and others. The categories include:

  • UIApplication+Dejal: Adds methods to get the first responder and keyboard view of the app.
  • UIBarButtonItem+Dejal: Convenience initializers to make UIBarButton instances based on and image, title, system item, custom view, spacer, or segmented control.
  • UIButton+Dejal: A more convenient title property, and a method to add a gloss effect.
  • UIColor+Dejal: Convenience initializers for more standard colors, or based on a platform-specific image or hex value.
  • UIImage+Dejal: Convenience initializers for tinted images, methods to overlay images with colors, and scaling methods.
  • UIImageView+Dejal: Make a white-background image view, and adjust the background color based on the highlighted state.
  • UILabel+Dejal: Convenience initializers for labels with various text, font, width etc attributes, and sizing methods.
  • UISegmentedControl+Dejal: Convenience initializer for a segmented control with specified items, target, action, and initial selection.
  • UITextField+Dejal: A selected range property, and support for gestures to move the insertion point by swiping.
  • UITextView+Dejal: Attributes of the selection or insertion point, and support for the insertion point swiping gestures.
  • UIView+Dejal: Properties for frame and bounds components and an image representation, methods to hide, add to and remove from the superview with animation.

You can get the code and more information from the Dejal Open Source page.

(Looking for an iOS or Mac developer? I'm available for contract or full-time work. Learn more about me.)

DejalAppKitCategories change: methods for radio buttons

Another developer post.

I recently released my DejalAppKitCategories open source project, which includes categories for OS X UI classes.

I am currently working on Time Out 2, and wanted to modernize the radio buttons in the preferences. In the AppKit release notes for OS X 10.10, Apple wrote:

Use of NSMatrix is informally deprecated. We expect to add the formal deprecation macros in a subsequent release, but its use is discouraged in the mean time. The primary use of NSMatrix is for radio button groups, so recall that for applications linked on 10.8 or later, radio buttons that share the same parent view and action will operate as a group.

Which is all well and good, but managing a radio group is annoying when using standalone buttons, so I added some methods to help.

These methods are part of my DejalAppKitCategories open source project, in the NSButton+Dejal category.

Here's the header:

@interface NSButton (DejalRadios)

@property (nonatomic, setter=dejal_setRadiosEnabled:) BOOL dejal_radiosEnabled;

- (void)dejal_selectRadioWithTag:(NSInteger)tag;
- (NSInteger)dejal_selectedRadioTag;

- (NSButton *)dejal_radioPassingTest:(BOOL (^)(NSButton *radio, BOOL *stop))predicate;
- (void)dejal_enumerateRadiosUsingBlock:(void (^)(NSButton *radio, BOOL *stop))block;

@end

And the implementation:

@implementation NSButton (DejalRadios)

/**
Assuming the receiver is a radio button, finds other radio buttons in the group (i.e. in the same superview and with the same action) and selects the one with the specified tag.  Invoke this on any of the radios in the group.  A replacement for -[NSMatrix selectCellWithTag:].

@param tag The tag value to select.

@author DJS 2015-01.
*/

- (void)dejal_selectRadioWithTag:(NSInteger)tag;
{
    [self dejal_enumerateRadiosUsingBlock:^(NSButton *radio, BOOL *stop)
     {
         radio.state = radio.tag == tag;
     }];
}

/**
Assuming the receiver is a radio button, finds other radio buttons in the group (i.e. in the same superview and with the same action) and returns the tag value of the selected radio.  Invoke this on any of the radios in the group.  A replacement for -[NSMatrix selectedTag].

@returns A tag value integer.

@author DJS 2015-01.
*/

- (NSInteger)dejal_selectedRadioTag;
{
    NSButton *foundRadio = [self dejal_radioPassingTest:^BOOL(NSButton *radio, BOOL *stop)
     {
         return radio.state;
     }];
   
    return foundRadio.tag;
}

/**
Returns YES if the radio group is enabled, or NO if not.  Simply returns the state of the receiver; the others are assumed to be the same.  (If you want to know if they are all enabled or disabled, probably best to use -dejal_enumerateRadiosUsingBlock: to scan the group, and handle a mixed case as needed.)

@author DJS 2015-01.
*/

- (BOOL)dejal_radiosEnabled;
{
    return self.enabled;
}

/**
Sets all of the radios in the group to be enabled or disabled.  A replacement for -[NSMatrix setEnabled:].

@author DJS 2015-01.
*/

- (void)dejal_setRadiosEnabled:(BOOL)enabled;
{
    [self dejal_enumerateRadiosUsingBlock:^(NSButton *radio, BOOL *stop)
     {
         radio.enabled = enabled;
     }];
}

/**
Assuming the receiver is a radio button, finds other radio buttons in the group (i.e. in the same superview and with the same action) and performs the block for each of them, passing the radio to the block.  Returns the one that returns YES, or nil if the block requests to stop before completion, or it completes without the block returning YES.  Invoke this on any of the radios in the group.

@param block A block that takes a radio button and stop boolean reference as parameters and returns a boolean.
@returns The found radio button, or nil if none is found.

@author DJS 2015-01.
*/

- (NSButton *)dejal_radioPassingTest:(BOOL (^)(NSButton *radio, BOOL *stop))predicate;
{
    for (NSButton *radio in self.superview.subviews)
    {
        // There's no reliable way to determine if a button is actually a radio button, but it's reasonable to assume that no non-radio will have the same action (and having the same action is what makes it a member of the group):
        if ([radio isKindOfClass:[NSButton class]] && radio.action == self.action && predicate)
        {
            BOOL stop = NO;
           
            if (predicate(radio, &stop))
            {
                return radio;
            }
           
            if (stop)
            {
                return nil;
            }
        }
    }
   
    return nil;
}

/**
Assuming the receiver is a radio button, finds other radio buttons in the group (i.e. in the same superview and with the same action) and performs the block for each of them, passing the radio to the block.  Invoke this on any of the radios in the group.

@param block A block that takes a radio button and stop boolean reference as parameters and returns void.

@author DJS 2015-01.
*/

- (void)dejal_enumerateRadiosUsingBlock:(void (^)(NSButton *radio, BOOL *stop))block;
{
    for (NSButton *radio in self.superview.subviews)
    {
        // There's no reliable way to determine if a button is actually a radio button, but it's reasonable to assume that no non-radio will have the same action (and having the same action is what makes it a member of the group):
        if ([radio isKindOfClass:[NSButton class]] && radio.action == self.action && block)
        {
            BOOL stop = NO;
           
            block(radio, &stop);
           
            if (stop)
            {
                return;
            }
        }
    }
}

@end

To use these methods, simply invoke on any of the radios in the group, e.g.

    [self.iconNoneRadio dejal_selectRadioWithTag:self.statusIconKind];
    self.iconNoneRadio.dejal_radiosEnabled = use;

And:

- (IBAction)chooseIcon:(id)sender;
{
    self.statusIconKind = self.iconNoneRadio.dejal_selectedRadioTag;
   
    [self maintainControls];
}

I hope this helps others! And, of course, if I'm missing anything obvious, or you have any suggestions or comments, please let me know.

This look useful? Do check out my other open source projects, too.

(Looking for an iOS or Mac developer? I'm available for contract or full-time work. Learn more about me.)

DejalAppKitExtensionCategories: an open source collection of categories to extend Foundation classes for OS X

I just released another open source collection of categories: DejalAppKitExtensionCategories.

DejalAppKitExtensionCategories is a collection of categories to extend Foundation classes with methods specific to OS X. The categories include:

  • NSAttributedString+AppKit+Dejal: Adds convenience initializers to load from a bundle resource, image URL or RTF, and to return a RTF.
  • NSDictionary+AppKit+Dejal: Extends NSDictionary and NSMutableDictionary to support NSColor values.
  • NSFileManager+AppKit+Dejal: Adds a method to determine if a path is a directory but not a package, and to move a file to the Trash.
  • NSObject+AppKit+Dejal: Extends the NSObject base class with an old-style modal did-end perform selector, and modifier key detection.
  • NSSString+AppKit+Dejal: Adds methods that depend on fonts, points and colors.
  • NSUserDefaults+AppKit+Dejal: Adds support for points, sizes and colors in NSUserDefaults.

These categories depend on the previously-published DejalFoundationCategories.

You can get the code and more information from the Dejal Open Source page.

Next up: my UIKit categories.

(Looking for an iOS or Mac developer? I'm available for contract or full-time work. Learn more about me.)

DejaAppKitCategories: an open source collection of AppKit-level categories for OS X

I just released some more open source code: DejalAppKitCategories.

DejalAppKitCategories is a collection of categories for AppKit on OS X, to add useful methods to classes like NSMenu, NSTableView, NSTextView, and others. The categories include:

  • NSButton+Dejal: A text color property and a method to display a menu.
  • NSImage+Dejal: Methods to draw flipped images, apply a badge or tint, or get a PNG representation.
  • NSMenu+Dejal: Methods to add and remove items.
  • NSOutlineView+Dejal: Methods for selected items and displaying a menu.
  • NSPopUpButton+Dejal: Methods to add and select items.
  • NSScreen+Dejal: Screen name methods.
  • NSSplitView+Dejal: Methods for split positions and collapsing and expanding.
  • NSTableView+Dejal: Selection, column and copying methods.
  • NSTextField+Dejal: Methods to set values, synchronize with a slider, and resize the window (using autoresizing).
  • NSTextView+Dejal: Properties for string, attributed string and RTF values, methods for length, range, appending, and selection.
  • NSToolbar+Dejal: Methods for the toolbar height and finding an item by identifier.
  • NSView+Dejal: Add a view as a fully-constrained subview, adjust autoresizing, scale, and set the alpha opacity.
  • NSWindow+Dejal: Methods to force editing to end, fade in a window, and adjust the sizing of the window based on a view.

You can get the code and more information from the Dejal Open Source page.

Stay tuned for more OS X and iOS open source code, coming your way over the next week or so.

(Looking for an iOS or Mac developer? I'm available for contract or full-time work. Learn more about me.)

DejalFoundationCategories: an open source collection of Foundation-level categories

Today I released two new open source projects: a tiny one, DejalUtilities, and a significantly larger one, DejalFoundationCategories.

DejalFoundationCategories is a collection of Foundation-level categories, to add useful methods to classes like NSArray, NSDictionary, NSString, and others. They work on both OS X and iOS, and include:

  • NSArray+Dejal: 30+ methods extending NSArray and NSMutableArray, including object matching, reversal, sorting, deep copying, adding and removing.
  • NSAttributedString+Dejal: 10+ methods extending NSAttributedString and NSMutableAttributedString, including convenience initializers, RTF and font methods.
  • NSData+Dejal: A couple of methods to make archiving and unarchiving objects slightly more convenient.
  • NSDate+Dejal: 50+ methods extending NSDate, including convenience initializers, handy date component properties and calculators, JSON date support, string formatting, and relative date output.
  • NSDictionary+Dejal: 25+ methods extending NSDictionary and NSMutableDictionary, including object matching, scalar support, deep copying, and more.
  • NSFileManager+Dejal: 15+ methods extending NSFileManager, including convenient file attributes, file renaming, and path building.
  • NSObject+Dejal: 15+ methods extending the NSObject base class, including key-value conveniences, “equivalent” comparisons, and performSelector methods.
  • NSString+Dejal: 80+ methods extending NSString and NSMutableString, including scalar value formatting, contains evaluation, comparisons, substring and range utilities, reformatting, checksum and encoding utilities, internet utilities, file path methods, and appending and replacing methods.
  • NSUserDefaults+Dejal: 15+ methods extending NSUserDefaults, including support for default values, sanitizing values, time intervals, factory settings, and copying preferences.

DejalUtilities is a single header file with some useful #define macros and static functions, also for both OS X and iOS:

  • Adds DejalClassAvailable() and DejalClassSelectorAvailable() macros, to help determine available APIs.
  • Adds DejalIntervalFromMinutes(), DejalIntervalFromDays(), DejalMinutesFromInterval(), DejalMonthsFromInterval(), and similar macros, to convert seconds to and from other units.
  • Adds DEJAL_FILE_NAME and DEJAL_COMPILE_DATE_TIME macros, to help with debug information.
  • Adds a DejalWeakSelf macro to easily make a weak representation of self for use with blocks.
  • Plus several other handy macros and inline functions.

You can get the code and more information from the Dejal Open Source page.

(Looking for an iOS or Mac developer? I'm available for contract or full-time work. Learn more about me.)

DejalBackgroundView: open source project for Mac to provide a background color and/or border color to a view

Yesterday Brent Simmons posted his first go at using IB_DESIGNABLE and IBInspectable, for a simple NSView subclass to provide a backgroundColor property to a view.

I've had a similar simple class for a while, so that inspired me to push it to GitHub as open source, too. My variation provides four new properties, to set either or both the background and border colors:

  • Adds a drawsBackground BOOL property. Defaults to YES.
  • Adds a drawsBorder BOOL property. Defaults to YES.
  • Adds a backgroundColor NSColor property. Defaults to nil, but lazily assigns a light blue color if not set to another color.
  • Adds a borderColor NSColor property. Defaults to nil, but lazily assigns a light gray color if not set to another color.
  • IB_DESIGNABLE support for all four properties, so they can be set in Interface Builder.

To use it, simply include the DejalBackgroundView.h and DejalBackgroundView.m files in your project, then change a container NSView to DejalBackgroundView in the view hierarchy, and set the colors as desired (via code or IB), e.g.:

self.backgroundView.backgroundColor = [NSColor lightGrayColor];

If you only want a background without a border, easily turn off the border drawing:

self.drawsBorder = NO;

All of these properties can be set in IB thanks to the IB_DESIGNABLE and IBInspectable attributes in Xcode 6.

As a bonus, the view is automatically treated as opaque if the background color is used, and is itself opaque.

This is a very simple class, but quite useful.

You can get the code and more information from the Dejal Open Source page.

(I've been meaning to push out more of my code as open source; I would like to take the time to release more, to give back to the community. Please let me know if you read this and would like to encourage me to do so.)

Dejal Open Source

Like anything else, source code can get a bit untidy and crusty after a while. My Cocoa code is about a decade old now, so it's been well overdue for a cleanup. I have just completed a process of reorganizing all of the source code for future Lion and iOS 5 editions of Dejal projects.

One of the changes was to rename the open source and shared code to use a "Dejal" prefix rather than "DS" as before. Although I could have left the old prefix, the new one fits better with Apple's naming guidelines: they reserve all two-letter prefixes for themselves.

I also rearranged the code. I used to have separate Subversion repositories for iOS and Mac projects. Now I have code for iOS, Mac and cross-platform mixed in a repository, since there is a lot of code that works on both platforms.

Another big change was to move the open source projects from Subversion to Git. I've long resisted Git, but it really seems the most popular version control system for open source projects. And GitHub is a very nice way to share Git projects, with handy online viewing of documentation and source code, plus useful additions like issue tracking.

So now my open source projects are hosted on GitHub. You can view my GitHub page. Initially I have two iOS projects there: the small DejalView (formerly DSView) project, plus the very popular DejalActivityView (formerly DSActivityView) project. I will add more over time.

These projects have been updated for iOS 5 and ARC, plus general code improvements. They also have new README and license files. They use a standard BSD license.

I also took this opportunity to redesign the Dejal Developer page. The design was inspired by Matt Gemmell's excellent open source page (I got his permission to copy his design, but it ended up somewhat different).

Take a look at the new pages:

  • Dejal Open Source: the main Developer page, with general information at the top, followed by information on each project (with more to come).
  • Dejal Open Source License: a summary of my intention with the license for the source code, suggested attribution format, and the legal text.
  • Developer Store: enables you to donate in appreciation for the open source, or buy non-attribution licenses.

This last is interesting — the open source is available completely free, provided that you give Dejal credit in your app's About view, documentation, or website. If the code helps you, it's often nice to give something back, so now you can express appreciation via a donation or (just for fun, also inspired by Matt) a gift from my Amazon.com Wish List. But if you can't or don't want to give credit, you can purchase licenses that allow you to use the code without having to give credit. Hopefully that'll cover everyone's needs, but if you have some other requirement, let me know.

I hope you find my open source code useful. Keep an eye out for future additions; I have a Mac project coming soon (a feature that will appear in Time Out 2).

You can follow @dejalopen on Twitter to get notifications of updates, or follow @dejal for general Dejal news. You can also subscribe to the RSS feed of the Dejal Blog filtered for Open Source topics.

DejalActivityView: open source iOS project to display an activity indicator with adjustable text

Note: this is an updated post based on an older one, due to renaming the project (was "DSActivityView") and moving it from Subversion to GitHub.

DejalActivityViewI wrote a reusable class for a couple of iOS apps I was working on, called DejalActivityView. I decided to release it as open source. It has proven quite popular with others, too. Read on for details (including a demo movie).

Firstly, I should say that this work was inspired in part by Matt Gallagher's excellent article, Showing a "Loading..." message over the iPhone keyboard. My code only uses the -keyboardView method from his article, but he deserves credit and thanks for that and many other helpful articles. If you're not reading his blog, Cocoa with Love, you're doing yourself a disservice.

Back to my class. Actually, there are four classes: DejalActivityView, DejalWhiteActivityView, DejalBezelActivityView, and DejalKeyboardActivityView. They provide four styles of activity view, and could easily be extended to support more.

DejalActivityView

DejalActivityViewThis does a simple horizontal-style loading view, intended for situations where you have a blank view while loading data. It can be displayed very easily — for the default "Loading..." label text, simply use:

[DejalActivityView activityViewForView:self.view];

The activity view is automatically added as a subview of the specified view (e.g. the current content view). No need to save the result to an ivar. It automatically supports rotation to any orientation, too.

You can specify a custom label via:

[DejalActivityView activityViewForView:self.view withLabel:@"Processing..."];

Or specify a custom width, e.g. so you can change the label while it is being displayed without upsetting the geometry, via:

[DejalActivityView activityViewForView:self.view withLabel:@"Connecting..." width:100];

You can also have it manage the network activity indicator in the status bar, via a showNetworkActivityIndicator boolean property. It is NO by default, but if set to YES the network activity indicator in the status bar will be displayed, and automatically hidden when the DejalActivityView is removed. For example:

[DejalActivityView activityViewForView:self.view].showNetworkActivityIndicator = YES;

You can also toggle it on an already-visible activity view:

[DejalActivityView currentActivityView].showNetworkActivityIndicator = YES;

When you're done with the activity view, simply invoke this to get rid of it:

[DejalActivityView removeView];

DejalWhiteActivityView

This is the same as DejalActivityView, but with a white indicator and text instead of black, for use in dark views.

DejalBezelActivityView

DejalBezelActivityViewThis is a subclass of DejalActivityView, which displays an animated round-rect-enclosed variation: it animates into view by zooming from full-screen, with a gray background fading in to cover the passed view, and animates out by zooming to half size and fading out the background (see below for a movie showing it in action). It is ideal for situations where you have content visible already, but want to do a network operation to validate or send data, or some other time-consuming activity.

Display it the same way as DejalActivityView:

[DejalBezelActivityView activityViewForView:self.view];

The [DejalBezelActivityView activityViewForView:withLabel:] and [DejalBezelActivityView activityViewForView:withLabel:width:] variations are also available.

You can also split the label over multiple lines, thanks to a change contributed by Suleman Sidat. Thank you! Simply include one or more \n sequences in the label text, e.g.

[DejalBezelActivityView activityViewForView:self.view withLabel:@"Split over\nMultiple lines..."]

Similarly, to display an activity view with just the activity indicator, and no label, simply specify a blank label:

[DejalBezelActivityView activityViewForView:self.view withLabel:@""]

To remove with animation, call:

[DejalBezelActivityView removeViewAnimated:YES];

DejalKeyboardActivityView

DejalKeyboardActivityViewThis is a subclass of DejalBezelActivityView, which displays over the keyboard, somewhat like the iOS 2 Text app used to do. It is useful to simply prevent further typing while validating a field or sending data (though you might also want to disable the field, to prevent pasteboard operations on it). No need to specify a view to use for this, since it uses the keyboard. (This class may be removed in the future, since it isn't that useful nowadays; let me know if you need it.)

[DejalKeyboardActivityView activityView];

Plus a [DejalKeyboardActivityView activityViewWithLabel:] variation for custom text. Remove it the same as for the bezel style:

[DejalKeyboardActivityView removeViewAnimated:YES];

Demo

I've included a demo project that builds an app to show the various options: the four styles, default or custom label text, covering just the content view or whole window, etc. It requires iOS 5. Here's a movie of an earlier version of the demo, showing the demo app running (this project used to be called "DSActivityView"):

You can get the code and more information from the Dejal Open Source page.

DejalView: open source project for iOS to detect a tap outside a button

Note: this is an updated post based on an older one, due to renaming the project (was "DSView") and moving it from Subversion to GitHub.

In Tweeps I have a button that I wanted to behave like the Delete button in a table view. You know, when you tap the delete toggle to the left of a cell, a red Delete button appears. And tapping anywhere other than that button will hide it without doing anything else:

Table Delete button
(Contacts app)

I couldn't see any obvious way to do it, so asked on the iPhone Developer forums, and got a helpful reply suggesting a UIWindow subclass, overriding -sendEvent:.

I tried implementing that, but what I really wanted was to override -hitTest:withEvent:, since I wanted to block taps on views other than a specific button, and the documentation says one should always invoke the superclass of -sendEvent:.

Then I noticed that -hitTest:withEvent: is actually defined in UIView, and further experimenting with the table Delete feature showed that it appears to be implemented UITableView, since the cancel tap behavior only occurs in the table, not the navigation bar or toolbar. Besides, implementing in a UIView subclass is more focal, so a better choice.

So here is my UIView subclass to do this. It uses a delegate approach, with a protocol to declare the method:

@class DejalView;

@protocol DejalViewDelegate <NSObject>
@optional

- (UIView *)view:(DejalView *)view hitTest:(CGPoint)point withEvent:(UIEvent *)event hitView:(UIView *)hitView;

@end

And the actual subclass interface:

@interface DejalView : UIView

@property (nonatomic, weak) id <DejalViewDelegate> viewDelegate;

@end

With the implementation just overriding the hit test method. It simply invokes the superclass then gives the delegate a chance to change it (or perform some other action) if it implements the delegate protocol method:

#import "DejalView.h"


@implementation DejalView

@synthesize viewDelegate = dejalViewDelegate;

/*
  hitTest:withEvent:
 
  Overrides this method to add support for the -view:hitTest:withEvent:hitView view delegate behavior.
 
  Written by DJS 2009-09.
*/

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
{
    UIView *hitView = [super hitTest:point withEvent:event];
   
    if ([self.viewDelegate respondsToSelector:@selector(view:hitTest:withEvent:hitView:)])
        return [self.viewDelegate view:self hitTest:point withEvent:event
            hitView:hitView];
    else
        return hitView;
}

@end

To use this, simply change a container UIView to DejalView in the view hierarchy, then set the delegate property to your view controller (via code or IB):

self.view.viewDelegate = self;

Then implement the -view:hitTest:withEvent:hitView: delegate method in your view controller, e.g. as follows — this will cause a tap on some special control (or if that control is hidden) to go through as normal, but tapping anywhere else in the view will hide the special control, without passing the tap on to whatever was actually tapped:

- (UIView *)view:(DSView *)view hitTest:(CGPoint)point
        withEvent:(UIEvent *)event hitView:(UIView *)hitView;
{
    if (someSpecialControl.hidden || hitView == someSpecialControl)
        return hitView;
   
    someSpecialControl.hidden = YES;
   
    return nil;
}

I hope this is useful to someone else too.

You can get the code and more information from the Dejal Open Source page.

DSActivityView updated to support multiple label lines, and demo of no label

This blog post has been replaced by a newer edition.

Please see blog posts on DejalActivityView.

DSActivityViewI've committed a minor update to the DSActivityView open source project for iOS. See the DSActivityView introductory post for more information, including a video demo.

This update adds support for splitting the label over multiple lines for the DSBezelActivityView variation. This change was contributed by Suleman Sidat. Thank you!

To use multiple lines for the label, simply include one or more \n sequences in the label text, e.g.


[DSBezelActivityView newActivityViewForView:self.view withLabel:@"Split over\nMultiple lines..."]

Similarly, to display an activity view with just the activity indicator, and no label, simply specify a blank label:


[DSBezelActivityView newActivityViewForView:self.view withLabel:@""]

DSActivityView is compatible with iOS 3.0 and later, including iOS 4 (and I believe iOS 5), on iPad, iPhone and iPod touch.

You can get the project from my Dejal Open Source Subversion repository via this Terminal command:


svn checkout http://dejal.svn.beanstalkapp.com/open/DSActivityView

Or browse the source directly on the web.

If you make any enhancements to DSActivityView, please contribute them back to me so I can share with other developers.

DSActivityView updated for iOS 4

This blog post has been replaced by a newer edition.

Please see blog posts on DejalActivityView.

DSActivityViewI've committed a minor update to the DSActivityView open source project for iOS. See the DSActivityView introductory post for more information, including a video demo.

This update adds a new prefix to the class methods to create the activity view, to make it more clear that they return a retained object, rather than autoreleased.

So you'd now display the activity view via something like this:


[DSActivityView newActivityViewForView:self.view]

This update also includes a bug fix for iOS 4, where the activity view would appear behind the keyboard.

It is compatible with iOS 3.0 and later, including iOS 4, on iPad, iPhone and iPod touch.

You can get the project from my Dejal Open Source Subversion repository via this Terminal command:


svn checkout http://dejal.svn.beanstalkapp.com/open/DSActivityView

Or browse the source directly on the web.

Building Universal iPad/iPhone apps

Warning, developer topic... uninterested customers can skip on to the next post....

I was having difficulty getting my new app, Tweeps, working as a universal app: running natively on both iPhone and iPad from one binary.

For some reason, I was in a Mac universal mindset. For Mac apps, a universal app uses two separate targets: one for PowerPC, one for Intel. This is necessary since they are of course very different architectures, so have to be compiled separately.

In iPhone OS, that isn't the case — both iPhone and iPad have the same processor architecture, so both editions can use the same code without needing conditional compilation.

However, there are important differences. Currently, iPhone (and iPod touch) is on iPhone OS 3.1.3, whereas iPad is on OS 3.2. The iPhone can't use OS 3.2, and iPad can't use 3.1. So extra steps are required.

The way this works is to set the Base SDK to the latest one you want to use (in this case 3.2), and the Deployment Target to the earliest you want to support (3.1). You can then use any available APIs from 3.1 and earlier with impunity, and can use APIs from 3.2 if you check that they are available before using them.

The recommended way to check for a new method is to use +instancesRespondToSelector:. For example, 3.2 renames the method to hide the status bar. So to use the new method if available, or fall back to the old method, you'd write:

    if ([UIApplication instancesRespondToSelector:@selector(setStatusBarHidden:withAnimation:)])
        [[UIApplication sharedApplication] setStatusBarHidden:hiding withAnimation:YES];
    else
        [[UIApplication sharedApplication] setStatusBarHidden:hiding animated:YES];

Sometimes, you need to take alternative code paths depending on whether you're running on iPad or iPhone. So the way to do that is:

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
        ...

I used the following definition to save some typing:

#define IS_IPAD        (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

That's all fine and good. But things get more curly when you want to use new classes. Apple's TopPaid sample code demonstrates the best way to handle this. You can load different xibs depending on which device you're running on, to set up the root views (e.g. to use a split view or a navigation controller). Each xib would load a different controller, which would contain relevant properties for each view.

There's one final gotcha that caused me trouble recently: you have to be careful about programmatically allocating newly introduced classes. If you run and it crashes with an error like the following, this is the issue:

dyld: Symbol not found: _OBJC_CLASS_$_UIPopoverController

The solution is to use NSClassFromString to resolve the class name, like so:

    Class popoverClass = NSClassFromString(@"UIPopoverController");
   
    if (popoverClass != nil)
        popoverController = [[popoverClass alloc] initWithContentViewController:contentViewController];

What got me was that this is necessary even in code only called on iPad, which I thought counter-intuitive. But it makes sense on further reflection, as the dynamic nature of ObjC means that it wants to resolve all class references when loading the bundle on startup — it doesn't know that that code won't ever get called if the current device happens to be an iPhone.

I hope this helps others having difficulty building universal iPhone/iPad apps.

More Tweeps for iPad mockups

I previously posted some mockups of an iPad edition of Tweeps, my new iPhone OS app to easily manage Twitter accounts.

But as I said, I've been struggling with coming up with a satisfactory design. My latest thought is that I shouldn't try to emulate a physical object like a notepad or book, but would be better following Apple's example with apps like Mail, and use a split view.

So here are my latest design mockups (again done in OmniGraffle).

In landscape orientation, a split view shows a list of pages on the left, and the details on the right; in this case, the main Profile page for your own account (click to see full-sized):

In portrait, the left view appears in a "popover" instead:

I'm not sure if the Edit button should be in the left or right views... but there's more room in the right, so that seems reasonable.

Here's a sample of the Following page in landscape:

What do you think? I think this is a better design, more clean and consistent — and allows me to use much the same color theme as on the iPhone, as a minor bonus.

Tweeps for iPad mockup

Now that Tweeps is available in the iPhone App Store, I'm starting work on the iPad edition.

Obviously, the iPad has a lot more screen space than the iPhone, so a different design is needed to take full advantage of this extras space. I've been thinking about iPad design concepts ever since the iPad was announced, but have yet to come up with something that entirely satisfies me.

A difficulty with coming up with a good design is that Tweeps can show any number of levels. You start with a list of your accounts, then show your profile overview, then can show a list of people you're following (for example), then delve deeper by showing the profile overview for one of them, and their followers, and so on to any number of levels. This works fine with the navigation display in the iPhone edition, where you can keep pushing views onto the screen, but is harder with a more traditional interface.

For quite a while, I've been thinking about something like the iPad Contacts app design, with a two-page book metaphor. The idea would be to display the profile details on the left page, and the avatar, web, map, following, followers etc views on the right (one at a time). It'd then flip the page when viewing a different person's information. That seems like a reasonable approach, though the very different content displays on the right seems to break the book metaphor.

The latest idea I've been exploring is more of a notepad metaphor. The idea is a single notepad page with the profile overview, and bookmark tabs (like Post-it® flags) sticking out from the right for related pages like those listed above. So you touch a tab to flip the notepad to that page. There would also be bookmark tabs on the left side to go back to the previous profile(s) or the accounts list.

Here's a rough mockup, done in the great OmniGraffle (click to see full-sized):

Don't worry about the fine details; as I said, it's very rough.

You'd tap the Following tab on the right to flip pages to the Following view, which would be similar to that in Tweeps now, except would have room to show more information about each person:

You could then go back to the profile overview via the new tab with the avatar icon on the left, or go straight to other pages via the other tabs on the right.

If you tap a row in the Following list, it'd flip the page to the profile overview for that person, and the tabs on the right would then show more information about them.

It might look better with a black background, to merge into the iPad bezel, as follows. In which case I'd eliminate the space around the edges (still shown in this mockup), providing more room for the content:

What do you think? Would this design work, or am I on the wrong track? Should I forget about trying for a real-world look? I'd love to hear other design ideas too.

In App Purchase for free iPhone apps

iPhone App StoreI've been meaning to follow up on this for the past few days. Back in March, I wrote a blog post titled "Apple, please support iPhone trial apps", where I urged Apple to reconsider their position that "free apps will always be free."

I discussed how not allowing free apps to use the new In App Purchase feature introduced in iPhone OS 3.0 was very limiting — this restriction prevented free trials of apps, as is very popular in Mac software, meaning that the only way a developer could provide a trial is to have two separate apps (e.g. Lite and Pro editions), which is more hassle for the developer and customers, including issues with migrating data.

So you can imagine how pleased I was when I learnt recently that Apple has removed this restriction. Now any app can use In App Purchase, including free ones. So now the trial distribution model is finally feasible.

There is no more need to release Lite and Pro editions. Developers can release a single application as a free download, perhaps with limited features, then enable people to purchase an upgrade to the "full" edition.

There are still some restrictions. The most common trial model for Mac software is a time-limited demo, where the application lets people evaluate it for a certain time period (e.g. 14 or 30 days), then disables some functionality if still not purchased. My Mac apps do this, as do most other "shareware" apps. This is not allowed on the iPhone App Store. iPhone apps have to be fully functional, and can't disable essential features.

But there are other things that can be done, like provide a basic set of functionality, and perhaps display ads, then the purchase provides extended functionality and removes the ads. So the app remains useful forever as a free app, but becomes more powerful (and without distracting ads) if the user chooses to purchase.

This is what I plan to do for my future iPhone apps. For the secret project I'm working on now, I'll release it as a free app, with ads and perhaps some feature limits (but nothing that impinges on the usability), then offer In App Purchase to disable the ads and extend the functionality.

It looks like it'll be quite easy to set up; for this sort of situation, it appears that the purchase can be handled entirely via Apple's server, storing the purchase in the iTunes account, enabling multiple phones used by that account to use the full app (e.g. when buying a new phone).

I haven't decided on terminology for this yet... call the purchased upgrade the "Paid" or "Premium" edition, or something else.

It'll be most interesting to see how many app developers adopt this technique. I've seen a few so far, and expect it to be quite popular, especially with the indie developers who are used to this kind of distribution model.

But, thank you Apple!

Syndicate content