Posts tagged ‘iOS SDK’

Xcode project line counts

If you are looking to find out how many lines of code are in each of the files of your Xcode project, you can use the following command in Terminal after you change into the root folder of your project:

find . \( -iname \*.m -o -iname \*.mm -o -iname \*.h \) -exec wc -l '{}' \+

SpriteKit game background music stopped working

In my SpriteKit game, all of a sudden the background music stopped playing, all I heard was silence no matter what the volume levels were in either the app settings or via the hardware buttons.

I am not sure why this happened, but I was able to figure out how to get it working again. The key was adding an AVAudioSession call before the existing AVAudioPlayer code. Here is some sample code of what I did:

AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
 
NSURL *url = [NSURL fileURLWithPath:@"your_sound_file.caf"];
_player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[_player setDelegate:self];
[_player prepareToPlay];
[_player play];

BTW, Happy Birthday to Brutus Beefcake. (I couldn’t really find any other good birthdays, deaths, or events in the Wikipedia page for today.)

Advent of Code

I have been going through the 2015 version of the Advent of Code, which is a web site that has a bunch of interesting programming puzzles. In an attempt to try to learn something new, I decided I would solve the puzzles using Swift. I am about half way through so far, and the results have been eye opening.

Here is the link to the Advent of Code web site:

http://adventofcode.com

And here is a link to my Github repository with the solutions:

https://github.com/Wave39/AdventOfCode

BTW, Happy Birthday to Mike Keneally, the excellent guitarist and musician who has worked with so many great artists, including Joe Satriani and Steve Vai.

How to make random entries into the iPhone address book (updated)

A few years have passed since I posted a blog entry entitled How to make random entries into the iPhone address book, and while you might be able to figure out how to make this code work if you try to use it with iOS 9, I figured it was time to revisit.

Here is the iOS 9 compliant method for creating the random address book entries:

- (IBAction)add25ButtonPressed:(id)sender
{
#define NUMBER_OF_RANDOM_ENTRIES    25
#define URL_FORMAT  @"http://old.wave39.com/roster/generate.php?f=csv&g=%@&num=%d&limit=99"
 
    CFErrorRef error1 = nil;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error1);
    ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error2) {
        // callback can occur in background, address book must be accessed on thread it was created on
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error2)
            {
                NSLog(@"Address book error: %@", ((__bridge NSError *)error2).localizedDescription);
            }
            else if (!granted)
            {
                NSLog(@"Access to the address book has been denied.");
            }
            else
            {
                // access granted
                NSString *genderOfNames = ((arc4random() % 2) == 1 ? @"m" : @"f");
                NSString *urlString = [NSString stringWithFormat:URL_FORMAT, genderOfNames, NUMBER_OF_RANDOM_ENTRIES];
                NSURL *url = [NSURL URLWithString:urlString];
                NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
                NSURLResponse *response = nil;
                NSError *error = nil;
                NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
                NSString *stringData = [NSString stringWithUTF8String:[data bytes]];
                NSString *areaCode = [NSString stringWithFormat:@"%03d", (arc4random() % 799 + 200)];
 
                NSArray *lineArray = [stringData componentsSeparatedByString:@"\n"];
                for (NSString *line in lineArray)
                {
                    if ([line length] > 0)
                    {
                        NSArray *fieldArray = [line componentsSeparatedByString:@","];
                        if ([fieldArray count] == 3)
                        {
                            ABRecordRef person = ABPersonCreate();
                            NSString *phone = [NSString stringWithFormat:@"%@-%03d-%04d", areaCode,
                                               (arc4random() % 799 + 200), (arc4random() % 9999)];
                            ABMutableMultiValueRef phoneNumberMultiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType);
                            CFStringRef phoneType = (arc4random() % 2 == 0 ? kABPersonPhoneMainLabel : kABPersonPhoneMobileLabel);
                            ABMultiValueAddValueAndLabel(phoneNumberMultiValue, (__bridge CFTypeRef)(phone), phoneType, NULL);
                            NSString *email = [NSString stringWithFormat:@"%@_%@@tapbandit.com", fieldArray[1], fieldArray[2]];
                            ABMutableMultiValueRef emailMultiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType);
                            CFStringRef emailType = kABHomeLabel;
                            ABMultiValueAddValueAndLabel(emailMultiValue, (__bridge CFTypeRef)(email), emailType, NULL);
                            ABRecordSetValue(person, kABPersonFirstNameProperty, (__bridge CFTypeRef)(fieldArray[1]), nil);
                            ABRecordSetValue(person, kABPersonLastNameProperty, (__bridge CFTypeRef)(fieldArray[2]), nil);
                            ABRecordSetValue(person, kABPersonPhoneProperty, phoneNumberMultiValue, nil);
                            ABRecordSetValue(person, kABPersonEmailProperty, emailMultiValue, nil);
                            ABAddressBookAddRecord(addressBook, person, nil);
                            NSLog(@"Created record for %@ %@ (%@: %@; %@)", fieldArray[1], fieldArray[2], phoneType, phone, email);
                            CFRelease(phoneNumberMultiValue);
                            CFRelease(emailMultiValue);
                            CFRelease(person);
                        }
                    }
                }
 
                ABAddressBookSave(addressBook, nil);
                NSLog(@"Done creating address book data.");
 
                CFRelease(addressBook);
            }
        });
    });
}

BTW, Happy Birthday to Windows, first available 30 years ago today. Coincidentally, on the same exact day those 30 years ago, the Pittsburgh Pirates hired Jim Leyland to be their manager. And in both instances, the rest is history.

NS_ENUM works without the typedef (sort of)

You may have heard that NS_ENUM works without the typedef (sort of). What I mean by this is that most of the time, it all just works even if you leave off the typedef on something like this:

NS_ENUM(NSInteger, BinaryItemType)
{
    BINARY_ITEM_TYPE_CONTACT_PHOTO = 0,
    BINARY_ITEM_TYPE_SIGNATURE_DATA,
    BINARY_ITEM_TYPE_BUSINESS_CARD,
};

However, if I ran into a situation where I did something to my code base to cause it to start throwing build errors like this:

duplicate symbol _BinaryItemType in:
    /Users/abc/Library/Developer/Xcode/DerivedData/xyz-bultlvoaxhkekcflkuawzlgzhwyq/Build/Intermediates/xyz.build/Debug-iphonesimulator/xyz.build/Objects-normal/x86_64/XYZViewController.o
    /Users/abc/Library/Developer/Xcode/DerivedData/xyz-bultlvoaxhkekcflkuawzlgzhwyq/Build/Intermediates/xyz.build/Debug-iphonesimulator/xyz.build/Objects-normal/x86_64/Contact+PDF.o
ld: 15 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

And of course, I did not realize that the typedef missing was causing this error. As soon as I changed the NS_ENUM to look like this, all went well:

typedef NS_ENUM(NSInteger, BinaryItemType)
{
    BINARY_ITEM_TYPE_CONTACT_PHOTO = 0,
    BINARY_ITEM_TYPE_SIGNATURE_DATA,
    BINARY_ITEM_TYPE_BUSINESS_CARD,
};

BTW, Happy Birthday to Rosalind Chao, who so ably portrayed Keiko O’Brien in Star Trek The Next Generation and Star Trek Deep Space Nine.

UITextField inside UITableViewCell moves one pixel on becoming first responder

If you have ever noticed that your UITextField inside of a UITableViewCell moves one pixel on becoming the first responder, and then moves one pixel back when resigning the first responder, then you are in luck, as I have found a work around.

Basically, I found that if your UITextField in Interface Builder is set to System 17.0 font, go ahead and change it to Helvetica Neue 17.0. For some reason, this works for me and does not move at all when becoming or resigning the first responder.

BTW, The King left us on this day back in 1977, so sing a verse of Jailhouse Rock or Heartbreak Hotel in his honor.

Expected expression error when creating new variable in switch case block

So I am coding along in my iOS app and I come across an Expected expression error when I am creating a new variable in switch case block. For example, this looks innocent enough…

switch (actionType)
{
    case 0:
        NSArray *componentArray = [theString componentsSeparatedByString:@","];
        // do more stuff here
        break;
    // more cases here
}

No matter what I do, the Xcode compiler complains about the NSArray line right below the case 0 statement. What kind of horse hockey is this? The code is so simple that it is laughable.

My investigations led me to discover that there is a C compilation rule that specifies that you cannot have a variable declaration right after a label. And apparently, case statements are compiled into labels. (By exactly what sorcery escapes me, I am sure if you are interested you can dig into the C language definitions and figure it out.)

So I am forced to do this instead:

NSArray *componentArray;
switch (actionType)
{
    case 0:
        componentArray = [theString componentsSeparatedByString:@","];
        // do more stuff here
        break;
    // more cases here
}

BTW, Happy Birthday to Cheryl Burke, who is by far my favorite pro dancer on Dancing With The Stars. I hope Cheryl returns to the show at some point in the future.

Custom UITableViewCell with class and nib file

It is only a matter of time before you want to create a custom UITableViewCell with class and nib file on the iOS platform. There are a couple of things to keep in mind when doing this so that you do not bang your head against the wall, trying to figure out why it is not working.

In order to do this, you will probably create a .m and .h file for your code, and a .xib file for the UI. After creating a new nib file, you will have to remove the blank UIView and put a blank UITableViewCell on the design surface. Make sure to get into the habit right away of setting both the File’s Owner and the top level table view cell to your new class name.

Then, if you any IBOutlet controls, when you are assigning the pointers in Interface Builder, make sure that you are dragging to the top level cell and not the File’s Owner, otherwise you will get a crash when the cell is being built.

BTW, Happy Birthday to Gary Lockwood, most famously of 2001: A Space Odyssey and the 2nd Star Trek pilot.

Extra space in grouped UITableView on iOS 7

So I noticed that there is a lot of extra space at the header and footer of my UITableView that is in my current project that I am updating to iOS 7. After some frustration, I finally put together this category that I hung off of UITableView:

- (void)ios7Check
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
    {
        self.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.bounds.size.width, 4.0f)];
        self.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.bounds.size.width, 4.0f)];
    }
}

Basically, all you have to do is call this from the viewDidLoad method of your view controller (or table view controller), and fire the ios7Check method like this:

[_theTableView ios7Check];  // if you have an instance variable pointing to your table view

or:

[self.tableView ios7Check];  // if your view controller is a UITableViewController

And you can adjust the spacing of the header and footer by changing the 4.0f values in the method above.

Here is what the grouped table view looks like without the code above, and with the code:

ios7-tableview-header-bigios7-tableview-header-small

EDIT: By the way, don’t try to set your tableHeaderView or tableFooterView frame to a height of 0.0f, as if you do then it just puts in the extra space. It must be that the Cocoa Touch framework sees that you have a zero height view, and just considers it as null. Even a height value of 0.1f is good enough, if you want a table view that cozies right up to the top with no space in between.

BTW, an anniversary farewell to Nelson Briles, who was a pitcher for the Pittsburgh Pirates (and other teams), and was a founding figure of the Pirates Fantasy Camp. Unfortunately he passed away on this day in 2005, so I did not get to meet him since my first Fantasy Camp was in 2011.

Different Settings app entries for Debug vs. Release builds

So compiler directives can only take you so far when building your Objective-C iOS apps, especially when you have testers who want to keep switching things up. Rebuilding and redeploying is not the end of the world, but it gets old quick.

Luckily I figured out how to switch up the Settings.bundle file so that I can have one version for the release app, and a different version with some extra options for the debug build. This way, they don’t have to drop by every hour when they want to change something that would previously require me to comment in/out some compiler directive and rebuild and redeploy.

The theory behind this is to create a new Settings_Debug.bundle file in the same location as the current Settings.bundle file, change up the plist for your additional settings, and then in the build process, just replace the plist file in Settings.bundle with the one from the Settings_Debug.bundle file. Sounds easy, right?

Here is basically what you need to do step by step:

  • Create a new Settings_Debug.bundle file
  • Copy the plist from Settings.bundle and make the necessary modifications in the Settings_Debug.bundle
  • Click the project in file explorer, select target, select Build Phases
  • Leave release version Settings.bundle in the Copy Bundle Resources, but remove Settings_Debug.bundle from Copy Bundle Resources
  • Click Add Build Phase > Add Run Script
  • Copy following into text view below Shell
if [ "${CONFIGURATION}" == "Debug" ]; then
cp -r "${PROJECT_DIR}/Shared/Settings_Debug.bundle/Root.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Settings.bundle/Root.plist"
echo "Debug settings bundle copied"
else
cp -r "${PROJECT_DIR}/Shared/Settings.bundle/Root.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Settings.bundle/Root.plist"
echo "Release settings bundle copied"
fi

Here are a few caveats to keep in mind…

  • In my example above, note that my Settings.bundle file is in a folder called Shared off the project directory
  • I am not using the Root.strings file
  • It was done this way because just copying the .bundle file threw errors trying to copy the .svn files and folders
  • For me, it seems way easier to work with Root.plist in source code mode instead of property list mode

BTW, it was pretty slim pickings (not Slim Pickens, one of the great characters of all time) on the births, deaths, and events for September 21, so let me just wish everyone a Happy International Day of Peace.