CFDictionaryGetValue throws EXC_BAD_ACCESS - objective-c

I found a code snipped from Getting graphic card information in objective C in Objective-C and I am currently trying to convert it to Swift.
I am trying to read a value from a CFMutableDictionary (code is below). However when I call the function CFDictionaryGetValue I get an error:
"Thread 1: EXC_BAD_ACCESS (code=1, address=0x656d614e4f60)"
Here is my current code:
static func getGpuName() {
var iterator: io_iterator_t = 0
let errCode: kern_return_t = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOPCIDevice"), &iterator)
if errCode != kIOReturnSuccess {
fatalError("Could not retrieve the service dictionary of \"IOPCIDevice\"")
}
// iterate over the pci devices
var device = IOIteratorNext(iterator)
while device != 0 {
var unmanagedServiceDictionary: Unmanaged<CFMutableDictionary>?
if IORegistryEntryCreateCFProperties(device, &unmanagedServiceDictionary, kCFAllocatorDefault, 0) != kIOReturnSuccess {
IOObjectRelease(device)
continue
}
if let serviceDictionary: CFMutableDictionary = unmanagedServiceDictionary?.takeRetainedValue() {
let name = CFDictionaryGetValue(serviceDictionary, "IOName")
}
// release the device
IOObjectRelease(device)
// get the next device from the iterator
device = IOIteratorNext(iterator)
}
}
Does anybody have an idea how I can read the value of the CFMutableDictionary?
Thanks :)

Handling the CoreFoundation API is a real pain.
The error occurs because you cannot pass a literal String as second parameter of CFDictionaryGetValue which must be a UnsafeRawPointer.
However the solution is pretty easy. Cast the dictionary to a Swift dictionary
if let serviceDictionary = unmanagedServiceDictionary?.takeRetainedValue() as? [String:Any] {
if let name = serviceDictionary["IOName"] as? String {
print(name)
}
}

Ok so after some more research I still don't know why the error is thrown. However I found a workaround by casting the dictionary to a NSDictionary.
The following code works now:
let serviceDictionary: NSDictionary = (unmanagedServiceDictionary?.takeRetainedValue())! as NSDictionary
if let name = serviceDictionary.value(forKey: "IOName") as? String {
print(name)
}

Related

Trouble converting NSData Objective-C code to Swift

I've been having issues converting an Objective-C snippet to Swift that uses NSData and CoreBluetooth. I have looked at this question and a couple others dealing with NSData in Swift but haven't had any success.
Objective-C Snippet:
- (CGFloat) minTemperature
{
CGFloat result = NAN;
int16_t value = 0;
// characteristic is a CBCharacteristic
if (characteristic) {
[[characteristic value] getBytes:&value length:sizeof (value)];
result = (CGFloat)value / 10.0f;
}
return result;
}
What I have so far in Swift (not working):
func minTemperature() -> CGFloat {
let bytes = [UInt8](characteristic?.value)
let pointer = UnsafePointer<UInt8>(bytes)
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
value = Int16(fPointer.pointee)
result = CGFloat(value / 10) // not correct value
return result
}
Does the logic look wrong here? Thanks!
One error is in
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
because the rebound pointer $0 is only valid inside the closure and must
not be passed to the outside. Also the capacity should be 1 for a
single Int16 value. Another problem is the integer division in
result = CGFloat(value / 10)
which truncates the result (as already observed by the4kman).
Creating an [UInt8] array from the data is not necessary, the
withUnsafeBytes() method of Data can be used instead.
Finally you could return nil (instead of "not a number") if no
characteristic value is given:
func minTemperature() -> CGFloat? {
guard let value = characteristic?.value else {
return nil
}
let i16val = value.withUnsafeBytes { (ptr: UnsafePointer<Int16>) in
ptr.pointee
}
return CGFloat(i16val) / 10.0
}
You should make the return value optional and check if characteristic is nil in the beginning with a guard. You should also explicitly convert the value to CGFloat, then divide it by 10.
func minTemperature() -> CGFloat? {
guard characteristic != nil else {
return nil
  }
let bytes = [UInt8](characteristic!.value)
let pointer = UnsafePointer<UInt8>(bytes)
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
let value = Int16(fPointer.pointee)
result = CGFloat(value) / 10
return result
}

Swift Cannot convert value of type CBPeripheral to expected argument type

The code I have:
func didDiscoverBLE(_ peripheral: CBPeripheral!, address: String!, rssi: Int32) {
DispatchQueue.main.async(execute: {() -> Void in
// Handle Discovery
self.arrayPeripehral.contains(where:peripheral)
return
})
self.arrayPeripehral.append(peripheral)
let title: String = "\(peripheral.name) \(address) (RSSI:\(rssi))"
self.arrayPeripheralName.append(title)
In this line i have a problem:
self.arrayPeripehral.contains(where:peripheral)
return
})
Has anyone an idea?
Here is the code I copied from the obective c to the swift and got stuck on this error
- (void)didDiscoverBLE:(CBPeripheral *)peripheral address:(NSString *)address rssi:(int)rssi
{
dispatch_async(dispatch_get_main_queue(), ^{
// Handle Discovery
if([arrayPeripehral containsObject:peripheral])
return;
[arrayPeripehral addObject:peripheral];
NSString * title = [NSString stringWithFormat:#"%# %# (RSSI:%d)", peripheral.name, address, rssi];
[arrayPeripheralName addObject:title];
Change the type of arrayPeripehral to [CBPeripheral] from [Any] that will give compiler more idea about its type then use contains(where:) like this to check array contains object or not.
var arrayPeripehral = [CBPeripheral]()
Now use contains(where:) this way to check array contains object or not.
if self.arrayPeripehral.contains(where: { $0.name == peripheral.name }) {
return
}
Also change type declaration of arrayPeripheralName to [String] from [Any] as of you are appending only String object in it.
var arrayPeripheralName = [String]()

"Type of expression is ambiguous without more context" [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have a chat controller with a WKInterfaceTable of canned messages and each table row is a different kind of rowController that comes with a WKInterfaceTable in WatchKit.
Each rowController references a MessageSource and MessageType which is defined in an enum.
The declaration of my enum looks good but the implementation syntax of the related dictionary needs some help.
Another issue related to the same blocks is the Swift conversion of my properties. I'm not sure if I have declared them correctly therefore they may be affecting the same blocks.
I have tried to trim as much code as possible because I know SO likes it that way. There are a few references in different functions though so I included what was needed to keep things explicit.
Obj-C
controller.m
typedef enum {
MessageSourceIncoming = 1,
MessageSourceOutgoing = 2
} MessageSource;
typedef enum {
MessageTypeText = 1,
MessageTypeVoice = 2,
MessageTypeImage = 3
} MessageType;
- (void)setupTable {
_messages = [NSMutableArray array];
for (int i = 0; i < rand()%20; i++) {
[_messages addObject:#{#"msg":#[#"Hi", #"OK", #"Nice to meet you", #"Fine"][rand()%4], #"source":#(rand()%2), #"type":#(rand()%3)}];
}
// clear the table rows
[_table removeRowsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, _table.numberOfRows)]];
for (int i = 0; i < _messages.count; i++) {
NSDictionary *messageDic = _messages[i];
[self insertRowForMessage:messageDic];
}
}
- (void)willActivate {
[_table scrollToRowAtIndex:_table.numberOfRows - 1];
if (_shouldSendVoice) {
NSDictionary *messageDic = #{#"source":#(MessageSourceOutgoing), #"type":#(MessageTypeVoice), #"path":_shouldSendVoice};
[_messages addObject:messageDic];
[self insertRowForMessage:messageDic];
_shouldSendVoice = nil;
}
}
Let's break it down:
enum MessageSource: Int {
case MessageSourceIncoming = 1
case MessageSourceOutgoing = 2
}
enum MessageType: Int {
case MessageTypeText = 1
case MessageTypeVoice = 2
case MessageTypeImage = 3
}
Nothing wrong with enums, however it's a question whether you need to give them integer values. You don't have to assign every value though:
enum MessageType: Int {
case MessageTypeText = 1
case MessageTypeVoice
case MessageTypeImage
}
would work just fine and the values would be the same.
var chat = NSDictionary()
var messages = NSMutableArray()
var shouldSendVoice = NSString()
chat should probably be a Swift dictionary but we don't have enough information to set the type so I will skip it.
shouldSendVoice looks like a boolean, why should we assign a NSString to it? I am not sure how you are using that one, so I won't rename it but let's make an optional string from it.
messages should be a Swift array. Let's create a type for Message:
struct Message {
let message: String?
let source: MessageSource
let type: MessageType
let path: String?
}
var chat = NSDictionary() // let's ignore this
var messages: [Message] = [] // empty swift array of messages
var shouldSendVoice: String? = nil // optional String
Now, let's just rewrite the rest:
override func willActivate() {
super.willActivate()
self.table.scrollToRowAtIndex(table.numberOfRows - 1)
// in Obj-C this was checking for nil!, we have to check explicitly in Swift
if let shouldSendVoice = self.shouldSendVoice {
// let's not use Dictionaries for custom objects
let message = Message(message: nil, source: .MessageSourceIncoming, type: .MessageTypeVoice, path: shouldSendVoice)
self.messages.append(message)
self.insertRowForMessage(message)
// I think you don't want new String here, just `nil`
shouldSendVoice = nil
}
}
func setupTable() {
// let's use a saner way to generate randoms
let numMessages = Int(arc4random_uniform(20))
self.messages = (0..<numMessages).map { _ in
let message = // randomize the message
let source = // randomize source
let type = // randomize type
return Message(message: message, source: source, type: type, path: nil)
}
// let's split multiple operations into separate lines to make code more readable
let indicesToRemove = NSIndexSet(indexesInRange:NSMakeRange(0, table.numberOfRows))
self.table.removeRowsAtIndexes(indicesToRemove)
// let's use for-in without using an index
for message in messages {
self.insertRowForMessage(message)
}
}

Accessing temp directory in Swift

I was trying to access temp directory in Swift. In Objective-C, I could use the following code to do so:
- (NSString *)tempDirectory {
NSString *tempDirectoryTemplate =
[NSTemporaryDirectory() stringByAppendingPathComponent:#"XXXXX"];
const char *tempDirectoryTemplateCString = [tempDirectoryTemplate fileSystemRepresentation];
char *tempDirectoryNameCString = (char *)malloc(strlen(tempDirectoryTemplateCString) + 1);
strcpy(tempDirectoryNameCString, tempDirectoryTemplateCString);
char *result = mkdtemp(tempDirectoryNameCString);
if (!result) {
return nil;
}
NSString *tempDirectoryPath = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempDirectoryNameCString length:strlen(result)];
free(tempDirectoryNameCString);
return tempDirectoryPath;
}
However, I'm a bit confuse about the type conversion and casting from Objective-C to Swift, such as const char * or CMutablePointer<CChar>. Is there any documents that I should look into?
Thanks.
How about something like :
public extension FileManager {
func createTempDirectory() throws -> String {
let tempDirectory = (NSTemporaryDirectory() as NSString).appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(atPath: tempDirectory,
withIntermediateDirectories: true,
attributes: nil)
return tempDirectory
}
}
It doesn't answer your question about char* but it's cleaner...
NSFileManager reference here.
Also check out this SO question regarding unique names.
According to Apple, use of NSTemporaryDirectory is discouraged:
See the FileManager method url(for:in:appropriateFor:create:) for the
preferred means of finding the correct temporary directory. For more
information about temporary files, see File System Programming Guide.
So instead, you should use FileManager.default.temporaryDirectory
or if you want an unique path:
let extractionPath = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
Swift 2.1 version:
func createTempDirectory() -> String? {
let tempDirURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("XXXXXX")
do {
try NSFileManager.defaultManager().createDirectoryAtURL(tempDirURL, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
return tempDirURL.absoluteString
}
Swift 3 and up
I think a good way to do this in swift is with an extension on FileManager. This should create a unique temporary folder and return the URL to you.
extension FileManager{
func createTemporaryDirectory() throws -> URL {
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString)
try createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
return url
}
}
Swift 3 version
func createTempDirectory() -> String? {
guard let tempDirURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("myTempFile.xxx") else {
return nil
}
do {
try FileManager.default.createDirectory(at: tempDirURL, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
return tempDirURL.absoluteString
}
A direct translation of your Objective-C code to Swift would be:
func tempDirectory()->String! {
let tempDirectoryTemplate = NSTemporaryDirectory() + "XXXXX"
var tempDirectoryTemplateCString = tempDirectoryTemplate.fileSystemRepresentation().copy()
let result : CString = reinterpretCast(mkdtemp(&tempDirectoryTemplateCString))
if !result {
return nil
}
let fm = NSFileManager.defaultManager()
let tempDirectoryPath = fm.stringWithFileSystemRepresentation(result, length: Int(strlen(result)))
return tempDirectoryPath
}
It uses the same mkdtemp() BSD method as your original code. This method creates
a directory name from the template which is guaranteed not to exist at the time where
the method is called.
Thanks to Nate Cook who figured out that reinterpretCast() can be used to treat the UnsafePointer<CChar> returned by mkdtemp() as a CString, so that it can be passed to stringWithFileSystemRepresentation(), see Working with C strings in Swift, or: How to convert UnsafePointer<CChar> to CString.
As of Xcode 6 beta 6, the reinterpretCast() is not necessary anymore and the
above code can be simplified to
func tempDirectory()->String! {
let tempDirectoryTemplate = NSTemporaryDirectory() + "XXXXX"
var tempDirectoryTemplateCString = tempDirectoryTemplate.fileSystemRepresentation()
let result = mkdtemp(&tempDirectoryTemplateCString)
if result == nil {
return nil
}
let fm = NSFileManager.defaultManager()
let tempDirectoryPath = fm.stringWithFileSystemRepresentation(result, length: Int(strlen(result)))
return tempDirectoryPath
}

Using Jastor to translate JSON/NSDictionary to Typed Swift classes

I'm going through Jastor's documentation:
There's an Objective-C implementation for returning arrays:
+ (Class)categories_class {
return [ProductCategory class];
}
This is my attempt at converting it to Swift, however it ends up not returning anything so I don't think it's implemented correctly:
#<_TtC4TestApp4Room: id = (null) {
resultCount = 50; // 50 is returning fine
results = ( // results is not
);
}>
NSDictionary response:
{
"resultCount" : 50,
"results" : [
{
"collectionExplicitness" : "notExplicit",
"discCount" : 1,
"artworkUrl60" : "http:\/\/a4.mzstatic.com\/us\/r30\/Features\/2a\/b7\/da\/dj.kkirmfzh.60x60-50.jpg",
"collectionCensoredName" : "Changes in Latitudes, Changes in Attitudes (Ultmate Master Disk Gold CD Reissue)"
}
]
}
Music.swift (not quite sure how to implement the results_class() method)
class Music : Jastor {
var resultCount: NSNumber = 0
var results: NSArray = []
class func results_class() -> AnyClass {
return Author.self
}
}
Author.swift
class Author {
var collectionExplicitness: NSString = ""
var discCount: NSNumber = 0
var artworkUrl60: NSString = ""
var collectionCensoredName: NSString = ""
}
I'm using the following syntax (adapted to your example):
static let results_class = Author.self
and everything works for me.
Other differences that may or may not have an effect:
I'm using Int instead of NSNumber and String instead of NSString (except for arrays).
I'm using implicitly wrapped optionals rather than assigning a default value to each field