Ok I record an audio file using AVAudioRecorder.. When I done with recording the delegate method calls..
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)avrecorder successfully:(BOOL)flag{
filePathAudio = [NSString stringWithFormat:#"%#",avrecorder.url];
BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:[avrecorder.url path]];
}
When I debug and check value of filePathAudio it shows
file:///private/var/mobile/Containers/Bundle/Application/E0B11F18-4607-402D-AE2C-B032F40E0ADF/MyApp.app/recordTest.WAV
and [avrecoder.url path] returns
/private/var/mobile/Containers/Bundle/Application/E0B11F18-4607-402D-AE2C-B032F40E0ADF/MyApp.app/recordTest.WAV
when I check 'fileExist' it show false.
Can someone help me with that I want to check file size that I record..
Use this:
BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:[avrecorder.url path]];
In Swift 4, you can check the existence of a file at a URL path using the FileManager's fileExist function like the following.
var fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
fileURL?.appendPathComponent("file.txt")
let doesFileExist = FileManager.default.fileExists(atPath: fileURL?.path ?? "non-existent-file")
Here it is wrapped in AVAudioRecorderDelegate's function to match your code above for convenience.
import AVFoundation
class FancyViewController: UIViewController, AVAudioRecorderDelegate {
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
let doesFileExist = FileManager.default.fileExists(atPath: recorder.url.path)
print("The file at \(recorder.url.path) does \(doesFileExist ? "" : "not") exist.")
}
...
Related
I am on OSX, Objective-C.
I have a path/NSURL like
/Users/xxx/Desktop/image2.png
But i pass it to a third party application that excpects finder pathes like
Harddisk:Users:Desktop:image2.png
Is there any method (i can't find) to convert pathes like that or get them out of an NSURL (if possible without string modifying)?
In AppleScript it is
return POSIX file "/Users/xxx/Desktop/image2.png" --> Harddisk:Users:xxx:Desktop:image2.png
EDIT: This is pretty much the same: Cocoa path string conversion
Unfortunately, the method is deprecated...
There is no (easy) alternative at the moment.
The function CFURLCopyFileSystemPath is not deprecated, only the enum case kCFURLHFSPathStyle is deprecated but the raw value 1 is still working and avoids the warning.
I'm using this category of NSString
#implementation NSString (POSIX_HFS)
- (NSString *)hfsPathFromPOSIXPath
{
CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)[NSURL fileURLWithPath:self], 1);
return (NSString *)CFBridgingRelease(hfsPath);
}
#end
The function works also in Swift. The Swift version is a bit more sophisticated and adds the trailing semicolon representing a dictionary implicitly, here as an extension of URL:
extension URL {
func hfsPath() -> String?
{
if let cfpathHFS = CFURLCopyFileSystemPath(self as CFURL, CFURLPathStyle(rawValue: 1)!) { // CFURLPathStyle.CFURLHFSPathStyle)
let pathHFS = cfpathHFS as String
do {
let info = try self.resourceValues(forKeys: [.isDirectoryKey, .isPackageKey])
let isDirectory = info.isDirectory!
let isPackage = info.isPackage!
if isDirectory && !isPackage {
return pathHFS + ":" // directory, not package
}
} catch _ {}
return pathHFS
}
return nil
}
}
Vadians answer is better than this one - but if vadians method is deprecated, this will be an alternative. Idea is to use applescripts methods to get HFS path called easily with an osascript from an NSString category.
NSString category (credits: https://stackoverflow.com/a/19014463/4591992)
#implementation NSString (ShellExecution)
- (NSString*)runAsCommand {
NSPipe* pipe = [NSPipe pipe];
NSTask* task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
[task setArguments:#[#"-c", [NSString stringWithFormat:#"%#", self]]];
[task setStandardOutput:pipe];
NSFileHandle* file = [pipe fileHandleForReading];
[task launch];
NSString* result = [[NSString alloc] initWithData:[file readDataToEndOfFile] encoding:NSUTF8StringEncoding];
return result;
}
#end
Usage for this case:
NSString* posixToHFS = [NSString stringWithFormat:#"osascript -e 'POSIX file \"%#\" as text'",filePath];
filePath = [posixToHFS runAsCommand];
In my own testing (on 10.13.6, 10.14.6 and 10.15b7), #vadian's solution doesn't work with paths where a folder name component contains a "/" (when viewed in Finder), which then appears as a ":" in a POSIX path and as a "/" in a HFS path.
Demonstration of the bug
Here's a quick test program that you can build by creating a new "command line" project in Xcode:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *posixPath = #"/EndsInSlash:";
NSURL *url = [NSURL fileURLWithPath:posixPath];
if (url == nil) {
NSLog(#"Oops, this went wrong");
} else {
CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)url, 1);
NSString *res = (NSString *)CFBridgingRelease (hfsPath);
NSLog(#"HFS path: <%#>", res);
}
}
return 0;
}
When you run it, you'll probably see a correct result printed, i.e. a path that ends in "/". However, that only works if the folder does not exist. So, create a folder named "EndsInSlash/" (not a file!) in your root folder and run the app again - now the resulting path does not end in "/" any more as it should.
Work-around
Below is a "smart" function that uses the faster CFURLCopyFileSystemPath() function whenever possible, i.e. unless a ":" appears in the POSIX path - in which case it performs the conversion on its own, by splitting up the POSIX path into its components, converting them individually (replacing ":" into "/"), prepending the volume name and then merging the components again. This appears to work fine even on macOS 10.15 (Catalina), despite the deprecation warnings.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
static NSString* stringWithHFSUniStr255(const HFSUniStr255* hfsString)
{
CFStringRef stringRef = FSCreateStringFromHFSUniStr(nil, hfsString);
NSString* result = CFBridgingRelease(stringRef);
return result;
}
NSString* hfsPathFromPOSIXPath (NSString *posixPath)
{
if (posixPath == nil) return #"";
if ([posixPath containsString:#":"]) {
// slow version, but can handle ":" appearing in path components
NSString *result = nil;
FSRef ref;
Boolean isDir;
if (FSPathMakeRef ((const UInt8*)posixPath.UTF8String, &ref, &isDir) == noErr) {
HFSUniStr255 elemName;
FSCatalogInfo catInfo;
NSMutableArray<NSString*> *elems = [NSMutableArray arrayWithCapacity:16];
while (FSGetCatalogInfo (&ref, kFSCatInfoNodeID, &catInfo, &elemName, nil, &ref) == noErr) {
[elems insertObject: stringWithHFSUniStr255(&elemName) atIndex:0];
if (catInfo.nodeID == 2) break;
}
result = [elems componentsJoinedByString:#":"];
}
return result;
} else {
// see https://stackoverflow.com/a/45085776/43615
NSURL *url = [NSURL fileURLWithPath:posixPath];
if (url == nil) {
// could not convert because the path doesn't exist
return nil;
}
CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)url, kCFURLHFSPathStyle);
return (NSString *)CFBridgingRelease (hfsPath);
}
}
#pragma clang diagnostic pop
See also
Discussion of a related bug with AppleScript, with a work-around: https://forum.latenightsw.com/t/xxx/2097
Bug report filed with Apple: http://www.openradar.me/radar?id=4994410022436864
I need to know how to convert saved NSData from an AVPlayer back into a playable format. But I cannot figure out how to convert this NSData into a dataString, which would then allow me to create and NSUrl. Let's say I have the following code:
NSURL *videoUrl=(NSURL*)[info objectForKey:UIImagePickerControllerMediaURL];
self.data=[NSData dataWithContentsOfURL:videoUrl];
Now later on when I get this data back, I call:
NSString *dataString = [NSString stringWithUTF8String:[self.data bytes]];
But the dataString is always nil. Why? Am I decoding it in the wrong format or something?
A URL is a reference to data. The data is a (possibly complicated) encoding of something like a movie.
I am not sure exactly what you are asking, but I think you want to get the data (as an NSData object) and then save it somewhere. If this is correct, then what you need to do is
[self.data writeToFile:myFilePath atomically:YES]
where myFilePath is a path to somewhere where you can store files.
You could implement AVAssetResourceLoaderDelegate to provide data for AVPlayer.
func resourceLoader(resourceLoader: AVAssetResourceLoader!, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest!) -> Bool {
if let data = videoData {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
if let infoRequest = loadingRequest.contentInformationRequest {
infoRequest.contentType = "public.mpeg-4" // UTI
infoRequest.contentLength = Int64(data.length)
infoRequest.byteRangeAccessSupported = true
}
if let request = loadingRequest.dataRequest {
let part = data.subdataWithRange(NSRange(location: Int(request.requestedOffset), length: Int(request.requestedLength)))
request.respondWithData(part)
}
loadingRequest.finishLoading()
}
return true
}
return false
}
To create an AVPlayer:
let asset = AVURLAsset(URL: NSURL(scheme: "yourcustomscheme", host: nil, path: "/pathtovideo"), options: nil)
asset.resourceLoader.setDelegate(self, queue: dispatch_get_main_queue())
let item = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: item)
I have a NSURL object. It has address of a filesystem element, it is either a file or a directory. I want to be able to tell if the NSURL is a directory or a file.
I have already tried this, which doesn"t seem to work!
NSURL * temp ....... ;// it is initialized and has a valid value
CFURLRef xx = (CFURLRef)CFBridgingRetain(temp);
if(CFURLHasDirectoryPath(xx)) NSLog(#"was a file");
else NSLog(#"was a folder");
NSNumber *isDirectory;
// this method allows us to get more information about an URL.
// We're passing NSURLIsDirectoryKey as key because that's the info we want to know.
// Also, we pass a reference to isDirectory variable, so it can be modified to have the return value
BOOL success = [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
// If we could read the information and it's indeed a directory
if (success && [isDirectory boolValue]) {
NSLog(#"Congratulations, it's a directory!");
} else {
NSLog(#"It seems it's just a file.");
}
With Swift 5, you can check if a URL path represents a directory or a regular file using one of the following macOS Playground sample codes.
#1. Using URL's hasDirectoryPath property
import Foundation
let url = URL(fileURLWithPath: "/Users/User/Desktop")
print("is directory:", url.hasDirectoryPath)
#2. Using Filemanager's attributesOfItem(atPath:) method
import Foundation
let url = URL(fileURLWithPath: "/Users/User/Desktop/File.pdf")
let attributes = try! FileManager.default.attributesOfItem(atPath: url.path)
if let type = attributes[FileAttributeKey.type] as? FileAttributeType {
print("is file:", type == FileAttributeType.typeRegular)
}
import Foundation
let url = URL(fileURLWithPath: "/Users/User/Desktop")
let attributes = try! FileManager.default.attributesOfItem(atPath: url.path)
if let type = attributes[FileAttributeKey.type] as? FileAttributeType {
print("is directory:", type == FileAttributeType.typeDirectory)
}
#3. Using URLResourceValues
import Foundation
let url = URL(fileURLWithPath: "/Users/User/Desktop")
if let resources = try? url.resourceValues(forKeys: [.isDirectoryKey]) {
let isDirectory = resources.isDirectory ?? false
print(isDirectory)
} else {
print("No such file or directory")
}
import Foundation
let url = URL(fileURLWithPath: "/Users/User/Desktop/File.pdf")
if let resources = try? url.resourceValues(forKeys: [.isRegularFileKey]) {
let isFile = resources.isRegularFile ?? false
print(isFile)
} else {
print("No such file or directory")
}
#4. Using FileManager's fileExists(atPath:isDirectory:)
import Foundation
let url = URL(fileURLWithPath: "/Users/User/Desktop")
var isDirectory: ObjCBool = false
let fileExists = FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory)
print("is directory:", fileExists && isDirectory.boolValue)
Starting [iOS 9, macOS 10.11, tvOS 9.0, watchOS 2.0], there is hasDirectoryPath:
url.hasDirectoryPath
If you know the file URL has been standardized, then you can test for a trailing slash.
-URLByStandardizingPath will standardize a file URL including ensuring a trailing slash if the path is a directory.
Here is a test which shows -URLByStandardizingPath adding the trailing slash:
// Get a directory, any directory will do
NSURL *initialURL = [[NSBundle mainBundle] bundleURL];
NSString *initialString = [initialURL absoluteString];
// String the trailing slash off the directory
NSString *directoryString = [initialString substringToIndex:[initialString length] - 1];
NSURL *directoryURL = [NSURL URLWithString:directoryString];
XCTAssertFalse([[directoryURL absoluteString] hasSuffix:#"/"],
#"directoryURL should not end with a slash");
XCTAssertTrue([[[directoryURL URLByStandardizingPath] absoluteString] hasSuffix:#"/"],
#"[directoryURL URLByStandardizingPath] should end with a slash");
As you can see, [[[directoryURL URLByStandardizingPath] absoluteString] hasSuffix:#"/"] is the test.
Starting iOS 8, in Swift 3, there is isDirectory:
(try? url.resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory ?? false
I am writing a little command line utility in Objective C which will check if a given path is a mount point, and if not, would mount a network share to it. I was going to write this in bash, but opted to try to learn Objective C instead. I am looking for Objective C equivalent of something like this:
mount | grep some_path
Basically a function I can use to test if a given path is currently used as a mount point. Any help would be appreciated. Thank you!
After some research I ended up using this code, in case any one needs it in the future:
NSArray * keys = [NSArray arrayWithObjects:NSURLVolumeURLForRemountingKey, nil];
NSArray * mountPaths = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:keys options:0];
NSError * error;
NSURL * remount;
for (NSURL * mountPath in mountPaths) {
[mountPath getResourceValue:&remount forKey:NSURLVolumeURLForRemountingKey error:&error];
if(remount){
if ([[[NSURL URLWithString:share] host] isEqualToString:[remount host]] && [[[NSURL URLWithString:share] path] isEqualToString:[remount path]]) {
printf("Already mounted at %s\n", [[mountPath path] UTF8String]);
return 0;
}
}
}
Note, the NSURL share is passed into the function as the path to the remote share. Filtering by the remount key gives you a list of mountpoint for remote filesystems, as local filesystems do not have that key set.
in swift:
func findMountPoint(shareURL: URL) -> URL?{
guard let urls = self.mountedVolumeURLs(includingResourceValuesForKeys: [.volumeURLForRemountingKey], options: [.produceFileReferenceURLs]) else {return nil}
for u in urls{
guard let resources = try? u.resourceValues(forKeys: [.volumeURLForRemountingKey]) else{
continue
}
guard let remountURL = resources.volumeURLForRemounting else{
continue
}
if remountURL.host == shareURL.host && remountURL.path == shareURL.path{
return u
}
}
return nil
}
I need to copy file from one OS X volume to another OS X volume. While an *.app isn't strictly speaking a file but a folder, user expect them to be a unit. Thus, if user selects a file, the app should not show its folder's contents, but copy it as a unit.
Therefore I ask, if there exists a recommended way to copy files using pure Cocoa code.
Optional: Which command line tool provides help and could be utilized by a Cocoa application.
NSFileManager is your friend:
NSError *error = nil;
if ([[NSFileManager defaultManager] copyItemAtPath:#"path/to/source" toPath:#"path/to/destination" error:&error])
{
// copy succeeded
}
else
{
// copy failed, print error
}
You can also use FSCopyObjectAsync function. You can display file copy progress and you can also cancel file copy using FSCopyObjectAsync().
Take a look at FSFileOperation example code.
This sample shows how to copy and move both files and folders. It
shows both the synchronous and asynchronous (using CFRunLoop) use of
the FSFileOperation APIs. In addition, it shows path and FSRef
variants of the API and how to get status out of the callbacks. The
API is conceptually similar to the FSVolumeOperation APIs introduced
in Mac OS X 10.2.
Example of FSCopyObjectAsync:
#import <Cocoa/Cocoa.h>
#interface AsyncCopyController : NSObject {
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object;
//delegate method
-(void)didReceiveCurrentPath : (NSString *)curremtItemPath bytesCompleted : (unsigned long long)floatBytesCompleted currentStageOfFileOperation : (unsigned long)stage;
-(void)didCopyOperationComplete : (BOOL)boolean;
-(void)didReceiveCopyError : (NSString *)Error;
-(void)cancelAllAsyncCopyOperation;
#end
#import "AsyncCopyController.h"
static Boolean copying= YES;
#implementation AsyncCopyController
static void statusCallback (FSFileOperationRef fileOp,
const FSRef *currentItem,
FSFileOperationStage stage,
OSStatus error,
CFDictionaryRef statusDictionary,
void *info )
{
NSLog(#"Callback got called. %ld", error);
id delegate;
if (info)
delegate = (id)info;
if (error!=0) {
if (error==-48) {
[delegate didReceiveCopyError:#"Duplicate filename and version or Destination file already exists or File found instead of folder"];
}
}
CFURLRef theURL = CFURLCreateFromFSRef( kCFAllocatorDefault, currentItem );
NSString* currentPath = [(NSURL *)theURL path];
// NSLog(#"currentPath %#", currentPath);
// If the status dictionary is valid, we can grab the current values to
// display status changes, or in our case to update the progress indicator.
if (statusDictionary)
{
CFNumberRef bytesCompleted;
bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary,
kFSOperationBytesCompleteKey);
CGFloat floatBytesCompleted;
CFNumberGetValue (bytesCompleted, kCFNumberMaxType,
&floatBytesCompleted);
// NSLog(#"Copied %d bytes so far.",
// (unsigned long long)floatBytesCompleted);
if (info)
[delegate didReceiveCurrentPath :currentPath bytesCompleted :floatBytesCompleted currentStageOfFileOperation:stage];
}
NSLog(#"stage %d", stage);
if (stage == kFSOperationStageComplete) {
NSLog(#"Finished copying the file");
if (info)
[delegate didCopyOperationComplete:YES];
// Would like to call a Cocoa Method here...
}
if (!copying) {
FSFileOperationCancel(fileOp);
}
}
-(void)cancelAllAsyncCopyOperation
{
copying = NO;
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object
{
NSLog(#"copySource");
copying = YES;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
NSLog(#"%#", runLoop);
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);
require(fileOp, FSFileOperationCreateFailed);
OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp,
runLoop, kCFRunLoopDefaultMode);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
return status;
}
require_noerr(status, FSFileOperationScheduleWithRunLoopFailed);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
//return NO;
}
// Create a filesystem ref structure for the source and destination and
// populate them with their respective paths from our NSTextFields.
FSRef source;
FSRef destination;
// Used FSPathMakeRefWithOptions instead of FSPathMakeRef
// because I needed to use the kFSPathMakeRefDefaultOptions
// to deal with file paths to remote folders via a /Volume reference
status = FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&source,
NULL);
require_noerr(status, FSPathMakeRefWithOptionsaSourceFailed);
Boolean isDir = true;
status = FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&destination,
&isDir);
require_noerr(status, FSPathMakeRefWithOptionsaDestDirFailed);
// Needed to change from the original to use CFStringRef so I could convert
// from an NSString (aDestFile) to a CFStringRef (targetFilename)
FSFileOperationClientContext clientContext;
// The FSFileOperation will copy the data from the passed in clientContext so using
// a stack based record that goes out of scope during the operation is fine.
if (object)
{
clientContext.version = 0;
clientContext.info = (void *) object;
clientContext.retain = CFRetain;
clientContext.release = CFRelease;
clientContext.copyDescription = CFCopyDescription;
}
// Start the async copy.
status = FSCopyObjectAsync (fileOp,
&source,
&destination, // Full path to destination dir
NULL,// Use the same filename as source
kFSFileOperationDefaultOptions,
statusCallback,
1.0,
object != NULL ? &clientContext : NULL);
//CFRelease(fileOp);
NSLog(#"Failed to begin asynchronous object copy: %d", status);
if (status) {
NSString * errMsg = [NSString stringWithFormat:#" - %#", status];
NSLog(#"Failed to begin asynchronous object copy: %d", status);
}
if (object)
{
[object release];
}
FSFileOperationScheduleWithRunLoopFailed:
CFRelease(fileOp);
FSPathMakeRefWithOptionsaSourceFailed:
FSPathMakeRefWithOptionsaDestDirFailed:
FSFileOperationCreateFailed:
return status;
}
#end
FSCopyObjectAsync is Deprecated in OS X v10.8
copyfile(3) is alternative for FSCopyObjectAsync. Here is example of copyfile(3) with Progress Callback.