最近开发了一些App,在iMac电脑上的阅读软件,想转换到IOS版本,这些文件可以通过 iTunes 的 file sharing 功能将文件导入到iPhone/iPad中的App中,前提是你的App在开发时,将info.plist文件中的Application supports iTunes file sharing 设置为Yes。
通过 iTunes将文件导入到你的应用中,操作比较简单,打开 iTunes,连接你的iPhone/iPad,选择你的设备,找到你要导入文件的应用,比如我的app,然后,点击添加,就可以将PGN文件导入到app的共享文件夹,这样,就可以用app打开这些文件了。
原来,没在IOS上做过这种功能,搜索IOS共享文件的读取,有几个例子,按一个简单的例子做好,提交到AppStore,审核被拒绝!原因是审核人员在导入文件后,无法看到导入的文件,就以功能缺陷为由拒绝了。我的App当时确实不能及时显示导入的文件,而是需要自己刷新一下,才能显示。但审核人员认为应该在导入后应可以立即显示出文件列表。
审核被拒绝后,又搜索了一下,按例子做了一个实时监控文件变化的,功能确实是可以实现的,但延迟时间比较长,有时候需要好几秒钟才能显示出文件列表来,不可忍受。最后,找到了苹果公司官方的例子,DirectoryWatcher,经测试效果非常好,一旦文件导入,立即就能显示。引入DirectoryWatcher后,提交Appstore,审核通过。代码比较简单,全部代码如下:
DirectoryWatcher.h
#import <Foundation/Foundation.h>
@class DirectoryWatcher;
@protocol DirectoryWatcherDelegate <NSObject>
@required
- (void)directoryDidChange:(DirectoryWatcher *)folderWatcher;
@end
@interface DirectoryWatcher : NSObject
{
id <DirectoryWatcherDelegate> __weak delegate;
int dirFD;
int kq;
CFFileDescriptorRef dirKQRef;
}
@property (nonatomic, weak) id <DirectoryWatcherDelegate> delegate;
+ (DirectoryWatcher *)watchFolderWithPath:(NSString *)watchPath delegate:(id<DirectoryWatcherDelegate>)watchDelegate;
- (void)invalidate;
@end
DirectoryWatcher.m
#import "DirectoryWatcher.h"
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#import <CoreFoundation/CoreFoundation.h>
@interface DirectoryWatcher (DirectoryWatcherPrivate)
- (BOOL)startMonitoringDirectory:(NSString *)dirPath;
- (void)kqueueFired;
@end
#pragma mark -
@implementation DirectoryWatcher
@synthesize delegate;
- (instancetype)init
{
self = [super init];
delegate = NULL;
dirFD = -1;
kq = -1;
dirKQRef = NULL;
return self;
}
- (void)dealloc
{
[self invalidate];
}
+ (DirectoryWatcher *)watchFolderWithPath:(NSString *)watchPath delegate:(id)watchDelegate
{
DirectoryWatcher *retVal = NULL;
if ((watchDelegate != NULL) && (watchPath != NULL))
{
DirectoryWatcher *tempManager = [[DirectoryWatcher alloc] init];
tempManager.delegate = watchDelegate;
if ([tempManager startMonitoringDirectory: watchPath])
{
// Everything appears to be in order, so return the DirectoryWatcher.
// Otherwise we'll fall through and return NULL.
retVal = tempManager;
}
}
return retVal;
}
- (void)invalidate
{
if (dirKQRef != NULL)
{
CFFileDescriptorInvalidate(dirKQRef);
CFRelease(dirKQRef);
dirKQRef = NULL;
// We don't need to close the kq, CFFileDescriptorInvalidate closed it instead.
// Change the value so no one thinks it's still live.
kq = -1;
}
if(dirFD != -1)
{
close(dirFD);
dirFD = -1;
}
}
@end
#pragma mark -
@implementation DirectoryWatcher (DirectoryWatcherPrivate)
- (void)kqueueFired
{
assert(kq >= 0);
struct kevent event;
struct timespec timeout = {0, 0};
int eventCount;
eventCount = kevent(kq, NULL, 0, &event, 1, &timeout);
assert((eventCount >= 0) && (eventCount < 2));
// call our delegate of the directory change
[delegate directoryDidChange:self];
CFFileDescriptorEnableCallBacks(dirKQRef, kCFFileDescriptorReadCallBack);
}
static void KQCallback(CFFileDescriptorRef kqRef, CFOptionFlags callBackTypes, void *info)
{
DirectoryWatcher *obj;
obj = (__bridge DirectoryWatcher *)info;
assert([obj isKindOfClass:[DirectoryWatcher class]]);
assert(kqRef == obj->dirKQRef);
assert(callBackTypes == kCFFileDescriptorReadCallBack);
[obj kqueueFired];
}
- (BOOL)startMonitoringDirectory:(NSString *)dirPath
{
// Double initializing is not going to work...
if ((dirKQRef == NULL) && (dirFD == -1) && (kq == -1))
{
// Open the directory we're going to watch
dirFD = open([dirPath fileSystemRepresentation], O_EVTONLY);
if (dirFD >= 0)
{
// Create a kqueue for our event messages...
kq = kqueue();
if (kq >= 0)
{
struct kevent eventToAdd;
eventToAdd.ident = dirFD;
eventToAdd.filter = EVFILT_VNODE;
eventToAdd.flags = EV_ADD | EV_CLEAR;
eventToAdd.fflags = NOTE_WRITE;
eventToAdd.data = 0;
eventToAdd.udata = NULL;
int errNum = kevent(kq, &eventToAdd, 1, NULL, 0, NULL);
if (errNum == 0)
{
CFFileDescriptorContext context = { 0, (__bridge void *)(self), NULL, NULL, NULL };
CFRunLoopSourceRef rls;
// Passing true in the third argument so CFFileDescriptorInvalidate will close kq.
dirKQRef = CFFileDescriptorCreate(NULL, kq, true, KQCallback, &context);
if (dirKQRef != NULL)
{
rls = CFFileDescriptorCreateRunLoopSource(NULL, dirKQRef, 0);
if (rls != NULL)
{
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
CFFileDescriptorEnableCallBacks(dirKQRef, kCFFileDescriptorReadCallBack);
// If everything worked, return early and bypass shutting things down
return YES;
}
// Couldn't create a runloop source, invalidate and release the CFFileDescriptorRef
CFFileDescriptorInvalidate(dirKQRef);
CFRelease(dirKQRef);
dirKQRef = NULL;
}
}
// kq is active, but something failed, close the handle...
close(kq);
kq = -1;
}
// file handle is open, but something failed, close the handle...
close(dirFD);
dirFD = -1;
}
}
return NO;
}
@end
将DirectoryWatcher.h和DirectoryWatcher.m导入到你的工程中。
在myViewController.h中,添加与读取共享文件夹的变量定义
#import <UIKit/UIKit.h>
#import "DirectoryWatcher.h"
@interface myViewController : UIViewController <DirectoryWatcherDelegate> {
//显示文件列表
IBOutlet UITableView *fileTableView;
//存储沙盒子里的所有文件
NSMutableArray *dirArray;
//苹果的方法
DirectoryWatcher *docWatcher;
}
@end
在myViewController.m中,实现读取共享文件清单
#pragma mark -
#pragma mark 文件变动监听方法
- (NSString *)applicationDocumentsDirectory {
//
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
- (void)directoryDidChange:(DirectoryWatcher *)folderWatcher {
//
dirArray = [[NSMutableArray alloc] init];
NSString *documentsDirectoryPath = [self applicationDocumentsDirectory];
NSArray *documentsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectoryPath
error:NULL];
for (NSString* curFileName in [documentsDirectoryContents objectEnumerator]) {
//
NSString *filePath = [documentsDirectoryPath stringByAppendingPathComponent:curFileName];
//
BOOL isDirectory;
[[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory];
// proceed to add the document URL to our list (ignore the "Inbox" folder)
if (!isDirectory)
{
//这里显示共享文件夹内的所有文件,如果想只显示你的文件,就对curFileName是否是这类进行判断
[dirArray addObject:curFileName];
}
}
//step6. 刷新列表, 显示数据
[fileTableView reloadData];
}
#pragma mark -
#pragma mark 文件变动监听方法结束
在viewDidLoad中加入相关代码
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
//苹果监听Document目录的文件改动
// start monitoring the document directory…
docWatcher = [DirectoryWatcher watchFolderWithPath:[self applicationDocumentsDirectory] delegate:self];
// scan for existing documents
[self directoryDidChange:docWatcher];
//保存一份txt文件到设备document文件夹中(为了测试方便)
//NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
//NSString *filePath;
//char *saves = "test_files";
//NSData *data = [[NSData alloc] initWithBytes:saves length:10];
//filePath = [documentsPath stringByAppendingPathComponent:@"test.pgn"];
//[data writeToFile:filePath atomically:YES];
}
在TableView的代理方法中加入
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//
return [dirArray count];
}
//设置cell的显示
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
static NSString *SimpleTableIdentifier = @"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:SimpleTableIdentifier];
}
NSUInteger row = [indexPath row];
NSString *str = [dirArray objectAtIndex:row];
cell.textLabel.text = str;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//选中某文件,读取文件
NSString *str = [dirArray objectAtIndex:[indexPath row]];
NSString *path=[[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:str];
NSData *reader = [NSData dataWithContentsOfFile:path];
//获得reader后,你想干什么,就是你自己的事了,哈哈。
}