Hiring: Customer Support Specialist

Update: This position has been filled.

Do you love Mac software and know your way around the Mac operating system (OS X)? Are you looking for part-time contract work at home? If so, we have an opportunity for you! πŸ™‚

Tao Effect LLC is currently looking to hire an independent contractor to help us with tech support for our highly rated OS X app, Espionage.

Candidate Requirements

  • Strongly familiar with Mac OS X and its software and utilities
  • Excellent command of the English language
  • Familiarity with Espionage (meaning you’ve at least downloaded and used the program)
  • Clear and friendly communications skills

Job Details

  • Flexible hours (approx. 5-10 hrs/week)
  • Work from home or wherever you find yourself

To apply, send an email to contact at taoeffect.com with subject ‘Tech Support Specialist’ with your resume.

Notice of delayed support due to emergency

I (Greg) recently found myself in an auto accident that resulted my hospitalization for a period of a few days, during which I was not able to access the internet. The good news is that no one is injured, the bad news though, for which I apologize profusely, is that we have a backlog of support email and forum posts. It has been several days since people have sent us support requests and they have not received a response at all because at the moment no one other than Greg is handling that. We are looking to rectify this situation.

We are very grateful for your understanding as we deal with the inevitable mess from this incident. In the meantime, here are some resources that may be of help to you:

Many thanks for the understanding!

Greg Slepak
Tao Effect LLC

Espionage 2.8.13 Released!

I know I know! I’m getting tired of the 2.8 series as well! :-p

  • NOTE: This update will cause the installer to run again (prompting you for your admin password)
  • FIXED: removed “failed to get node path” console messages in ispyd.log on Lion
  • FIXED: issue with application associations on Lion
  • IMPROVED: removed some 10.5 dependencies. recompiled with llvm-gcc.

Bear with us, Lion turned out to cause some unexpected issues that weren’t caught in the 2.8.12 release, so this maintenance release is mainly for Lion compatibility.

As mentioned, this release no longer supports Mac OS 10.5 Leopard. If you need a Leopard compatible version of Espionage, please use version 2.8.10 instead.

Enjoy! πŸ˜€

Espionage 2.8.12 Released!

Espionage 2.8.12 brings two important announcements:

  • NOTE: With Lion approaching, this will likely be the last release to support Mac OS 10.5 Leopard
  • CHANGED: Removed ability to backup unlocked folders for integrity reasons (you can force it on an unlocked folder by holding the Option key when clicking on Espionage in the menubar)
  • FIXED: “Folder is being backed up, please wait…” notification appearing inappropriately
  • FIXED: “Backups Successful” notification appearing inappropriately
  • FIXED: Update issue on Leopard
  • IMPROVED: Updated documentation on corrupt database error

Two Important Announcements

Leopard support is going away

Apple is moving quickly and the changes that they have been adding to Lion are making it difficult to continue support Leopard. We want to make sure though that our customers have plenty of time to update to the latest and greatest operating system from Apple, which is why we make it a point to support the current OS and the previous OS. With the release of 2.8.11, we managed to release a version of Espionage that supports all three operating systems, but it was not easy. We would like to take advantage of some of the great new features coming in Lion, so, with Lion expected next month, this will likely to be the last version of Espionage to support Leopard.

Unlocked folders will no longer automatically backup

Removing a feature, especially a feature that is probably used by many, definitely requires justification, and so, here is why we are doing this:

Espionage’s backups uses a program called rsync to quickly backup only the changed items of an encrypted folder. It came to our attention recently that there was a small but nonetheless real possibility that a corrupt backup could be made if a folder was being backed up while its contents were being modified. Because rsync is not designed to handle this situation properly, we have temporarily disabled this feature to avoid the risk of creating corrupt backups.

HOWEVER! We have not completely disabled it. πŸ˜€

Because the ability to backup folders while they’re unlocked is so useful, we’ve made it possible to do so via a little hidden shortcut until we can properly and safely handle the unlocked-folder backup situation ourselves:

  1. Hold down the Option key and then click on the Espionage icon in the menu bar.
  2. As you hover over the unlocked folders, you should see the “Backup Now” menu item appear on them.

You can then click on “Backup Now”, but only if you’re certain that the folder is not being accessed by any program, and make sure to not do anything with the folder until the backup is finished.

Enjoy! πŸ˜€

Espionage 2.8.11 Released!

Espionage 2.8.11 provides Lion compatibility and other enhancements and fixes:

  • NEW: Lion support (now fully 64-bit!)
  • NEW: Complete extended attributes support for the encrypted folder itself (xattrs on files inside the folder were always supported)
  • NEW: Bitcoin application template
  • IMPROVED: Custom folder icon support
  • IMPROVED: Prevent unlocked folders from being backed up to ensure backup integrity
  • IMPROVED: Updated documentation and added example with link to blog instructions for Microsoft Outlook
  • FIXED: Espionage won’t complain if run from an external disk (or separate partition)
  • FIXED: Helper failed to run if home folder is located on non-root drive (e.g. /Volumes/etc..)
  • FIXED: Problems encrypting iPhoto Library
  • FIXED: “Don’t have a record for it” messages wouldn’t go away (hopefully for real this time)
  • FIXED: Alignment of master password field with written text above it
  • FIXED: On folder restore, sparsebundles were incorrectly sent to the trash with a sparseimage file extension
Lion Support

It is recommended to update Espionage prior to upgrading to Lion. If you were not able to do this, no sweat, simply download a fresh copy from our website and replace your copy of Espionage with it.

Full Extended Attributes support

Extended attributes are pieces of data that can be attached to files and folders (metadata in a sense). Some applications on OS X use them for various purposes, like indicating that a folder should be displayed as a bundle in the Finder (iPhoto does this). Espionage now fully supports them on the encrypted folders themselves (extended attributes on files and folders *within* the encrypted folder have always been supported).

Enjoy! πŸ˜€

Better Objective-C through Clojure Philosophy

EDIT June 2, 2014: The TERecord implementation described here is not how the current TERecord implementation works. I updated the README on GitHub to show how it currently works.

Ever since discovering Lisp, I’ve felt an almost… loathing for other languages. They make things too complicated. You feel that other languages encourage code that ends up ugly, verbose.. inelegant. This feeling is so well known, it even has a name, the “The Lisp Snob“. Well, I’m quite sorry, but I think it’s too late for me, I’ve succumbed to the snobbery!

It’s possible, however, to bring over some of the ideas from the Land of Lisp to other languages. Indeed this is essentially what has been happening for the past several decades, people just call it something different (“a new language”, or “new features”). Today, I’d like to show what happens when you bring one such idea to Objective-C:

It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.

β€”Alan Perlis

PXSourceList is a wonderful and popular Cocoa view for creating iTunes-like source lists created by Alex Rozanski:

Unfortunately, the example code that created the screenshot above suffers from a very common programming paradigm called Object Oriented Programming (OOP). The OOP paradigm has dominated Earth’s developers for the past several decades. I used to follow (and teach!) it, but after learning Clojure, I’ve changed my mind. I now agree with the Alex Rozanski quote above and Rich Hickey: it is better to have a small number of core data structures (preferably immutable) that are manipulated by hundreds of functions.

For this post, I will focus on just one of the many benefits of dropping the OOP perspective: code brevity.

The chief source responsible for the screenshot above is copied below. My point here isn’t to ask you to actually read through all of it, just note its size.

Instead of this:

// =========================
// = File SourceListItem.h =
// =========================

#import <Cocoa/Cocoa.h>

@interface SourceListItem : NSObject{
    NSString *title;
    NSString *identifier;
    NSImage *icon;
    NSInteger badgeValue;
    
    NSArray *children;
}

@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *identifier;
@property (nonatomic, retain) NSImage *icon;
@property NSInteger badgeValue;

@property (nonatomic, copy) NSArray *children;

//Convenience methods
+ (id)itemWithTitle:(NSString*)aTitle identifier:(NSString*)anIdentifier;
+ (id)itemWithTitle:(NSString*)aTitle identifier:(NSString*)anIdentifier icon:(NSImage*)anIcon;


- (BOOL)hasBadge;
- (BOOL)hasChildren;
- (BOOL)hasIcon;

@end

// =========================
// = File SourceListItem.m =
// =========================

#import "SourceListItem.h"

@implementation SourceListItem

@synthesize title;
@synthesize identifier;
@synthesize icon;
@synthesize badgeValue;
@synthesize children;

#pragma mark -
#pragma mark Init/Dealloc/Finalize

- (id)init
{
    if(self=[super init])
    {
        badgeValue = -1;    //We don't want a badge value by default
    }
    
    return self;
}


+ (id)itemWithTitle:(NSString*)aTitle identifier:(NSString*)anIdentifier
{   
    SourceListItem *item = [SourceListItem itemWithTitle:aTitle identifier:anIdentifier icon:nil];
    
    return item;
}


+ (id)itemWithTitle:(NSString*)aTitle identifier:(NSString*)anIdentifier icon:(NSImage*)anIcon
{
    SourceListItem *item = [[[SourceListItem alloc] init] autorelease];
    
    [item setTitle:aTitle];
    [item setIdentifier:anIdentifier];
    [item setIcon:anIcon];
    
    return item;
}

- (void)dealloc
{
    [title release];
    [identifier release];
    [icon release];
    [children release];
    
    [super dealloc];
}

- (void)finalize
{
    title = nil;
    identifier = nil;
    icon = nil;
    children = nil;
    
    [super finalize];
}

#pragma mark -
#pragma mark Custom Accessors

- (BOOL)hasBadge
{
    return badgeValue!=-1;
}

- (BOOL)hasChildren
{
    return [children count]>0;
}

- (BOOL)hasIcon
{
    return icon!=nil;
}

@end

// ======================
// = File AppDelegate.m =
// ======================

#import "AppDelegate.h"
#import "SourceListItem.h"

@implementation AppDelegate

#pragma mark -
#pragma mark Init/Dealloc

- (void)awakeFromNib
{
    [selectedItemLabel setStringValue:@"(none)"];
    
    sourceListItems = [[NSMutableArray alloc] init];
    
    //Set up the "Library" parent item and children
    SourceListItem *libraryItem = [SourceListItem itemWithTitle:@"LIBRARY" identifier:@"library"];
    SourceListItem *musicItem = [SourceListItem itemWithTitle:@"Music" identifier:@"music"];
    [musicItem setIcon:[NSImage imageNamed:@"music.png"]];
    SourceListItem *moviesItem = [SourceListItem itemWithTitle:@"Movies" identifier:@"movies"];
    [moviesItem setIcon:[NSImage imageNamed:@"movies.png"]];
    SourceListItem *podcastsItem = [SourceListItem itemWithTitle:@"Podcasts" identifier:@"podcasts"];
    [podcastsItem setIcon:[NSImage imageNamed:@"podcasts.png"]];
    [podcastsItem setBadgeValue:10];
    SourceListItem *audiobooksItem = [SourceListItem itemWithTitle:@"Audiobooks" identifier:@"audiobooks"];
    [audiobooksItem setIcon:[NSImage imageNamed:@"audiobooks.png"]];
    [libraryItem setChildren:[NSArray arrayWithObjects:musicItem, moviesItem, podcastsItem,
                              audiobooksItem, nil]];
    
    //Set up the "Playlists" parent item and children
    SourceListItem *playlistsItem = [SourceListItem itemWithTitle:@"PLAYLISTS" identifier:@"playlists"];
    SourceListItem *playlist1Item = [SourceListItem itemWithTitle:@"Playlist1" identifier:@"playlist1"];
    
    //Create a second-level group to demonstrate
    SourceListItem *playlist2Item = [SourceListItem itemWithTitle:@"Playlist2" identifier:@"playlist2"];
    SourceListItem *playlist3Item = [SourceListItem itemWithTitle:@"Playlist3" identifier:@"playlist3"];
    [playlist1Item setIcon:[NSImage imageNamed:@"playlist.png"]];
    [playlist2Item setIcon:[NSImage imageNamed:@"playlist.png"]];
    [playlist3Item setIcon:[NSImage imageNamed:@"playlist.png"]];
    
    SourceListItem *playlistGroup = [SourceListItem itemWithTitle:@"Playlist Group" identifier:@"playlistgroup"];
    SourceListItem *playlistGroupItem = [SourceListItem itemWithTitle:@"Child Playlist" identifier:@"childplaylist"];
    [playlistGroup setIcon:[NSImage imageNamed:@"playlistFolder.png"]];
    [playlistGroupItem setIcon:[NSImage imageNamed:@"playlist.png"]];
    [playlistGroup setChildren:[NSArray arrayWithObject:playlistGroupItem]];
    
    [playlistsItem setChildren:[NSArray arrayWithObjects:playlist1Item, playlistGroup,playlist2Item,
                                playlist3Item, nil]];
    
    [sourceListItems addObject:libraryItem];
    [sourceListItems addObject:playlistsItem];
    
    [sourceList reloadData];
}

By using “special” dictionaries, we can greatly reduce code size, and enhance flexibility

Clojure has the concept of “records”. These are basically “special dictionaries” (AKA maps). Clojure uses maps/dictionaries all over the place, because it realizes that it’s not only not necessary to create a new class for every type of data you have, but it’s counter-productive.

Today I spent some time adapting the idea of Clojure’s records into its Objective-C equivalent simply through the creation of categories on the NSDictionary and NSMutableDictionary classes. The result is that I was able to reproduce the screenshot above with the following code (equivalent sections to the ones above):

// ======================
// = File AppDelegate.m =
// ======================

#import "AppDelegate.h"

@protocol SourceListItem
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *identifier;
@property (nonatomic, retain) NSNumber *type;
@property (nonatomic, retain) NSImage *icon;
@property (nonatomic, retain) NSMutableArray *children;
@end

@implementation AppDelegate

- (void)awakeFromNib
{
    sourceListItems = [[NSMutableArray alloc] initWithObjects:
_MD(@"title", @"LIBRARY",
                           @"identifier", @"library",
                           @"children",
                           NSARY(_MD(@"title", @"Music",
                                     @"identifier", @"music",
                                     @"icon", NSIMG(@"music")),
_MD(@"title", @"Movies",
                                    @"identifier", @"movies",
                                    @"icon", NSIMG(@"movies")),
_MD(@"title", @"Podcasts",
                                     @"identifier", @"podcasts",
                                     @"icon", NSIMG(@"podcasts")),
_MD(@"title", @"Audiobooks",
                                     @"identifier", @"audiobooks",
                                     @"icon", NSIMG(@"audiobooks")))),
_MD(@"title", @"PLAYLISTS",
                           @"identifier", @"playlists",
                           @"children",
                           NSARY(_MD(@"title", @"Playlist1",
                                     @"identifier", @"playlist1",
                                     @"icon", NSIMG(@"playlist")),
_MD(@"title", @"Playlist Group",
                                     @"identifier", @"playlistgroup",
                                     @"icon", NSIMG(@"playlistFolder"),
                                     @"children",
                                     NSARY(_MD(@"title", @"Child Playlist",
                                               @"identifier", @"childplaylist",
                                               @"icon", NSIMG(@"playlist")))),
_MD(@"title", @"Playlist1",
                                     @"identifier", @"playlist1",
                                     @"icon", NSIMG(@"playlist")),
_MD(@"title", @"Playlist1",
                                     @"identifier", @"playlist1",
                                     @"icon", NSIMG(@"playlist")))),
                       nil];
    [sourceList reloadData];
}

Not only is the code above more flexible and significantly shorter, but it’s clearer as well! You can practically visualize the GUI’s structure from the code itself!

The rest of the code (related to the delegate methods) is essentially equivalent, except it’s again, shorter.

“Magic” Dictionaries with Properties!

Dictionaries are created using the _MD and _D macros (for creating mutable and immutable dictionaries, respectively). These are plain old standard Foundation dictionaries. They can be saved to the hard disk, read back, key-value coded, etc. The reason for the macros is code brevity and because they use a new dictionaryWithKeysAndObjects: method that’s added to all dictionaries through the categories (I have no idea why Apple’s engineers throught it would be better to have values come before keys as with dictionaryWithObjectsAndKeys:…).

Next, let’s have a look at how dictionary values are manipulated and obtained. Most semi-intelligent languages have a map literals and simple syntax for fetching and setting map key/value pairs. Objective-C has no such intelligence imparted to it. However, it does have the very nifty forwardInvocation: mechanism. By combining forwardInvocation: with Objective-C protocols and properties, it’s possible to create “Objective-C records”.

Instead of this:

- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
{
    //Works the same way as the NSOutlineView data source: `nil` means a parent item
    if(item==nil) {
        return [sourceListItems count];
    }
    else {
        return [[item children] count];
    }
}

We can now use the dot-syntax notation to do this:

- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id<SourceListItem>)item
{
    return item ? [item.children count] : [sourceListItems count];
}

Behind the scenes, item.children is turned into [item objectForKey:@”children”].

Setting values is also done through the standard dot-notation of Objective-C properties:

- (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id<SourceListItem>)item
{
    item.title = object;
}

Grab on Github

You’ll find the project for TERecord here, and the converted example of PXSourceList here.

Espionage 2.8.9 Released!

The changes below have been updated to include those of 2.8.10.

An important maintenance update on the road to 2.9!

  • NEW: Sparrow application template
  • IMPROVED: SmartMove is now even smarter (detects change in parent folder location at lock/unlock).
  • IMPROVED: Added documentation on how to recover from a corrupt database
  • IMPROVED: Espionage can create folders that haven’t been created yet (but should be, like “Mail Downloads”)
  • SECURITY: Remind user to secure-empty trash after changing the encryption on a folder
  • FIXED: 2.8.8 broke Backup on Lock
  • FIXED: “Don’t have a record for it” messages wouldn’t go away
  • FIXED (2.8.10): Apps did not get associated properly in 2.8.9

SmartMove Improved

Espionage’s SmartMove feature gets an update in this version, making it more robust. Previously, Espionage was only able to track when the folder itself was moved, but would lose track of it if one of the parent folders were moved or renamed. Now, Espionage will sync up with the folder’s new location once the folder is locked or unlocked. All of the code related to moving and renaming folders has also been consolidated into one main class.

Enjoy! πŸ˜€

Espionage 2.8.8 Released + Microsoft Outlook Tutorial

  • NEW: Evernote application template (be sure to quit the Elephant in the menubar before encrypting!)
  • NEW: Instructions for Microsoft Outlook
  • IMPROVED: iCal template associates Mail with the Calendars folder now because of Todos
  • IMPROVED: Outdated backups of sparseimages (when folder changed to sparsebundle) are deleted (and vice versa)
  • FIXED: Rare situation where Espionage’s main window wouldn’t show because of recent updates
  • FIXED: Issue where templates wouldn’t work if some core apps were deleted (like iPhoto, etc.)
  • FIXED: Rare hang that could occur during simultaneous backup + changing encryption of folder
  • FIXED: Possibly fixed issue encrypting folders outside of a FileVault encrypted Home directory, testing needed

Using Espionage With Microsoft Outlook

Espionage + Microsoft Outlook

We’re happy to announce that with the release of Office 2011, Microsoft has fixed an issue that made encrypting Entourage data with Espionage difficult. Now, Entourage is replaced by a program called Outlook, and while there are technical reasons as to why we can’t include an application template for Outlook, you can encrypt Outlook’s data with Espionage. Below are the instructions:

Step 1 – Quit all open Office Programs and Microsoft Messenger

Before messing with an application’s data, make sure it’s not running!

Step 2 – Drag the “Microsoft User Data” folder onto Espionage

It’s located in:

/Users/[your username]/Documents/Microsoft User Data

Step 3 – Associate this folder with *all* Office applications

Microsoft’s applications can access each others data, and because of this it makes more sense to just encrypt the entire Microsoft User Data folder instead of attempting to isolate individual folders (you’re welcome to try encrypting more specific folders, just make sure to keep backups).

So, the next step is to locate the Microsoft Office folder and drag all the applications in it onto the box near the bottom. If you use Microsoft Messenger, associate that as well (that will also keep your chat logs secured):

Step 4 (Optional) – Keep the folder unlocked while logged in

Since so many applications are associated with this folder, you might want to just have the folder remain unlocked while you are logged into your user account. You can do this by checking the Autounlock At Login checkbox for the folder (in Espionage), and also disabling the Lock On Quit checkbox by editing the application associations on the folder. If you choose to do this, just make sure to enable your screensaver password (in Apple’s Security System Preferences).

That’s It!

Your Office data and emails are now encrypted.

Enjoy! πŸ˜€

Happy Holidays from Tao Effect!

Hey there blog readers!

Hope your holiday break is going *fantastically well*!

If it’s going less then stellar, remember, what matters most is not so much the situation that you find yourself in, but how you decide to react to it.

I was reminded of this during a walk today, where I saw plenty of examples of people who have very few possessions or the “luxuries of modern day life”. Though they probably didn’t get a Kindle or an iPad under their non-existent Christmas tree, they nevertheless were smiling, cracking jokes, and some were jamming on one instrument or another. I’m sure every passerby’s day was improved as a result. Their joy was contagiousβ€”it certainly made me smile.

This, I think, is what the Christmas spirit is really about. No matter where you find yourself, you get to choose how you react to your circumstances, and some of the best gifts you can give don’t cost a penny.

Happy Holidays,

Your Friends at Tao Effect