From what I have seen, if you are building a OSX desktop HTML5 app and want localStorage to persist in your WebView wrapper, you need to do something like this:
WebPreferences* prefs = [webView preferences];
[prefs _setLocalStorageDatabasePath:#"~/Library/Application Support/MyApp"];
[prefs setLocalStorageEnabled:YES];
Taken from: How do I enable Local Storage in my WebKit-based application?
But this doesn't seem to work for me in Xcode 4.3. Instead I get
"No visible #interface for 'WebPreferences' declares the selector '_setLocalStorageDatabasePath:'
"No visible #interface for 'WebPreferences' declares the selector 'setLocalStorageEnabled:'
I'm very new to Objective C, and are probably doing something silly like not including some header or something.
I've included the WebKit framework and both of these headers:
#import <WebKit/WebKit.h>
#import <WebKit/WebPreferences.h>
And what's weird is that I can access other methods of prefs, i.e. [prefs setDefaultFontSize:10] - but just not the two above that I listed.
Any ideas? Is this something that has been removed in Xcode 4.3?
OK, I have a solution. I looked at the source code to macgap and noticed how they were dealing with this issue.
It turns out the error message I was getting does make a little sense - I needed to declare an interface for WebPreferences first.
#interface WebPreferences (WebPreferencesPrivate)
- (void)_setLocalStorageDatabasePath:(NSString *)path;
- (void) setLocalStorageEnabled: (BOOL) localStorageEnabled;
#end
...
WebPreferences* prefs = [WebPreferences standardPreferences];
[prefs _setLocalStorageDatabasePath:"~/Library/Application Support/MyApp"];
[prefs setLocalStorageEnabled:YES];
[webView setPreferences:prefs];
Like I said, I'm new to Objective-C. I don't really get why the interface is needed in order to call those two methods (i.e. when I can call the other methods without the interface).
There's good news and there's bad news; I'm going to make the assumption that you want the bad news first (it'd be easier if I answered your question with the bad news first anyway).
The Bad News
The only answer to why this is happening is that Xcode 4.3 doesn't offer those methods anymore. That question that you linked to, "How do I enable Local Storage in my WebKit-based application?" was last active over a year ago (with the accepted answer being edited in early 2011). There have been at least two updates to Xcode since then (probably more and I'm just not remembering them), and it seems feasible to me that Apple would want to keep their private methods private, so it's safe to assume that they removed them as well as the support for setLocalStorageEnabled:.
The reasons that I don't think that there is any other answer to your problem are the following:
Both methods that you call on the WebPreferences instance are not supported. It's not just the private method, so Apple must have modified the WebPreferences class, removing not only setLocalStorageEnabled: but also support for private methods such as _setLocalStorageDatabasePath:. Why they supported private methods to begin with, I don't know, but they've definitely cracked down on their support because I haven't seen an opportunity to implement a private method in quite some time.
If implementing the private method (or even the other, public method) were possible, it'd be as easy as your code makes it out to be. If one looks at the linked question, they don't mention any difficult steps to getting the code to be supported. There isn't any way to import a private part of a framework such as WebKit without doing some heavy-lifting with regards to not only finding the private part but getting it into your code as well. Even if you can get those methods supported in your code after all of that heavy-lifting, it'd be highly unlikely that Apple would be very happy with it and they'd probably deny your app from the app store.
Sorry to be a Debbie-downer about it all, but I just don't think that your code would work anymore without some deep diggging and large workarounds. If you want it to work easily, you'll probably have to go back to early 2011 and make your app then instead.
The Good News
There is probably some solution that doesn't involve private and unsupported methods that I'm just not aware of because of my lack of experience using WebKit. Instead of looking for an answer for why your code isn't working, I'd start looking for alternatives for what your code is supposed to do.
I have found a solution to the persistence problem. see my post at
Local Storage in webview is not persistent
Related
trying to use the initwithcoder init method in my custom NSControl Class.
It works just fine and does what I need it to do. However and this leads me to asking this question on this here forum -> in the class reference of NCControl when you scroll down to initWithCoder and click on it it states SDKs
macOS 10.10+ which leads me to believe that it would not work and do what I need it to do on versions prior to that... Unless Apple's documentation once again is wrong...
BTW - from Apple's own reference (Online as well as offline) apparently the NSCoder Class is also suffering from this SDKs macOS 10.10+
https://developer.apple.com/reference/foundation/nscoder
The strange thing is that in the documentation describing how a NIB is loaded and which init methods it calls on various objects, it describes initWithCoder as being the designated initializer but that documentation is from before 10.10.
Thanks to anyone who can set my mind at ease ;-)
Yes it will not work before defined version. I think you are checking versions of Swift, if you select objective-c you should see version 10.0+ which i think should work fine for your need. If you change the language, you can put your mind to ease :)
I am assuming if you would like to support things that far back you are going to use objective-c over swift.
Check the below image for NSCoder documentation after you select objective-c on right hand side.
I am using a 3rd party Obj-C static library for and API in a RubyMotion project and one of the classes in the library defines some public instance variables in the interface section in the API header file. Here is the code as it's pretty short:
#interface TransporterFile : NSObject
{
#public
NSString *name;
TransporterFileType type;
NSDate *modifyTime;
NSDate *createTime;
unsigned long long size;
}
- (BOOL) isFolder;
- (BOOL) isShared;
#end
In ObjC, these values can be reached like this:
name = transporterFile->name
but there is no getter defined so you can't use dot notation.
In RubyMotion, probably because there is no getter, there is no instance variable exposed to me. I have tried using the .instance_variables method and it returns an empty array. I also tried the instance_variable_get('#name') method but that doesn't work either. I am able to call the two methods isFolder and isShared and they work as expected.
Update: I tried doing a similar thing in the Swift language and it had similar problems and that led me to a suggestion to use the valueForKeyPath('name') method to access the 'name' instance variable. That worked in Swift and led me to check if a similar method was available to RubyMotion. The good news is that this works but is a bit clunky so I'll leave this question open for now in the hope that a better answer is available.
I realise the 3rd party library is not following best practice and I will recommend to the vendor that they define some properties instead if using instance variables directly but for now I need a workaround.
Can anyone please suggest a way to access these public instance variables from RubyMotion or perhaps by wrapping the vendor's library in another ObjC library. I've never written an ObjC library (wrapper or not) so would appreciate some advice before I embark on this option.
The full API can be seen here: https://secure.connecteddata.com/developer
Note that I'm using Mac OS X 10.10 with Xcode 6.1.1 and the latest version of RubyMotion. The Mac OS X API download from the above site is missing the header file so I used the header from the iOS download.
Many thanks,
Craig.
I updated my question to explain that I found the following method allows me to access the public instance variables.
obj.valueForKeyPath('name')
However, I'd be happy to hear of any better ways to do this as it's a bit clunky.
ps. Why is it that I can spend hours trying to find a solution to a problem then within minutes of posting the question on an open forum, I find a potential answer? Grr... :)
Is it at all possible to have Xcode create a .playground file for Objective-C instead of Swift? Are there any available Xcode plugins that allow that?
You can quickly test code snippets using a test case in a new project. Just create a new project and go to the Navigator in the left pane and hit the Test Navigator button. Then follow this guide
The setup code will look a little different than a swift playground, but it still allows you to prototype and play around.
There is a very good library developed by Krzysztof Zabłocki in Github entitled KZPlayground that support both code in Playgrounds for Objective-C and Swift and a lot of cool features.
I hope this can help you.
If the only purpose is to test out Objective-C snippets, i would really recommend you an OS X command line Tool project.
There are enough moving parts in a playground, and all of those would have to be reimplemented for Objective-C. Reliable playgrounds also depend on definite initialization which Objective-C does not have.
For instance consider:
var d: NSData // this is not initialized, so I can't use it
vs.
NSData *d; // this is also not initialized, but now I can use it
If I am the person storing the description of your NSData for the sidebar, now I know that I am not supposed to do
describe(d)
in the Swift case, but for the Objective-C case, I don't have equal knowledge and I run the risk of saying
[d description]; // even though d is a random pointer now.. oops, I just crashed!
In short, I don't think any such thing exists, and making one work would also involve some trickery
Have tried to use "+resetStandardUserDefaults" of the NSUserDefaults class to reset the default values to their "factory" settings. It turns out to be not the appropriate method to use. Find out from some archived discussions in SO that we should use the instance method "removeObjectForKey" to remove the user's settings instead.
But am just curious and hope that someone could explain the actual usage of "+resetStandardUserDefaults". Have read the reference web manual about this. But it is very brief and I don't seem to get what it really means ...
Somewhere (usually in the app delegate +initialize) you declare the standard user defaults (preferences of your app at the first launch).
+resetStandardUserDefaults makes your app mean it's the first launch and uses the user defaults the way you declared them in the app delegate.
THE ABOVE IS THE WRONG ANSWER
As Apple's documentation says, it:
Synchronizes any changes made to the shared user defaults object and releases it from memory
The reset portion refers to the fact that the standardUserDefaults object in memory is destroyed, so you get a fresh one from the file system next time you use [NSUserDefaults standardUserDefaults] but it has nothing to do with any first launch behaviour.
The call would make sense if it was renamed flushDefaultsToDisk or similar. It is the most misleadingly-named part of iOS SDK I've seen so far.
(I corrected this in-place as this rates quite highly on searches and didn't want to put the correction in a comment which someone might not read.)
i a newbie for iphone development. I got some questions here.
I know IB is a convinience tool for UI desgin and you also can do most things programmatically. I am just wondering, when should I create an interface controll without IB and why so. I am trying to form a good habit for this. Thank you very much. A friend told me that when efficiency should be considered for the application, then i should create interface controller programmatically, any other cases?
I am studying Learn Objective c on Mac now. It says that "Apple suggests you avoid using autorelease on your own code". So, does it mean I cannot use "autorelease" or just i should avoid using it. For example, can i use following code in my own code for iphone development?
#Interface Test {
A* a;
}
#Implementation {
a = [[[A alloc]init]autorelease];
}
Thank you for your time to read this. I am looking forward to answers :D.
Sometimes creating custom controls will require you to build them programmatically.
No, it's not that you can not use autorealease, it's just that using it adds to the burden of memory management. But in some cases you probably don't have an option, such as when you have a method that returns a temporary object. Using retain/release method to manually control object lifespan is the recommended way of doing things in iPhone development. Please see Autorelease pool for more details.
See Matt Gallagher's blog post for a discussion of efficiency. Simply put, one method is probably no more efficient than the other. This depends on your specific application, of course. Generally speaking, do what is most comfortable in designing your user interface. Make optimizations as needed.
1) If you are doing basic views that interface building has the widgets ready to go for, then use interface builder, it will make your life easier. Plus if you are just starting out it will let you get some sample code out the door faster. I'm not a huge fan of interface builder, but if you have to maintain code, you'll come across it so good thing to get familiar with it.
2) I don't think that autorelease is a bad thing. If you are writing single threaded code there is not as much to worry about. However, the thing that can come back to bite you is that you don't actually know when things will be released. So if you have programmed poorly, and try to reference an object that you have autoreleased later in code then you may get inconsistent behavior. I autorelease, but I also am very good about retain/release in other parts of my code that is passed these objects.