How to properly use CFStringGetCString in swift? - swift

I am looking at the docs for
CFStringGetCString
and AXUIElementCopyAttributeValue.
CFStringGetCString takes the param buffer: UnsafeMutablePointer<Int8>!
AXUIElementCopyAttributeValue takes the param value: UnsafeMutablePointer<CFTypeRef?>
For the latter, I can do a call like this:
var value: CFTypeRef?
let err = AXUIElementCopyAttributeValue(element, attribute as CFString, &value);
This satisfies the doc asking for an UnsafeMutablePointer of type CFTypeRef.
However I can't get the same logic to apply by doing
let buffer: Int8!
CFStringGetCString(attribute as! CFString, &buffer, 2048, CFStringBuiltInEncodings.UTF8.rawValue)
I also tried
let buffer: Int8?
CFStringGetCString(attribute as! CFString, &buffer!, 2048, CFStringBuiltInEncodings.UTF8.rawValue)
Either way, it complains about using buffer before it's initialized, even though it never complained about value in the working method with similar param requirements.
All the working examples I've seen for CFStringGetCString are using objective-c like syntax with *. Not sure what the proper swift way is here.
I also tried this way to get the value I wanted:
let app = AXUIElementCreateSystemWide();
var valueString = "";
var value: CFTypeRef?
// An exception on execution happens here when passing app
// Passing in the element I clicked instead of app
// yields error -25205 (attributeunsupported)
let err = AXUIElementCopyAttributeValue(app, "AXFocusedApplication" as CFString, &value);
if (err == AXError.success) {
valueString = value! as! NSString as String;
} else {
print("ERROR!");
print(err.rawValue);
}
return valueString;

Why are you torturing yourself with CFStringGetCString? If you have a CFString in Swift, you can cast it to a String and get a C string from that:
let cString: [Int8] = (cfString as String).cString(using: .utf8)!
Note also that the value of the kAXFocusedApplicationAttribute is not a CFString. It is an AXUIElement.
Here's my playground test:
import Foundation
import CoreFoundation
let axSystem = AXUIElementCreateSystemWide()
var cfValue: CFTypeRef?
AXUIElementCopyAttributeValue(axSystem, kAXFocusedApplicationAttribute as CFString, &cfValue)
if let cfValue = cfValue, CFGetTypeID(cfValue) == AXUIElementGetTypeID() {
let axFocusedApplication = cfValue
print(axFocusedApplication)
}
The first time I executed this playground, I got a system dialog box telling me that I need to give Xcode permission to control my computer. I went to System Preferences > Security & Privacy > Privacy > Accessibility, found Xcode at the bottom of the list, and turned on its checkbox.
Here's the output of the playground:
<AXUIElement Application 0x7fb2d60001c0> {pid=30253}
I assume you're on macOS since the AXUI API is only available on macOS. If you just want the name of the front application as a string, you can do this:
if let frontAppName = NSWorkspace.shared.frontmostApplication?.localizedName {
print(frontAppName)
}

Related

Swift: URL(fileURLWithPath:) returns no value at all, not even nil

I am getting a weird result where URL(fileURLWithPath:) returns no value when called in a certain way. I've included code below (a simple macOS command line tool) to illustrate the problem.
I am using Xcode 10.3, on macOS 10.14.6. It also happens on iOS 12.4.1.
To see the problem, run the code below. Place a breakpoint on the return statement in the weirdStuff(withURL:) function, and on the 2nd-to-last line. Run the app, and when the code breaks on the return line, hover the pointer over the 4 lets in the function to examine their values. Here's what's weird:
badURL will not show any value at all! And the "Variables View" pane of the debug console will show that the value has not been defined.
copyOfBadURL will contain the correct value from badURL even though apparently badURL doesn't exist!
The conclusion is that URL(fileURLWithPath:) is failing (eg, badURL) when the fileURLWithPath: parameter value is a variable, but succeeds (goodURL) when the parameter value is a constant string. That can't be right!
Next, continue running the app so that you get the to other breakpoint. If you hover the pointer over url2 you'll see that the correct value was returned via return badURL. You can also see that badURLNowGood has a value as expected, even though this is essentially the same call that failed when called within my testing function.
What's going on here? I've never seen a variable never even be defined (after execution) before (ie, badURL).
import Foundation
func weirdStuff(withURL urlParam: URL) -> URL {
let pathFromParam = urlParam.path
let badURL = URL(fileURLWithPath: pathFromParam)
let goodURL = URL(fileURLWithPath: "/Dir1/file.txt")
let copyOfBadURL = badURL
print("func: badURL = \(badURL)") // is valid here but not if you hover above
print("func: copyOfBadURL = \(copyOfBadURL)")
print("func: goodURL = \(goodURL)")
return badURL // Breakpoint on this line; hover over 4 values above
}
// Call function with URL weirdness
let url1 = URL(fileURLWithPath: "/Dir1/file.txt", isDirectory: true)
let url2 = weirdStuff(withURL: url1)
print("main: weirdStuff(withURL:) result = \(url2)")
// Now do similar calls outside of the function
let pathFromURL1 = url1.path
var badURLNowGood = URL(fileURLWithPath: pathFromURL1)
let goodURLUsingString = URL(fileURLWithPath: "/Dir1/file.txt")
print("main: badURLNowGood = \(badURLNowGood)") // Breakpoint here
print("main: goodURLUsingString = \(goodURLUsingString)")

AutoreleasingUnsafeMutablePointer crashes the app

I receive some data from internet and need to guess the encoding if it's not provided, so I use this function stringEncoding(for:encodingOptions:convertedString:usedLossyConversion:), and it requires passing AutoreleasingUnsafeMutablePointer for receiving the converted string, I wrote code like this:
var str = "Hello, playground"
func decode(data: Data) -> String? {
var covertedString = NSString()
let stringPointer = AutoreleasingUnsafeMutablePointer<NSString?>(&covertedString)
guard NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: stringPointer, usedLossyConversion: nil) != 0 else {
return nil
}
return covertedString as String
}
let data = str.data(using: .utf8)!
decode(data: data)
While the covertedString I got out of the function call is correct, the app always crashes. Any idea why AutoreleasingUnsafeMutablePointer is make it crashes? I tried to not passing convertedString, then it's not crashing any more, so looks like it's the root case. Any idea why it's crashing?
I am using Xcode Version 10.1 (10B61), with Swift 4
In your particular case the problem is that you have created an NSString, but then taken a pointer to a NSString?, which is a different thing.
But that doesn't really matter here. You don't create AutoreleasingUnsafeMutablePointer directly (or generally any kind of UnsafePointer). They're not promised to be valid by the time you use them. Instead, you create them implicitly using &.
func decode(data: Data) -> String? {
var convertedString: NSString? = "" // <- Make sure to make this optional
guard NSString.stringEncoding(for: data,
encodingOptions: nil,
convertedString: &convertedString, // <- Use &
usedLossyConversion: nil) != 0
else {
return nil
}
return convertedString as String?
}

Swift 4 - Catch Error if Value not in Plist

I have this code
let path = self.userDesktopDirectory + "/Library/Preferences/.GlobalPreferences.plist"
let dictRoot = NSDictionary(contentsOfFile: path)
if let dict = dictRoot{
try print(dict["AppleLocale"] as! String)
}
If the Value "AppleLocale" didnt exists the script crashes. What I must add to "catch" the Error and avoid the crash?
If the Value "AppleLocale" didnt exists the script crashes. What I
must add to "catch" the Error and avoid the crash?
depends on what's the reason for causing the crash. Mentioning that "If the Value AppleLocale didnt exists" means the the reason for the crash would be the force casting:
dict["AppleLocale"] as! String
probably, it has nothing to do with the try, it would be:
Unexpectedly found nil while unwrapping an Optional value
Means that at some point dict["AppleLocale"] could be nil or even if it contains a value as not a string it will crash (optional). You have to make sure that dict["AppleLocale"] is a valid (not nil) string, there are more than just one approach to follow for doing it, for instance you could do optional binding, like this:
let path = self.userDesktopDirectory + "/Library/Preferences/.GlobalPreferences.plist"
let dictRoot = NSDictionary(contentsOfFile: path)
if let dict = dictRoot{
if let appleLocale = dict["AppleLocale"] as? String {
print(appleLocale)
} else {
// `dict["AppleLocale"]` is nil OR contains not string value
}
}
Actually, I would assume that you don't have to deal with try for such a case.

Create a Secure Random number in iOS using Swift?

public func createSecureRandomKey(numberOfBits: Int) -> Any {
let attributes: [String: Any] =
[kSecAttrKeyType as String:CFString.self,
kSecAttrKeySizeInBits as String:numberOfBits]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
return ""
}
return privateKey
}
I am trying to create Secure random number like above way, but returning nothing, Could any one please help me. Thanks.
It looks like you are using the wrong function. With your function you are generating a new key. But as your title says you want to generate secure random numbers.
For this there is a function called: SecRandomCopyBytes(::_:)
Here is a code snippet taken from the official apple documentation how to use it:
var bytes = [Int8](repeating: 0, count: 10)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
if status == errSecSuccess { // Always test the status.
print(bytes)
// Prints something different every time you run.
}
Source: Apple doc

Dictionary from Plist gives inconsistent results with same key

Inspired by this question:
Is there a way of getting a Mac's icon given its model number?
I was working around getting similar results but using the Mac's model identifier as a start, not the Mac's model number.
But I'm stuck with a weird problem when using my Mac's model identifier to find the related system icon.
On my office machine I get "iMac14,2" as a model identifier.
When I load this plist as a dictionary...
/System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/English.lproj/SIMachineAttributes.plist
...I see that it has keys for all Mac models, including "iMac14,2", and the values contain, among other things, the URL for the icon.
However, when I try to grab this dictionary's value for the identifier key ("iMac14,2") I got nil, although I get the actual value if I grab it with a literal key.
But the literal key and the key I get from my modelIdentifier function are the same. It looks like it anyway...
To grab the model identifier:
func modelIdentifier() -> String? {
let service: io_service_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice").takeUnretainedValue())
let cfstr = "model" as CFString
if let model = IORegistryEntryCreateCFProperty(service, cfstr, kCFAllocatorDefault, 0).takeUnretainedValue() as? NSData {
if let nsstr = NSString(data: model, encoding: NSUTF8StringEncoding) {
return String(nsstr)
}
}
return nil
}
if let id = modelIdentifier() {
println(id) // prints "iMac14,2"
}
Finding the value with this result fails:
if let dict = NSDictionary(contentsOfFile: "/System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/English.lproj/SIMachineAttributes.plist") as? [String:AnyObject] {
if let id = modelIdentifier() {
if let result = dict[id] as? [String:AnyObject] {
print(result) // nil
}
}
}
But if I do the same with a literal string it works:
if let result = dict["iMac14,2"] as? [String:AnyObject] {
print(result)
}
result:
[architecture: x86_64, LOCALIZABLE: {
description = "iMac with 27\" widescreen LED-backlit display, introduced late 2013.";
marketingModel = "27\" iMac (Late 2013)";
model = iMac;
processor = "Quad-core Intel Core i5, Quad-core Intel Core i7";
}, hardwareImageName: /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/com.apple.imac-unibody-27-no-optical.icns]
What is wrong here?
The strings look the same but aren't the same?
Or am I missing something else?
The NSData created by IORegistryEntryCreateCFProperty for the model key contains a NUL-terminated string. You're including that NUL in your NSString.
Create your string this way instead:
if let nsstr = NSString(CString: UnsafePointer<Int8>(model.bytes), encoding: NSUTF8StringEncoding) {
return String(nsstr)
}