2010-03-22 20:00:00 -0400
We’ve been really busy here at Zetetic lately and we’re looking for a new teammate to help us develop and support client systems and our own products. As a small and flexible software development consultancy, we’re mostly interested in personal qualities, not resume buzzwords:
- Natural or Artificial intelligence (robots welcome)
- Intense desire to learn new things & hack on technology
- An unflappably positive attitude
- Plays well with others
A background in one or more of the following goes a long way too!
- Strength in at least one Object Oriented Programming Language (C#, Ruby, Java, Obj-c). Anything except VB.
- Desire to “switch things up” and work on multiple programming languages
- Experience with databases, relational SQL, or otherwise (mongo, couch, etc)
- Security Technology (i.e LDAP authentication, Single Sign-on, PKI)
- Linux and general networking
- 2-5 years of software development experience
- Located in Philadelphia, NYC, or Central NJ to allow collaborative work with our current team members
In short, we mostly care that you’re smart, love to hack, and get things done.
If this sounds like you, or someone you know, please reach out and let us know: sjlombardo@zetetic.net.
2010-03-19 20:00:00 -0400
Updated: 6/6/2012 This blog post is out of date, and the software referenced in it, Strip Sync, has been discontinued in favor of Strip for Windows and Strip for OS X.
I have a couple small things to fix still, but we’re planning to start the beta for Strip Sync next week. We’ve been getting a lot of “where is it!?!” emails, so we thought it’d be a good idea to post some screens and show you that it’s not vaporware. I’m heading development on the Mac side, so I’ll show you screens from Mac OS X. You’ll get a look at the .NET version for Windows soon.
When you start up the Strip Sync application, it is locked, and requires you to enter your access password:
On first-time start-up, it will ask you to set a password for the local database replica. I should note that, at least for the time being, the password on your desktop database must match that of your iPhone database. Still gotta work out nicely paginated printing, and to be honest I’m not sure it’s necessary. Will probably be left out on initial release, as anybody can export to CSV and print that, and stick it in a safe.
Once you’re in, you’ll see the main utility window:
One of the key features here, Import, can be fired off pretty easily to import Strip CSV data (more on that in a later post, I have an EBNF that I need to clean up for programmers):
Then, you fire up the iPhone version, select the Sync tab, browse for your desktop on the local network, and begin the sync operation:
Those other buttons on the select switch indicate performing a restore from your desktop, and performing an authoritative override of what’s on the desktop. Once we’ve chosen the desktop we wish to sync with, we go for it (click to embiggen):
Here we can see that a new category called “trekkers” has been added:
And we can also see that the data import supports multiple values for field types on an entry:
The reason this worked is because they were delimited by a | character in the CSV data shown above. It can be escaped with a backspace. Like I said above, more on the import format later. Obviously, my Trek knowledge above is bogus, just needed some data.
2010-03-18 20:00:00 -0400
I’ll be attending this year’s Ruby Nation conference in Reston, VA April 9-10, and I’m quite looking forward to it. The list of speakers and talks is absolutely fantastic, and includes the excellent Nick Sieger, whom I had the pleasure of first meeting at Ruby Fringe in 2008. There are still a couple of open registration seats left if you haven’t signed up yet. If any of the Ruby heads out there want to meet up, I’ll be in town from the evening of April 8th to the morning of April 11th. You can find me on Twitter as @billymeltdown.
I’ve been spending a little time revisiting some of the core concepts and industry practices with Ruby, and recently Bret Morgan of DBL Systems pointed me in the direction of Gregory Brown’s newly published book, Ruby Best Practices. The first chapter alone is excellent, a kind of sermon for Test-Driven Development, and I highly recommend it.
2010-03-16 20:00:00 -0400
Time for a little business, friends! We have a new application in the iTunes App Store, and we call it Codebook: A Secure iPhone Notebook for People with Secrets!
Download it now in the iTunes App Store for only $2.99.
My bias being what it is, I recommend that you replace your other notes apps with this elegantly simple notebook that keeps all your data private, and just gets out of the way. Codebook is perfect for attorneys, physicians, journalists — anybody who needs to keep their notes confidential.
Codebook is built on the same rock-solid encryption engine we use for STRIP, SQLCipher. It’s perfect for storing sensitive meeting notes, terrible poetry, anything you want to keep secret should you lose your iPhone or iPod Touch. ALL data stored in Codebook is stored using the robust and peer-reviewed 256-bit AES encryption implementation in OpenSSL.
I used to use Apple’s included Notes application a lot myself, but I’m getting less and less comfortable storing stuff in there. Here’s my use-case, since I’ve been using Codebook for a few months now:
I, for one, have a penchant for thinking up lots of bad “lyrics” for my band. I’ll be on the subway reading some radical article in Harper’s, and BOOM, I get this great idea! I fire up Codebook, tap in my password real quick, and tap out my idea. If I want to add it to a previous bit of my wondrous witticisms, they’re incredibly easy to find via the Search feature, or just by going back in time and scanning the titles.
It may not be totally evident in the screens here, but we opted to use the first line of each note as its title (an idea we got from John Gruber’s writings about the untitled document syndrome, and the Simplenote app), so you don’t have to think of one. It’s just there, it just works.
One last thing we made sure we included: Email forwarding of individual notes. You just might want to mail something out and we wanted to make that easy to do.
Stephen and I started work on this back in February of 2009, and have basically sat on it since while we put priority on Strip. Recently, I took some time to clean it up and get it ready for a proper release. Feels good to finally have it out there in the store. If you give Codebook a spin, let us know what you think!
2010-02-23 19:00:00 -0500
Recently, I wanted to implement an interface where a user holds down on a UIView class or subclass to reveal a copy menu. Basically, I didn’t want to have to present the user with a UITextField
just to provide the ability to copy some data to the pasteboard. In this case, I’m working with a UILabel, but a similar paradigm already exists in many Apple-supplied apps where one can hold down on an image and be presented with the Copy menu option.
Going in it seemed pretty straight-forward, but it ended up taking me the better part of an afternoon of trial and error alongside the Event Handling section of iPhone Application Programming Guide to work it all out, so I believe a tutorial is in order. A reference project with sample code is available on Github.
Getting a Hold of a Hold
One can easily calculate the length of time a touch was held when the touch ends (by calculating the difference between the timestamp
properties of the passed UIEvent
and UITouch
objects), but I found making this the point of responding to the event less than ideal because it means responding to the user’s interaction after the user lifts her finger, rather than while she is holding down on the screen. I’d rather respond while the user is still holding her finger down to let her know that the instruction was received. If the software will only respond after the user lifts her finger, she has no idea how long she has to hold her finger down, which is a nuisance, really.
Old Cocoa pros and experienced UIKitters probably saw the solution from a mile away: we intercept the touches began event for the view we’re interested in, and tell some object to do something after a long enough delay (the minimum time we want a user to have to hold to do something). We then cancel the request if any of the other touch events fire before our delay hits. That looks something like this, depending on your needs:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *t = [touches anyObject];
if ([t locationInView:someViewWeAreInterestedIn])
[self performSelector:@selector(showMenu) withObject:nil afterDelay:0.8f];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showMenu) object:nil];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showMenu) object:nil];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showMenu) object:nil];
}
There may be better ways to do this, but this seems pretty solid. In the sample code you can see this at work in the view controller, which shows a hidden image once a user holds down on another image for long enough. Just under a second (0.8s) seemed to feel right to me.
- (void)holdingView:(id)view {
[hiddenView setHidden:NO];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *imageTouches = [event touchesForView:imageView];
if ([imageTouches count] > 0) {
[self performSelector:@selector(holdingView:) withObject:imageView afterDelay:0.8f];
}
[super touchesBegan:touches withEvent:event];
}
Implementing a Custom Copy
I feel like there’s real pun-potential for this subtitle, but reasonably groan-inducing text is eluding me. In any event, now that we can detect when a user has held our view long enough to warrant a response, we need to make a move: presenting the UIMenuController
with the Copy option and actually copying something in response. I’m sure there are various approaches that can be taken, but my approach was to start by subclassing UILabel
, curious to hear other ideas.
First, I wired the subclass to intercept touch events, and to save that touch-down for the extra point (ho!):
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self canBecomeFirstResponder]) {
[self becomeFirstResponder];
UITouch *t = [touches anyObject];
holdPoint = [t locationInView:self];
[self performSelector:@selector(showMenu) withObject:nil afterDelay:0.8f];
}
}
// (other touches* methods implemented to cancel perform) ...
Showing the menu itself is a touch awkward, you need to provide a “target rectangle” (CGRect
) to UIMenuController
to tell it about where on the screen you want the menu to appear (it can appear above or below this point, depending on proximity to the screen bounds).
- (void)showMenu {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(reset) name:UIMenuControllerWillHideMenuNotification object:nil];
// bring up editing menu.
UIMenuController *theMenu = [UIMenuController sharedMenuController];
CGRect myFrame = [[self superview] frame];
CGRect selectionRect = CGRectMake(holdPoint.x, myFrame.origin.y - 12.0, 0, 0);
[self setNeedsDisplayInRect:selectionRect];
[theMenu setTargetRect:selectionRect inView:self];
[theMenu setMenuVisible:YES animated:YES];
// do a bit of highlighting to clarify what will be copied, specifically
_bgColor = [self backgroundColor];
[_bgColor retain];
[self setBackgroundColor:[UIColor blackColor]];
}
Note that I’m registering for a notification: I basically wanted to know whenever the menu disappeared, because that would mean it’s time to stop high-lighting the text in the label, and restore the original background color. Totally not required for getting the menu on screen.
Next we have to make it clear to the UIMenuController that we mean serious business, and what kind of business we intend to conduct. In my case, I was only interested in Copy, but other options are available:
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
BOOL answer = NO;
if (action == @selector(copy:))
answer = YES;
return answer;
}
And in my case, the data I’m looking to copy is simply the text of the label itself, and I just want to put it on the general pasteboard so the user can paste it into another app, or wherever:
- (void)copy:(id)sender {
UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
[gpBoard setValue:[self text] forPasteboardType:@"public.utf8-plain-text"];
}
That’s it!
Zetetic is the creator of the encrypted iPhone data vault and password manager
Strip and the open source encryption-enhanced database engine
SQLCipher.