Sunday, March 24, 2013

Displaying an alert view with blocks

The default method for displaying an alert view is somewhat cumbersome if you have a dialog with many possible choices. I had a table cell that had two buttons, one that would add to a number, and one that would subtract. I wanted to then add the ability to directly set the number. As I started adding more choices, I ended up with a cumbersome dialog. The default alert view protocol expects only one callback, so you have to track what dialog you are showing, and have an case statement or if/else block for that. Anytime you see that construct, you know it is time to refactor.

Before

This code starts to get ugly quickly, multiple repeated lines, and the need to put an if/else in my alert callback.  The logic was also hard to follow.

- (IBAction)subtract:(id)sender {
    UIAlertView * alert = [[UIAlertView alloc] initWithTitle:self.tracker.subtractString message:@"How much:" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:self.tracker.subtractString,nil];
    alert.alertViewStyle = UIAlertViewStylePlainTextInput;
    UITextField * alertTextField = [alert textFieldAtIndex:0];
    alertTextField.keyboardType = UIKeyboardTypeNumberPad;
    alertTextField.placeholder = [NSString stringWithFormat:@"How much to %@",self.tracker.subtractString];
    self.add=false;
    [alert show];
}
- (IBAction)add:(id)sender {
    UIAlertView * alert = [[UIAlertView alloc] initWithTitle:self.tracker.addString message:@"How much:" delegate:self cancelButtonTitle:@"Continue" otherButtonTitles:self.tracker.addString,nil];
        alert.alertViewStyle = UIAlertViewStylePlainTextInput;
    UITextField * alertTextField = [alert textFieldAtIndex:0];
    alertTextField.keyboardType = UIKeyboardTypeNumberPad;
    alertTextField.placeholder = [NSString stringWithFormat:@"How much to %@",self.tracker.addString];
    @"Enter how much to heal";
    self.add=true;
     [alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    if (alertView.cancelButtonIndex!=buttonIndex)
    {
        int num= [[[alertView textFieldAtIndex:0] text] intValue];
        if (self.add)
        {
            self.tracker.currentValue+=num;

        }
        else
        {
            self.tracker.currentValue-=num;
        }
        NSError *error;
        [self.tracker.managedObjectContext save:&error];
        //self.currentLabel.text=[NSString stringWithFormat:@"%d",self.tracker.currentValue];
    }
}

The ability to create a block, an inline method, allows this code to be extracted into a common class, and then made FAR simpler for the receiver.

The first step was creating the alert class that can display an alert view. This class assumes that there is only one prompt active at a time.
@interface AlertButtonHelper : NSObject


@property(nonatomic, copy) void (^action)(NSString *);

- (void)promptForInput:(NSString *)prompt title:(NSString *)title withAcceptButton:(NSString *)acceptButton
          endingAction:(void (^)(NSString *))action keyboardType:(enum UIKeyboardType)keyboardType;

@end

@implementation AlertButtonHelper {


}
// The main command to prompt.
- (void)promptForInput:(NSString *)prompt title:(NSString *)title withAcceptButton:(NSString *)acceptButton endingAction:(void (^)(NSString *))action keyboardType:(enum UIKeyboardType)keyboardType {
        UIAlertView * alert = [[UIAlertView alloc] initWithTitle:title message:prompt delegate:self cancelButtonTitle:@"Cancel" 
                                           otherButtonTitles:acceptButton,nil];
        alert.alertViewStyle = UIAlertViewStylePlainTextInput;
        UITextField * alertTextField = [alert textFieldAtIndex:0];
        alertTextField.keyboardType = keyboardType;
        alertTextField.placeholder = [NSString stringWithFormat:prompt];
        
         [alert show];
        self.action=action;
}

// The standard alert callbakc.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{

    NSString *value = [alertView textFieldAtIndex:0].text;
    if (self.action!=nil)
    {
        self.action(value);
    }
    
}
@end

I ended up with a bunch of blocks that looked like this. Where the only difference was the one line where the number was used.
            void (^actionToTake)(NSString *) = ^(NSString *string) {
                int num= [string intValue];
                self.tracker.currentValue=num;
                NSError *error;
                [self.tracker.managedObjectContext save:&error];

            };
    
I'm using AppCode, which has a nifty extract as block feature.  I used it to transform it into
- (void (^)(NSString *))getModifyFunction:(void (^)(int))modifyValue {
    void (^actionToTake)(NSString *) = ^(NSString *string) {
            int num= [string intValue];
        modifyValue(num);
        NSError *error;
            [self.tracker.managedObjectContext save:&error];

        };
    return actionToTake;
}


 void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) {
        self.tracker.maxValue = num;
    }];

The final client code is:

- (IBAction)subtract:(id)sender {

    NSString *str=[NSString stringWithFormat:@"How much to %@",self.tracker.subtractString]  ;

    void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) {
        self.tracker.currentValue -= num;
    }];
        [self.helper promptForInput:str title:self.tracker.subtractString withAcceptButton:self.tracker.subtractString
                       endingAction:actionToTake keyboardType:UIKeyboardTypeNumberPad];

}


- (IBAction)maxSelected:(id)sender {
    NSString *str=[NSString stringWithFormat:@"Enter max Value"]  ;

    void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) {
        self.tracker.maxValue = num;
    }];
    [self.helper promptForInput:str title:@"Max Value" withAcceptButton:@"Set Max"
                       endingAction:actionToTake keyboardType:UIKeyboardTypeNumberPad];


}

- (void (^)(NSString *))getModifyFunction:(void (^)(int))modifyValue {
    void (^actionToTake)(NSString *) = ^(NSString *string) {
            int num= [string intValue];
        modifyValue(num);
        NSError *error;
            [self.tracker.managedObjectContext save:&error];

        };
    return actionToTake;
}

- (IBAction)add:(id)sender {
    NSString *str=[NSString stringWithFormat:@"How much to %@",self.tracker.addString]  ;

    void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) {
        self.tracker.maxValue += num;
    }];

    [self.helper promptForInput:str title:self.tracker.addString withAcceptButton:self.tracker.addString
                   endingAction:actionToTake keyboardType:UIKeyboardTypeNumberPad];
}

- (IBAction)currentSelected:(id)sender {
    NSString *str=[NSString stringWithFormat:@"Enter current Value"]  ;

    void (^actionToTake)(NSString *)= [self getModifyFunction:^(int num) {
        self.tracker.currentValue = num;
    }];
            [self.helper promptForInput:str title:@"Set Value" withAcceptButton:@"Set Value"
                           endingAction:actionToTake keyboardType:UIKeyboardTypeNumberPad];
}


I initialize helper in the init method.

 if (self.helper==nil)
    {
        self.helper=[[AlertButtonHelper  alloc] init];
    }
The result has strange syntax, but eliminates a lot of redundant code. The use of blocks is a great way to isolate the 'different' items. The next stage would be to extract a protocol for the alert interface, so that the method of displaying alerts could change if desired.

Tuesday, October 4, 2011

App Code Beta

One of the difficulties that I've had with XCode is that the SDE is not as slick as I'm used to. When combined with some of the peculiarities of the language, it is more painful to code in this than in Eclipse for Java or Visual Studio for C# (especially with the Resharper plugin). I've found that JetBrains, the creators of IntellJ and Resharper have created a SDE called AppCode for Objective C. I've tried it and it is far easier to use for most things than XCode (either 3 or 4). Right now the Beta is free, and you can sign up for a 50% discount on the final product.

I highly recommend it.  The smart code completion is far better, it can automatically make properties for you, it has far better refactoring, and navigation.

The only downsides are that it doesn't support interface builder, or modifying a SQL-Lite database.  Also if you want to modify your project settings you still need to boot up XCode.

XCode 4 static libraries in a project.

I'm actually trying to get back into IOS development. I was attempting to get my static library to work properly in XCode 4. Apple does not make it easy to do this. I found a really good blog article that helps with this. Using Open Source Static Libraries in XCode 4. While written for open source libraries, it works great with my personal static library as well.

Friday, March 18, 2011

Best eBook Lending Service

With the recent establishment of the ability to lend ebooks with the Kindle and the Nook, several 'swap clubs' have sprung up around the net where you can make your ebooks available for lending and borrow ebooks from others.  I've tried three of the services and have found that they all work, although the last one I've tried has been FAR  more successful.

Booklending.com (formly Kindlelending.com until Amazon complained about their trademark). This is a pretty good service.  It operations on a 'push' principle.  When you log in you register what books you are interested in, and then wait for someone to send them to you.   When you make your books available it will show if anyone is waiting for those books and offer up to 3 people that you can send to.

It has a limited ability to see which books are available for lending, but no real strong search capabilities.   I've received several books from here and it works OK.

I've also lent books from this site.  When you lend a book it opens Amazon in a frame and you can then use the Amazon lending capabilities to send your book out.  When you list the books you have available you have to manually enter them.

http://www.lendink.com/  Lendink is another site, it has an advantage in that you can see what books are available and search by genre.  It operates on a 'pull' principle.  When you see a book you are interested in you can click it and the site will send an email to the owner of the book that you are interested in lending it.
I've also lent books through this service.  It operates very similarly to the booklending.com site.  When you list the books you have available you have to manually enter them.

This site supports both Nook and kindle books.

Finally the last and best site is:

lendle.me
This is the latest site I've tried.  Unlike the other sites you have to enter your books before you can borrow from it.  It provides a bookmark that runs a script on your Amazon page to parse out which books you have available.  It then saves all of those and figures out which ones are lendable.  

It gives you two credits (loans) to start, and then as you register books it gives you more.  I had 10 credits after I parsed out my books.

For the previous sites you want to request many books, you don't have a good hit ratio.  However for lendle.me I had a 7 out of 10 books lent to me in less than 48 hours, I had requested several because based on the other sites I expected to only get 2-3 hits with 10 requests.  Lendle.me seems to have a wider selection of books (probably because it parses them from Amazon).   I can generally read a book in 2 days, so I'm going to have to stop requesting just to keep up.

If you are borrowing Kindle books, I recommend lendle.me, if you are borrowing Nook books then lendink is your choice.  However it doesn't hurt to be on all 3 sites to maximize your borrowing and lending chances.   

I've been using the lending services to borrow books that I would check out of a library.  I've added some new authors to my 'buy list' after doing so.   It stretches my dollars and makes me more likely to take a chance, you can borrow the first book in a series and see if you want to be spending your money on the rest.

Monday, March 14, 2011

Application not picking up changes in a static library

I recently wasted a lot of time, I was attempting to modify a class in a static library, and no matter what I did, it would NOT work.   Eventually I added some log statements and they didn't show up, although old ones did.

I found that when I made modifications to my static library the main application was not relinked.

For my WallPaperEdit application, I found that the main executable was WallPaperEdit in the directory

/Users/myusername/Library/Developer/Xcode/DerivedData/WallPaperEDit_randomLetters/Build/Products/Debug-iphonesimulator/WallPaperEdit.app

When I made a change in my static library, it was not being picked up for this, and it was not being relinked.  Even doing a clean did not work.

If I modify a file in my main library, then it recompiles and relinks this, and my new change is picked up.  I'm going to use that as a workaround for now, and then try and find the build setting that makes this behavior as it properly does.

While XCode4 says it supports automated dependencies between multiple projects, I've found that it can sometimes be painful.

Tuesday, February 15, 2011

Apple's new subscription model.

I'm seeing a lot of reports on Apple's new subscription model.  Providers that provide content that is purchased outside the app store now has to provide a 'in app purchase' to get that material, costing 30%.   This is a killer, move on Apple's part, and if they enforce it you can say goodbye to Kindle, Nook, Netflix, etc...  The companies can't afford to take this cut.

With the agency model for ebooks, 5 out of 6 of the big publishers in the US now set their prices for their ebooks.  When Amazon sells one of these ebooks, it gets 30% of the list price, and the publisher gets 70% of the list price.  By the contract Amazon can NOT change this price.

If they were to make this available as an in-app purchase, then Apple would get 30%, the publisher would get 70%, and Amazon would get nothing.  Obviously Amazon will not go for this.

I wonder if Apple is attempting to push all other ebook providers off the App Store, or if it is just rattling cages.   If they do push all other ebook providers off, then I might have to reconsider using the Apple Ecosystem.   I have NO desire to use iBooks, and will never use it.   It ties me to one device, and Apple has just shown how unwise that is.

This seems like a foolish move now that serious iPhone contenders are on the horizon, between Android, and now Windows 7 (with the Nokia deal), IOS could have some troubles.  I would rather be using the Windows 7 toolset instead of IOS, however the market is not currently there.

Friday, December 17, 2010

Side Track: Unreal Development Kit

OK, I've been working (slowly) over the last month on refactoring my flow, but I'm not ready to post stuff.  However I decided that I was going to download the Unreal Development Kit and see if I could get 'Epic Citadel' to compile and install my own version.  Step 1 is to boot up a windows box and download the development kit.   It's a 1.1 GB download.  

The UDK is Windows only, but interestingly enough it can send data directly to your iphone for testing and running.  For submission to the developer program you need to use your Mac.  I'm not sure if that means you can't use Objective-C stuff directly in it, probably not.

It was trivially easy to get stuff running on the iPhone.  I installed the application, started the Mobile Editor, and used their sample map.   I then ran play install on iOS device, and it prompted me for my provisioning certificate, my developer certificate and for some plist information.  I just moved those over from my mac, and boom I had a level running on my iPhone 4.

I don't have much knowledge of the unreal editor, I played with the very first version when Unreal first came out.  It looks like a non-trivial learning curve, however for those with skills creating models, textures and scripting, I think getting a really nice game has just become a lot easier.  I look forward to a good game in the Epic Citadel map.  (If I read their licensing terms correctly, you can use those models in your game, your milage may vary, I'm not a lawyer.)   (Infinity blade is OK, but not my style of game).

Epic did a great job of getting a third party editor working with iOS.  I'm sure there are pitfalls in moving forward with a real game, but the first impressions are VERY impressive.
(Epic requires the following text when writing documentation about using the Unreal Development Kit, since they are providing this for FREE, I think it's quite reasonable to comply with their legal requirements.)




Note this blog post is not endorsed by Epic games. 
Unreal Development Kit (UDK) © 2009, Epic Games, Inc. Epic, Epic Games, Gears of War, Gears of War 2, Unreal, AnimSet Viewer, AnimTree Editor, Unreal Cascade, Unreal Content Browser, Unreal Development Kit, Unreal Editor, Unreal Engine, Unreal Kismet, Unreal Lightmass, Unreal Matinee, Unreal PhAT, UnrealScript and Unreal Tournament are trademarks or registered trademarks of Epic Games, Inc. in the United States of America and elsewhere. All other trademarks are the pr