iBeacons

iBeacons是iOS7的新增的功能,可用于室内近场定位。iOS设备可以检测到附近的iBeacons兼容设备,在当前版本中甚至可以在应用后台运行的情况下获得回调。

iBeacons设备往往都是低功耗的,用Bluetooth Low Energy低功耗蓝牙实现,设计良好的iBeacons设备在一节CR2032纽扣电池支持下应能支持一年以上的使用时间。

iBeacons还有一个有用的特性是可以距离感应,当你的iOS设备检测到了一个iBeacons设备,那么app即可通过CoreLocation监测设备的距离,通过回调的形式通知距离变化。

i

常见应用场景

可以有不少有趣的应用场景,随便列举一些。

  • 商场购物时,靠近某些商品时,提示用户正在打折,或者查看商品的具体信息以及评价。靠近交费区域时,使用某一信用卡支付可以获得优惠。
  • 室内导航,例如机场登机口指示
  • 各种大会入场checkin,免去繁琐的签到流程
  • 各种展览场所,当用户靠近某些展品时,就可以获得相应的介绍信息

什么时候应该使用iBeacons

  • 同时需要检测多个区域
  • 被检测区域可以是一个移动的区域,例如汽车,列车。。
  • 检测区域范围有限的情况(一般不会大于100m)
  • 每个区域需要一些标识做区分的情况
  • 每个区域可以在室内,同一建筑物内,可以有重合
  • 用户愿意开启低功耗蓝牙来检测区域

如何把iOS设备变成一个iBeacon

所有支持低功耗蓝牙技术的iOS设备都可以变成一个iBeacon设备,被其它iOS设备检测到。例如iPhone 4s, iPod Touch5, iPad3及之后的设备,都支持低功耗蓝牙技术。

定义你的iBeacon

首先需要了解4个属性

  • proximity UUID , 是一个iBeacon或一组iBeacons设备的唯一标识,标明它的类型,你可以用osx下的 uuidgen
    命令生成
  • 一个内部identifier ,你在app中自定义的标识
  • 一个 major identifier, 可用于区分一组拥有相同proximity UUID的设备
  • 一个 minor identifier, 可用于区分一组拥有相同proximity UUID和相同major identifier的设备

创建并广播一个iBeacon区域

首先需要定义一个CLBeaconRegion来实现iBeacon,如下

NSUUID *myProximityUUID = [[NSUUID alloc]
    initWithUUIDString:@"566C5595-6EC7-4F08-909F-C954BDCA6CD3"];
NSNumber *branchNumber = @42;
NSNumber *tillNumber = @3;
CLBeaconRegion *region = [[CLBeaconRegion alloc]
    initWithProximityUUID:myProximityUUID
    major:[branchNumber unsignedShortValue]
    minor:[tillNumber unsignedShortValue]
    identifier:@"com.mycompany.store"];

然后把这些信息交由CBPeripheralManager进行广播


NSDictionary *peripheralData =
    [region peripheralDataWithMeasuredPower:nil];
CBPeripheralManager *myPeripheralManager =
    [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
[myPeripheralManager startAdvertising:peripheralData];

于是一个iOS设备就变成了一个iBeacon了

兼容的iBeacon设备

目前Apple并未公布iBeacons的具体技术细节,但是仍然有不少第三方通过蓝牙嗅探工具反向工程实现了iBeacons兼容设备。在国外已经有一些成品上市,例如

  • estimote
  • sticknfind
  • paypal,它甚至将iBeacons融入了它的支付技术

本人也利用TI公司的CC2541实现了一个简单的iBeacons兼容模块,为了未来可能的变化,做了预留的空中升级接口,现在某宝尝试性的对外出售,适用于有一定电子技术动手能力的爱好者测试。

参考链接

使用CocoaPods之后的头文件包含问题

使用了一段时间CocoaPods来管理Objective-c的类库,方便了不少。但是有一个小问题,当我在xcode输入import关键字的时候,没有自动联想补齐代码的功能,需要手工敲全了文件名,难以适应。

在stackoverflow上找到了解决办法:

Go to the Target > \”Build Settings\” tab and find the \”User Header Search Paths\” setting.

Set this to \”$(BUILT_PRODUCTS_DIR)\” and check the \”Recursive\” check box.

Now the built target will search the workspace’s shared build directory to locate the linkable header files.

简单说就是这么几步:

  • 选择Target -> Build Settings 菜单,找到\”User Header Search Paths\”设置项
  • 新增一个值$(BUILT_PRODUCTS_DIR),并且选择\”Recursive\”,这样xcode就会在项目目录中递归搜索文件

自动补齐功能马上就好使了。

iPhone中png图片格式处理

众所周知,iPhone中应用自带的png图片已经是经过压缩处理的,无法直接查看,但是可以通过工具转换为原图。

转换为原图的方法

在安装好Xcode之后(我安装的版本是4.3),可使用命令行转换

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/pngcrush -revert-iphone-optimizations src.png dst.png

这个命令行太长,不好记,所以我在~/.bash_profile中加入一个alias。

alias pngcrush=\"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/pngcrush -revert-iphone-optimizations \"

所以之前的命令可以简化为

pngcrush src.png dst.png

批量转换png

用简单的shell就可以批量转换png图片为原图。

for i in `ls *.png`; do pngcrush $i /tmp/$i; mv /tmp/$i $i; done

小说跟踪器 —— 另一款iPhone上的小说阅读,更新提醒应用

不才的另一个iPhone应用 —— 小说跟踪器也已经出现在苹果的应用商店。

小说跟踪器的用途

小说跟踪器可以试阅起点中文网的小说,订阅小说的章节更新通知。因为目前的阅读软件已经多的不得了,所以这款应用没有把阅读功能作为重点来做,只提供了基本的试阅功能。但是你会发现它的 章节更新通知 功能很好用,很及时而且也稳定,最重要的是,还免费

所以,这个应用适用于起点小说的重度书友,跟书不愁。

目前支持的设备有iPhone以及iTouch,在iOS 4.3及以上版本都可以使用。

应用截图

书架
\"书架\"

章节通知
\"章节通知\"

阅读界面
\"阅读\"

网站

iOS 5的文件存储策略应对

苹果在iOS 5系统时,对app的文件存储提出了新的要求。从它的guildline来看,是推荐开发者尽量把app生成的文件放在Caches目录下的。原文如下:

Only user-generated data or that cannot otherwise be recreated by your application, should be stored in the /Documents directory and rest should be stored to /Library/Caches directory。

照做会怎么样?

如果这么做的话,会出现两种情况

  1. 如果对此置之不理,继续把应用生成的文件放在Documents目录下,那么这些文件会被备份到iTunes或者iCloud。如果这些文件很大,那么用户可能需要为了同步消耗不少流量,然后苹果可能会因此拒绝你的应用上架,这是一个悲剧。
  2. 如果开发者照Apple说的干,把应用生成的文件放在Caches目录下,那么苹果不会拒绝你的应用,很happy。但是iOS 5会在磁盘空间紧张的时候删除Caches目录下的文件,这对用户来说可能是一个更大的悲剧。

如何应对新的文件存储策略?

开发者在这时陷入了两难的境地,但是到了iOS 5.0.1的时候,开发者多了第三种选择:

  • 继续把文件存储在Documents目录下,但是标记这些文件为不需要备份。详情请参考 technote (QA1719)

原文如下:

Q: My app has a number of files that need to be stored on the device permanently for my app to function properly offline. However, those files do not contain user data and don\’t need to be backed up. How should I store those files in iOS 5?

A: Starting in iOS 5.0.1 a new \”do not back up\” file attribute has been introduced allowing developers to clearly specify which files should be backed up, which files are local caches only and subject to purge, and which files should not be backed up but should also not be purged. In addition, setting this attribute on a folder will prevent the folder and all of its contents from being backed up.

代码示例

给文件加上\”do not back up\”属性的代码如下,需要注意这个是iOS 5.0.1才有效,低于这个版本就别费劲了。


#include 
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    const char* filePath = [[URL path] fileSystemRepresentation];
 
    const char* attrName = \"com.apple.MobileBackup\";
    u_int8_t attrValue = 1;
 
    int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
    return result == 0;
}

如何将TTURLRequest和OAuthConsumer搭配使用

TTURLRequest是three20开发框架提供的一个url请求类,它是NSURLRequest类的扩展,有如下优点:

  • post数据方便,只需要构建一个参数的dictionary就可以了,像get方法一样简单
  • 支持磁盘缓存,而NSURLRequest仅支持内存缓存
  • 因为TTTableViewController + TTURLRequestModel的存在,搭配使用效果良好

在ios的开发中我有时也用到了OAuthConsumer进行oauth授权,利用OAuthConsumer的fetcher类请求远程数据,所以我想TTURLRequest和OAuthConsumer能不能搭配使用,这样就能用上three20相关便利方法加载远程数据。于是做了点简单的测试,果然成功了:)大体思路是:

  • 先利用OAuthConsumer根据提交的参数计算出oauth的Authorization认证头
  • 把Authorization头加到TTURLRequest
  • 由TTURLRequest提交数据。

直接上代码说话:

计算Authorization头

利用OAuthConsumer计算出Authorization头,为之后的请求做准备


//start
OAConsumer * consumer = [[OAConsumer alloc] initWithKey:yourConsumerKey secret:yourSecret];
NSURL * url = [NSURL URLWithString:@"http://your-api-host/your-method"];
OAToken * authToken = [[OAToken alloc] initWithKey:yourAuthToken secret:yourAuthSecrent];
OAMutableURLRequest * request = [[OAMutableURLRequest alloc] initWithURL:url 
                                                                consumer:consumer 
                                                                   token:authToken 
                                                                   realm:nil 
                                                       signatureProvider:[[[OAPlaintextSignatureProvider alloc] init] autorelease]];
[request setHTTPMethod:@"POST"];

NSMutableArray * params = [NSMutableArray array];
OARequestParameter * p1 = [[OARequestParameter alloc] initWithName:@"param1" value:@"i'm param1"];
[params addObject:p1];

[request setParameters:params];
[request prepare];

TTDINFO(@"Authorization is %@", [request valueForHTTPHeaderField:@"Authorization"]);

使用TTURLRequest请求数据

将Authorization头附加到TTURLRequest,然后请求远程接口


TTURLRequest* req = [TTURLRequest requestWithURL:request.URL.absoluteString delegate:self];
req.response = [[[TTURLDataResponse alloc] init] autorelease];
req.httpMethod = @"POST";
req.cachePolicy = TTURLRequestCachePolicyNone;
[req setValue:[request valueForHTTPHeaderField:@"Authorization"] forHTTPHeaderField:@"Authorization"];
[req send];

这个方法不需要对TTURLRequest进行修改,简单有效。