65.9K
CodeProject 正在变化。 阅读更多。
Home

开发一个发送短信的应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (50投票s)

2012年6月2日

CPOL

2分钟阅读

viewsIcon

95810

downloadIcon

3380

能够通过互联网连接从iPhone发送短信,这有多么棒?

背景

在我的文章 "如何从桌面应用程序发送短信" 中,我提到了 CardBoardFish,这是一个简单易用的API,用于在全球范围内发送短信。

SendSMS 应用

该应用非常基础和简单。它只有一个屏幕,看起来像这样

The SendSMS App Screen

基本上,您输入发件人的姓名或号码(可以是任何内容),收件人的国家代码和电话号码以及一条消息,然后按“发送”。

然后发送消息,并弹出一个确认号。

OK Confirmation

该应用最重要的构建块是 Send 例程(在 ViewController.m 中)

- (void) send {    
     @autoreleasepool {
        
        NSString *url = [NSString stringWithFormat:
        @"http://sms1.cardboardfish.com:9001/HTTPSMS?S=H&UN=%@&P=%@&DA=%@%@&SA=%@&M=%@&DC=4",
        @"<PLACE YOUR USER NAME HERE>", @"<PLACE YOUR PASSWORD HERE", txtCountryCode.text, 
        txtDestinationNo.text, txtName.text,[self stringToHex:txtMessage.text]];
        
        url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        NSLog(@"url %@", url);
        
        NSError* connError = 0;
        NSURLResponse* response;
        
        NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] 
                   cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20];
        
        NSData* data = [ NSURLConnection sendSynchronousRequest:
                         request returningResponse:&response error:&connError ];
        NSString* resp = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"response: %@", resp);

与设备的通讯录交互

为了方便地从您的 iPhone 中选择现有联系人,使用了 openContacts 例程,该例程使用了一个名为 ABPeoplePickerNavigationController 的类,根据 Apple 的说法,它“实现了一个视图控制器,该控制器管理一组视图,允许用户从通讯录中选择一个联系人或其联系人信息项之一”。

- (IBAction)openContacts:(id)sender {
    
    ABPeoplePickerNavigationController *picker = 
                    [[ABPeoplePickerNavigationController alloc] init];
    picker.peoplePickerDelegate = self;
    NSArray *displayedItems = [NSArray arrayWithObjects:
                              [NSNumber numberWithInt:kABPersonPhoneProperty], 
                              [NSNumber numberWithInt:kABPersonEmailProperty],
                              [NSNumber numberWithInt:kABPersonBirthdayProperty], nil];
    
    picker.displayedProperties = displayedItems;
    [self presentModalViewController:picker animated:YES];
    [picker release];
} 

从通讯录中选择一个条目后,我们会对其执行一些基本的清理操作,例如删除前导零和空格、破折号和括号。因此,数字“(04) 888-3333”变为“48883333”,这是 SDK 所需的格式。

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)
   peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person 
   property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
    NSString* phone = nil;
    ABMultiValueRef phoneNumbers = ABRecordCopyValue(person,
                                                     property);
    if (ABMultiValueGetCount(phoneNumbers) > 0) {
        phone = (__bridge_transfer NSString*)
        ABMultiValueCopyValueAtIndex(phoneNumbers, identifier);
    } else {
        phone = @"[None]";
    }
    phone = [phone stringByReplacingOccurrencesOfString:@"(" withString:@""];
    phone = [phone stringByReplacingOccurrencesOfString:@")" withString:@""];
    phone = [phone stringByReplacingOccurrencesOfString:@"-" withString:@""];
    phone = [phone stringByReplacingOccurrencesOfString:@" " withString:@""];
    
    if ([[phone substringWithRange:NSMakeRange(0, 1)] isEqual:@"0"] ) {
        phone = [phone substringFromIndex:1];
    }
    
    txtDestinationNo.text = phone;
    [self dismissModalViewControllerAnimated:YES];
    
    return NO;
}

国家标志

我添加的一个不错的部分是国家列表以及每个国家的国旗。虽然不是真的必要……我使用了 226 个名为每个国旗的国家代码的 .PNG 图像,所以 49.png 包含德国国旗,依此类推...

The Country Code Selector

然后 CountryCodesViewController.m 源文件看起来像这样

#import "CountryCodesViewController.h"
@interface CountryCodesViewController ()
@end
@implementation CountryCodesViewController
@synthesize mainViewController;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] 
                           stringByAppendingPathComponent:@"Flags"];
    countryCodes = [[NSFileManager defaultManager] 
                   contentsOfDirectoryAtPath:sourcePath error:NULL];
    [countryCodes retain];
}
- (void)viewDidUnload
{
    [tblCountryCodes release];
    tblCountryCodes = nil;
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
    [tblCountryCodes release];
    [super dealloc];
}
#pragma mark UITableView methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [countryCodes count];    
}
- (UITableViewCell *)tableView:(UITableView *)tableView 
              cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [[[UITableViewCell alloc] 
       initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
    
    NSString *strFlagName = [countryCodes objectAtIndex:indexPath.row];
    
    
    cell.textLabel.text = [strFlagName substringToIndex:[strFlagName length] - 4];
    
    cell.imageView.image = 
            [UIImage imageNamed:[NSString stringWithFormat:@"Flags/%@",strFlagName]]; 
    
    return cell;
    
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *strFlagName = [countryCodes objectAtIndex:indexPath.row]; 
    
    [mainViewController selectCountryCode:
            [strFlagName substringToIndex:[strFlagName length] - 4]];
    
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
    
    [self dismissModalViewControllerAnimated:YES];
    
}
- (IBAction)okPressed:(id)sender {
    [self dismissModalViewControllerAnimated:YES];
}
@end 

错误处理

当发生错误时,会出现一个错误代码,指示错误的性质。HTTPSMS SDK 文档解释了每个错误号。

可能出现几种导致传输失败的情况。

第一个是连接错误。如果 iPhone 未连接到互联网,则可能发生此类错误。在这种情况下,我们希望提醒用户,并将短信放入队列中,以便在 iPhone 连接到互联网时发送。我们还希望,如果设备离线期间编写了几条短信,它们都将被放入队列中,并在可能的情况下进行传输。

此示例不执行任何逻辑错误检查(例如验证电话号码、国家代码等的格式),并且无论如何,只有在将消息发送到 Web 服务后,才可能会出现其他错误代码。

        if ( connError ) {
            NSLog(@"%@",[connError description]);
            
            NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
         
            [message setValue:txtCountryCode.text forKey:@"countryCode"];
            [message setValue:txtDestinationNo.text forKey:@"destinationNo"];
            [message setValue:txtName.text forKey:@"name"];
            [message setValue:txtMessage.text forKey:@"message"];
            
            [self performSelectorOnMainThread:@selector(addToQueue:) 
                                  withObject:message waitUntilDone:NO];
            [message release];
            UIAlertView* alert = [[UIAlertView alloc] init];
            alert.title = @"Send SMS";
            alert.message = @"Can't access Internet. 
                            The message will be queued to be sent later";
            [alert addButtonWithTitle:@"Ok"];
            [alert show];
            [alert release];            
            
        } else {
            UIAlertView* alert = [[UIAlertView alloc] init];
            alert.title = @"Send SMS";
            alert.message = resp;
            [alert addButtonWithTitle:@"Ok"];
            [alert show];
            [alert release];
            
            if ([resp rangeOfString:@"OK"].location != NSNotFound) {
                [self performSelectorOnMainThread:@selector(sendComplete) 
                                        withObject:nil waitUntilDone:YES];
            }            
        }
        
        [resp release];
    }    
    
    loadingView.hidden = YES;
}   

延伸阅读

关注点

如果您需要有关 iOS 开发的帮助,请阅读这篇 文章

Michael Haephrati , CodeProject MVP 2013

历史

  • 2013年2月16日:初始版本
© . All rights reserved.