Not sure where I got this code. I suspect Shane posted at some point or in one of his books and I altered… I have a library file and main script calling it. It either returns a _NSTaggedDate error or causes the script to quit unexpectedly. Ive tried a few options…
Main Script code…
use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use defaultsLib : script "defaultsLibBeta"
use scripting additions
property NSApp : a reference to current application's NSApp
property NSUserDefaults : a reference to current application's NSUserDefaults
property theDefaults : missing value
set theDefaults to current application's NSUserDefaults's standardUserDefaults()
set defaultsObj to defaultsLib's makeDefaultsWithID:theID factoryValues:(missing value)
set lastDate to theDefaults's objectForKey:"lastDate" ---causes NSDate Error
--tried this also
--set lastDate to defaultsObj's dateForKey:"lastDate" ---unexpectedly quits
set lastDate to my cocoaToASValue(lastDate)
set nowDate to current date
set todayNow to date string of (current date)
set todayStart to date (todayNow & "at 12:00:00 AM")
--
if lastDate is not missing value and lastDate < todayStart then
---do something
end
Library script ---------------------------
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"
-- classes, constants, and enums used
property NSUserDefaults : a reference to current application's NSUserDefaults
property |NSURL| : a reference to current application's |NSURL|
script forInheritance
-- required for inheritance
end script
on makeDefaultsWithID:scriptId factoryValues:initialValues
script
-- classes, constants, and enums used
property NSUserDefaults : a reference to current application's NSUserDefaults
property parent : forInheritance
property scriptDefaults : missing value
--- from logs portion
property |NSURL| : a reference to current application's |NSURL|
property NSFileManager : a reference to current application's NSFileManager
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to 4
property NSString : a reference to current application's NSString
-- Storage handlers
-- This method is use for strings, records, lists, Cocoa dates
on setObject:anObject forKey:aKey
scriptDefaults's setObject:anObject forKey:aKey
end setObject:forKey:
on setolddate:aDate forKey:aKey
scriptDefaults's setObject:aDate forKey:aKey
end setolddate:forKey:
on setInteger:anInteger forKey:aKey
scriptDefaults's setInteger:anInteger forKey:aKey
end setInteger:forKey:
on setReal:aDouble forKey:aKey
scriptDefaults's setDouble:aDouble forKey:aKey
end setReal:forKey:
on setBool:aBool forKey:aKey
scriptDefaults's setBool:aBool forKey:aKey
end setBool:forKey:
-- File storage methods
on setFile:aFile forKey:aKey
set thePath to POSIX path of theFile
set theURL to current application's class "NSURL"'s fileURLWithPath:thePath
scriptDefaults's setURL:theURL forKey:aKey
end setFile:forKey:
on setAlias:anAlias forKey:aKey
set thePath to POSIX path of anAlias
set theURL to current application's class "NSURL"'s fileURLWithPath:thePath
set theURL to theURL's fileReferenceURL()
scriptDefaults's setURL:theURL forKey:aKey
end setAlias:forKey:
-- Retrievers, returning AppleScript values
on stringForKey:aKey
return (scriptDefaults's stringForKey:aKey) as string
end stringForKey:
on listForKey:aKey
return (scriptDefaults's arrayForKey:aKey) as list
end listForKey:
on olddateForKey:aKey
return (scriptDefaults's objectForKey:aKey) as date
end olddateForKey:
on recordForKey:aKey
return (scriptDefaults's dictionaryForKey:aKey) as record
end recordForKey:
on integerForKey:aKey
return (scriptDefaults's integerForKey:aKey) as integer
end integerForKey:
on realForKey:aKey
return (scriptDefaults's doubleForKey:aKey) as real
end realForKey:
on boolForKey:aKey
return (scriptDefaults's boolForKey:aKey) as boolean
end boolForKey:
on fileForKey:aKey
set theURL to scriptDefaults's URLForKey:aKey
return (theURL's |path|() as text) as «class furl»
end fileForKey:
on dateASKey:aKey
set theDate to scriptDefaults's objectForKey:aKey
return theDate as date
end dateASKey:
-- Handlers for dealing with AS dates
on registerDateDefault:aDate forKey:aKey
set theData to current application's NSKeyedArchiver's archivedDataWithRootObject:aDate
set aDictionary to current application's NSDictionary's dictionaryWithObject:theData forKey:aKey
scriptDefaults's registerDefaults:aDictionary
end registerDateDefault:forKey:
on setDate:aDate forKey:aKey
set theData to current application's NSKeyedArchiver's archivedDataWithRootObject:aDate
scriptDefaults's setObject:theData forKey:aKey
end setDate:forKey:
on dateForKey:aKey
set theData to scriptDefaults's objectForKey:aKey
return (current application's NSKeyedUnarchiver's unarchiveObjectWithData:theData) as date
end dateForKey:
-- Generic handlers for AppleScript values (dates, enumerators, etc)
on registerASDefault:aValue forKey:aKey
set theData to current application's NSKeyedArchiver's archivedDataWithRootObject:aValue
set aDictionary to current application's NSDictionary's dictionaryWithObject:theData forKey:aKey
scriptDefaults's registerDefaults:aDictionary
end registerASDefault:forKey:
on setASValue:aValue forKey:aKey
set theData to current application's NSKeyedArchiver's archivedDataWithRootObject:aValue
scriptDefaults's setObject:theData forKey:aKey
end setASValue:forKey:
on ASValueForKey:aKey
set theData to scriptDefaults's objectForKey:aKey
set aValue to (current application's NSKeyedUnarchiver's unarchiveObjectWithData:theData)
set anArray to current application's NSArray's arrayWithArray:{aValue}
return item 1 of (anArray as list)
end ASValueForKey:
on is_running(appName)
tell application "System Events" to (name of processes) contains appName
end is_running
on itemExists(theItem)
set pathStr to POSIX path of theItem
return (NSFileManager's defaultManager()'s fileExistsAtPath:pathStr) as boolean
end itemExists
end script
set ASPrefs to result
set theDefaults to current application's NSUserDefaults's standardUserDefaults()
theDefaults's registerDefaults:initialValues
set ASPrefs's scriptDefaults to theDefaults
return ASPrefs
end makeDefaultsWithID:factoryValues:
Shane,
I tried prefsStorageLib like it better BUT had similar errors. This forced me track down that I was comparing NSDate with AppleScript Dates. It seems to work in unreliably. So that’s Fixed now…
Now I have a new issues.
Testing code in Script Debugger works perfect.
I have subroutines in the applet that use the prefsLib & then scripts that need to use the same ID/Prefs.
if I put this in all of the subroutines and applet will it work?
if current application's id = id of me then -- must be running as applet
prepare storage for (path to me)
else
set theID to "com.myinfo.myapp"
prepare storage for domain theID
end
Shane
It’s still causing errors in a ScriptBundle being called by an Applet via Run Script. No matter what I get
If it is
prepare storage for (path to me)
and
if it is
prepare storage for domain theID
I have been troubleshooting it and the issue seems to be in the code
set theResult to NSUserDefaults's alloc()'s initWithSuiteName:domainString
if theResult = missing value then error "The 'prepare storage' command was unable to initialize user defaults. Unknown error"
This is how I do it and it works. Are you doing anything different?
(I have a script that builds a version of this from the script in the front Script Debugger tabs, and uses whatever properties it finds in that script, and it works every time).
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use script "PrefsStorageLib" version "1.1.1"
property prop1 : "A"
property prop2 : 2
property prop3 : {}
set myDomain to "com.myInfo.scriptInfo"
PersistentVariables(myDomain)
--StorePersistentValues()
--RetreiveStoredValues()
on PersistentVariables(myDomain)
prepare storage for domain myDomain
set my prop1 to value for key "prop1"
set my prop2 to value for key "prop2"
set my prop3 to value for key "prop3"
end PersistentVariables
on StorePersistentValues()
assign value prop1 to key "prop1"
assign value prop2 to key "prop2"
assign value prop3 to key "prop3"
end StorePersistentValues
on RetreiveStoredValues()
set my prop1 to value for key "prop1"
set my prop2 to value for key "prop2"
set my prop3 to value for key "prop3"
end RetreiveStoredValues
Short answer I don’t think so. It’s only happening in the Script Bundle not the applet. Using the prepare storage for domain “com.somedomain.appname” doesn’t work.
I am combining an Applet with script bundles inside of the applet and share the same properties. It was working before 8 very reliably. I think it has to do with Properties. SD8 clears them when saving. This is when things started breaking or there’s a new include Frameworks checkbox in Export which I have tried both ways. Not blaming SD8…Im assuming its me… I think its likely a clash of variable names. For example one of my scripts had a properties theDefaults : missing value and all had theID which I was using to label the Domain. Both are in Shanes library…but after changing still get same error.
Completely diff subject…Would love to see your script that creates the properties. Very cool.
Here’s the script. It works with ScriptDebugger and the script it produces (on the clipboard) will use Shane’s PrefsStorageLib which is available here:
PrefsStorageLib is a library that makes it easy for scripts to store persistent values in their existing preference property list files using the standard application defaults system. This gets around the problem where applets cannot retain values between launches because they are notarized, or locked to avoid repeated authorization dialogs. Works with macOS 10.11 or later. (Updated March 11, 2020)
set saveTID to AppleScript's text item delimiters
set myText to GetScriptText(1) --Change to (2) if
-- you're editing in the first window and working on the second
set keysAndValues to {}
set AppleScript's text item delimiters to {" : "}
set x to 0
repeat with thisGraph in myText
set x to x + 1
set thisGraph to thisGraph as text
try
copy word 1 of thisGraph to paraLead
if paraLead is "property" then
set lastPropertyLine to x
set thisKey to word 2 of thisGraph
set keyValue to the rest of text items of thisGraph as text
set the end of keysAndValues to {thisKey, keyValue}
end if
end try
end repeat
set AppleScript's text item delimiters to {""}
set defaultValues to {"{¬"}
set valuesForKeyLines to {}
set savingValueLines to {}
set backupWriteCalls to {}
set readBackupCalls to {}
set keyValueCount to count of keysAndValues
repeat with x from 1 to keyValueCount
set thisKey to item x of keysAndValues
set {keyName, keyValue} to thisKey
if x = keyValueCount then
set the end of defaultValues to {keyName, ":", "{}¬"} as text
else
set the end of defaultValues to {keyName, ":", "{}, ¬"} as text
end if
--set the end of defaultValues to {keyName, ":", "{}"} as text
set the end of valuesForKeyLines to " set my " & keyName & " to value for key \"" & keyName & "\""
set the end of savingValueLines to " assign value " & keyName & " to key \"" & keyName & "\""
end repeat
set the end of defaultValues to "}"
set AppleScript's text item delimiters to {return}
set defaultValues to defaultValues as text
set storagePrepCall to {("prepare storage for (path to me)")} as text
set AppleScript's text item delimiters to {return}
set PersistentVariableHandler to {"on PersistentVariables()", ¬
storagePrepCall, ¬
"end PersistentVariables"} as text
set SaveVariablesHandler to {"on StorePersistentValues()", ¬
savingValueLines, ¬
"end StorePersistentValues"} as text
set RetreiveVariableHandler to {"on RetreiveStoredValues()", ¬
valuesForKeyLines, ¬
"end RetreiveStoredValues"} as text
set persistentVariableCalls to {{""}, ¬
{"PersistentVariables()"}, ¬
{"--StorePersistentValues()"}, ¬
{"--RetreiveStoredValues()"}, ¬
{""}}
set item lastPropertyLine of myText to {item lastPropertyLine of myText, persistentVariableCalls}
set the end of myText to {¬
PersistentVariableHandler, "", ¬
SaveVariablesHandler, "", ¬
RetreiveVariableHandler, ¬
""}
set myText to myText as text
set myText to FindChange(myText, "√¬√", "¬" & return)
set the clipboard to myText
set resultAlertreply to display alert ("Paste in persistent variables") ¬
message ("The Clipboard contains the entire script with persistant variable handlers" & return & return & "You may paste it into your script.") ¬
as informational ¬
buttons {"OK"} ¬
default button 1 ¬
giving up after 30
return myText as text
on FindChange(textToSearch, textToFind, textToReplace)
set saveTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to textToFind
set textToSearch to every text item of textToSearch
set AppleScript's text item delimiters to textToReplace
set textToSearch to textToSearch as string
set AppleScript's text item delimiters to saveTID
return textToSearch
end FindChange
on GetScriptText(DocNumber)
--tell application "TextWrangler" to set myText to text of window 1
tell application "Script Debugger"
tell its document DocNumber
set myText to its source text
end tell
end tell
set myText to paragraphs of myText
set the beginning of myText to ""
set AppleScript's text item delimiters to {return}
set myText to myText as text
set myText to FindChange(myText, "¬" & return, "√¬√")
set textHeader to {}
if return & "use scripting additions" is not in myText then
set the end of textHeader to "use scripting additions"
end if
if return & "use script \"PrefsStorageLib\"" is not in myText then
set the end of textHeader to "use script \"PrefsStorageLib\" version \"1.1.0\""
end if
if return & "use script \"FileManagerLib\"" is not in myText then
set the end of textHeader to "use script \"FileManagerLib\" version \"2.3.5\""
end if
set myText to (textHeader as text) & myText
set myText to paragraphs of myText
return myText
end GetScriptText
@Shane Just wanted to point out to you that this issue was resolved by workaround when sharing prefs between Scripts/Bundles. There seems to be a bug in PrefsStorageLib. If I specify the domain in a script bundle via
prepare storage for domain myDomain
and the script is triggered by another script, it is creating a plist file
not actually using the domain name specified. It is strange because if I run it from script debugger, it is fine. Run triggered by another script/app it fails to use the domain specified.
The workaround was to specify the location of the actual app that created the .plist file via Posix path. Instead of using (path to me) or domain which wouldn’t work, I ended up using
I can see how there needs to be a solution to if it cannot figure out what to name the plist file, name it. I just thought that was the intent of the domain.
So…Not a bug…might good to document, that even if you specify the domain, it will be ignored if triggered by another app or script with the possible workaround.
Thanks as always for your awesome tools and support!
The key is that defaults are normally stored according to the host’s domain – that is, the application running the script. if you run a script other than as an applet, the host and the script will be two different entities.
This is covered in the Read Me – see the section Using PrefsStorageLib outside applets.