Objective c - DRDevice.h - objective-c

I'm trying to check my "tray" to see if it open or not, but i can't get it to work, it says:
"_DRDeviceIsTrayOpenKey", referenced from:
´ -[UntitledAppDelegate applicationDidFinishLaunching:] in UntitledAppDelegate.o
ld: symbol(s) not found
the code is:
#import <Foundation/Foundation.h>
#import <DiscRecording/DRCoreDevice.h>
#import <DiscRecording/DRMSF.h>
#import <AvailabilityMacros.h>
extern NSString* const DRDeviceIsTrayOpenKey;
if (!DRDeviceIsTrayOpenKey == NO ) {
[NSApp terminate:nil];
}
info:
DRDeviceIsTrayOpenKey
extern NSString* const DRDeviceIsTrayOpenKey;
Discussion
One of the keys in the dictionary returned by the status method. NSNumber containing a boolean value indicating whether the device's tray is open or not.
Availability
Introduced in Mac OS X v10.2
please help me fix that, i think my code is wrong.

Yes, your code is wrong. That DRDeviceIsTrayOpenKey is a key used to retrieve a value from a NSDictionary.
So if you can get your Disk Recording status, you'd do something like this:
// this code would depend on you passing in a valid DRDevice object
// which I've named myDiscRecordingDevice. A computer can have multiple
// DVD / CD readers attached, so you need to specify which one you care about
NSDictionary * status = [myDiscRecordingDevice status];
NSString* state = [status objectForKey: DRDeviceMediaStateKey];
if ([state isEqualTo: DRDeviceMediaStateNone])
{
if ([[status objectForKey: DRDeviceIsTrayOpenKey] boolValue])
return trayOpen;
return noDisc;
}

Related

iOS 11 - Core Data - UIColor no longers works as transformable attribute

I store colours in my binary Core Data store using a transformable attribute, specifying the class of the attribute as UIColor like so:
#import "CoreDataEntity+CoreDataClass.h"
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface CoreDataEntity (CoreDataProperties)
+ (NSFetchRequest<CoreDataEntity *> *)fetchRequest;
#property (nullable, nonatomic, retain) UIColor *transformable;
#property (nullable, nonatomic, copy) NSString *string;
#end
NS_ASSUME_NONNULL_END
In the iOS 11 Beta this has stopped working with an error like this :
NSUnderlyingException=value for key 'NS.objects' was of unexpected class 'UIColor'. Allowed classes are '{(\n NSDecimalNumber,\n NSData,\n NSUUID,\n NSNumber,\n NSDate,\n NSArray,\n NSOrderedSet,\n NSDictionaryMapNode,\n NSString,\n NSSet,\n NSDictionary,\n NSURL,\n NSNull\n)}'.}";
NSUnderlyingException = "Can't read binary data from file";
}
I managed to replicate the specific problem in an XCode project on GitHub (Must be run with the XCode Beta twice to get the error).
In the demo project the store type is controlled by NSPersistentStoreDescription and setting it to NSBinaryStoreType, which I do in the AppDelegate in the exanple project, and I add objects in application didFinishLaunchingWithOptions, otherwise it's the standard template from an iOS11 app with core data. Plus a small datamodel and classes.
If you run the project twice, the first time it creates the datastore and everything is fine. The second time, the datastore tries to open and crashes the app. This problem only seems to be related to binary datastores from what I can tell, if I use an SQL backed datastore it works. However, my app is in the wild and uses binary.
I've reported it to Apple as a bug and sought help on the developer forums, but Apple has not acknowledged the bug and no help was coming.
I'm getting a bit worried as the iOS11 release date draws nearer and I have no solution, my app just won't work in iOS11.
I've tried changing the property to NSData and seeing if it was possible to just unarchive the data, but it seems it's still stored internally as a UIColor somehow and the database just won't open.
Can anyone see a workaround? I have the app in the wild, and possibly pushing out an update to convert the datastores before iOS11 could work for some, but that isn't going to guarantee all users get the fix and they could lose their data.
EDIT 1:
Radar number : 33895450
EDIT 2:
It just occured to me that this applies to any transformable attribute in core data, the values supported in the error message are just the default property types.
EDIT 3:
Just out of curiosity I filled out all the fields for the transformable attribute (it was never required before).
I added "NSKeyedUnarchiveFromData" to value transformer name of the core data entity, it should be the default, but you never know. No effect. It must be using the value transformer anyway to know that it's a UIColor.
I filled in the custom class field to be UIColor, no effect.
Edit 5 : I noticed earlier that UIColor now supports NSSecureCoding, should security somehow be the issue somehow overlooked in the other store typed.
Edit : Now that iOS is released, i’ve used one of my TSIs to further escalate this. Do i get them back if i have to use one to get them to fix their software?
Edit : Apple got back to me on my TSI, they said it’s under investigation, there is no workaround, and to wait on the bug. They refunded my TSI because they couldn’t help.
Edit 8: Same problem on macOS High Sierra, with NSColor instead of UIColor.
Apple still have not given me any feedback on my actual bug report.
Well Apple got back to me, there are new persistentStore options!
The text I got from apple:
/* Allows developers to provide an additional set of classes (which
must implement NSSecureCoding) that should be used while decoding a
binary store. Using this option is preferable to using
NSBinaryStoreInsecureDecodingCompatibilityOption.
*/ COREDATA_EXTERN NSString * const NSBinaryStoreSecureDecodingClasses
API_AVAILABLE(macosx(10.13),ios(11.0),tvos(11.0),watchos(4.0));
/* Indicate that the binary store should be decoded insecurely. This
may be necessary if a store has metadata or transformable properties
containing non-standard classes. If possible, developers should use
the NSBinaryStoreSecureDecodingClasses option to specify the contained
classes, allowing the binary store to to be securely decoded.
Applications linked before the availability date will default to using
this option.
*/ COREDATA_EXTERN NSString * const NSBinaryStoreInsecureDecodingCompatibilityOption
API_AVAILABLE(macosx(10.13),ios(11.0),tvos(11.0),watchos(4.0));
It's not immediately clear, but basically you have to supply an NSSet of classes you use as transformable attributes that comply with NSSecureCoding as an option when opening your persistent store.
An example for mine using the UIColor :
NSError *localError;
NSDictionary *options;
if (#available(iOS 11.0, *)) {
options = #{
NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES,
NSBinaryStoreSecureDecodingClasses : [NSSet setWithObjects:[UIColor class], nil]
};
} else {
// Fallback on earlier versions
options = #{
NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES,
};
}
NSPersistentStore *newStore = [self.psc addPersistentStoreWithType:NSBinaryStoreType configuration:#"iOS" URL:psURL options:options error:&localError];
EDIT: Adding a solution for the newer way to open core data persistent stores using NSPersistentStoreDescription. This code is based on the current core data template.
- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
#synchronized (self) {
if (_persistentContainer == nil) {
NSURL *defaultURL = [NSPersistentContainer defaultDirectoryURL];
defaultURL = [defaultURL URLByAppendingPathComponent:#"CoreDataTransformableAttribBug.binary"];
_persistentContainer = [[NSPersistentContainer alloc] initWithName:#"CoreDataTransformableAttribBug"];
NSPersistentStoreDescription *desc = [NSPersistentStoreDescription persistentStoreDescriptionWithURL:defaultURL];
desc.type = NSBinaryStoreType;
if (#available(iOS 11.0, *)) {
[desc setOption:[NSSet setWithObjects:[UIColor class], nil] forKey:NSBinaryStoreSecureDecodingClasses];
}
_persistentContainer.persistentStoreDescriptions = #[desc];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
NSLog(#"Unresolved error %#, %#", error, error.userInfo);
abort();
} else {
NSLog(#"Description = %#", storeDescription);
}
}];
}
}
return _persistentContainer;
}
I also updated my gitHub project with the fix in a branch
George did all the hard work. I only applied it to Swift. Here is my solution. I put it into my NSPersistentDocument descendant.
override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
var options = storeOptions != nil ? storeOptions! : [String:Any]()
if #available(OSX 10.13, *) {
options[NSBinaryStoreSecureDecodingClasses] = NSSet(object: NSColor.self)
}
options[NSMigratePersistentStoresAutomaticallyOption] = true
options[NSInferMappingModelAutomaticallyOption] = true
try super.configurePersistentStoreCoordinator(for: url, ofType: fileType, modelConfiguration: configuration, storeOptions: options)
}
Now I can read my files again. Thanks George!

OS X count pressed keys without accessibility access

I found that upwork.app can count pressed keys without accessibility access but I can not understand how it is done.
I read a lot of themes like this one:
OSX: Detect system-wide keyDown events?
In all themes are saying that process should be trusted "Enable access for assistive devices" and I can not find how upwork.app can track keys without this.
This is official documentation for tracking events:
https://developer.apple.com/reference/appkit/nsevent/1535472-addglobalmonitorforeventsmatchin
Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).
In apple mail list is saying same:
http://lists.apple.com/archives/carbon-dev/2010/Feb/msg00043.html
I think that upwork.app use some hacks.
How I can count pressed keys without accessibility access?
Still haven't received your answer in the comments, but since that might also help other people in the future I decided to answer anyway.
With the IOKit you can detect the keyboard has a device, and get the key press events like device events. I've used that to detect joystick events, but it should work with keyboards has well. I assume that the modifications that I made are enough and should work, however my Xcode is updating now, so I wasn't able to test it yet.
KeyboardWatcher.h File:
#import <Foundation/Foundation.h>
#import <IOKit/hid/IOHIDManager.h>
#import <IOKit/hid/IOHIDKeys.h>
#interface KeyboardWatcher : NSObject{
IOHIDManagerRef HIDManager;
}
#property (nonatomic) int keysPressedCount;
+(instancetype)sharedWatcher;
-(void)startWatching;
-(void)stopWatching;
#end
KeyboardWatcher.m File:
#import "KeyboardWatcher.h"
#implementation KeyboardWatcher
static KeyboardWatcher *_sharedWatcher;
+(instancetype)sharedWatcher {
#synchronized([self class]) {
if (!_sharedWatcher){
_sharedWatcher = [[KeyboardWatcher alloc] init];
}
return _sharedWatcher;
}
return nil;
}
-(instancetype)init {
self = [super init];
if (self){
self.keysPressedCount = 0;
}
return self;
}
-(void)startWatching {
[self watchDevicesOfType:kHIDUsage_GD_Keyboard];
}
-(void)watchDevicesOfType:(UInt32)deviceType {
// Create an HID Manager
HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
// Create a Matching Dictionary
CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
// That will make the app just return the computer keyboards
CFDictionarySetValue(matchDict, CFSTR(kIOHIDPrimaryUsageKey), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &deviceType));
// Register the Matching Dictionary to the HID Manager
IOHIDManagerSetDeviceMatching(HIDManager, matchDict);
// Register the HID Manager on our app’s run loop
IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
// Open the HID Manager
IOReturn IOReturn = IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone);
// Register input calls to Handle_DeviceEventCallback function
IOHIDManagerRegisterInputValueCallback(HIDManager, Handle_DeviceEventCallback, nil);
if (IOReturn) NSLog(#"IOHIDManagerOpen failed.");
}
-(void)stopWatching {
HIDManager = NULL;
}
static void Handle_DeviceEventCallback (void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value){
IOHIDElementRef element = IOHIDValueGetElement(value); // Keyboard pressed key
uint32_t uniqueIdentifier = IOHIDElementGetCookie(element); // Unique ID of key
int elementValue = (int)IOHIDValueGetIntegerValue(value); // Actual state of key (1=pressed)
NSLog(#"Unique ID = %u; Value = %d", uniqueIdentifier, elementValue);
if (elementValue == 1) KeyboardWatcher.sharedWatcher.keysPressedCount++;
}
#end
In case you want to identify which unique ID is which key, you can use these enums (instead of importing Carbon you can just create a CGKeyboardMapping.h file and paste them there):
https://stackoverflow.com/a/16125341/4370893
At last, in order to use it, you just need to do that to start watching for keyboard events:
[[KeyboardWatcher sharedWatcher] startWatching];
Get the key pressed count with that:
[[KeyboardWatcher sharedWatcher] keysPressedCount];
And that to stop:
[[KeyboardWatcher sharedWatcher] stopWatching];
These were my references to write my original joystick code:
http://ontrak.net/xcode.htm
http://ontrak.net/xcode2.htm
https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/HID/new_api_10_5/tn2187.html
http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-315.7.16/IOHIDFamily/IOHIDUsageTables.h
As soon as the update finishes I will test the code and inform if it's working or not for sure.
EDIT: Just tested and it's working. Don't forget to add the IOKit framework to the project.

'unrecognized selector sent to instance'?

I'm new to Objective C and having trouble understanding why I am getting this error. I've checked other similar questions, but haven't been able to resolve the issue.
The error is "-[NSConcreteMutableData base64Decoded]: unrecognized selector sent to instance 0x6e15610"
Here is a snippet of the problem code, where the call to base64Decoded is causing the crash.
#import "DDData.h"
- (NSString *)decodeBase64:(NSString *)input
{
NSData* dataDecoded = [[input dataUsingEncoding:NSUTF8StringEncoding] base64Decoded];
return [NSString stringWithUTF8String:[dataDecoded bytes]];
}
And in DDData.h:
#import <Foundation/Foundation.h>
#interface NSData (DDData)
- (NSData *)base64Decoded;
#end
and DDData.m:
#implementation NSData (DDData)
- (NSData *)base64Decoded
{
// Excluding function code, as it never gets to here
}
#end
Just a note that the Project has ARC enabled. Any ideas as to what might be the issue here? Thanks.
EDIT: I have adjusted the above code to help debug the error:
NSData* dataDecoded = [input dataUsingEncoding:NSUTF8StringEncoding];
[dataDecoded base64Decoded];
dataDecoded gets a value from dataUsingEncoding, it is not nil when the call to base64Decoded is made. When I step over to the called to base64Decoded, it crashes.
Insert a break point in your code and step through it and you'll see exactly where it breaks.
You may also want to check that the DDData files are properly included in your project by looking at the target membership of those files, the .m should be ticked.
SimonH pointed out the solution correctly in one of the sub-comments. I was having the same problem with a custom method i defined in a NSData category. The solution better explained:
Make sure the .m file is included in the projects Build Phases->Compile Sources.
Right click on the .m file in the project navigator and click "Show file Inspector". Under File Inspector make sure you check the target you are building for otherwise it wont be included and the calling that method will crash.
You get that kind of message if you try to execute an undefined method on an object. Try it like this:
NSData *dataDecoded = [[input dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
There is no base64Decoded method as far as I know, but there is base64EncodedString. So when you send the base64Decoded message to your NSData object, it isn't recognized because it's simply not there.
Follow below debugging steps to resolve it.
Put breakpoints in your code and check step by step where that is breaking.
Also, check if you have added DDData.m source file in your project target properly.
You should also check the object presence before using it. Check below sample code.
- (NSString *)decodeBase64:(NSString *)input {
if(input) {
NSData *utfData = [input dataUsingEncoding:NSUTF8StringEncoding];
if(utfDFata) {
NSData* dataDecoded = [utfDFata base64Decoded];
return [NSString stringWithUTF8String:[dataDecoded bytes]];
}
}

PhoneGap plugin: AudioEncode success callback never called

I'm using the AudioEncode plugin for PhoneGap (Cordova) on iOS. After updating a couple of lines for a newer version of Cordova, it appears to be correctly encoding wav files as m4a. In the Xcode console I see:
AVAssetExportSessionStatusCompleted
doing success callback
When I look at the file system on the phone, the wav file has indeed become a m4a. However, the success callback (where I upload the file to a server) is never run. This is the relevant code in the plugin:
-(void) doSuccessCallback:(NSString*)path {
NSLog(#"doing success callback");
NSString* jsCallback = [NSString stringWithFormat:#"%#(\"%#\");", self.successCallback, path];
[self writeJavascript: jsCallback];
[self.successCallback release];
}
My code in the app goes like this:
function encodeSuccess (path) {
console.log('Audio encoded to M4A! Preparing to upload...')
// file transfer code...
}
console.log('Preparing to encode audio file...')
window.plugins.AudioEncode.encodeAudio(entry.fullPath, encodeSuccess, fail)
I'm assuming the doSuccessCallback function in the plugin needs to be updated, but I don't have experience with Objective C or PhoneGap plugins, so I'm stuck at this point.
Any ideas?
UPDATE
In the Objective C function posted above, I tried logging self.successCallback, and it logged as <null>. Then I went up to the top of the main encodeAudio function, and the argument which is assigned to self.successCallback ([arguments objectAtIndex:1]) also logs as <null>. So, it seems to me that the callbacks are not being passed into the main function successfully.
This is the AudioEncode.h file, maybe someone can spot the problem here:
#interface AudioEncode : CDVPlugin {
NSString* successCallback;
NSString* failCallback;
}
#property (nonatomic, retain) NSString* successCallback;
#property (nonatomic, retain) NSString* failCallback;
- (void)encodeAudio:(NSArray*)arguments withDict:(NSDictionary*)options;
Ok, I figured this out by reading the basic examples in the Cordova Plugin Development Guide closely. The problem was with the ordering of parameters for cordova.exec(), which must have changed recently.
I plan to submit a pull request on GitHub with a working version of the plugin, but for now, here's the basic solution.
Before asking this question, I had updated the imports in AudioEncode.h from #import <PhoneGap/PGPlugin.h> to:
#import <Cordova/CDVPlugin.h>
#import <Cordova/CDVPluginResult.h>
Any reference to PGPlugin should also be updated to CDVPlugin, and PhoneGap should become cordova.
Here's the crux of the problem: in AudioEncode.js, cordova.exec() (where the original plugin calls PhoneGap.exec()) needs to be called like this:
AudioEncode.prototype.encodeAudio = function(audioPath, successCallback, failCallback) {
cordova.exec(successCallback, failCallback, "AudioEncode", "encodeAudio", [audioPath]);
};
If you don't order the parameters like this, the callbacks won't be passed in (although audioPath was...). Look at the docs for more details, but the parameters have to be the two callbacks first, the module name, the module action, and finally an array of extra parameters.
Then, you'll need to read in the parameters in the main encodeAudio function like this:
self.callback = [[arguments objectAtIndex:0] retain];
NSString* audioPath = [arguments objectAtIndex:1];
Note that there is only one callback object now, which contains references to the success and fail callbacks. This means that whenever the plugin sets up variables for successCallback and failCallback, you now only need callback (e.g. #synthesize callback). This is also declared in the AudioEncode.h file with #interface and #property.
Now, when actually firing the callbacks & returning data (in the doSuccessCallback and doFailCallback functions), you need to use CDVPluginResult, like this:
CDVPluginResult* pluginResult = nil;
NSString* javaScript = nil;
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:path];
javaScript = [pluginResult toSuccessCallbackString:self.callback];
[self writeJavascript: javaScript];
[self.callback release];
Until I get the updated module up on GitHub, this should help anyone to get the plugin working.

Categories in Objective-C aren't working

I'm developing an iOS application that needs to deploy to iOS 3.1.3. I need to extend some of the functionality of the NSData class and am using the following code inside NSData+Base64 (truncated to show the interesting part):
NSData+Base64.h
[...]
#interface NSData (Base64)
+ (NSData *)dataFromBase64String:(NSString *)aString;
- (NSString *)base64EncodedString;
#end
NSData+Base64.m
#implementation NSData (Base64)
[...]
//
// base64EncodedString
//
// Creates an NSString object that contains the base 64 encoding of the
// receiver's data. Lines are broken at 64 characters long.
//
// returns an autoreleased NSString being the base 64 representation of the
// receiver.
//
- (NSString *)base64EncodedString
{
size_t outputLength;
char *outputBuffer =
NewBase64Encode([self bytes], [self length], true, &outputLength);
NSString *result =
[[[NSString alloc]
initWithBytes:outputBuffer
length:outputLength
encoding:NSASCIIStringEncoding]
autorelease];
free(outputBuffer);
return result;
}
#end
However, when I try to message this selector:
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *hash = [HMAC base64EncodedString];
I get the following error:
-[NSConcreteData base64EncodedString]: unrecognized selector sent to instance 0x6146e70
2010-11-09 13:44:41.443 SpringboardApplication[21318:40b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteData base64EncodedString]: unrecognized selector sent to instance 0x6146e70'
I read a lot about iOS 3.1.x having problems with categories. I tried adding the flags -all_load and -ObjC (both separately and together) to no avail. I would really appreciate some direction of how to get this selector to work.
Thanks!
It really seems like your category isn't being compiled or linked into the same target that you're using it from. You should make sure that NSData+Base64.m is marked to be compiled by the same target that it's being used from by getting info on the two files and comparing the targets they're assigned to.
A test you can perform is to add a line with an #error error message to NSData+Base64.m, which will cause the build to fail when it gets to that file. Like this:
#error We're now compiling NSData+Base64.m
Then look and see which target fails to compile.
I had the same issue with ARC project which was linking with non-ARC module having category extension.
Fixed the issue by adding "Other Linker Flags: -all_load" in parent ARC project.
Have you #imported the header file for your category? I know it sounds simple, but I forget nearly every time.
There is a great post on The Carbon Emitter about about handling categories in iOS. It details an easy way to handle importing categories to your project.
Make a file containing all of your category imports, in this example it is Extensions.h:
#import "NSDate+Formatting.h"
#import "UIFonts+MyFonts.h"
#import "UIViewController+Tourbot.h"
Add import your file in AppName-Prefix.pch:
#import <Availability.h>
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.h>
#import "Extensions.h" // Add import here
#endif
In My case when I got this error I simply added the .m file in the Compiled resources, and it get worked. This can be achieved by selecting target project->Build Phases->Compile Sources. Then you click on the + button from its bottom left. In this case you may add 'NSData+Base64.m' file to the compile sources. Then you clean your project and run. I guess this may help.