Convert AVPlayer NSData to NSString (Or player format) - objective-c

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)

Related

FileExistAtPath return false

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.")
}
...

How to translate this method from Objective-C to Swift

I'm trying to translate this class from Objective-C to Swift. I'm almost done except for this method:
-(void) handleCookiesInRequest:(NSMutableURLRequest*) request
{
NSURL* url = request.URL;
NSArray* cookies = [self cookiesForURL:url];
NSDictionary* headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
NSUInteger count = [headers count];
__unsafe_unretained id keys[count], values[count];
[headers getObjects:values andKeys:keys];
for (NSUInteger i=0;i<count;i++) {
[request setValue:values[i] forHTTPHeaderField:keys[i]];
}
}
My attempt:
func handleCookiesInRequest(request: NSMutableURLRequest) {
var url = request.URL
var cookies = self.cookiesForURL(url!)
var headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies)
var count = headers.count
for i in 0...count {
request.setValue(value: headers.values[i], forHTTPHeaderField: headers.keys[i])
}
}
I get an error in the line that set value saying
CustomHTTPCookieStorage.swift:88:21: Type '(value: $T8, forHTTPHeaderField: $T21)' does not conform to protocol '_SignedIntegerType'
Can you help me?
NSHTTPCookie.requestHeaderFieldsWithCookies() returns a dictionary, not an array. You meant this:
for (key, value) in headers {
request.setValue(value as? String, forHTTPHeaderField: key as! String)
}
You can make this a bit safer this way:
if let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies) as? [String:String] {
for (key, value) in headers {
request.setValue(value, forHTTPHeaderField: key)
}
}
Of course, if you haven't set any headers yet, this can all be replaced with:
request.allHTTPHeaderFields = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies)
But this will blow away any preset headers, so the semantics are a little different.

NSString to NSData using stringWithUTF8String and dataUsingEncoding returns nil

When i convert the NSString to NSData and back i get a nil result
NSString * test = #"Wft0r3qkzXd5TDBeCahUB3MtHuc8Axwr";
NSData * testData = [test dataUsingEncoding:NSUTF8StringEncoding];
NSString * result = [NSString stringWithUTF8String:[testData bytes]];
Out
Printing description of testData:
<57667430 7233716b 7a586435 54444265 43616855 42334d74 48756338 41787772>
Printing description of result:
<nil>
I believe you're looking for this method:
- (instancetype)initWithData:(NSData *)data
encoding:(NSStringEncoding)encoding
From the apple doc's here: NSString class reference
In Swift, you can see the round-trip from string to data to string as follows:
let srcString = "Wft0r3qkzXd5TDBeCahUB3MtHuc8Axwr"
let stringAsData = srcString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let dstString = NSString(data: stringAsData, encoding: NSUTF8StringEncoding)!
dstString // => "Wft0r3qkzXd5TDBeCahUB3MtHuc8Axwr"
Basically, you want to use NSString(data:NSData,encoding:UInt) not NSString(UTF8String:UnsafePointer<Int8>).
An NSData is a Cocoa object that wraps a buffer of bytes. But the method you were using takes an UnsafePointer<Int8>, in other words, a raw pointer, which is just a numerical memory address pointing to the start of an array of bytes.
Swift 3
let string = "aHft0r3qkzjuh5rdsaeCahUB3MtHuc8Axwrfdvxgf"
let stringData = string.data(using: String.Encoding.utf8, allowLossyConversion: false)!
let finalString = NSString(data: stringData, encoding: String.Encoding.utf8.rawValue)!;

How to find out if the NSURL is a directory or not

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

How to retrieve values from settings.bundle in Objective-c/Swift?

I have created a project that set and retrieve values from settings.bundle. I have also set some defaults values in settings.bundle file. Now the problem is when I retrieve values as
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
loginName.text = [defaults objectForKey:#"login_name"];
for the first time it shows null, but the values get set in iPhone application settings.
If I change the values or set it manually, then values are retrieved properly.
Help me out
Although you define the defaults settings, they are not really stored as a value. They are stored as default. If you try to read it, the value is null. Default setting is another property as value is. But it doesnt mean that will write the default value as a default.
What I do is, first, check if some setting,(that I'm sure that should have a value) has anything stored on it. If it doesn't have anything then I write all the defaults.
Here is an example.
on AppDelegate.m I check if email_notificaciones_preference has a value, if not, I write ALL default settings to each setting.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSUserDefaults * standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString * email_notificaciones_preference = [standardUserDefaults objectForKey:#"email_notificaciones_preference"];
if (!email_notificaciones_preference) {
[self registerDefaultsFromSettingsBundle];
}
}
This function is what I use to write defaults to each element.
#pragma NSUserDefaults
- (void)registerDefaultsFromSettingsBundle {
// this function writes default settings as settings
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:#"Settings" ofType:#"bundle"];
if(!settingsBundle) {
NSLog(#"Could not find Settings.bundle");
return;
}
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:#"Root.plist"]];
NSArray *preferences = [settings objectForKey:#"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
for(NSDictionary *prefSpecification in preferences) {
NSString *key = [prefSpecification objectForKey:#"Key"];
if(key) {
[defaultsToRegister setObject:[prefSpecification objectForKey:#"DefaultValue"] forKey:key];
NSLog(#"writing as default %# to the key %#",[prefSpecification objectForKey:#"DefaultValue"],key);
}
}
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
}
Hope that helps.
If anyone needs it - I translated the answer from MIQUEL to Swift (as good as I could as I'm still learning) :
var standardUserDefaults = NSUserDefaults.standardUserDefaults()
var us: AnyObject? = standardUserDefaults.objectForKey("your_preference")
if us==nil {
self.registerDefaultsFromSettingsBundle();
}
And the func registerDefaultsFromSettingsBundle:
func registerDefaultsFromSettingsBundle() {
// this function writes default settings as settings
var settingsBundle = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")
if settingsBundle == nil {
NSLog("Could not find Settings.bundle");
return
}
var settings = NSDictionary(contentsOfFile:settingsBundle!.stringByAppendingPathComponent("Root.plist"))!
var preferences: [NSDictionary] = settings.objectForKey("PreferenceSpecifiers") as [NSDictionary];
var defaultsToRegister = NSMutableDictionary(capacity:(preferences.count));
for prefSpecification:NSDictionary in preferences {
var key: NSCopying? = prefSpecification.objectForKey("Key") as NSCopying?
if key != nil {
defaultsToRegister.setObject(prefSpecification.objectForKey("DefaultValue")!, forKey: key!)
}
}
NSUserDefaults.standardUserDefaults().registerDefaults(defaultsToRegister);
}
Updated for Swift 3:
func registerDefaultsFromSettingsBundle() {
let userDefaults = UserDefaults.standard
if let settingsURL = Bundle.main.url(forResource: "Root", withExtension: "plist", subdirectory: "Settings.bundle"),
let settings = NSDictionary(contentsOf: settingsURL),
let preferences = settings["PreferenceSpecifiers"] as? [NSDictionary] {
var defaultsToRegister = [String: AnyObject]()
for prefSpecification in preferences {
if let key = prefSpecification["Key"] as? String,
let value = prefSpecification["DefaultValue"] {
defaultsToRegister[key] = value as AnyObject
debugPrint("registerDefaultsFromSettingsBundle: (\(key), \(value)) \(type(of: value))")
}
}
userDefaults.register(defaults: defaultsToRegister)
} else {
debugPrint("registerDefaultsFromSettingsBundle: Could not find Settings.bundle")
}
}
Updated version for swift 2.1:
func registerDefaultsFromSettingsBundle() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let settingsURL = NSBundle.mainBundle().URLForResource("Root", withExtension: "plist", subdirectory: "Settings.bundle"),
settings = NSDictionary(contentsOfURL: settingsURL),
preferences = settings["PreferenceSpecifiers"] as? [NSDictionary] {
var defaultsToRegister = [String: AnyObject]()
for prefSpecification in preferences {
if let key = prefSpecification["Key"] as? String,
value = prefSpecification["DefaultValue"] {
defaultsToRegister[key] = value
NSLog("registerDefaultsFromSettingsBundle: (\(key), \(value)) \(value.dynamicType)")
}
}
userDefaults.registerDefaults(defaultsToRegister);
} else {
NSLog("registerDefaultsFromSettingsBundle: Could not find Settings.bundle");
}
}
You can use a simple property wrapper like this:
Usage
#SettingsBundleStorage(key: "storageUsage_preference")
var storageUsage: Double
Note that this is 100% objective-c compatible by just adding #objc before the variable.
Implementation of the code behind this:
Settings bundle values are live in the UserDefaults so you can use a custom PropertyWrapper for it. The following wrapper will work for any UserDefault value, including all values of the SettingsBundle.
Property wrapper
#propertyWrapper
public struct SettingsBundleStorage<T> {
private let key: String
public init(key: String) {
self.key = key
setBundleDefaults(plist: .root) // This is the main plist
setBundleDefaults(plist: .child(name: "DeveloperOptions")) // This is an example child.
}
public var wrappedValue: T {
get { UserDefaults.standard.value(forKey: key) as! T }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}
The root and the children
You should pass the following enum for the root and the child plists:
extension SettingsBundleStorage {
enum PList {
case root
case child(name: String)
var name: String {
var file: String
switch self {
case .root: file = "Root"
case .child(let name): file = name.replacingOccurrences(of: ".plist", with: "")
}
file.append(".plist")
return file
}
}
}
Find and set defaults if needed.
This wrapper finds the default value of the bundle keys with this function:
extension SettingsBundleStorage {
func setBundleDefaults(plist: PList = .root) {
let settingsName = "Settings"
let settingsExtension = "bundle"
let settingsPreferencesItems = "PreferenceSpecifiers"
let settingsPreferenceKey = "Key"
let settingsPreferenceDefaultValue = "DefaultValue"
guard let settingsBundleURL = Bundle.main.url(forResource: settingsName, withExtension: settingsExtension),
let settingsData = try? Data(contentsOf: settingsBundleURL.appendingPathComponent(plist.name)),
let settingsPlist = try? PropertyListSerialization.propertyList(
from: settingsData,
options: [],
format: nil) as? [String: Any],
let settingsPreferences = settingsPlist?[settingsPreferencesItems] as? [[String: Any]] else {
return assertionFailure("Can not get the \(plist.name) from the bundle: \(settingsName)")
}
var defaultsToRegister = [String: Any]()
settingsPreferences.forEach { preference in
if let key = preference[settingsPreferenceKey] as? String {
defaultsToRegister[key] = preference[settingsPreferenceDefaultValue]
}
}
UserDefaults.standard.register(defaults: defaultsToRegister)
}
}
This wrapper can store/restore any kind of codable into/from the user defaults including all Swift standard data types that are already conformed to the codable.
Also, you can find a similar but with way less code version for accessing any key-value from any user default, you can take a look at this answer here
try this
To register Default Values of Setting bundles
NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:defaultValue forKey:#"key"];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
Before retrieving setting bundles values synchronize data
[[NSUserDefaults standardUserDefaults] synchronize]