Differentiating between new documents and restored documents in a Cocoa application - cocoa

In my project I need to be able to tell the difference between documents created by the user and those restored at application launch by restoreStateWithCoder because there are some thing s that need to be done for new documents, but not restored ones. How can I do this?

How about subclassing "NSDocument" and using that subclass for your document?
Then, you can catch "restoreStateWithCoder" as it happens and set a unique flag (e.g. a BOOL property) for those documents that are restored from disk and not created fresh via "File -> New" command.
You can also attempt to "method swizzle" "restoreStateWithCoder", but you have to decide what property to set in which object.

[Answering this for Swift, but the general idea works for Objective-C as well]
When a document is brand new, you generally get a call to the following function:
convenience init(type tyepName: String) throws
You could set a flag in that function (say needSpecialHandling = true, a variable which is originally initialised to false) to say whether you need some special handling for such cases.
Then in the makeWindowControllers() function you use that variable to trigger invoking the special code (if true) the same way you invoked it possibly in the windowControllerDidLoadNib function.

Related

macOS command line app - User Defaults dictionaryRepresentation shows too many values

I a developing a macOS commandline application in Xcode, which uses User Defaults. I have the following code for my User Defaults
if let configDefaults = UserDefaults.init(suiteName: "com.tests.configuration") {
configDefaults.set("myStringValue", forKey: "stringKey")
configDefaults.synchronize()
print(configDefaults.dictionaryRepresentation())
}
This will create my own .plist file in the ~/Library/Preferences folder. If I look into the file, I can see only my single value which I added, which is perfectly fine. But when I call dictionaryRepresentation() on my UserDefaults object, the there are a lot of other attributes (I guess from the shared UserDefaults object), like
com.apple.trackpad.twoFingerFromRightEdgeSwipeGesture or AKLastEmailListRequestDateKey
Looking into the documentation of UserDefaults, it seems that this has to do with the search list of UserDefaults and that the standard object is in the search list:
func dictionaryRepresentation() -> [String : Any]
Returns a dictionary that contains a union of all key-value pairs in the domains in the search list.
There are also the methods addSuite and removeSuite for a UserDefaults object, so I am guessing I need to remove the .standard suite from my configDefaults object, but I don't know the name, which should be used for that in the method.
Is it possible to remove the .standard defaults from the dictionary representation? I basically just want all of my own data in a dictionary, nothing more.
The reason I am trying to get only my values from the UserDefaults, is that a have a number of object of a custom type Connection (which store the configuration to connect to a server), which are saved in the UserDefaults. On program start I want to be able to load all objects into my app. Therefore I thought I could use dictionaryRepresentation(), as it would return all elements in the UserDefaults. But then there should be only my Connection objects in the dictionary, so that I can cast it to [String: Connection].
Given your purpose (in your latest edit of your question), what you should do is store a collection of Connection objects under a single key. Then, look up that key to get the collection.
It's not clear if the collection should be an array of Connection objects or a dictionary mapping strings to Connections. That's a choice you can make.
But, in any case, you shouldn't rely on the defaults being empty of everything else.
In other words, you would do:
UserDefaults.standard.set(yourStringToConnectionDictionary, forKey:"yourSingleKey")
and later:
let connectionMap = UserDefaults.dictionary(forKey:"yourSingleKey")
then look up Connections in the connectionMap by their name/ID/whatever.
Though the other solution proposed by Ken Thomases may be better from a design standpoint, I've found a solution that does exactly what I initially wanted. Calling
UserDefaults.standard.persistentDomain(forName: "com.company.TestApp.configuration")
Returns a dictionary containing only the values I've added to the domain com.company.TestApp.configuration, using
let configs = UserDefaults.init(suiteName: "com.company.TestApp.configuration")!
configs.set(someData, forKey: someKey)
Strangely in the Apple documentation says this about persistentDomain(forName:):
Calling this method is equivalent to initializing a user defaults object with init(suiteName:) passing domainName and calling the dictionaryRepresentation() method on it.
But this is not the case (see my question). Clarification on that subject is more than welcome.

How to set cluster resource "Use Network Name for computer name" checkbox programmatically?

I am programmatically setting up a cluster resource (specifically, a Generic Service), using the Windows MI API (Microsoft.Management.Infrastructure).
I can add the service resource just fine. However, my service requires the "Use Network Name for computer name" checkbox to be checked (this is available in the Cluster Manager UI by looking at the Properties for the resource).
I can't figure out how to set this using the MI API. I have searched MSDN and multiple other resources for this without luck. Does anybody know if this is possible? Scripting with Powershell would be fine as well.
I was able to figure this out, after a lot of trial and error, and the discovery of an API bug along the way.
It turns out cluster resource objects have a property called PrivateProperties, which is basically a property bag. Inside, there's a property called UseNetworkName, which corresponds to the checkbox in the UI (and also, the ServiceName property, which is also required for things to work).
The 'wbemtest' tool was invaluable in finding this out. Once you open the resource instance in it, you have to double-click the PrivateProperties property to bring up a dialog which has a "View Embedded" button, which is then what shows you the properties inside. Somehow I had missed this before.
Now, setting this property was yet another pain. Due to what looks like a bug in the API, retrieving the resource instance with CimSession.GetInstance() does not populate property values. This misled me into thinking I had to add the PrivateProperties property and its inner properties myself, which only resulted in lots of cryptic errors.
I finally stumbled upon this old MSDN post about it, where I realized the property is dynamic and automatically set by WMI. So, in the end, all you have to do is know how to get the property bag using CimSession.QueryInstances(), so you can then set the inner properties like any other property.
This is what the whole thing looks like (I ommitted the code for adding the resource):
using (var session = CimSession.Create("YOUR_CLUSTER", new DComSessionOptions()))
{
// This query finds the newly created resource and fills in the
// private props we'll change. We have to do a manual WQL query
// because CimSession.GetInstance doesn't populate prop values.
var query =
"SELECT PrivateProperties FROM MSCluster_Resource WHERE Id=\"{YOUR-RES-GUID}\"";
// Lookup the resource. For some reason QueryInstances does not like
// the namespace in the regular form - it must be exactly like this
// for the call to work!
var res = session.QueryInstances(#"root/mscluster", "WQL", query).First();
// Add net name dependency so setting UseNetworkName works.
session.InvokeMethod(
res,
"AddDependency",
new CimMethodParametersCollection
{
CimMethodParameter.Create(
"Resource", "YOUR_NET_NAME_HERE", CimFlags.Parameter)
});
// Get private prop bag and set our props.
var privProps =
(CimInstance)res.CimInstanceProperties["PrivateProperties"].Value;
privProps.CimInstanceProperties["ServiceName"].Value = "YOUR_SVC_HERE";
privProps.CimInstanceProperties["UseNetworkName"].Value = 1;
// Persist the changes.
session.ModifyInstance(#"\root\mscluster", res);
}
Note how the quirks in the API make things more complicated than they should be: QueryInstances expects the namespace in a special way, and also, if you don't add the network name dependency first, setting private properties fails silently.
Finally, I also figured out how to set this through PowerShell. You have to use the Set-ClusterParameter command, see this other answer for the full info.

NSDocument detect when a new document is created as opposed to a saved document restored

I found a major problem with the architecture of my Document based app.
Basically a store the model (a simple string) in a global variable, every time the text in field changes. I have the document save this string as it's data, and restore re-opened files using this data.
Now, the major problem that I now see is that if I restore any saved file, I populate the global variable from the document in the documents "readFromData" function (works).
But if I create a new document, "readFromData" is never called, so I have no way to set the global string to "", and thus my new documents global variable is still populated with the last saved string. (I use this to put the string back into the text view on load.
So as a simple workaround, I would need to be able to use a function that is automatically called and only ever called by the creation of a new document, to set my global variable back to "".
I can not find such a function I can override. Does one exist..?
I am not sure I understand what you are trying to do.
You could use this NSDocument initializer:
/* Initialize a new empty document of a specified type,
and return it if successful.
…
You can override this method to perform initialization that
must be done when creating new documents but should not be done
when opening existing documents.
*/
- (instancetype)initWithType:(NSString *)typeName error:(NSError **)outError;
This is invoked exactly once per document at the initial creation of the document. It will not be invoked when a document is opened after being saved to disk.

setObjectId is not working

I wonder if is possible to assign an id when an item is created with parse:
ParseObject parseWord = new ParseObject(DataBaseHelper.TABLE_WORD);
parseWord.setObjectId(idRow);
parseWord.put(Word.NAME, word.getName());
parseWord.put(Word.TYPE, word.getType());
parseWord.put(Word.TRANSLATE, word.getTranslate());
parseWord.put(Word.EXAMPLE, word.getExample());
parseWord.put(Word.NOTE, word.getNote());
parseWord.put(Word.SYNC_AT, today);
parseWord.saveInBackground();
This code is not working, it doesnt save the item in the server. If I delete the setObjectId(idRow); it works. What am I doing wrong?.
Is there anyway to know when the saveInBackground is done?
Thanks
According to the ParseObject.setObjectID() API doc:
Setter for the object id. In general you do not need to use this.
However, in some cases this can be convenient. For example, if you are
serializing a ParseObject yourself and wish to recreate it, you can
use this to recreate the ParseObject exactly.
Also from the API doc:
An object id is assigned as soon as an object is saved to the server.
A reason, as the quote suggests, you might need to set the object ID is if you, wish to do something like save the fields of a parse object to a file. If you wanted to take the fields from your file and recreate a parse object, THEN you'd need to set it, as that's not done for you if you're not saving it to the server and just using an instance of the object for purposes internal to your application.

QTP Visibility Issue with a strange solution

I'm testing a Java web application on QTP. In one screen, not all fields are visible initially (ie: they're 'below the fold'), so this:
Browser("x").Page("y").JavaApplet("z").JavaInternalFrame("a").JavaEdit("txtName").Set "bob"
Causes an unspecified error to occur.
But if I change it slightly, to:
Browser("x").Page("y").JavaApplet("z").JavaInternalFrame("a").JavaEdit("txtName").Object.SetText "bob"
It works fine. Why?
If the object Properties for the JavaEdit Box which is set by the Developers is different from other similar objects. hence it has to be done in that way.
There are many instance like where one cell of the Java Table is set by the value using SetCellData for the Edit operation and for a same kind operation usually we use Type / SendKey methods.

Resources