Swift Cannot convert value of type CBPeripheral to expected argument type - objective-c

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]()

Related

CFDictionaryGetValue throws EXC_BAD_ACCESS

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

Calling swift completion block in Objective C class file. Error: parameter name omitted

I am attempting to call the following Swift function from an Objective C class.
swift class function
#objc public func loaddevice(completionswift:#escaping (String) -> Void){
appSyncClient?.fetch(query: GetDevicesQuery(), cachePolicy: .returnCacheDataAndFetch) { (result, error) in
if error != nil {
print(error?.localizedDescription ?? "")
return
} else if let str = result?.data?.devices?.deviceId {
completionswift(str)
}
}
}
I am calling it from the Objective C class like this:
[self.appClient loaddeviceWithCompletionswift:^(NSString * _Nonnull) {
}];
It is giving me error: parameter name omitted.
Thanks
Seems like you forgot to name your parameter.
[self.appClient loaddeviceWithCompletionswift:^(NSString * _Nonnull str) {
}];
Try to do:
[self.appClient loaddeviceWithCompletionswift:^(NSString * _Nonnull deviceID) {
}];
Where deviceID will be the NSString passed in the completion block

Swift: Alternatives to super class methods to generate objects

I have the following Objective-C code I'm trying to convert to swift:
-(id)initWithBook:(NSString*)bookTitle author:(NSString*)author description:(NSString*)description{
self = [super init];
if (self) {
self.bookTitle = [bookTitle copy];
self.author = [author copy];
self.description = [uri description];
}
return self;
}
+(NSArray*)listOfBooks:(NSArray*)jsonWithBooks{
NSMutableArray *elements = [NSMutableArray new];
for (NSDictionary *dictElment in jsonRespnse){
Books *booksData = [[Books alloc] initWithBook:[dictElment objectForKey:#"bookTitle"]
title:[dictElment objectForKey:#"author"]
description:[dictElment objectForKey:#"description"]];
[elements addObject:booksData];
}
return [NSArray arrayWithArray:elements];
}
In my Objective-C code I'm calling super class "+(NSArray*)listOfBooks:(NSArray*)jsonWithBooks" to generate NSArray of objects. But I haven't found an equivalente on Swift. Any of you knows what would be the best alternative to do something like this?
I'm trying to use #Alexander example but my project crash in the following line:
let inventoryBooks = Book.books(fromDictArray: json .object(forKey: "books") as! [[String : String]] )
I check the type for this:
json .object(forKey: "books")
As follow:
let arrayOfBooks = json .object(forKey: "books")
if arrayOfBooks is NSArray {
print("nsarray")
}
if arrayOfBooks is [[String:String]] {
print("string:string")
}
if arrayOfBooks is NSDictionary {
print("NSDic")
}
And is printing nsarray
My question. What I'm doing wrong or do I need to change the signature on this function:
static func books(fromDictArray array: [[String: String]]) -> [Book?] {
return array.map(Book.init)
}
This sample of the json response:
{
books = (
{
caption = "";
"display_sizes" =(
{
name = thumb;
uri = "https://someUrl.com/img.jpg";
}
);
id = 123;
"max_dimensions" = {
height = 4912;
width = 7360;
};
title = "Learning Swift";
author = "Some guy"
}
{
caption = "";
"display_sizes" =(
{
name = thumb;
uri = "https://someUrl.com/img.jpg";
}
);
id = 123;
"max_dimensions" = {
height = 4912;
width = 7360;
};
title = "Swift";
author = "me meme"
}
)
}
Here is how I would write this code in idiomatic Swift:
struct Book {
let title: String
let author: String
let description: String
/* an implicit member wise initializer is generated,
which would otherwise look something like this:
init(title: String, author: String, description: String) {
self.title = title
self.author = author
self.description = description
} */
}
// Initialization from Dictionaries
extension Book {
init?(fromDict dict: [String: Any]) {
guard
let title = dict["bookTitle"] as? String,
let author = dict["author"] as? String,
let description = dict["description"] as? String
else { return nil }
self.init(
title: title,
author: author,
description: description
)
}
static func books(fromDictArray array: [[String: Any]]) -> [Book?] {
return array.map(Book.init)
}
}
Here are some notable points:
Book is a struct. Such a broad description of a book doesn't need to support the notion of identity. I.e., your book named "Harry Potter", by "J.K. Rowling" with the description "Some description" can be considered to be the same as my book with the same values. There's no apparent need (yet) to distinguish the identity of your book vs the identity of mine.
Book has an implicit memberwise initializer init(title:author:description:) which simply initializes its fields to the given parameters.
An extension is made which compartmentalizes all dictionary related tasks into a single unit.
A failable initializer, init?(fromDictArray:) is made, which returns a new book based off the given dict (presumably created from your JSON). This initializer is fault tolerant. If the dict provided is invalid, then the initializer will simply return nil, without crashing your program.
A static method is made on the Book struct, books(fromDictArray:), which will create an array of optional books ([Book?], a.k.a Array<Optional<Book>> out of the given dict. It is then the job of the consumer of this method to deal with the nil values, those resulting from invalid dicts, as they please.
They could ignore the nil books:
let books = Book.books(fromDictArray: myDictArray).flatMap{$0}
They could crash if a nil book is found:
let books = Book.books(fromDictArray: myDictArray) as! [Book]
Or they can handle the nil cases in some unique way:
let books = Book.books(fromDictArray: myDictArray).map{ book in
if book == nil {
print("A nil book was found")
}
}
As already mentioed by Dan, the Swift equivalent would be a class func:
class func listOfBooks(jsonWithBooks: [NSDictionary]) -> [Book] {
var books = [Book]()
for json in jsonWithBooks {
let book = Book(
book: json["bookTitle"]!,
author: json["author"]!,
description: json["description"]!
)
books.append(book)
}
return books
}

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