Swift UnsafeMutableRawPointer returns class instead of instance - swift

I’d expect this code to return an NSString with the ID of the currently selected input source. Instead, it seems to return one of the NSString classes itself.
import Foundation
import Carbon
let current = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
let id = TISGetInputSourceProperty(current, kTISPropertyInputSourceID).load(as: NSString.self)
id.length
When I run this in my macOS app, I get this error message in the logs: +[__NSCFConstantString _fastCStringContents:]: unrecognized selector sent to class 0x7fff92cf79e8. How can I fix this issue so I get the correct returned value?

TISGetInputSourceProperty() returns an (unmanaged) raw pointer which must be converted to a CFStringRef, not dereferenced with load(). The CFString can then be bridged to a Swift String.
let current = TISCopyCurrentKeyboardInputSource().takeRetainedValue()
if let ptr = TISGetInputSourceProperty(current, kTISPropertyInputSourceID) {
let id = Unmanaged<CFString>.fromOpaque(ptr).takeUnretainedValue() as String
print(id) // com.apple.keylayout.German
}
Note also that takeRetainedValue() must be used on the return value of TISCopyCurrentKeyboardInputSource() because that function returns a (+1) retained reference, otherwise you'll have a memory leak.

Related

Variable used within its own initial value Swift 3

I try to convert my code to swift 3 an I have spent hours on the following error:
Type 'Any' has no subscript members
Here's was my original code:
let data: AnyObject = user.object(forKey: "profilePicture")![0]
I looked at the answers here but I'm still stuck. (I do programming as a hobby, I'm not a pro :/)
I've try that:
let object = object.object(forKey: "profilePicture") as? NSDictionary
let data: AnyObject = object![0] as AnyObject
But now I get this error:
Variable used within its own initial value
Second issue: Use always a different variable name as the method name, basically use more descriptive names than object anyway.
First issue: Tell the compiler the type of the value for profilePicture, apparently an array.
if let profilePictures = user["profilePicture"] as? [[String:Any]], !profilePictures.isEmpty {
let data = profilePictures[0]
}
However, the array might contain Data objects, if so use
if let profilePictures = user["profilePicture"] as? [Data], !profilePictures.isEmpty {
let data = profilePictures[0]
}
Or – what the key implies – the value for profilePicture is a single object, who knows (but you ...)
And finally, as always, don't use NSArray / NSDictionary in Swift.

Swift mutable dictionary being treated as immutable

I am trying to implement In App Purchases, and I am tracking which purchases a user has purchased via NSUserDefaults. I have a function that sets the values of each purchase, but when it runs, I get an error saying that I am mutating the dictionary of purchase values even though the dictionary is declared with a var instead of a let and is an NSMutableDictionary. Sometimes it does work, but most of the time it doesn't. I get a few warnings about declaring my variables with let instead of var, but I ignore them to give my variables maximum mutability. Why does this not work?
The error I get is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
Code:
static func setPurchased(purchase:PurchaseID, value:Bool)
{
let defaults = NSUserDefaults.standardUserDefaults()
if (defaults.objectForKey(PURCHASES_KEY) == nil)
{
initializePurchases() // go set some initial values
}
if var purchases = (defaults.objectForKey(PURCHASES_KEY) as? NSMutableDictionary) // WARNING: should be declared with let because not mutated
{
print("setting purchase \(purchase.id()) to \(value)")
var key = purchase.id() // WARNING: should be declared with let because not mutated
purchases[key] = value // CRASH HERE
defaults.setObject(purchases, forKey:PURCHASES_KEY)
defaults.synchronize()
}
}
This is not the right way of converting an immutable dictionary into its mutable counterpart.
var already ensures that whatever is returned from defaults.objectForKey(PURCHASES_KEY) will be copied as a mutable type so all you need is specify the type of the mutable object which in our case can safely be Dictionary<String: AnyObject> if you are sure all keys are String type:
if var purchases = defaults.objectForKey(PURCHASES_KEY) as? Dictionary<String: AnyObject> {
...
purchases[key] = value
}
Please see this SO question for more information about mutability/immutability in collection types.

Swift Error: Ambiguous reference to member 'subscript'

I'm new to coding and picked up some open source project to get the idea.
I'm getting the error:
Ambiguous reference to member 'subscript'
in the code below:
let pictures = ( selectedRestaurant["Pictures"] as! NSArray ) // Error
let picture = ( pictures[zoomedPhotoIndex] as! NSDictionary )
let pictureURL = picture["url"] as! String
let imageURL = NSURL(string: pictureURL)
let urlRequest = NSURLRequest(URL: imageURL!)
NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue()) {
response, data, error in
if error == nil && data != nil {
self.imageView.image = UIImage(data: data!)
self.imageView.contentMode = UIViewContentMode.ScaleAspectFit
}
}
Just specify explicitly what is the type of pictures:
So instead of:
let pictures = selectedRestaurant["Pictures"] as! NSArray
Write:
let pictures: NSArray = selectedRestaurant["Pictures"] as! NSArray
For me the answer was to specifically state the type of array I was casting to:
if let foo = dictionary["bar"] as? [String]
It means that "Pictures" is not a valid subscript. It looks like you are creating a constant named pictures and you are trying to assign it a value of selectedRestaraunt["Pictures"] and then trying to cast it as an NSArray. If selectedrestaraunt is already an array, then what goes in the [] brackets after selectedRestaraunt should be an integer value which will refer to an index in the selectedRestaraunt array. Obviosuly "Pictures" is not an integer, it is a string.
If you are trying to access an array within an array. Meaning that Pictures is an array stored within the selectedRestarauntarray then you can access it by using selectedRestaraunt[index of Pictures array] where [index of pictures array] is an integer which is equal to the index number in which the Picutres array resides within the selectedRestaraunt array
I managed to get this error in a somewhat weird way. I had code like this:
cell.textLabel = anArrayOfStrings[indexPath.item].uppercased()
And I was stumped as to why it couldn't figure out that this was an array, even though I very clearly declared its type. I broke the line in two and finally got a helpful error message:
let name = anArrayOfStrings[indexPath.item].uppercased()
cell.textLabel = name
I was trying to assign a String to a UILabel, but somehow the point at which the type inference engine failed was at the subscript.
So my advice to anyone stumped by this is to try to break up your statement into bite-sized chunks that the Swift type inference engine can more easily digest.
As Eric and Eugene mentioned in their comments it is impossible to review the issue you are having without knowing the selectedRestaurant type. That is after all why you are getting the compiler ambiguity error.
I have to respectfully disagree with MikeG though. The problem is not one of a valid subscript. You'd be getting that kind of error, if for example you had a selectedRestaurant type of [NSNumber:AnyObject], where clearly String is no longer valid since the dictionary key could only be an NSNumber.

Who to know in every moment if there's internet connection with Swift [duplicate]

I'm trying to keep track of the network status. I went through the code of the FXReachability. Specifically the following method.
- (void)setHost:(NSString *)host
{
if (host != _host)
{
if (_reachability)
{
SCNetworkReachabilityUnscheduleFromRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFRelease(_reachability);
}
_host = [host copy];
_status = FXReachabilityStatusUnknown;
_reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [_host UTF8String]);
SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL };
SCNetworkReachabilitySetCallback(_reachability, ONEReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
}
What it does is it keeps checking the connection to the specified host. I'm trying to convert this method to Swift and I'm having a couple of problems.
1. Converting the host string to UTF8.
I have to convert the host string to UTF8 and pass it into the SCNetworkReachabilityCreateWithName method. I can't find the exact Swift equivalent of UTF8String property in Objective-C. But there is a property called utf8 for the String type. According to the documentation,
You can access a UTF-8 representation of a String by iterating over its utf8 property. This property is of type String.UTF8View, which is a collection of unsigned 8-bit (UInt8) values, one for each byte in the string’s UTF-8 representation:
How can I get a complete UTF8 representation of a string instead of a collection of unsigned 8-bit (UInt8) values?
2. Callback function for SCNetworkReachabilitySetCallback.
Th second parameter of this function expects something of type SCNetworkReachabilityCallBack. I dived in to the source file and found out that it's actually a typealias.
typealias SCNetworkReachabilityCallBack = CFunctionPointer<((SCNetworkReachability!, SCNetworkReachabilityFlags, UnsafeMutablePointer<Void>) -> Void)>
I defined it like this.
let reachabilityCallBack: SCNetworkReachabilityCallBack = {
}()
But I get the error Missing return in a closure expected to return 'SCNetworkReachabilityCallBack' when in that typealias you can clearly see that it returns Void.
Is this the wrong way to do this?
These are the problems I'm facing. I've been at this for hours now with no luck so I'd really appreciate any help.
Thank you.
Your first problem can be solved with
let reachability = host.withCString {
SCNetworkReachabilityCreateWithName(nil, $0).takeRetainedValue()
}
Inside the closure, $0 is a pointer to the NUL-terminated UTF-8 representation of the
String.
Update: As Nate Cook said in
a now deleted answer and also here,
you can actually pass a Swift string to a function taking a UnsafePointer<UInt8>
directly:
let reachability = SCNetworkReachabilityCreateWithName(nil, host).takeRetainedValue()
Unfortunately there is (as far as I know) currently no solution to your second problem.
SCNetworkReachabilitySetCallback expects a pointer to a C function as second
parameter, and there is currently no method to pass a Swift function or closure.
Note that the documentation for SCNetworkReachabilityCallBack
shows only Objective-C but no Swift.
See also Does Swift not work with function pointers?.
Update for Swift 2: It is now possible to pass a Swift closure
to a C function taking a function pointer parameter. Also
SCNetworkReachabilityCreateWithName()
does not return an unmanaged object anymore:
let host = "google.com"
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
let reachability = SCNetworkReachabilityCreateWithName(nil, host)!
SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
print(flags)
}, &context)
SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes)
Update for Swift 3 (Xcode 8):
let host = "google.com"
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
let reachability = SCNetworkReachabilityCreateWithName(nil, host)!
SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
print(flags)
}, &context)
SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(),
CFRunLoopMode.commonModes.rawValue)

Convert String to UnsafeMutablePointer<char_t> in Swift

I'm working with a third party c API I'm trying to call one of the functions with a simple string. Something like this:
some_c_func("aString");
I get a build error:
Type 'UnsafeMutablePointer<char_t>' does not conform to protocol 'StringLiteralConvertible'
I've seen some suggestions to use utf8 on String or similar conversions, which gets nearly there, but with the following error:
some_c_func("aString".cStringUsingEncoding(NSUTF8StringEncoding));
'UnsafePointer<Int8>' is not convertible to 'UnsafeMutablePointer<char_t>'
How can I create an UnsafeMutablePointer?
It all depends on what char_t is.
If char_t converts to Int8 then the following will work.
if let cString = str.cStringUsingEncoding(NSUTF8StringEncoding) {
some_c_func(strdup(cString))
}
This can be collapsed to
some_c_func(strdup(str.cStringUsingEncoding(NSUTF8StringEncoding)!))
WARNING! This second method will cause a crash if func cStringUsingEncoding(_:) returns nil.
Updating for Swift 3, and to fix memory leak
If the C string is only needed in a local scope, then no strdup() is needed.
guard let cString = str.cString(using: .utf8) else {
return
}
some_c_func(cString)
cString will have the same memory lifecycle as str (well similar at least).
If the C string needs to live outside the local scope, then you will need a copy. That copy will need to be freed.
guard let interimString = str.cString(using: .utf8), let cString = strdup(interimString) else {
return
}
some_c_func(cString)
//…
free(cString)
it may be simpler than that - many C APIs pass strings around as char * types, and swift treats these as unsafe.
try updating the C API (good) or hack it's header files (bad) to declare these as const char * instead.
in my experience this allows you to pass standard swift String types directly to the C API.
apparently a constant is required, in order to conform to the protocol.
I haven't tried passing strings like that, but I have a C function that I call from Swift, that takes a lot more parameters than shown here, among which is a reference to a Swift C typecast buffer to hold an error string. The compiler doesn't complain and the function call works. Hopefully this will steer you closer to the answer and you can provide an update with the final answer or someone else can.
var err = [CChar](count: 256, repeatedValue: 0)
var rv = somefunc((UnsafeMutablePointer<Int8>)(err))
if (rv < 0) {
println("Error \(err)")
return
}