How do I create a Numbers spreadsheet using objective-c? - objective-c

I'm writing a Cocoa application and I'd like to generate a Numbers spreadsheet from my application using Scripting Bridge. I've generated the Numbers.h file and linked the ScriptingBridge.framework per the directions in Apple's Documentation. Below is the code I'm using to try to simply create a Numbers document and save it.
NSString *path = #"/Users/username/Desktop/Test.numbers";
NumbersApplication *numbers = [SBApplication applicationWithBundleIdentifier:#"com.apple.iWork.Numbers"];
[numbers activate];
NumbersDocument *document = [[[numbers classForScriptingClass:#"document"] alloc] initWithProperties:[NSDictionary dictionaryWithObjectsAndKeys:project.title, #"name", nil]];
[[numbers documents] addObject:document];
[document saveAs:nil in:[NSURL URLWithString:path]];
The code compiles and runs and when I try the saveAs:in: method I get the following error:
-[SBProxyByClass saveAs:in:]: object has not been added to a container yet; selector not recognized [self = 0x2005912e0]
Is there something else I have to do besides adding the document to the [numbers documents] array?
I'm open to using AppleScript, but I'd prefer to using the Scripting Bridge if I can.

Ehh, Numbers scripting with SB; two black arts for the price of one. I would suggest trying to do it in AppleScript first, in order to narrow down the problem a bit.
If it breaks in AS too, then either you've phrased the commands wrongly or there's a problem in Numbers. Since most application scripters use AppleScript, you'll find it easier to get help if you can present code they'll recognise.
If it works, then either your translation of the commands to ObjC is incorrect or there's a problem in SB. Having a working example in AS will provide a starting point for figuring out where things are going wrong.
You might also look into objc-appscript, which provides a more reliable, less obfuscated alternative to SB. Its ASTranslate tool makes it easy to translate working AS commands to ObjC syntax.

Numbers doesn't yet support creation of documents via Applescript. You have to use GUI scripting. The new version of Numbers is supposed to be out Jan 6, 2011 and (hopefully) will fix its severely limited Applescript support.

Related

Best way to store and reference a list of 3rd party API keys and constants in code?

I've got a list of constants for numbers api's that I'd like to collect in one location in code and am wondering about the best ways to do this. Here's what I have so far..
A static array with all the constants...
static NSDictionary* kApiConstants =
[NSDictionary dictionaryWithObjectsAndKeys:
#"crittercism-app-id", #"myAppId",
#"crittercism-key", #"myAppKey",
#"crittercism-secret", #"mySecretKey",
#"content-server-url-dev", #"http://my-dev-url/",
#"content-server-url-stg", #"http://my-staging-url",
#"content-server-url-pro", #"http://my-production-url",
nil];
I then have a macro for quick retrieval of items in the array...
#define MYAPIKEY(x) [kApiConstants objectForKey:x]
I like this setup. It makes code cleaner to read overall and makes merging easier for my purposes between branches in our git repo. One feature I would love to have at build/compile time is if a string is not in the dictionary then a build and/or compiler error would get flagged to indicate this.
I'm sure others have run into this situation before with so many 3rd party libraries, sdk's and what have you in a project it's hard to keep track of them all. For those willing to share what systems have you come up with to help with this?
In my case this is for an iOS project but the situation applies for any kind of project really.
What advantage do you see in using a dictionary over just making them constants on their own? Your wish:
One feature I would love to have at build/compile time is if a string is not in the dictionary then a build and/or compiler error would get flagged to indicate this.
would be solved by just making them constants in their own right.

Hot Code Swapping in Xcode?

I am doing iOS game development using the cocos2d framework, and I tremendously envy the ability for Eclipse Java programmers to hot swap their code while they are debugging their application (i.e. change out variable values within a method and have the program live update as if you had a REPL without having to recompile).
This seems like something that would be absolutely tremendously helpful when it came to iOS development, where my development environment is (obviously) Xcode and the language I am programming in is Objective C. I have googled around but havent been able to find anything - so I thought I would ask the community.
Does anyone know if there a way to Hot Swap code in Xcode while programming in Objective C?
Thanks in advance
There is a great plugin which allow changing code in live, working APP. It is called InjectionPlugin.
As FAQ says:
How does it work? The Objective-C runtime allows you to load a new version of a class into an application using a bundle even if there is already an implementation linked into the application. Swizzling is used as the bundle is loaded to switch the existing class to use the methods of the new implementation. This can be done repeatedly and works for OSX and iOS applications and on iOS devices.
I made some small video which shows how to install and use this plugin
http://nomtek.com/developers/how-to-install-and-use-injection-plugin-for-xcode/
Hope it helps!
Not possible with the current tools.
Keep in mind that iOS applications are signed -- if you change a single byte you'd have resign the whole thing. One could imagine making it work using runtime's support for dynamically adding and removing methods. But that'd surely require adding some extra stuff to support it on the device, and that's something that malware could easily take advantage of. So it's probably not a feature you'll be likely to see anytime soon.
By the way, Xcode versions 1.x-3.x did have a "Fix and Continue" feature. You could edit as you were debugging, use the Fix and Continue command, and continue running the updated code. I believe it was removed at some point, perhaps due to some combination of: requiring that your project be configured to use "zero link" and perhaps some other things; being less than completely reliable; probably not supporting iOS; the switch to llvm; other changes in Xcode 4. Maybe it'll come back someday -- if you want it back, file a bug -- but again, I think supporting it on iOS would be a challenge.
If you're just talking about changing variable values then you can achieve that surreptitiously via lldb (or, presumably) gdb. Supposing you had:
- (void)uselessMethod
{
NSString *localString = #"I'm some local text";
NSLog(#"%#", localString);
}
And put a breakpoint on the NSLog, at that point you could ask lldb to evaluate a reassignment of localString as a means of performing it. E.g.
po localString = #"Hat"
If you then allow program execution to continue, you should find that the reassignment has stuck. Similarly you can call any method or perform any other sort of assignment.
I've just tested this against Xcode 4.3.2.
You can hot swap a variable value in Xcode by:
expression <variable> = <value>;.
By having a break point in the place where you wanna change the value and doing the command in Xcode console.
Example:
// Messages From Console
(lldb) expression graphFlag = #"X"; // Update variable value
(__NSCFConstantString *) $0 = 0x36f95718 #"X" // Xcode prints the updated value
(lldb) expression graphFlag; // Printing value through expression command
(__NSCFConstantString *) $1 = 0x36f95718 #"X" // Hot Swapped variable value

What's the right way to parse an ISO8601 date in cocoa?

I would like to parse ISO8601 dates in Cocoa, both for iOS 4+ and OSX 10.6+
There are a few questions about this on StackOverflow already, but in my opinion none of them contain good answers. Here's what I think constitutes a good answer:
The answer should point to code with support for ISO8601. This code should compile cleanly under XCode 4 for both iOS 4+ and OSX 10.6+.
The code should support all possible ISO8601 date formats.
Please note that there are many, many possibilities here. Simply answering with one or two format strings for NSDateFormatter is not going to cut it.
The answer should not be this library. That's because it is riddled with dangerous 32-bit assumptions, it's far more complicated than necessary, and it doesn't compile clean with XCode4/Clang. Bottom line: I don't trust it at all!
Thanks, fellow Cocoa-ites. I'm excited to find out if there's a real answer here!
The best way is this library. ☺
I should add a link on that page to the Bitbucket repo, which contains newer source code (including 32-bit and Clang fixes!) and has an issue tracker. If you find any other bugs in it, please file them.
I'd also like to know what you mean by “more complicated than necessary”. Normal usage is very simple:
ISO8601DateFormatter *formatter = [[[ISO8601DateFormatter alloc] init] autorelease]; //Or, if you prefer, create it once in -init and own it until -dealloc
NSDate *parsedDate = [formatter dateFromString:inString];
NSString *unparsedString = [formatter stringFromDate:inDate];
You can switch out dateFromString: or stringFromDate: for one of the longer methods if you need more information (e.g., to preserve the time zone).
If you mean something else, I want to hear it so I can improve the library.

Getting the POSIX path of the Finder's front window

How can I get the POSIX path of the Finder window that is currently at the top of its window list?
Preferably with some kind of Cocoa framework but I am open for anything.
Background:
I would need this because I want to make a selection of files matching a regex pattern, starting recursively from this path. The idea is to use
[[NSWorkspace sharedWorkspace] subpathsAtPath:thePath]
method to get all the descendants of this path, use "grep" in an NSTask (to avoid packaging a regex support framework) and use
[[NSWorkspace sharedWorkspace] selectFile:aFile inFileViewerRootedAtPath:thePath]
in a loop looping through an NSArray made from the entries returned by grep.
So far, I have looked at NSWorkspace, NSFileManager and NSApplication plus other keyword searches within the Xcode Documentation.
Thanks for checking out my question!
Andre
PS: I am not sure about the grep part, maybe I'll just slap RegexKit Lite in there...
You can probably ask the Finder this via an AppleScript.
This* one-liner works for me:
osascript -e 'tell application "Finder" to set myname to POSIX path of (target of window 1 as alias)'
*a modified version of this.
I'm developing an commercial application that does exactly what you describe and I've been messing with different ways of doing this for over a year now. I'm a newbie developer, so I'm totally open to the idea that there may be a much better way of doing it than my way, but it seems to be stable and work.
I use Apple Script
I get the path of the active document every 3 seconds, but if the active application is the Finder, I get the path of the active window. I do this using Apple Script as described by the accepted answer.
Getting Window List using Carbon
To get the window list of all processes to get the window ID (something Apple Script can't do), I use CGWindowListCopyWindowInfo as detailed in this question:
Getting a unique ID for a window of another application
This presents me an array with all the windows of all processes ordered by which is frontmost. So all I need to do is pluck the first entry from the array. This can also be used to get a screengrab of the front window, if that's helpful to you, as shown in the Son of Grab sample application, which has been invaluable to me as a working example.
Sending Apple Script from Cocoa
When it comes to Apple Script, I've experimented with all 3 that Jim suggests and my conclusion is that each has it's issues, both in terms of stability and flexibility.
Apple Event Manager relies on you
sending raw Apple Events. For me,
this was too much like hard work and
too low level.
Using NSAppleScript I found to be
slow and error prone. It's also
pretty clumsy when you want to send a
whole variety of Apple Script
messages as I do.
When reading about Scripting Bridge,
I got very excited, only to try it
out and be very disappointed. For the
attributes of the windows I was
trying to grab, it didn't even
recognise them as existing. It seemed
to me to be buggy and weird. I expect
if the commands you're sending are
supported by SB, it would be the best
option.
A Fourth Option
I now rely on a fabulous Objective C wrapper around Apple Script called AppScript. It's been around for many, many years, it's stable, the developer is great and most of all it just works.
It has rock solid methods which allow you to send Apple Script and it'll even return errors for you neatly without a whole load of potentially buggy and messy code needing to check for them.
I've been using it for a year now with no problems. If you have any other questions about how to do this, please don't hesitate to comment and I'll try my best to answer them.
Getting the POSIX path represented by the frontmost window in the Finder is going to involve sending Apple Events to the Finder in one way or another.
Your choices include:
Using the Apple Event Manager (or NSAppleEventDescriptor and friends) directly.
Using NSAppleScript or OSAScript objects.
Using ScriptingBridge.
If you are building a Cocoa app, OSAScript is probably the most natural choice.
It is possible get the POSIX path of the Finder's front window by using the ScriptingBridge with Swift
import Foundation
import ScriptingBridge // imports: ScriptingBridge.SBApplication, .SBElementArray, .SBObject
// SBApplication? to SBApplication to FinderApplication
let finder = (SBApplication(bundleIdentifier: "com.apple.finder")!) as FinderApplication
let windowList: SBElementArray = finder.windows!()
guard windowList.count > 0,
let window = windowList[0] as? FinderWindow,
let windowProperties = window.properties,
let windowTarget = windowProperties["target"] as? FinderFolder,
let windowUrlOptionalStr = windowTarget.URL,
let windowUrlSubStr = windowUrlOptionalStr
.removingPercentEncoding?
.dropFirst(7) // "file://" 7 characters
else {
// … handle failure
}
let windowUrl = URL(
fileURLWithPath: String(windowUrlSubStr),
isDirectory: true,
relativeTo: nil)

SQLite Access in Objective-C and Cocoa

I am in the process of learning Objective-C for Mac/iPhone development and decided to try and write something useful without looking at the bible (Aaron Hillegass: Cocoa Programming 3rd Edition).
I'm writing a simple puzzle game. The data that defines the levels is stored as a string in a SQLite database and read into a level object with this line of code:
tempLevel.levelData = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
I have two other lines that read in other properties from the database (integers this time, not strings) and they work fine so I am wondering if anyone can help me with the problem.
When this line of code executes I get the following error:
*** +[NSString stringWithUTF8String:]: NULL cString
I would greatly appreciate any help you can give. If you need any more information I would be glad to provide it.
Thanks!
Not really an answer, but there's no good reason to deal with sqlite directly when there are some great wrappers available:
http://cocoaheads.byu.edu/resources/sqlite
Most likely it is an error in compiledStatement. As a general point, you should check that you get an actual value from sqlite3_column_text first, since getting back NULL is often the "correct" response to what you ask for.