Unexpectedly found nil while unwrapping an Optional value (AppleScript result) - swift

I am trying to make a program in Swift 2 that runs and gets the result of an AppleScript script.
Here is my code:
import Foundation
func runAppleScript(script:String) -> String
{
let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>()
let startAtLoginScript: NSAppleScript = NSAppleScript(source: script)!
let theDiscriptor:NSAppleEventDescriptor = startAtLoginScript.executeAndReturnError(errorInfo)
let theResult:String = theDiscriptor.stringValue! //This is whats causing the error
return theResult
}
let scriptResult = runAppleScript("tell app \"Spotify\" to playpause")
NSLog("\(scriptResult)")
The problem is the program crashes and outputs:
fatal error: unexpectedly found nil while unwrapping an Optional value
in the console. I have also tried if let else, however that does not work either. How would I fix this issue?
This was tested using a OS X Command Line template using the swift language.

Actually the error could come from NSAppleScript(source: script)! so the proper solution is to return an Optional String and not use force unwrapping at all:
func runAppleScript(script:String) -> String? {
let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?> = nil
let startAtLoginScript = NSAppleScript(source: script)
let theDescriptor = startAtLoginScript?.executeAndReturnError(errorInfo)
return theDescriptor?.stringValue
}
if let scriptResult = runAppleScript("tell app \"Spotify\" to playpause") {
NSLog("\(scriptResult)")
} else {
print("the script execution failed")
}
If you prefer having a default value instead of nil when it fails, then no need to return an Optional:
func runAppleScript(script:String) -> String {
let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?> = nil
let startAtLoginScript = NSAppleScript(source: script)
let theDescriptor = startAtLoginScript?.executeAndReturnError(errorInfo)
return theDescriptor?.stringValue ?? "" // if nil, returns the default ""
}
let scriptResult = runAppleScript("tell app \"Spotify\" to playpause")
NSLog("\(scriptResult)")
As for using the new Swift 2 error handling system, none of the methods you're using inside runAppleScript are throwing errors, so it would only work if you used a custom error type and throw the errors yourself. Example:
enum MyAppleScriptError: ErrorType {
case ExecutingScriptFailed
case GettingStringValueFailed
}
func runAppleScript(script:String) throws -> String {
let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?> = nil
let startAtLoginScript = NSAppleScript(source: script)
guard let theDescriptor = startAtLoginScript?.executeAndReturnError(errorInfo) else {
throw MyAppleScriptError.ExecutingScriptFailed
}
guard let value = theDescriptor.stringValue else {
throw MyAppleScriptError.GettingStringValueFailed
}
return value
}
do {
let scriptResult = try runAppleScript("tell app \"Spotify\" to playpause")
NSLog("\(scriptResult)")
} catch {
print(error)
}
Swift 3
Same idea, but some implementation details are different.
func runAppleScript(_ script:String) -> String? {
let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?>? = nil
if let startAtLoginScript = NSAppleScript(source: script) {
let theDescriptor = startAtLoginScript.executeAndReturnError(errorInfo)
return theDescriptor.stringValue
}
return nil
}
if let scriptResult = runAppleScript("tell app \"Spotify\" to playpause") {
NSLog("\(scriptResult)")
} else {
print("no return value")
}
And with error handling:
enum MyAppleScriptError: ErrorProtocol {
case ExecutingScriptFailed
case GettingStringValueFailed
}
func runAppleScript(_ script:String) throws -> String {
let errorInfo: AutoreleasingUnsafeMutablePointer<NSDictionary?>? = nil
let startAtLoginScript = NSAppleScript(source: script)
guard let theDescriptor = startAtLoginScript?.executeAndReturnError(errorInfo) else {
throw MyAppleScriptError.ExecutingScriptFailed
}
guard let value = theDescriptor.stringValue else {
throw MyAppleScriptError.GettingStringValueFailed
}
return value
}
do {
let scriptResult = try runAppleScript("tell app \"Spotify\" to playpause")
NSLog("\(scriptResult)")
} catch {
print(error)
}

I have fixed my own code.
import Foundation
func runAppleScript(script:String) -> String
{
let theResult:String
let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>()
let startAtLoginScript: NSAppleScript = NSAppleScript(source: script)!
let theDiscriptor:NSAppleEventDescriptor = startAtLoginScript.executeAndReturnError(errorInfo)
if let _ = theDiscriptor.stringValue
{
theResult = theDiscriptor.stringValue!
} else {
theResult = ""
}
return theResult
}
let scriptResult = runAppleScript("")
What I had to do was check if theDiscriptor.stringValue has a value before unwrapping it. The error that I was getting is because I was trying to check the value after I had unwrapped it. Simply removing the ! on the check fixed my problem.
Edit
When trying this in Swift 3, the code let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>() no longer works. To fix this, I have updated the code.
func runAppleScript(script:String) -> String?
{
var theResult:String?
let startAtLoginScript: NSAppleScript = NSAppleScript(source: script)!
var errorInfo:NSDictionary? = nil
let theDiscriptor:NSAppleEventDescriptor = startAtLoginScript.executeAndReturnError(&errorInfo)
if let _ = theDiscriptor.stringValue {theResult = theDiscriptor.stringValue!}
return theResult
}
Bonus
By returning an optional string, it allows you to check if the code returned a value.
Example:
Old way
let output = runAppleScript("script")
if output != ""
{
//Script returned date
} else {
//Script did not return data
}
New way
if let output = runAppleScript("script")
{
//Script returned data
} else {
//Script did not return data
}

Related

Cannot find type 'AWSS3TransferManagerUploadRequest' in scope

I am new in Swift. Since I update podfile I am facing issue in AWSS3
Cannot find type 'AWSS3TransferManagerUploadRequest' in scope
Cannot find type 'AWSS3TransferManagerDownloadRequest' in scope
There is also import AWSS3 in ViewController.
I am not understanding the problem. Did someone face the same issue?
I also check this https://stackoverflow.com/questions/32659346/awss3transfermanageruploadrequest-in-xcode-7
But it does not help.
var uploadRequests = Array<AWSS3TransferManagerUploadRequest?>()
var uploadFileURLs = Array<URL?>()
var downloadRequests = Array<AWSS3TransferManagerDownloadRequest?>()
func download(_ downloadRequest: AWSS3TransferManagerDownloadRequest) {
switch (downloadRequest.state) {
case .notStarted, .paused:
let transferManager = AWSS3TransferManager.default()
transferManager.download(downloadRequest).continueWith(block: { (task) -> AWSTask<AnyObject>? in
if let error = task.error {
if error.domain == AWSS3TransferManagerErrorDomain as String
&& AWSS3TransferManagerErrorType(rawValue: error.code) == AWSS3TransferManagerErrorType.paused {
print("Download paused.")
} else {
print("download failed: [\(error)]")
}
} else if let exception = task.error {
print("download failed: [\(exception)]")
} else {
DispatchQueue.main.async(execute: { () -> Void in
print("downloaded file url: \(downloadRequest.downloadingFileURL)")
// if let index = self.indexOfDownloadRequest(self.downloadRequests, downloadRequest: downloadRequest) {
// self.downloadRequests[index] = nil
// self.downloadFileURLs[index] = downloadRequest.downloadingFileURL
//
// let indexPath = NSIndexPath(forRow: index, inSection: 0)
// self.collectionView.reloadItemsAtIndexPaths([indexPath])
// }
})
}
return nil
})
break
default:
break
}
}
func downloadAll() {
for (_, value) in self.downloadRequests.enumerated() {
if let downloadRequest = value {
if downloadRequest.state == .notStarted
|| downloadRequest.state == .paused {
self.download(downloadRequest)
}
}
}
// self.collectionView.reloadData()
}
func listObjects() {
let s3 = AWSS3.default()
let listObjectsRequest = AWSS3ListObjectsRequest()
listObjectsRequest?.bucket = "dice-ios"
s3.listObjects(listObjectsRequest!).continueWith { (task) -> AnyObject? in
if let error = task.error {
print("listObjects failed: [\(error)]")
}
if let exception = task.error {
print("listObjects failed: [\(exception)]")
}
if let listObjectsOutput = task.result {
if let contents = listObjectsOutput.contents {
for s3Object in contents {
let downloadingFileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("download").appendingPathComponent(s3Object.key!)
let downloadingFilePath = downloadingFileURL.path
if FileManager.default.fileExists(atPath: downloadingFilePath) {
self.downloadRequests.append(nil)
self.downloadFileURLs.append(downloadingFileURL)
} else {
let downloadRequest = AWSS3TransferManagerDownloadRequest()
downloadRequest?.bucket = "dice-ios"
downloadRequest?.key = s3Object.key
downloadRequest?.downloadingFileURL = downloadingFileURL
self.downloadRequests.append(downloadRequest)
self.downloadFileURLs.append(nil)
}
}
DispatchQueue.main.async(execute: { () -> Void in
// self.collectionView.reloadData()
})
}
}
return nil
}
}
My code is like this I am getting issue since I update the podfile.I am facing issue in AWS3 as it is updated. I need to know what to replace.
I coped with the same problem when I was integrating the pod named AWSS3.
I fixed it by installing the specific version of these pods.
Please check this URL
https://cocoapods.org/pods/AWSS3#changelog
In my case, I installed the v2.16.0.
I think it is concerning with the Xcode version.
pod 'AWSS3', '~> 2.16.0'
I hope this helps you.
You need to downgrade your AWSS3 pod version. Check the pod folder to see if the tool you're using is there (In my case, AWSS3TransferManager.h was missing from current version, downgraded to a version that had it).

Swift - Can I read the value of a enum as an optional without doing a switch?

Does Swift have a syntax similar to case .enumCase(let value) = enum that can be used as a one liner to read the enum as a specific case, or a nil
For instance with a Swift.Result instead of
let error: Error?
if case let .failure(value) = result {
error = value
} else {
error = nil
}
can I write something as
case let .failure(error) = result // Well this I can't
let error = result.as(.failure)
To simplify working with Result (or other enums) you can use next extension:
extension Result {
var value: Success? {
if case let .success(value) = self {
return value
}
return nil
}
var error: Failure? {
if case let .failure(error) = self {
return error
}
return nil
}
}
Then you can get optional values in single line:
let value = result.value
let error = result.error
if let value = result.value {
print(value)
}
if let error = result.error {
print(error)
}

Guard statment in Swift

I'm struggling with using a guard statement in Swift
The following is designed to strop force unwrapping
let pages = content.allpages?.pages?.compactMap{ $0.page?.html }
let titles = content.allpages?.pages?.compactMap{ $0.page?.title }
guard pages != nil && titles != nil else { let error = NSError(domain: "", code: -300, userInfo: [:]);
observer.onError(error); return }
let both = Array(zip(pages!, titles!))
It works, but I wanted to do something like
guard let pages = content.allpages?.pages?.compactMap{ $0.page?.html }, titles = content.allpages?.pages?.compactMap{ $0.page?.title } else {return}
but can't, some error about using autonomous arguments in the closure?
Why?
Trailing closure syntax isn't allowed in guard statements, because of some implementation difficulties.
Here's how I would write this:
guard let pages = content.allpages?.pages?.lazy.compactMap({ $0.page }) else {
observer.onError(NSError(domain: "", code: -300, userInfo: [:]))
return
}
let pageHTMLs = pages.compactMap { $0.html }
let pageTitles = pages.compactMap { $0.title }
let both = Array(zip(pages, titles))
Just add each closure inside a pair of brackets. (Also, add let for the titles)
guard let pages = content.allpages?.pages?.compactMap ({ $0.page?.html }), let titles = content.allpages?.pages?.compactMap ({ $0.page?.title }) else { return }

DynamoDB scan : Does not return correct values

I have written the following function but I have a problem with its returning value.
In my console I can see the results pulled from AWS dynamoDB, I can even display it. As you can see, I am printing item variable.
I instantiated an array in my function and append each item pulled from AWS to it but it returning nil.
Can you please see what I miss in my code thanks.
func scanClientList(_ startFromBeginning: Bool) -> [Client]{
var clients = [Client]()
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
let queryExpression = AWSDynamoDBScanExpression()
//queryExpression.exclusiveStartKey = self.userId
queryExpression.limit = 20
dynamoDBObjectMapper.scan(Client.self, expression: queryExpression).continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask!) -> AnyObject! in
if let paginatedOutput = task.result {
for item in paginatedOutput.items as! [Client] {
print("ITEMS: \(item)")
clients.append(item)
}
if paginatedOutput.lastEvaluatedKey == nil {
}
}
UIApplication.shared.isNetworkActivityIndicatorVisible = false
if let error = task.error as? NSError {
print("Error: \(error)")
}
return nil
})
return clients
}
Actually my function here works perfectly, I didn't initialize the variable clients in the functions
I had something like
var clients = [Client]()?
and changed it to
var clients:Array<Client> = []

how to cast from CFTypeRef to AXUIElement in Swift

This code produces the expected debugging output type = AXUIElement, but dumps stack and says the dynamic cast failed at the actual point of the cast:
func mainWindow() {
var ptr: Unmanaged<AnyObject>?
let kAXMainWindow: CFString! = "AXMainWindow" as NSString
let appRef: AXUIElement! = AXUIElementCreateApplication(self.pid()).takeRetainedValue()
let err = AXUIElementCopyAttributeValue(appRef, kAXMainWindow, &ptr)
if err == AXError(kAXErrorSuccess) {
let val: AnyObject? = ptr?.takeRetainedValue()
if val != nil {
let value: AnyObject = val!
let description = CFCopyTypeIDDescription(CFGetTypeID(value))
println("type = \(description)")
let element = value as AXUIElement
}
else {
println("got nil result")
}
}
}
What's the right way to get this done?
This code worked as of XCode 6.1 and Swift 1.1.
However, it's 3 years later now and Swift has gotten a lot better. Still, this is still a top result when you search for how to work with the Accessibility API from Swift. So I'm back to update with the current simplest way I know:
func AXUIWindowArray(processIdentifier pid:pid_t) -> [AXUIElement] {
var result = [AXUIElement]()
var windowList: AnyObject? = nil // [AXUIElement]
let appRef = AXUIElementCreateApplication(pid)
if AXUIElementCopyAttributeValue(appRef, "AXWindows" as CFString, &windowList) == .success {
result = windowList as! [AXUIElement]
}
return result
}