Creating Bookmarks - xcode

Today I'm working with an interesting problem. I'm trying to code simple bookmarks, and by that I mean the most basic functionality; store the url, and load the url when you tap it.
Right now, the idea is that I would use the following:
currentURL = currentWebView.request.URL.absoluteString;
To retrieve the current URL, and then possibly store that within NSDefaults.
(Open to any other suggestions on better ways of preforming this)
However, the problem that I'm having is that, assuming I've gotten that far (retrieved the URL and stored it), how would I go about putting then somewhere in a list format that displays the url that was bookmarked, and then loading them (from that separate bookmarks view) in the main view that the UIWebView is contained in?
Your time is much appreciated
--Jake
NOTE: If you need more information or anything I could possibly help you with in order to come to a solution, just ask

Simple
Suppose your viewcontroller where your webview resides, is WebViewController and where list shows is BookmarkViewController.
Create a property in BookmarkViewController
#property(nonatomic,retain)WebViewController *maincontroller;
Send the reference of your WebViewController to BookmarkViewController when you create BookmarkViewController object
bookmarkobject.maincontroller=self;
and then In didSelectRowAtIndexPath
either use
1) [maincontroller.webView loadRequest:url];
or
make a method in WebViewController with parameter url and call this method from didSelectRowAtIndexPath
2) [maincontroller loadUrlInWebView:url];
and dismiss your modal
or create a property of url in WebViewController and in didSelectRowAtIndexPath
3) [maincontroller urlstring:url];
and in viewWillAppear load this url in your webview
Hope any of these 3 methods will help you.

Related

macOS Apple Help Authoring - Anchors

I'm trying to make an Apple Help book for my macOS app that I'm ready to release. However, I am trying to make anchors work in my HTML. By Apple's definition:
"Anchors allow you to uniquely identify topics in your help book. When
a user follows a link to an anchor, Help Viewer loads the page
containing the anchor. ... You can also use anchors to load an
anchored page from within your application by calling the the
NSHelpManager method openHelpAnchor:inBook: ..."
Example from Apple: <a name="ArrivalTimesUsingStopID"></a>
In my Apple, I have an NSAlert which has the following code to display the help button so that when you click on it, it opens the specified anchor string.
alert.showsHelp = true
alert.helpAnchor = NSHelpManager.AnchorName(stringLiteral: "ArrivalTimesUsingStopID")
Running the code does display the help button and Mac Help does open, but to an error saying that the specified content cannot be found. Not sure why the anchors aren't working because I can access the Help Book if I go to the Help menu and open it from there.
Furthermore, Apple's document states:
The NSAlert, SFChooseIdentityPanel, SFCertificatePanel classes provide
help buttons for dialogs. To display such a help button and link it to
an anchor in your help book, use the methods setShowsHelp: and
setHelpAnchor: in those classes.
and the documentation for these properties in NSAlert state:
-setShowsHelp:YES adds a help button to the alert panel. When the help button is pressed, the delegate is first consulted. If the delegate
does not implement alertShowHelp: or returns NO, then -[NSHelpManager
openHelpAnchor:inBook:] is called with a nil book and the anchor
specified by -setHelpAnchor:, if any. An exception will be raised if
the delegate returns NO and there is no help anchor set.
...so I know that I am using these two properly.
I also understand that I need to create a .helpindex file every time I update my Apple Help book HTML documents. I'm using "Help Indexer.app" which is in the Additional Xcode Tools on developer.apple.com. I make sure that:
I have the option set to index all anchors.
Any HTML page with an anchor has <meta name="ROBOTS" content="ANCHORS"> in the header so anchors are indexed.
My Apple Help book plist file correctly points to the .helpindex file created by "Help Indexer.app".
But even with all of this, I cannot get it to open the Apple Help book to the correct anchor or even the Title page of my Apple Help book.
I've read
https://developer.apple.com/library/archive/documentation/Carbon/Conceptual/ProvidingUserAssitAppleHelp/user_help_intro/user_assistance_intro.html#//apple_ref/doc/uid/TP30000903-CH204-CHDIDJFE
from cover to cover multiple times and I cannot find a solution or anywhere online.
I've also tried opening it manually, but it just opens to the same error saying the specified content couldn't be found with the following code:
let bookName = Bundle.main.object(forInfoDictionaryKey: "CFBundleHelpBookName") as! String
NSHelpManager.shared.openHelpAnchor("ArrivalTimesUsingStopID", inBook: bookName)
Using nil for the inBook parameter doesn't work either:
NSHelpManager.shared.openHelpAnchor("ArrivalTimesUsingStopID", inBook: nil)
Any ideas?
I'm not sure if this is the answer at this point, but it is an answer and one that seems to do the trick. I wasn't able to get the helpAnchor in the Alert to work, but using the help delegate, the method outlined below works.
I started out my day trying to open the Help Book to a simple anchor. I'm sure this used to work using the NSHelpManager in the past, but it does not appear to in recent versions of the OS.
Watching the console while opening my under-development App's help book resulted in the following:
Opening URL help:openbook=%22com.ClueTrust.Cartographica.help*1.5.2d1%22 with application <FSNode 0x6000006a1b40> { isDir = y, path = '/System/Library/CoreServices/HelpViewer.app' }
Opening to my anchor using NSHelpManager resulted in:
Opening URL help:anchor=SpatialJoinOperation%20bookID=%22com.ClueTrust.Cartographica.help%22%20appID=%22com.ClueTrust.Cartographica%22 with application <FSNode 0x6000006a8260> { isDir = y, path = '/System/Library/CoreServices/HelpViewer.app' }
And, it didn't result in opening to my anchor.
I tried appending the *<version> to my URL:
Opening URL help:anchor=SpatialJoinOperation%20bookID=%22com.ClueTrust.Cartographica.help*1.5.2d1%22%20appID=%22com.ClueTrust.Cartographica%22 with application <FSNode 0x600000682c20> { isDir = y, path = '/System/Library/CoreServices/HelpViewer.app'
Looking deeper into the Console, though, I noticed that this is definitely triggering a network request and there's an unsupported URL coming back.
It's not clear to me if help:anchor=... does not function any longer, but I did find a relatively easy, but annoying way around the problem.
Anchors within help will definitely be opened when using a help: URL that is formatted like a file: URL and contains an anchor; and they will open to the correct anchor location.
This requires locating the specific help book and HTML file so that you can specify precisely where to open.
NSURL *helpBookURL = [NSBundle.mainBundle URLForResource:#"Cartographica" withExtension:#"help"];
NSBundle *helpBundle = [NSBundle bundleWithURL:helpBookURL];
NSURL *helpPageURL = [helpBundle URLForResource:#"Spatial_Join" withExtension:#"html"];
NSURLComponents *urlParts = [NSURLComponents componentsWithURL:helpPageURL resolvingAgainstBaseURL:NO];
urlParts.scheme=#"help";
urlParts.fragment=#"SpatialJoinOperation";
NSURL *finalHelpURL = urlParts.URL;
[NSWorkspace.sharedWorkspace openURL:finalHelpURL];
Basically:
Get the URL for the help book (need to do this in a way that gets it from the resource path, hence we're using NSBundle)
Locate the page containing the reference based on prior knowledge (in this case Spatial_Join.html is our filename, so we have the bundle look for it by name and extension.
Use the NSURLComponents interface to mutate the NSURL by changing the scheme from file to help and adding our achor in the fragment.
Finally, open the newly-created URL
It's not pretty, but it does appear to be effective and safe, at least in a non-sandboxed macOS App under 10.15.
Note that I could make some assumptions here about the help book name, but for illustration purposes this seems more clear, and because of the way resources work, it's not clear that those assumptions about the names would be appropriate in all situations.
My final result was this helper method:
- (void)openHelpPage:(NSString*)pageName anchor:(NSString * _Nullable)anchor bookName:(NSString * _Nullable)bookName
{
NSURL *helpBookURL = [NSBundle.mainBundle URLForResource:bookName withExtension:#"help"];
NSBundle *helpBundle = [NSBundle bundleWithURL:helpBookURL];
NSURL *helpPageURL = [helpBundle URLForResource:pageName withExtension:#"html"];
NSURLComponents *urlParts = [NSURLComponents componentsWithURL:helpPageURL resolvingAgainstBaseURL:NO];
urlParts.scheme=#"help";
if (anchor)
urlParts.fragment=anchor;
NSURL *finalHelpURL = urlParts.URL;
[NSWorkspace.sharedWorkspace openURL:finalHelpURL];
}
Call site syntax is:
// to specific anchor on a page
[self openHelpPage: #"Spatial_Join" anchor: #"SpatialJoinOperation" helpBook: nil];
// to specific page
[self openHelpPage: #"Spatial_Join" anchor: nil helpBook: nil];
I tried getting the help bundle with [NSBundle bundleWithIdentifier:] using the help bundle ID, but that returned nil. However, [NSBundle URLForResource:withExtension] will take a nil argument for the resourceName and get the first item that matches the extension. In my case (and I believe many) there is only one help resource, so this allows for a method that doesn't require knowledge of the Application's help book name.
I was finally able to get this working in a sandboxed application.
If you're using a Help button directly, you can use something like:
#IBAction func helpButtonAction(_ sender: Any)
{
if let bookName = Bundle.main.object(forInfoDictionaryKey: "CFBundleHelpBookName") as? String {
NSHelpManager.shared.openHelpAnchor("MY_ANCHOR_HERE", inBook: bookName)
}
}
If you're using an NSAlert(), you can use its help button with an anchor this way:
let alert = NSAlert()
...
alert.showsHelp = true
alert.helpAnchor = NSHelpManager.AnchorName("MY_ANCHOR_HERE")
A few things I learned the hard way:
Make sure your HTML page for your Help Book has a the proper setup for an anchor with:
<meta name="robots" content="anchors"> in the <head> section as well as a proper header tag in the fashion of:
<a name="MY_ANCHOR_HERE"></a> in your <body> section.
Make sure you use "Help Indexer.app" to index your Help Book. I found that it will not work unless you index your Help Book using this app. This app can be downloaded from developer.apple.com under More Downloads. They usually release a new version with every Xcode update. You want to look for "Additional Tools" and the specific indexer app will be located in
Additional Tools > Utilities > Help Indexer.app
Additionally, macOS does not like when you have multiple Help Books. This means, multiple copies of your Application on your Mac no matter where they reside. This could be in your Debug folder and your Application folder as your most common places. I found that deleting the copy in my Applications folder usually helps macOS not get confused when opening a Help Book. I have also found it to open up older versions of the Help Book so it's best to make sure you only have once copy of your app on your Mac when debugging help books.
But other than that, they should open just fine with simply an Anchor string and a few lines of code depending on how you display your Help button!

How to use NSObjectController and Managed Object Context using Cocoa Bindings

Searched entire Internet but couldn’t find the modern solution for my problem.
I want to use NSObjectController in pair with Core Data through Cocoa Bindings and struggle to set it up properly. Worth noting that I’m using latest version of Xcode and Swift.
What I’ve done:
For testing purposes I’ve done the following:
Created an macOS app with “Use Core Data” option selected (the app is not document based);
Dragged 2 NSTextFields into the Storyboard Dragged NSObjectController to the view controller scene;
Added Employee Entity to Core Data model with 2 attributes “name” and “surname”;
Done everything from the answer in How do I bind my Array Controller to my core data model?
Set NSObjectController to entity mode and typed in “Employee”,
Prepares Content selected, Use Lazy Fetching selected so all three options checked;
Binded the NSObjectController’s Managed Object Context in bindings inspector to the View Controller’s managedObjectContext;
Binded NSTextFields as follows: Value - Object Controller, Controller key - selection, Model Key Path - name (for 1st text field) and surname (for 2nd).
That’s it.
First set of questions: What I did wrong and how to fix it if it’s not completely wrong approach?
I’ve read in some post on stackoverflow that doing it that way allows automatic saving and fetching from Core Data model. That’s why I assumed it should work.
So here is a Second set of questions:
Is it true?
If it is then why text fields are not filled when view is displayed?
If it is not then how to achieve it if possible (trying to write as less code as possible)?
Third question: If I used approach that is completely wrong would someone help me to connect Core Data and NSObjectController using Cocoa bindings and show me the way of doing so with as less code written as possible using the right approach?
Taking into account that there no fresh posts about this topic in the wilds I think the right answer could help a lot of people that are developing a macOS app.
Thanks in advance!
I think your basic approach is correct, although it is important to understand that you need a real object, an instance, in order for it to work.
Creating a NSManagedObject subclass is generally desirable, and is almost always done in a real project, so you can define and use properties. You can do it easily nowadays by selecting the data model in Xcode's Project Navigator and clicking in the menu: Editor > Create NSManagedObject Subclass…. Technically it is not necessary, and in a demo or proof-of-concept, you often muddle through with NSManagedObject.
Assuming you are using the Xcode project template as you described, wherein AppDelegate has a property managedObjectContext, the following function in your AppDelegate class will maintain, creating when necessary, and return, what I call a singular object – an object of a particular entity, in this case Employee, which your app requires there to be one and only one of in the store.
#discardableResult func singularEmployee() -> NSManagedObject? {
var singularEmployee: NSManagedObject? = nil
let fetchRequest: NSFetchRequest<NSManagedObject> = NSFetchRequest(entityName: "Employee")
let objects = try? self.managedObjectContext.fetch(fetchRequest)
singularEmployee = objects?.first
if singularEmployee == nil {
singularEmployee = NSEntityDescription.insertNewObject(forEntityName: "Employee", into: self.managedObjectContext)
}
return singularEmployee
}
Then, add this line of code to applicationDidFinishLaunching
singularEmployee()

Change UILabel from appDelegate

I want to do some stuff from the appDelegate in Xcode. One of these things are to change a UILabel.
ViewController *viewController = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"id"];
viewController.label.text = #"heeej";
I was told this would do the trick. Doesn't work. Label doesn't change. Does anyone know what the problem is?
There are several problems:
Don't do anything in the AppDelegate except for loading the window an the initial view controllers (if needed).
You are instantiating a new view controller in the first line. I assume you already have a view controller and you want to change the label in that view controller. Than you need a reference to that view controller and use this reference to send messages to it.
The label should not be a property of the view controller. You should try to follow the design pattern Model-View-Controller or the pattern Model-View-ViewModel. Ask you preferred search engine what those are if you don't know.
id as an identifier for anything in Objective-C is a bad idea because this is used as an object type.
Edit: You don't need a reference to change a property in the view controller. Use notifications to update the label. The system sends a notification with the name UIApplicationWillEnterForgroundNotification' (see [here][1]). Add the view controller as an observer to the[NSNotificationCenter defaultCenter]` for this name and react on the notification. Read the Apple documentation if you don't know what I am talking about.

reference between app controller and window controllers

at the risk of a public flogging, I was hoping someone could clarify my understanding of communicating between the app controller and a window controller. Here is the scenario using xcode4.
using the default AppDelegate.h and .m as the "controller" (which is also a delegate to MainMenu.xib). Have an ivar called int counter.
Created page.xib and PageController.h and .m (subclass NSWindowController). Imported it into AppDelegate.m
Used IBAction to create and view the page object. Like this:
if (!page1) {
PageController *page1 = [[Page
if (!page1) {
page1 = [[PageControoer alloc] initWithWindowNibName:#"page"];
}
[page1 showWindow:sender];
So the new window pops and we can press buttons, etc. The code for the new window is all in PageController.h and .m. and things basically work.
That is the context, here is where I'm confused.
a) question: let's say I want to access that original ivar in AppDelegate.h called counter from PageController. Either retrieving or updating the variable. What approach would I take?
b) confirm: let's say I'm back in the AppDelegate and want to get access to a selector from page1. I believe I can do this as so: [page1 runaction]; or [[page1 variable] setStringValue:#"hello"];
(this complies but I'm not sure it really works because I can't get the changes into the xib view.)
ok and the stumper. Say another view was created with another view controller call it Page2Controller.h and .m.
c) how should data flow between page and page2 -> via the AppDelegate or directly? what would the syntax look like to connect them together?
I've been following tutorials, but they don't really cover this back and forth messaging. Thanks for all the help!
a) Generally, if you want to have data that is accessed by your controllers, it should be in a model which they are given access to in some way. You can access things in the app delegate using this method:
AppDelegate* appDelegate = [[NSApplication sharedApplication] delegate];
[appDelegate <some method>];
b) I don't understand what you're asking. If the app delegate has a pointer to page1, then yes, you can call it directly.
c) Again, you should have a data model of some sort. The controllers should update the data model when the user changes the view. You can use notifications, IBActions, and direct calls to do the updating, for example. You should look up the Model, View, Controller design pattern.

Why won't my WebView display the URL I've specified?

I'm working with a WebView. If I place this call in my -awakeFromNib:
[myWebView setMainFrameURL:#"http://www.google.com"];
I get the behavior I want. Google's homepage displays in my WebView. But, I need to set the main frame's URL in another method from a mutable array containing NSStrings.
This method call
[myWebView setMainFrameURL:[mutableArrayContainingNSStrings objectAtIndex:0]];
does not update the WebView. Do I need to tell the WebView to reload? I tried -setNeedsDisplay:YES and -reload to no avail.
I figured out the problem. The WebView is an IBOutlet. I was doing the method call that was giving me problems before -awakeFromNib. Moved that one line to -awakeFromNib and it's sorted now. Thanks for the input. +1 to my list of newbie posts.
Have you tried setting a breakpoint on your action method to check that both myWebView and [mutableArrayContainingNSStrings objectAtIndex:0] are pointing to the correct objects?
Several things
(1) I second Marc's comment in making sure that it is pointing at the correct objects
(2) It's been a while since I've dealt with WebKit directly but I know that it's possible you may have strings like "www.google.com" and strings like that won't work, you need "http://www.google.com" which you have in your first call to myWebView. Make sure all of your URL Strings are like this. It's the browser (Safari/Chrome/etc) that expands www.google.com out into http://www.google.com so that you can actually visit the site
If none of that works show the strings here so I can verify and run tests to see what's going on with the WebView

Resources