I'm trying to implement WKWebView on Cocoa/MacOS but I'm getting this error:
Unable to load Info.plist exceptions (eGPUOverrides)
Couldn't read values in CFPrefsPlistSource<0x600002909f10>
(Domain: com.apple.Accessibility, User: kCFPreferencesCurrentUser,
ByHost: No, Container: kCFPreferencesNoContainer, Contents Need Refresh: Yes): accessing preferences outside an application's container requires user-preference-read or file-read-data sandbox access
Here is my implementation:
import Cocoa
import WebKit
class ViewController: NSViewController {
#IBOutlet var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
if let url = URL(string: "https://www.apple.com") {
let request = URLRequest(url: url)
webView.load(request)
}
}
}
My question is how can I fix this issue on my implementation?
Related
I apologize for not providing it with a code. The reason I posted it with images because xcode is LLVM(low level virtual machine) compiler which has mostly UI environment especially for the configuration part. Such as creating either an outlet or action for object by dragging them to the view controller instead defining it by a code directly. Thus since it was so, I think people would easily notice my mistake faster.
import Cocoa
import MapKit
class ViewController: NSViewController, CLLocationManagerDelegate {
#IBOutlet var mapView: MKMapView!
var locManager = CLLocationManager()
var currentLocation = CLLocation()
var locationManager = CLLocationManager()
var didFindMyLocation = false
var strForCurLatitude = "";
var strForCurLongitude = "";
override func viewDidLoad() {
super.viewDidLoad()
let distancespan:CLLocationDegrees = 2000
let busCScampuslocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(currentLocation.coordinate.latitude, currentLocation.coordinate.longitude)
mapView.setRegion(MKCoordinateRegion.init(center: bsuCScampuslocation, latitudinalMeters: distancespan, longitudinalMeters: distancespan), animated: true)
print(currentLocation.coordinate.latitude)
}
...
}
Swift 5:
ViewController.swift
import Cocoa
import CoreLocation
import MapKit
class ViewController: NSViewController, CLLocationManagerDelegate {
let manager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
//This is where you can update the MapView when the computer is moved (locations.last!.coordinate)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
print("location manager auth status changed to: " )
switch status {
case .restricted:
print("restricted")
case .denied:
print("denied")
case .authorized:
print("authorized")
case .notDetermined:
print("not yet determined")
default:
print("Unknown")
}
}
Add these lines to Info.plist or set the NSLocationAlwaysAndWhenInUseUsageDescription and/(or?) NSLocationUsageDescription to a plaintext description of why you need to access the users location
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Allows us to locate you</string>
<key>NSLocationUsageDescription</key>
<string>Allows us to locate you</string>
If the user has Location Services off, the authorization status will be 'denied'
I am currently trying to deploy a responsive website on mobile devices (iPhone & iPad) via a WebView.
Here are some details regarding the app :
the application's only purpose is to display the webView containing the website
using the WKWebView component since UIWebView is deprecated in my environment
any external links should load a new page within the webView
currently using Swift 3 and Xcode 9.2
made an Outlet between the WebKit WebView and the ViewController
edited the info.plist ; App Transport Security Setting > set NSAllowArbitraryLoads to true
source code :
ViewController.swift
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
// edit the url the app should load here
private var domain = "https://example.com/"
#IBOutlet weak var webView: WKWebView!
override func loadView() {
//delegates the navigation of other urls while still inside the webview
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self
webView.allowsLinkPreview = false
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// calls the website in a 'iframe' style
let url = URL(string: domain)
let request = URLRequest(url: url!)
webView.load(request)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping ((WKNavigationActionPolicy) -> Void)) {
// outputs are for debugging purpose
print("webView :\(webView)" + "\n")
print("decidePolicyForNavigationAction :\(navigationAction)" + "\n")
print("decisionHandler :\(decisionHandler)" + "\n")
switch navigationAction.navigationType {
case .linkActivated:
print("request : " + navigationAction.request.url!.absoluteString)
self.webView?.load(navigationAction.request)
return
default:
break
}
if let url = navigationAction.request.url {
print("url : " + url.absoluteString)
}
decisionHandler(.allow)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
AppDelegate.swift :
import UIKit
import WebKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
}
My trouble comes when trying to open external and internal links ; the app will throw a signal SIGABRT.
From what I understand, this error usually happens when there is an Outlet mismatch, but my project has only a single Outlet (WebView / ViewController). I tried deleting it and re-creating it to no avail.
If the issue is that no Outlet are made between the page loaded from an external link and my app I fail to see how to solve this since I have no control over the website.
Note : when I set webView.allowsLinkPreview to true (in func loadView()), the app loads the new page but takes a considerable amount of time. Also the function webView isn't called a second time when this happens.
Any help will be much appreciated.
Thank you.
Edit. as requested in a comment, here is what the debugger throws :
libc++abi.dylib: terminating with uncaught exception of type NSException (lldb)
Also, the Thread 1 SIGABRT signal appears to be in the AppDelegate class.
The interesting error message should be
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Completion handler passed to -[myAppName webView:decidePolicyForNavigationAction:decisionHandler:] was not called'
In webView(_ webView:, decidePolicyFor navigationAction:, decisionHandler:) in one particular case you don't do decisionHandler(someValue), that's why it crashes.
So in
case .linkActivated:
print("request : " + navigationAction.request.url!.absoluteString)
self.webView?.load(navigationAction.request)
return
You need to do decisionHandler(someValue) before you do the return.
I have gone through many questoions but none of them snaswers my query.
I am trying to load initial window programmatically
Here is what I have done.
I have added main.swift as-
import Cocoa
private func runApplication(
application: NSApplication = NSApplication.sharedApplication(),
delegate: NSApplicationDelegate? = AppDelegate(),
bundle: NSBundle = NSBundle.mainBundle(),
nibName: String = "MainMenu",
var topLevelObjects: NSArray? = nil) {
setApplicationDelegate(application, delegate)
}
private func setApplicationDelegate(application: NSApplication, delegate: NSApplicationDelegate?) -> NSApplication {
if let delegate = delegate {
application.delegate = delegate
}
return application
}
runApplication()
Appdelegate.swift is-
import Cocoa
//#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var appControl:AppFlow?
func applicationDidFinishLaunching(aNotification: NSNotification) {
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
override init() {
//
self.appControl = AppFlow()
super.init()
}
}
And in AppFlow I am trying to load window controller from storyboard.-
import Cocoa
class AppFlow{
let initialStoryBoard:NSStoryboard?
override init() {
self.initialStoryBoard = NSStoryboard(name: "Main" , bundle : nil)
super.init()
var windowController = (self.initialStoryBoard?.instantiateControllerWithIdentifier("mainWindow")) as! NSWindowController
windowController.window?.makeKeyAndOrderFront(nil)
}
}
But I am not able to launch initial window controller and view controller. App starts and terminates automatically, no window is presented to user.
What I am doing wrong? Thanks for your help.
Here is what I did in order to load initial window from storyboard (as well as MainMenu) programmatically without attribute #NSApplicationMain and function NSApplicationMain(_, _)
File: AppConfig.swift (Swift 4)
struct AppConfig {
static var applicationClass: NSApplication.Type {
guard let principalClassName = Bundle.main.infoDictionary?["NSPrincipalClass"] as? String else {
fatalError("Seems like `NSPrincipalClass` is missed in `Info.plist` file.")
}
guard let principalClass = NSClassFromString(principalClassName) as? NSApplication.Type else {
fatalError("Unable to create `NSApplication` class for `\(principalClassName)`")
}
return principalClass
}
static var mainStoryboard: NSStoryboard {
guard let mainStoryboardName = Bundle.main.infoDictionary?["NSMainStoryboardFile"] as? String else {
fatalError("Seems like `NSMainStoryboardFile` is missed in `Info.plist` file.")
}
let storyboard = NSStoryboard(name: NSStoryboard.Name(mainStoryboardName), bundle: Bundle.main)
return storyboard
}
static var mainMenu: NSNib {
guard let nib = NSNib(nibNamed: NSNib.Name("MainMenu"), bundle: Bundle.main) else {
fatalError("Resource `MainMenu.xib` is not found in the bundle `\(Bundle.main.bundlePath)`")
}
return nib
}
static var mainWindowController: NSWindowController {
guard let wc = mainStoryboard.instantiateInitialController() as? NSWindowController else {
fatalError("Initial controller is not `NSWindowController` in storyboard `\(mainStoryboard)`")
}
return wc
}
}
File main.swift (Swift 4)
// Making NSApplication instance from `NSPrincipalClass` defined in `Info.plist`
let app = AppConfig.applicationClass.shared
// Configuring application as a regular (appearing in Dock and possibly having UI)
app.setActivationPolicy(.regular)
// Loading application menu from `MainMenu.xib` file.
// This will also assign property `NSApplication.mainMenu`.
AppConfig.mainMenu.instantiate(withOwner: app, topLevelObjects: nil)
// Loading initial window controller from `NSMainStoryboardFile` defined in `Info.plist`.
// Initial window accessible via property NSWindowController.window
let windowController = AppConfig.mainWindowController
windowController.window?.makeKeyAndOrderFront(nil)
app.activate(ignoringOtherApps: true)
app.run()
Note regarding MainMenu.xib file:
Xcode application template creates storyboard with Application Scene which contains Main Menu. At the moment seems there is no way programmatically load Main Menu from Application Scene. But there is Xcode file template Main Menu, which creates MainMenu.xib file, which we can load programmatically.
This is not how you start (and maintain) an application's main run loop. See #NSApplicationMain. This causes the main run loop to be set up and run until it's terminated. There's no need for a main.swift file any longer, as you can just put this into your app delegate's file directly.
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
}
}
Xcode's new application project template does this for you.
I made a very simple Swift application that loads a webpage with links on it. Whenever I click the links, they do not open. How would I got about having the links on the loaded .html webpage open in a browser window for OS X?
Here is my implementation:
import Cocoa
import WebKit
class ViewController: NSViewController {
#IBOutlet weak var webView: WebView!
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "URL"
self.webView.mainFrame.loadRequest(NSURLRequest(URL: NSURL(string: urlString)!))
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
First, set your WebView's policy delegate and your initial URL as a class variable:
let url = NSURL(string: "http://www.google.com/")!
// ...
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.webView.policyDelegate = self
self.webView.mainFrame.loadRequest(NSURLRequest(URL: self.url))
}
Then, override the delegate methods to intercept navigation.
override func webView(webView: WebView!, decidePolicyForNewWindowAction actionInformation: [NSObject : AnyObject]!, request: NSURLRequest!, newFrameName frameName: String!, decisionListener listener: WebPolicyDecisionListener!) {
println(__LINE__) // the method is needed, the println is for debugging
}
override func webView(webView: WebView!, decidePolicyForNavigationAction actionInformation: [NSObject : AnyObject]!, request: NSURLRequest!, frame: WebFrame!, decisionListener listener: WebPolicyDecisionListener!) {
if request.URL!.absoluteString == self.url.absoluteString { // load the initial page
listener.use() // load the page in the app
} else { // all other links
NSWorkspace.sharedWorkspace().openURL(request.URL!) // take the user out of the app and into their default browser
}
}
Also you can decide what links to open in WebView and what - in browser as easy as to write target attribute in your html page like
external page
And use target check in the decidePolicyForNewWindowAction, menthioned above. I've placed the full answer in this question thread. Hope you can translate it to swift yourself.
I have had bad luck finding any examples on the web that closely match what I am trying to do. I am trying to using NSPageController to view and switch between multiple NSPageControllers. My steps.
I create a new OS X swift project
I add an object to the ViewController and make it of NSPageController class.
I add two buttons, one I label "Next" and the other one I label "Back" for the transitions.
I link the buttons to the NSPageController object as navigateForward and navigateBack actions.
I create an outlet in the custom NSViewController class for the NSPageController object and add the specific NSPageController delegate methods.
I add two additional view controllers in storyboard and create an identifier for them to reference back in my custom view controller class: Wizard1, Wizard2.
import Cocoa
class ViewController: NSViewController, NSPageControllerDelegate {
#IBOutlet var myPageController: NSPageController!
override func viewDidLoad() {
super.viewDidLoad()
let vc1: AnyObject? = self.storyboard!.instantiateControllerWithIdentifier("Wizard1")
let vc2: AnyObject? = self.storyboard!.instantiateControllerWithIdentifier("Wizard2")
self.myPageController.arrangedObjects.append(vc1!)
self.myPageController.arrangedObjects.append(vc2!)
// Do any additional setup after loading the view.
}
override init?(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
myPageController = NSPageController()
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil?)
}
required init?(coder aDecoder: NSCoder) {
myPageController = NSPageController()
super.init(coder:aDecoder)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func pageController(pageController: NSPageController, identifierForObject object: AnyObject!) -> String! {
return "View"
}
func pageController(pageController: NSPageController, viewControllerForIdentifier identifier: String!) -> NSViewController! {
let vc1: AnyObject? = self.storyboard!.instantiateControllerWithIdentifier("Wizard1")
return vc1 as NSViewController
}
func pageController(pageController: NSPageController, prepareViewController viewController: NSViewController!, withObject object: AnyObject!) {
viewController.representedObject = object
}
func pageControllerDidEndLiveTransition(pageController: NSPageController) {
pageController.completeTransition()
}
func pageControllerWillStartLiveTransition(pageController: NSPageController) {
self.presentViewControllerAsModalWindow(self.storyboard?.instantiateControllerWithIdentifier("Wizard2") as NSViewController)
}
}
The error I get when pressing the Next button is:
-[NSNib initWithNibNamed:bundle:] could not load the nibName: NSPageController in bundle (null).
Perhaps you are trying to load a nib with the wrong name in AppDelegate.m or wherever you are initializing your page controller.
Otherwise you have missed creating a .xib file and to name it NSPageController. When creating a Cocoa Touch Class there is a checkbox to also create an xib file for your class if needed.
This line is responsible for the error:
myPageController = NSPageController()
You're trying to initialize a view controller without a nib, that's why it does not work. By default the NSViewController's name is taken to identify the nib that corresponds to it. In your case it is "NSPageController".