Posts tagged ‘iOS SDK’

Calling a .NET web service endpoint with NSURLSessionUploadTask

I have been going through the process of removing open source components from my apps when Cocoa Touch and the associated frameworks catch up with what I need to do.

Let’s say for the sake of argument you have a .NET web service that looks like this:

using System;
using System.Web.Script.Services;
using System.Web.Services;
 
namespace My_WebRole
{
    /// <summary>
    /// Summary description for CreateCustomer
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    [ScriptService]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
    // [System.Web.Script.Services.ScriptService]
    public class CreateCustomer : WebService
    {
        [WebMethod]
        [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
        public string Create()
        {
            try
            {
                var myName = Context.Request.Form["MyName"];
                var companyName = Context.Request.Form["CompanyName"];
                var db = new MyServicesDataContext();
                var row = new CustomerInformation { MyName = myName, CompanyName = companyName, CreationDate = DateTime.Now };
                db.CustomerInformations.InsertOnSubmit(row);
                db.SubmitChanges();
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
            return "OK";
        }
    }
}

The NSURLSessionUploadTask class was added in iOS 7, which is sufficiently long enough ago to be stable to implement. (As long as your app is going to require iOS 7 or newer, of course.)

Here is what the code looks like that calls the above web service:

+ (BOOL)pushTheData:(NSString *)myName companyName:(NSString *)companyName
{
    NSMutableString *urlString = [NSMutableString stringWithString:PUSH_DATA_URL];
    NSString *boundary = [NSString boundaryString];
    NSMutableData *body = [NSMutableData data];
 
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"MyName"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"%@\r\n", myName] dataUsingEncoding:NSUTF8StringEncoding]];
 
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"CompanyName"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"%@\r\n", companyName] dataUsingEncoding:NSUTF8StringEncoding]];
 
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
 
    NSURL *url = [NSURL URLWithString:urlString];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];
 
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    configuration.HTTPAdditionalHeaders = @ { @"Content-Type"  : [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] };
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
 
    NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error)
        {
            // handle the error
            return;
        }
 
        NSLog(@"Response: %@", [data utf8String]);
        // do something here
    }];
 
    [task resume];
 
    return YES;
}

The above method uses a category that I have set up off of NSString that creates a new boundary string each time it is called. Here is that method:

+ (NSString *)boundaryString
{
    NSString *uuidStr = [[NSUUID UUID] UUIDString];
    return [NSString stringWithFormat:@"Boundary-%@", uuidStr];
}

BTW, Happy Birthday to Jeff Beck. He has a new album coming out soon, and I can’t wait to hear it.

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.