Calling Swift Methods from AppleScript - swift

I am trying to write an AppleScript that calls my Swift application to get a value. The method takes a string and needs to return another string.
Here is my .SDF file:
<suite name="My Suite" code="MySU" description="My AppleScript suite.">
<class name="application" code="capp" description="An application's top level scripting object.">
<cocoa class="NSApplication"/>
<element type="my types" access="r">
<cocoa key="types"/>
</element>
</class>
<command name="my command" code="MyCOMMND" description="My Command">
<parameter name="with" code="MyPR" description="my Parameter" type="text">
<cocoa key="myParameter"/>
</parameter>
<result type="text" description="the return value"/>
<cocoa method="myCommand:"/>
</command>
</suite>
The corresponding Swift code is fairly simple:
func myCommand(_ command: NSScriptCommand) -> String
{
if let myParameter = command.evaluatedArguments?["myParameter"] as? String
{
return "Hello World!"
}
else
{
return "Nothing happening here. Move on."
}
}
and finally my AppleScript is here:
tell application "MyApp"
set r to my command with "Hello"
end tell
When I execute the AppleScript it recognises my command, but it does not call the Swift code that I've tried to associate with it. Neither Xcode or AppleScript report a problem. Have I misses something out or put my code in the wrong place?

For this sort of scripting I would recommend a command-first (aka verb-first) approach rather than the object-first approach you are attempting. Your sdef would look like this (replacing "MyProject" with the name of your project, i.e. your application's Swift module name):
<dictionary xmlns:xi="http://www.w3.org/2003/XInclude">
<suite name="My Suite" code="MySU" description="My AppleScript suite.">
<command name="my command" code="MySUCMND" description="My Command">
<cocoa class="MyProject.MyCommand"/>
<parameter name="with" code="MyPR" description="my Parameter" type="text">
<cocoa key="myParameter"/>
</parameter>
<result type="text" description="the return value"/>
</command>
</suite>
</dictionary>
The MyCommand class should look like this:
class MyCommand : NSScriptCommand {
override func performDefaultImplementation() -> Any? {
if let _ = self.evaluatedArguments?["myParameter"] as? String
{
return "Hello World!"
}
else
{
return "Nothing happening here. Move on."
}
}
}
The "ModuleName.ClassName" sdef tip comes from Swift NSScriptCommand performDefaultImplementation

Related

Kotlin Extension Functions Databinding

Is there any possibility to use extension function with a databinding?
XML:
<data>
<import type="my.package.domain.country.model.City.streetName" />
<variable
name="city"
type="my.package.domain.country.model.City" />
</data>
<TextView
android:id="#+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{city.street.streetName()}" />
my.package.domain.country.model.city
data class City(
val id: String,
val street: Street
)
fun City.streetName(): String = street.houseNumber
Error
[kapt] An exception occurred:
android.databinding.tool.util.LoggedErrorException: Found data binding
errors.
****/ data binding error ****msg:cannot find method streetName() in class my.package.domain.country.model.City
Thanks ;)
You have to import CityKt firstly into xml
<import type="my.package.domain.country.model.CityKt" />
int the data section
then you can use it like this
<TextView
android:id="#+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{CityKt.streetName(city)}" />
If you review CityKt you will see that there is static Java method with City as first argument
While #skiff2011 is correct, one could also use alias to prevent the Kt postfix.
For example, a certain extension function is located in ExtensionFunctionsKt can be aliased by ExtensionFunctions
<import
alias="ExtensionFunctions"
type="com.helloworld.app.util.ExtensionFunctionsKt" />
<variable
name="someData"
type="com.helloworld.app.model.SomeData" />
The ExtensionFunction alias can now be used to call the extension function. The first argument still needs to be the extended class variable.
<TextView
android:id="#+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{ExtensionFunctions.doStuff(someData)}" />
Extension function
Ex:
fun Context.isDarkMode(): Boolean {
}
Now we have Extenstion function for dark mode which can be accessed using context.
Usually in Kotlin class we can access using context obj like below.
context.isDarkMode()
But in Xml,we can't access like above.
We need to import Extension function by appending KT to class.
Ex) If your file name is Extensions,then import should be ExtensionsKt.
Import like below in xml.
<import type="com.example.extensions.ExtensionsKt" />
And then we can access Extension function like below i.e we need to pass object for which we are creating Extension i.e in our case it is context.
app:context='#{(ExtensionsKt.isDarkMode(context)}'
If using Utils class you can to this:
object Utils(){
fun streetName(): String {}
}
<import
type="com.helloworld.app.util.Utils" />
<TextView
android:id="#+id/txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{Utils.INSTANCE.streetName()}"
/>
Use view model for to call extension functions.

Making Cocoa Application Scriptable Swift

Goal
I am trying to make my Cocoa Application that has been written in Swift scriptable from Applescript.
What I've Done
I have created a SDEF file, configured my info.plist and created a class which I think is appropriate.
definition.sdef
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="SamX">
<!-- specific suite(s) for the application follow... -->
<suite name="SamX Scripting Suite" code="Samx" description="Suite for communication with the application">
<command name="savedoc" code="corecnte" description="description">
<cocoa class="ProjectName.ScriptingSaveNotification" id="BLah"/>
<parameter name="with dname" code="WTdc" type="text" optional="no" description="description">
<cocoa key="DocumentName"/>
</parameter>
<result type="boolean" description="The result of the invocation. True if it succeeds, False if it does not"/>
</command>
</suite>
</dictionary>
info.plist
ScriptingSaveNotification.swift
import Foundation
import Cocoa
class ScriptingSaveNotification: NSScriptCommand, NSUserNotificationCenterDelegate {
override func performDefaultImplementation() -> AnyObject? {
let parms = self.evaluatedArguments
var name = ""
if let args = parms {
if let DocumentName = args["DocumentName"] as? String {
name = DocumentName
}
}
debugPrint("We were prompted to save");
return "hello world"
}
func userNotificationCenter(center: NSUserNotificationCenter, shouldPresentNotification notification: NSUserNotification) -> Bool {
debugPrint("We were prompted to save");
return true
}
}
Where I Am
I have an application that launches. The application's SDEF file appears to be reflecting in the Applescript Editor. The Applescript editors also returns a dictionary definition. However when I run the command, I always get an output of 5 (int), and none of my debug lines appears to be outputting in Xcode.
It appears to me that maybe I'm referencing my class in the SDEF improperly. But I'm not 100% sure. I've tried renaming it several times. Any help would be greatly appreciated.
Applescript Dictionary
Test Script
tell application "MyApplication"
set testString to "Hello"
set returnValue to savedoc testString
display alert returnValue
end tell
Edit:
The main issue is that you don't actually use the with dname parameter in your script. It should be:
set returnValue to savedoc with dname testString
That said, the info below is still valid for creating a proper sdef and the other suggestions/examples may be helpful.
This is a basic example of passing a string in the evaluatedArguments of the NSScriptCommand and then returning that string as the result of the script command in Swift (you could return a boolean on success/failure of the command or any other type of result; and, actually, in your sdef you say you're going to return a boolean but your command is returning a string (text in sdef definitions)). Creating your sdef can be tricky. Your command's code should start with the suite's code and you can remove the id and optional parameter (if you omit the optional parameter, the default is that the parameter is required). If you do just need a single parameter you could also just use the direct-parameter instead.
You can download a demo project:
ScriptableSwift.zip
Here are the relevant bits (aside from the plist entries that you have correct in your tests).
ScriptableSwift.sdef
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="ScriptableSwift Terminology">
<suite name="ScriptableSwift Scripting Suite" code="SSss" description="Standard suite for application communication.">
<command name="save" code="SSssSave" description="Save something.">
<cocoa class="ScriptableSwift.SaveScriptCommand"/>
<parameter name="in" code="Fpat" type="text" description="The file path in which to save the document.">
<cocoa key="FilePath"/>
</parameter>
<result type="text" description="Echoes back the filepath supplied."/>
</command>
</suite>
</dictionary>
SaveScriptCommand.swift
import Foundation
import Cocoa
class SaveScriptCommand: NSScriptCommand {
override func performDefaultImplementation() -> AnyObject? {
let filePath = self.evaluatedArguments!["FilePath"] as! String
debugPrint("We were prompted to save something at: \(filePath)");
return filePath
}
}
Test AppleScript
tell application "ScriptableSwift" to save in "path/to/file"
Result:
"path/to/file"

Parse xml result to uitableview in swift

How can i parse xml result to tableview with two lines per cell in Swift?
I want to show the result like this in a table view
TableView
================
userID: brat_peet
password: 12124521
================
userID: Miketyson
password: Miketyson
================
How can i do it?
Here is my xml result.
<Record table="User" partial="true">
<Field name="userID" primaryKey="true"><![CDATA[brat_peet]]></Field>
<Field name="password" primaryKey="true"><![CDATA[12124521]]></Field>
</Record>
<Record table="User" partial="true">
<Field name="userID" primaryKey="true"><![CDATA[Miketyson]]></Field>
<Field name="password" primaryKey="true"><![CDATA[Miketyson]]></Field>
</Record>
<Record table="User" partial="true">
<Field name="userID" primaryKey="true"><![CDATA[Kennedy]]></Field>
<Field name="password" primaryKey="true"><![CDATA[754123654]]></Field>
</Record>
</ATBResponse>
For the tableView:
"Two lines per cell" is an irrelevant value. You can use "custom" style cells and put in as many Text Labels (and images and whatever) you want inside each cell. So, to have two "lines" per cell you can simply choose "custom" cell, drag it down to increase the size and insert two Labels inside.
For the XML parsing:
First you have to understand how a XML is parsed in Swift. Here's a tutorial I wrote:
https://medium.com/#lucascerro/understanding-nsxmlparser-in-swift-xcode-6-3-1-7c96ff6c65bc
In your case, you have to construct your code so you only check the <Field> element when inside a <Record> element. Then you differentiate between "username" and "password" data by checking your <Field>'s attributes.
For the String:
Lastly, your resulting foundCharacters will not be the final data you need. There are many methods of the String type that can solve this. One example is to use the method removeRange to remove the starting and ending characters, since they appear to always be the same ones (in your case: "<![CDATA[" and "]]>", assuming you're getting them returned as String types).

JSP/JS formatting in eclipse?

Folks is there any plugin for eclipse which provides the standard formatting/indentation.I could find basic settings
at I could find a few basic setting in Web->JSP Files->Editor but does not help much. I am looking for very basic indentation/formatting . Also it can format javascript code in jsp. For example:-
<s:if test="myManager">
<s:set var="cust" value="%{'customer'}" />
<s:set var="custMode" value="%{'custEdit'}" />
</s:if>
should be changed to below on save
<s:if test="myManager">
<s:set var="cust" value="%{'customer'}" />
<s:set var="custMode" value="%{'custEdit'}" />
</s:if>
Javascript Example
function test(){
var test1;
var test2;
if(someCondition)
{
var test3;
}
}
should be changed to below on save
function test(){
var test1;
var test2;
if(someCondition)
{
var test3;
}
}
I am not exaclty looking for above format but yes some meaningful/readable format/indentaion for jsp and javascript code. I am using eclipse helios. I did not get any open source plugin on net. i am sure there must be some utility available but somehow i am not getting through it .
It would be good if it can format scriplets also.

dynamic MenuContribution - Get a warning

I am using dynamic MenuContribution and get a warning that two of my referenced identifiers "cannot be found". Even though the contribution works. These warnings bug me.
I have a CompoundContributionItem implementation defined in one of my plugins. Basically it looks like this:
public class ViewerHistoryMenuItems extends CompoundContributionItem
implements IExecutableExtension {
private static final String PARAM_TYPE = "type";
private static final String PARAM_COMMAND = "command";
// some fields
public void setInitializationData(final IConfigurationElement config,
final String propertyName, final Object data) {
/* set fields */
}
protected final IContributionItem[] getContributionItems() {
/* create Items */
}
}
In other plugins I use this ContributionItem implementation by declaring the following:
<menuContribution locationURI="menu:mylocationUri">
<dynamic id="myId">
<class class="ViewerHistoryMenuItems">
<parameter
name="type"
value="someValue">
</parameter>
<parameter
name="command"
value="someCommandId">
</parameter>
</class>
</dynamic>
<command
commandId="someCommandId"
icon="anIcon.png">
</command>
</menuContribution>
When looking at the Problems-View I get two entries there (for each plug-in, which uses this contribution):
**Referenced identifier 'type' in attribute 'name' cannot be found**
**Referenced identifier 'command' in attribute 'name' cannot be found**
What am I missing here? Any ideas, why I get this warning?
PS: It doesn't help, to make the two fields PARAM_TYPE & PARAM_COMMAND public
I do not think this is related to the presence of internal fields within a class.
If you look at a similar error (not the same since it includes annotationType), the correction involved the definition of said Referenced identifier:
Referenced identifier 'com.atlassian.connector.eclipse.cruicible.ui.comment.annotation'
in attribute 'annotationType' cannot be found
Fixed with:
+ <extension
+ point="org.eclipse.ui.editors.annotationTypes">
+ <type
+ markerType="com.atlassian.connector.eclipse.crucible.ui.com.atlassian.connector.eclipse.cruicible.ui.comment.marker"
+ name="com.atlassian.connector.eclipse.cruicible.ui.comment.annotation">
+ </type>
+ </extension>
+ <extension
+ id="com.atlassian.connector.eclipse.cruicible.ui.comment.marker"
+ point="org.eclipse.core.resources.markers">
+ </extension>
Considering the extension point org.eclipse.ui.menus help page:
<!ELEMENT parameter EMPTY>
<!ATTLIST parameter
name IDREF #REQUIRED
value CDATA #REQUIRED
>
A parameter to either an executable extension or a command -- depending on where it appears in the extension.
name - The name is either the name of the parameter to pass to the executable extension, or the identifier of the parameter for the command.
value - The value to pass for this parameter.
You need to reference in the name attribute an id present somewhere else in your plugin.xml.
Sure thing, VonC. Here we go:
Within the dynamic declaration (see above) there are two parameter references
<parameter
name="type"
value="someValue">
</parameter>
<parameter
name="command"
value="someCommandId">
</parameter>
These two parameter are meant to be passed to the command itself. The command declaration is within the same plugin.xml but wasn't declaring these two commandParameters.
What I did was adding these missing commandParameters, resolving the missing reference, which was clearly stated by the warning.
<command
categoryId="aCategory"
id="someCommandId"
name="%theName">
<commandParameter
id="type"
name="type"/>
<commandParameter
id="command"
name="command">
</commandParameter>
</command>
So, you were absolutely right by saying "the correction involved the definition of said reference identifier". The question just was where and what I had to define.
I think, I wasn't thinking about the most obvious in this case.