Mac-to-bluetooth device file transfer, simple example? - objective-c

I've spent two days googling and reading the Bluetooth programming guide while trying to piece together a small Mac app that will retrieve images from a drop folder and send any new files to a predetermined device over Bluetooth. There doesn't seem to be many good examples available.
I'm at the point where I'm able to spawn the Bluetooth Service Browser and select the device and its OBEX service, establishing a service and creating a connection, but then nothing more happens. Could anyone please point me in the direction of/show me a simple example that would work?
AppDelegate source code enclosed. Thanks for reading!
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
IOBluetoothServiceBrowserController *browser = [IOBluetoothServiceBrowserController serviceBrowserController:0];
[browser runModal];
//IOBluetoothSDPServiceRecord
IOBluetoothSDPServiceRecord *result = [[browser getResults] objectAtIndex:0];
[self describe:result];
if ([[result.device.name substringToIndex:8] isEqualToString:#"Polaroid"]) {
printer = result.device;
serviceRecord = result;
[self testPrint];
}
else {
NSLog(#"%# is not a valid device", result.device.name);
}
}
- (void) testPrint {
currentFilePath = #"/Users/oyvind/Desktop/_DSC8797.jpg";
[self sendFile:currentFilePath];
}
- (void) sendFile:(NSString *)filePath {
IOBluetoothOBEXSession *obexSession = [[IOBluetoothOBEXSession alloc] initWithSDPServiceRecord:serviceRecord];
if( obexSession != nil )
{
NSLog(#"OBEX Session Established");
OBEXFileTransferServices *fst = [OBEXFileTransferServices withOBEXSession:obexSession];
OBEXDelegate *obxd = [[OBEXDelegate alloc] init];
[obxd setFile:filePath];
[fst setDelegate:obxd];
OBEXError cnctResult = [fst connectToObjectPushService];
if( cnctResult != kIOReturnSuccess ) {
NSLog(#"Error creating connection");
return;
}
else {
NSLog(#"OBEX Session Created. Sending file: %#", filePath);
[fst sendFile:filePath];
[printer openConnection];
}
}
else {
NSLog(#"Error creating OBEX session");
NSLog(#"Error sending file");
}
}
#end

OK; here's what ultimately became the core parts of the functionality. The application I made was a sort of print server for Polaroid instant printers that would only accept images over Object Push.
First, ensure watched folder exists.
/*
Looks for a directory named PolaroidWatchFolder in the user's desktop directory
and creates it if it does not exist.
*/
- (void) ensureWatchedFolderExists {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *url = [NSURL URLWithString:#"PolaroidWatchFolder" relativeToURL:[[fileManager URLsForDirectory:NSDesktopDirectory inDomains:NSUserDomainMask] objectAtIndex:0]];
BOOL isDir;
if ([fileManager fileExistsAtPath:[url path] isDirectory:&isDir] && isDir) {
[self log:[NSString stringWithFormat:#"Watched folder exists at %#", [url absoluteURL]]];
watchFolderPath = url;
}
else {
NSError *theError = nil;
if (![fileManager createDirectoryAtURL:url withIntermediateDirectories:NO attributes:nil error:&theError]) {
[self log:[NSString stringWithFormat:#"Watched folder could not be created at %#", [url absoluteURL]]];
}
else {
watchFolderPath = url;
[self log:[NSString stringWithFormat:#"Watched folder created at %#", [url absoluteURL]]];
}
}
}
Then scan for available printers:
/*
Loops through all paired Bluetooth devices and retrieves OBEX Object Push service records
for each device who's name starts with "Polaroid".
*/
- (void) findPairedDevices {
NSArray *pairedDevices = [IOBluetoothDevice pairedDevices];
devicesTested = [NSMutableArray arrayWithCapacity:0];
for (IOBluetoothDevice *device in pairedDevices)
{
if ([self deviceQualifiesForAddOrRenew:device.name])
{
BluetoothPushDevice *pushDevice = [[BluetoothPushDevice new] initWithDevice:device];
if (pushDevice != nil)
{
[availableDevices addObject:pushDevice];
[pushDevice testConnection];
}
}
}
}
That last function call is to the BluetoothPushDevice's built-in method to test the connection. Here is the delegate handler for the response:
- (void) deviceStatusHandler: (NSNotification *)notification {
BluetoothPushDevice *device = [notification object];
NSString *status = [[notification userInfo] objectForKey:#"message"];
if ([devicesTested count] < [availableDevices count] && ![devicesTested containsObject:device.name]) {
[devicesTested addObject:device.name];
}
}
Upon server start, this method will run in response to a timer tick or manual scan:
- (void) checkWatchedFolder {
NSError *error = nil;
NSArray *properties = [NSArray arrayWithObjects: NSURLLocalizedNameKey, NSURLCreationDateKey, NSURLLocalizedTypeDescriptionKey, nil];
NSArray *files = [[NSFileManager defaultManager]
contentsOfDirectoryAtURL:watchFolderPath
includingPropertiesForKeys:properties
options:(NSDirectoryEnumerationSkipsHiddenFiles)
error:&error];
if (files == nil) {
[self log:#"Error reading watched folder"];
return;
}
if ([files count] > 0) {
int newFileCount = 0;
for (NSURL *url in files) {
if (![filesInTransit containsObject:[url path]]) {
NSLog(#"New file: %#", [url lastPathComponent]);
[self sendFile:[url path]];
newFileCount++;
}
}
}
}
When new files are found, ww first need to find a device that is not busy recieving a file of printing it:
/*
Loops through all discovered device service records and returns the a new OBEX session for
the first it finds that is not connected (meaning it is not currently in use, connections are
ad-hoc per print).
*/
- (BluetoothPushDevice*) getIdleDevice {
for (BluetoothPushDevice *device in availableDevices) {
if ([device.status isEqualToString:kBluetoothDeviceStatusReady]) {
return device;
}
}
return nil;
}
Then a file is sent with this method:
- (void) sendFile:(NSString *)filePath {
BluetoothPushDevice *device = [self getIdleDevice];
if( device != nil ) {
NSLog(#"%# is available", device.name);
if ([device sendFile:filePath]) {
[self log:[NSString stringWithFormat:#"Sending file: %#", filePath]];
[filesInTransit addObject:filePath];
}
else {
[self log:[NSString stringWithFormat:#"Error sending file: %#", filePath]];
}
}
else {
NSLog(#"No idle devices");
}
}
Upon transfer complete, this delegate method is called:
/*
Responds to BluetoothPushDevice's TransferComplete notification
*/
- (void) transferStatusHandler: (NSNotification *) notification {
NSString *status = [[notification userInfo] objectForKey:#"message"];
NSString *file = ((BluetoothPushDevice*)[notification object]).file;
if ([status isEqualToString:kBluetoothTransferStatusComplete]) {
if ([filesInTransit containsObject:file]) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
[fileManager removeItemAtPath:file error:&error];
if (error != nil) {
[self log:[NSString stringWithFormat:#"**ERROR** File %# could not be deleted (%#)", file, error.description]];
}
[self log:[NSString stringWithFormat:#"File deleted: %#", file]];
[filesInTransit removeObject:file];
}
else {
[self log:[NSString stringWithFormat:#"**ERROR** filesInTransit array does not contain file %#", file]];
}
}
[self updateDeviceStatusDisplay];
}
I hope this helps someone!

Related

Importing SimpleAuth ForSquareWeb - 9 build time errors

I'm relatively new to objective-c and hardly know much of swift.
I've been trying to make an app which will implement simpleAuth in order to create a link to the ForsquareWeb API.
I'm using cocoapods and have imported the SimpleAuth related files into my product.
Every file seems to be fine except the SimpleAuth target, specifically the SimpleAuthForSquareWebProvider.m file. This is what the file itself looks like;
//
// SimpleAuthFoursquareWebProvider.m
// SimpleAuth
//
// Created by Julien Seren-Rosso on 23/01/2014.
// Copyright (c) 2014 Byliner, Inc. All rights reserved.
//
#import "SimpleAuthFoursquareWebProvider.h"
#import "SimpleAuthFoursquareWebLoginViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import "UIViewController+SimpleAuthAdditions.h"
#implementation SimpleAuthFoursquareWebProvider
#pragma mark - SimpleAuthProvider
+ (NSString *)type {
return #"foursquare-web";
}
+ (NSDictionary *)defaultOptions {
// Default present block
SimpleAuthInterfaceHandler presentBlock = ^(UIViewController *controller) {
UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:controller];
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
UIViewController *presented = [UIViewController SimpleAuth_presentedViewController];
[presented presentViewController:navigation animated:YES completion:nil];
};
// Default dismiss block
SimpleAuthInterfaceHandler dismissBlock = ^(id controller) {
[controller dismissViewControllerAnimated:YES completion:nil];
};
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:[super defaultOptions]];
options[SimpleAuthPresentInterfaceBlockKey] = presentBlock;
options[SimpleAuthDismissInterfaceBlockKey] = dismissBlock;
return options;
}
- (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion {
[[[self accessToken]
flattenMap:^RACStream *(NSString *response) {
NSArray *signals = #[
[self accountWithAccessToken:response],
[RACSignal return:response]
];
return [self rac_liftSelector:#selector(dictionaryWithAccount:accessToken:) withSignalsFromArray:signals];
}]
subscribeNext:^(NSDictionary *response) {
completion(response, nil);
}
error:^(NSError *error) {
completion(nil, error);
}];
}
#pragma mark - Private
- (RACSignal *)accessToken {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_async(dispatch_get_main_queue(), ^{
SimpleAuthFoursquareWebLoginViewController *login = [[SimpleAuthFoursquareWebLoginViewController alloc] initWithOptions:self.options];
login.completion = ^(UIViewController *login, NSURL *URL, NSError *error) {
SimpleAuthInterfaceHandler dismissBlock = self.options[SimpleAuthDismissInterfaceBlockKey];
dismissBlock(login);
// Parse URL
NSString *fragment = [URL fragment];
NSDictionary *dictionary = [CMDQueryStringSerialization dictionaryWithQueryString:fragment];
NSString *token = dictionary[#"access_token"];
// Check for error
if (![token length]) {
[subscriber sendError:error];
return;
}
// Send completion
[subscriber sendNext:token];
[subscriber sendCompleted];
};
SimpleAuthInterfaceHandler block = self.options[SimpleAuthPresentInterfaceBlockKey];
block(login);
});
return nil;
}];
}
- (RACSignal *)accountWithAccessToken:(NSString *)accessToken {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSDictionary *parameters = #{ #"oauth_token" : accessToken };
NSString *query = [CMDQueryStringSerialization queryStringWithDictionary:parameters];
NSString *URLString = [NSString stringWithFormat:#"https://api.foursquare.com/v2/users/self?v=20140210&%#", query];
NSURL *URL = [NSURL URLWithString:URLString];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[NSURLConnection sendAsynchronousRequest:request queue:self.operationQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 99)];
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if ([indexSet containsIndex:statusCode] && data) {
NSError *parseError = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parseError];
if (dictionary) {
[subscriber sendNext:dictionary];
[subscriber sendCompleted];
}
else {
[subscriber sendError:parseError];
}
}
else {
[subscriber sendError:connectionError];
}
}];
return nil;
}];
}
- (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSString *)accessToken {
NSMutableDictionary *dictionary = [NSMutableDictionary new];
NSDictionary *userData = account[#"response"][#"user"];
// Provider
dictionary[#"provider"] = [[self class] type];
// Credentials
dictionary[#"credentials"] = #{
#"token" : accessToken
};
// User ID
dictionary[#"uid"] = userData[#"id"];
// Raw response
dictionary[#"extra"] = #{
#"raw_info" : userData
};
// User info
NSMutableDictionary *user = [NSMutableDictionary new];
if (userData[#"contact"][#"email"]) {
user[#"email"] = userData[#"contact"][#"email"];
}
if (userData[#"firstName"]) {
user[#"first_name"] = userData[#"firstName"];
}
if (userData[#"lastName"]) {
user[#"last_name"] = userData[#"lastName"];
}
user[#"name"] = [NSString stringWithFormat:#"%# %#", user[#"first_name"], user[#"last_name"]];
user[#"gender"] = userData[#"gender"];
if ([userData[#"photo"] isKindOfClass:NSDictionary.class]) {
user[#"image"] = [NSString stringWithFormat:#"%#500x500%#", userData[#"photo"][#"prefix"], userData[#"photo"][#"suffix"]];
} else if ([userData[#"photo"] isKindOfClass:NSString.class]) {
user[#"image"] = userData[#"photo"];
}
if (userData[#"photo"]) {
user[#"photo"] = userData[#"photo"];
}
if (userData[#"homeCity"]) {
NSString *homecity = [[userData[#"homeCity"] componentsSeparatedByString:#","] firstObject];
user[#"location"] = homecity;
}
user[#"urls"] = #{
#"Foursquare" : [NSString stringWithFormat:#"https://foursquare.com/user/%#", userData[#"id"]],
};
dictionary[#"info"] = user;
return dictionary;
}
#end
I think it would be easier to show you just a screen shot of the errors and where they're arising in the code itself;
I would really appreciate some insight into where the problem lies. I'm not sure why many of the errors say use of undeclared identifiers, are the imported files not correct?
After trying to re-install the pod file as it was suggested a class hadn't been installed I still have the same problem. Here are screen shots of my podfile and the terminals output when installing the pods;
I just used the Cocoapods application rather than terminal. I got additional information when clicking install.
"[!] The dPicDid [Debug] target overrides the ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES build setting defined in `Pods/Target Support Files/Pods-dPicDid/Pods-dPicDid.debug.xcconfig'. This can lead to problems with the CocoaPods installation
[!] The dPicDid [Release] target overrides the ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES build setting defined in Pods/Target Support Files/Pods-dPicDid/Pods-dPicDid.release.xcconfig'. This can lead to problems with the CocoaPods installation
- Use the$(inherited)flag, or
- Remove the build settings from the target.
- Use the$(inherited)` flag, or
- Remove the build settings from the target.

Crashes when copying SQLite db from bundle to documents and then querying

This makes absolutely no sense (to me).
SQLite will throw EXC_BAD_ACCESS errors randomly (on the sqlite3_prepare_v2 line in the Db class) when querying when I copy the database from the bundle to the documents folder in a background thread. But, if I don't perform it in a background thread then no crashes ever occur.
The reason I want to perform the operation in a thread is that it is quite large (~150MB).
My didFinishLaunchingWithOptions looks like this - the working script is commented out below the GCD thread:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dispatch_queue_t setupDb = dispatch_queue_create("setupDb", NULL);
dispatch_async(setupDb, ^{
[Db setup];
dispatch_async(dispatch_get_main_queue(), ^{
[Db connect];
[[NSNotificationCenter defaultCenter] postNotificationName:#"databaseReady" object:nil];
});
});
// If using this code, though, it works perfectly fine
// [Db setup];
// [Db connect];
// [[NSNotificationCenter defaultCenter] postNotificationName:#"databaseReady" object:nil];
return YES;
}
The database class looks like (_statement is defined in the .h):
#implementation Db
static sqlite3 * _db;
static NSString * _dbPath;
+ (BOOL)setup {
NSString * sqlBundlePath = [[NSBundle mainBundle] pathForResource:#"db" ofType:#"sqlite"];
NSString * documentsFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString * sqlDocumentPath = [[documentsFolder stringByAppendingPathComponent:#"db"] stringByAppendingPathExtension:#"sqlite"];
NSFileManager * fileManager = [NSFileManager defaultManager];
// Make sure we don't already have the database in our documents, we don't want to overwrite!
if (! [fileManager fileExistsAtPath:sqlDocumentPath]) {
BOOL success = [fileManager copyItemAtPath:sqlBundlePath toPath:sqlDocumentPath error:nil];
if (! success) {
NSLog(#"Error copying database");
return NO;
}
}
_dbPath = sqlDocumentPath;
return YES;
}
+ (BOOL)connect {
sqlite3_config(SQLITE_CONFIG_SERIALIZED);
sqlite3_open([_dbPath UTF8String], &_db);
return YES;
}
- (BOOL)prepare:(const char *)sql {
if (! sqlite3_prepare_v2(_db, sql, -1, &_statement, NULL) == SQLITE_OK) {
NSLog(#"Error whilst preparing query: %s", sqlite3_errmsg(_db));
sqlite3_finalize(_statement);
return NO;
}
return YES;
}
- (BOOL)step {
if (sqlite3_step(_statement) == SQLITE_ROW) {
return YES;
}
return NO;
}
- (void)finalise {
sqlite3_finalize(_statement);
}
#end

NSURLSession downloadTask not releasing memory

Due to the customer can't implement just few downloads in his server in a short time and backgroundDownloadTaks were very inconsistent when there are so much files (500-1000 downloads) I decide to use NSURLDownloadTask without background NSURLSession.
It works quite well with a large amount of files, but there is an inconvenience. Memory usage is always growing untill I get a memory warning. When I get it I cancel pending tasks and free NSURLCache but memory is not released so when you resume the downloads you get the same memory warning.
I'm not using cancelWithResumeData for cancelling the tasks.
This is my code
- (void) startDownloadFiles:(NSMutableArray*)arrayFiles
{
if([[UIDevice currentDevice] isMultitaskingSupported])
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (!self.session)
{
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPMaximumConnectionsPerHost = 5;
sessionConfiguration.timeoutIntervalForRequest = 0;
sessionConfiguration.timeoutIntervalForResource = 0;
sessionConfiguration.requestCachePolicy = NSURLCacheStorageNotAllowed;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:nil];
}
//Resetting session
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionTask *_task in downloadTasks)
{
[_task cancel];
}
[self.session resetWithCompletionHandler:^{
for (id<FFDownloadFileProtocol> file in self.selectedCatalogProducto.downloadInfo.arrayFiles)
{
if (cancel)
break; //Do not create more taks
if (![file isDownloaded])
[self startDownloadFile:file];
}
}];
}];
});
}
}
- (void) startDownloadFile:(id<FFDownloadFileProtocol>)file
{
if (![file isDownloading])
{
if ([file taskIdentifier] == -1
&& ! cancel)
{
NSURLSessionDownloadTask *task = [self.session downloadTaskWithURL:[file downloadSource]];
if (task)
{
[file setDownloadTask:task];
[file setTaskIdentifier:[file downloadTask].taskIdentifier];
[[file downloadTask] resume];
}
else
{
NSLog(#"Error creando tarea para descargar %#", [file downloadSource]);
}
}
}
}
#pragma mark - Auxiliar Methods
-(id<FFDownloadFileProtocol>)getFileDownloadInfoIndexWithTaskIdentifier:(unsigned long)taskIdentifier
{
for (id<FFDownloadFileProtocol> file in self.selectedCatalogProducto.downloadInfo.arrayFiles)
{
if (file.taskIdentifier == taskIdentifier) {
return file;
}
}
return nil;
}
#pragma mark - NSURLSessionDownloadTaskDelegate
- (void) URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) {
NSLog(#"Unknown transfer size");
}
else
{
// Locate the FileDownloadInfo object among all based on the taskIdentifier property of the task.
id<FFDownloadFileProtocol> file = [self getFileDownloadInfoIndexWithTaskIdentifier:downloadTask.taskIdentifier];
// Calculate the progress.
file.downloadProgress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
// [[NSOperationQueue mainQueue] addOperationWithBlock:^{
// NSLog("%# ; %f", [file fileName], [file downloadProgress]);
// }];
}
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
id<FFDownloadFileProtocol> file = [self getFileDownloadInfoIndexWithTaskIdentifier:downloadTask.taskIdentifier];
if (file)
{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *destinationURL = [[NSURL fileURLWithPath:tempPath] URLByAppendingPathComponent:[file fileName]];
if ([fileManager fileExistsAtPath:[destinationURL path]]) {
NSError *delError = nil;
[fileManager removeItemAtURL:destinationURL error:nil];
if (delError)
{
NSLog(#"Error borrando archivo temporal en %#", [destinationURL path]);
}
}
BOOL success = [fileManager copyItemAtURL:location
toURL:destinationURL
error:&error];
if (success) {
// Change the flag values of the respective FileDownloadInfo object.
file.isDownloading = NO;
file.isDownloaded = YES;
// Set the initial value to the taskIdentifier property of the file object,
// so when the start button gets tapped again to start over the file download.
}
else
{
NSLog(#"Unable to copy temp file to %# Error: %#", [destinationURL path], [error localizedDescription]);
}
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
{
indexFile++;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self numFilesDownloaded:indexFile];
}];
}
}
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
id<FFDownloadFileProtocol> file = [self getFileDownloadInfoIndexWithTaskIdentifier:task.taskIdentifier];
if (error != nil
&& error.code != -999)
{
//No se ha producido error o se ha cancelado la tarea bajo demanda
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(#"Download: %#. \n Downlonad completed with error: %#", [task.response.URL absoluteString], [error localizedDescription]);
if (!cancel)
{
NSString *alertBody = #"Se ha producido un error en la descarga, por favor reanúdela manualmente";
if ([error.domain isEqualToString:#"NSPOSIXErrorDomain"] && (error.code == 1) )
{
alertBody = #"Se ha interrumpido la descarga debido a que su iPad está bloqueado por código. Por favor reanude la descarga manualmente y evite que el iPad se bloquee";
}
// Show a local notification when all downloads are over.
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = alertBody;
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
[self errorDownloading:error.localizedDescription];
}
}];
}
else if (file)
{
NSLog(#"%# download finished successfully.", [[file downloadSource] absoluteString]);
file.taskIdentifier = -1;
// In case there is any resume data stored in the file object, just make it nil.
file.taskResumeData = nil;
file.downloadTask = nil;
}
else if (cancel)
{
NSLog(#"Tarea cancelada");
}
if (self.selectedCatalogProducto.downloadInfo.arrayFiles.count == indexFile
&& !cancel)
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if (!complete)
{
complete = YES;
[self downloadComplete];
}
}];
}
task = nil;
}
#pragma mark - Memory warning
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if (_isDownloading)
{
[self storeCatalogProductInfo:self.selectedCatalogProducto andDownloadInfo:YES];
[self stopDownloading];
}
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[self.session.configuration.URLCache removeAllCachedResponses];
}
And these are two snapshot of memory usage
Memory usage increasing when files are downloading
Download tasks are stopped but memory is not released
Why can't I release memory?
Thanks for any help provided
You need to call the invalidateAndCancel method on your NSURLSession instance when you're done using it, otherwise it will leak memory.

Core Data Failing on saveToURL on UIManagedDocument

I'm trying to set up core data but when i'm running saveToURL on the UIManagedDocument it's failing to create it. This is my code;
#property (nonatomic, strong) UIManagedDocument *currentUserDatabase;
#synthesize currentUserDatabase = _currentUserDatabase;
- (void)setCurrentUserDatabase:(UIManagedDocument *)currentUserDatabase
{
_currentUserDatabase = currentUserDatabase;
[self useDocument];
}
- (void)isUserLoggedIn
{
if (!self.currentUserDatabase) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentationDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Current User Database"];
self.currentUserDatabase = [[UIManagedDocument alloc] initWithFileURL:url];
}
}
- (void)useDocument
{
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.currentUserDatabase.fileURL path]]) {
[self.currentUserDatabase saveToURL:self.currentUserDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if (success) {
NSLog(#"create success");
} else {
NSLog(#"create failed");
}
//[self getCurrentUser];
}];
} else if (self.currentUserDatabase.documentState == UIDocumentStateClosed) {
[self.currentUserDatabase openWithCompletionHandler:^(BOOL success) {
//[self getCurrentUser];
}];
} else if (self.currentUserDatabase.documentState == UIDocumentStateNormal) {
//[self getCurrentUser];
}
}
isUserLoggedIn gets called first, but when it gets created, I get the log "create failed" - I don't understand because there is no errors. Are there any ways to get error messages? Anyone know why it's not working?
Pain is relieved, I really have to start reading my code letter for letter! I was using the NSDocumentationDirectory instead of the NSDocumentDirectory

Pick Music from iOS Library and Send / Save

I have a query regarding Music Files.
I want to select 1/more Music files from the Music Library of iPhone/iTouch/iPad and Save in my Documents Folder or Send it to Server.
I roughly went through MPMediaPickerController and AddMusic(Sample Code).
But, I could only get details of selecting Songs and Playing it.
Is it possible to Save those selected Songs, which I can also use to Send to Server ?
Thanks
Yes, it is possible.
I think it will help you
// Have to add this framework
//CoreMedia.framework
//AudioToolbox.framework
//CoreAudio.framework
//MediaPlayer.framework
//AVFoundation.framework
//in UploadAudioViewController.h file
#import <UIKit/UIKit.h>
#import <CoreAudio/CoreAudioTypes.h>
#import <AudioToolbox/AudioToolbox.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreMedia/CoreMedia.h>
#interface UploadAudioViewController : UIViewController<UIActionSheetDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate,MPMediaPickerControllerDelegate,AVAudioRecorderDelegate, AVAudioPlayerDelegate,AVAudioSessionDelegate>{
MPMediaItem *song;
NSURL *exportURL;
}
#property (nonatomic, retain) NSData *audioData;
// in UploadAudioViewController.m file
#import "UploadAudioViewController.h"
//#import < AudioToolbox/AudioToolbox.h>
#interface UploadAudioViewController ()
#end
#implementation UploadAudioViewController
#synthesize musicPlayer,audioData;
-(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.
self.title = #"Upload Audio";
self.musicPlayer = [MPMusicPlayerController iPodMusicPlayer];
self.audioData=nil;
}
#pragma mark Browse Audio from Device
-(void)PickAudioForIndex_iPhone
{
if ([[[UIDevice currentDevice] model] isEqualToString:#"iPhone Simulator"]) {
//device is simulator
UIAlertView *alert1;
alert1 = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"There is no Audio file in the Device" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok",nil];
alert1.tag=2;
[alert1 show];
//[alert1 release],alert1=nil;
}else{
MPMediaPickerController *mediaPicker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeMusic];
mediaPicker.delegate = self;
mediaPicker.allowsPickingMultipleItems = NO; // this is the default
[self presentViewController:mediaPicker animated:YES completion:nil];
}
}
#pragma mark Media picker delegate methods
-(void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection {
// We need to dismiss the picker
[self dismissViewControllerAnimated:YES completion:nil];
// Assign the selected item(s) to the music player and start playback.
if ([mediaItemCollection count] < 1) {
return;
}
song = [[mediaItemCollection items] objectAtIndex:0];
[self handleExportTapped];
}
-(void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker {
// User did not select anything
// We need to dismiss the picker
[self dismissViewControllerAnimated:YES completion:nil ];
}
-(void)handleExportTapped{
// get the special URL
if (! song) {
return;
}
//[self startLoaderWithLabel:#"Preparing for upload..."];
NSURL *assetURL = [song valueForProperty:MPMediaItemPropertyAssetURL];
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil];
NSLog (#"Core Audio %# directly open library URL %#",
coreAudioCanOpenURL (assetURL) ? #"can" : #"cannot",
assetURL);
NSLog (#"compatible presets for songAsset: %#",
[AVAssetExportSession exportPresetsCompatibleWithAsset:songAsset]);
/* approach 1: export just the song itself
*/
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
initWithAsset: songAsset
presetName: AVAssetExportPresetAppleM4A];
NSLog (#"created exporter. supportedFileTypes: %#", exporter.supportedFileTypes);
exporter.outputFileType = #"com.apple.m4a-audio";
NSString *exportFile = [myDocumentsDirectory() stringByAppendingPathComponent: #"exported.m4a"];
// end of approach 1
// set up export (hang on to exportURL so convert to PCM can find it)
myDeleteFile(exportFile);
//[exportURL release];
exportURL = [NSURL fileURLWithPath:exportFile];
exporter.outputURL = exportURL;
// do the export
[exporter exportAsynchronouslyWithCompletionHandler:^{
int exportStatus = exporter.status;
switch (exportStatus) {
case AVAssetExportSessionStatusFailed: {
// log error to text view
NSError *exportError = exporter.error;
NSLog (#"AVAssetExportSessionStatusFailed: %#", exportError);
//errorView.text = exportError ? [exportError description] : #"Unknown failure";
//errorView.hidden = NO;
//[self stopLoader];
//[self showAlertWithMessage:#"There ia an error!"];
break;
}
case AVAssetExportSessionStatusCompleted: {
NSLog (#"AVAssetExportSessionStatusCompleted");
//fileNameLabel.text = [exporter.outputURL lastPathComponent];
// set up AVPlayer
//[self setUpAVPlayerForURL: exporter.outputURL];
///////////////// get audio data from url
//[self stopLoader];
//[self showAlertWithMessage:#"There ia an error!"];
NSURL *audioUrl = exportURL;
NSLog(#"Audio Url=%#",audioUrl);
self.audioData = [NSData dataWithContentsOfURL:audioUrl];
break;
}
case AVAssetExportSessionStatusUnknown: {
NSLog (#"AVAssetExportSessionStatusUnknown");
//[self stopLoader];
//[self showAlertWithMessage:#"There ia an error!"];
break;
}
case AVAssetExportSessionStatusExporting: {
NSLog (#"AVAssetExportSessionStatusExporting");
//[self stopLoader];
//[self showAlertWithMessage:#"There ia an error!"];
break;
}
case AVAssetExportSessionStatusCancelled: {
NSLog (#"AVAssetExportSessionStatusCancelled");
//[self stopLoader];
//[self showAlertWithMessage:#"There ia an error!"];
break;
}
case AVAssetExportSessionStatusWaiting: {
NSLog (#"AVAssetExportSessionStatusWaiting");
//[self stopLoader];
//[self showAlertWithMessage:#"There ia an error!"];
break;
}
default: {
NSLog (#"didn't get export status");
//[self stopLoader];
//[self showAlertWithMessage:#"There ia an error!"];
break;
}
}
}];
}
#pragma mark conveniences
NSString* myDocumentsDirectory(){
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [paths objectAtIndex:0];;
}
void myDeleteFile (NSString* path){
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSError *deleteErr = nil;
[[NSFileManager defaultManager] removeItemAtPath:path error:&deleteErr];
if (deleteErr) {
NSLog (#"Can't delete %#: %#", path, deleteErr);
}
}
}
// generic error handler from upcoming "Core Audio" book (thanks, Kevin!)
// if result is nonzero, prints error message and exits program.
static void CheckResult(OSStatus result, const char *operation)
{
if (result == noErr) return;
char errorString[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(result);
if (isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4])) {
errorString[0] = errorString[5] = '\'';
errorString[6] = '\0';
} else
// no, format it as an integer
sprintf(errorString, "%d", (int)result);
fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
exit(1);
}
#pragma mark core audio test
BOOL coreAudioCanOpenURL (NSURL* url){
OSStatus openErr = noErr;
AudioFileID audioFile = NULL;
openErr = AudioFileOpenURL((__bridge CFURLRef) url,
kAudioFileReadPermission ,
0,
&audioFile);
if (audioFile) {
AudioFileClose (audioFile);
}
return openErr ? NO : YES;
}
I have tried and succeed in exporting the ipod library song . Please find my code below:
extension AudioPostViewController: MPMediaPickerControllerDelegate {
func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
dismissViewControllerAnimated(true, completion: {
print("You selected \(mediaItemCollection)")
let item: MPMediaItem = mediaItemCollection.items[0]
let pathURL: NSURL? = item.valueForProperty(MPMediaItemPropertyAssetURL) as? NSURL
if pathURL == nil {
Alert.showPopupWithMessage("Unable to read DRM protected file.")
return
}
let title = item.valueForProperty(MPMediaItemPropertyTitle) as? String ?? "Now Playing..."
print("\(pathURL), title : \(title) ")
// get file extension andmime type
let str = pathURL!.absoluteString
let str2 = str.stringByReplacingOccurrencesOfString("ipod-library://item/item", withString: "")
let arr = str2.componentsSeparatedByString("?")
var mimeType = arr[0]
mimeType = mimeType.stringByReplacingOccurrencesOfString(".", withString: "")
// Export the ipod library as .m4a file to local directory for remote upload
let exportSession = AVAssetExportSession(asset: AVAsset(URL: pathURL!), presetName: AVAssetExportPresetAppleM4A)
exportSession?.shouldOptimizeForNetworkUse = true
exportSession?.outputFileType = AVFileTypeAppleM4A
FilePath.removeAudioFile() // Remove file if it exists
let fileUrl = FilePath.testFilePathURL()
exportSession?.outputURL = fileUrl
exportSession?.exportAsynchronouslyWithCompletionHandler({ () -> Void in
if exportSession!.status == AVAssetExportSessionStatus.Completed {
dispatch_async(dispatch_get_main_queue(), {
// Prepare audio to play
self.audioTitle = title
self.audioPath = fileUrl
self.mimeType = "audio/m4a"
self.audioFileName = "audio.m4a"
UIView.animateWithDuration(0.45, animations: {
if Platform.DeviceType.iPhone4 {
self.libraryBottomSpacing.constant = 5
self.view.layoutIfNeeded()
} else {
self.libraryBottomSpacing.constant = 25
self.view.layoutIfNeeded()
}
}, completion: {
(v: Bool) in
self.loadJukeBox(self.audioPath!, title: title)
})
})
} else {
dispatch_async(dispatch_get_main_queue(), {
Alert.showPopupWithMessage("Unable to read file as its DRM protected.")
})
}
})
})
}
func mediaPickerDidCancel(mediaPicker: MPMediaPickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
}
Is it possible to save those selected songs, which I can also use to send to server?
No, you can only play songs from the iTunes library.
The raw audio files are not accessible, probably due to copyright protection.