OSX Swift webview load local file - macos

I know this has been asked many times and I have read quite a few questions
and googled for days with no success so far.
I just want to load a local html file in a desktop app, truth is for this project I need a JS library and most of it is already done as a webpage (css, js and html, no server side processing needed). I dont want to force the app to load the webpage from an internet server so as to not force the users to have an internet connection. Needless to say I am completely inexperienced in Swift and apple development.
Right now this is the problem I am having:
the ide complaints about the params and I cant seem to get them right.
For reference here is a snippet of my latest code:
class AppDelegate: NSObject, NSApplicationDelegate
{
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var webView: WebView!
typealias NSSize = CGSize
func applicationDidFinishLaunching(aNotification: NSNotification?)
{
self.window.title = "Chess Study Room"
var try1 = "main.html";
println(try1);
var try2 = NSURL(string: try1)!;
println(try2);
var try3 =
NSBundle.URLForResource("main", withExtension: "html", subdirectory: "web", inBundleWithURL: try2);
println(try3);
var try4 = NSBundle.pathForResource("main", ofType: "html", inDirectory: "web");
println(try4);
var try5 = NSString.stringByAppendingPathComponent("main.html");
println(try5);
// var myInternalUrl = NSURL(string: myInternalHtml)!;
//NSLog("%s", myInternalHtml!);
var request = NSURLRequest(try1,
NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData,
60
);
self.webView.frameLoadDelegate = self;
self.webView.mainFrame.loadRequest(
request!
);
}
But as you can see from my gif I've tried other things as well. The full errors are /path/TestWebView/AppDelegate.swift:32:35: Extra argument in call and /path/TestWebView/AppDelegate.swift:32:36: Missing argument for parameter 'cachePolicy' in call
At this point try1 and try2 output "main.html", try3 and try4 output nil and try5 outputs"(Function)"
The structure of folders is this:
I added the folder "web" as a reference (as advised in another question) but I doubt this would work to ship just one package...
I dont know if there's any difference but I'm not targetting iOS, I want this to be a desktop app.
Any advise will be greatly appreciated

After reading this post in the apple forums and putting it together with this other question I was able to come up with the following working code:
func applicationDidFinishLaunching(aNotification: NSNotification?)
{
self.window.title = "My App Title"
var try6 = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("main", ofType:"html")!)
var request = NSURLRequest(URL: try6!);
self.webView.frameLoadDelegate = self;
self.webView.mainFrame.loadRequest(request);
}

Related

PFLoginViewController´s Twitter, Facebook buttons not working when subclassing

When I subclass PFLoginViewController from Parse so that I can customise UI, both Twitter and Facebook buttons stop working as they used to if I were not subclassing.
This is the class I instantiate my subclass from:
class SettingsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate, MFMailComposeViewControllerDelegate
{
#IBOutlet weak var loadingIndicator: UIActivityIndicatorView!
#IBOutlet weak var tableView: UITableView!
// THIS DOES WORK (without subclassing)
//var loginVC: PFLogInViewController = PFLogInViewController()
var loginVC:LogInViewController {
get{
let livc = LogInViewController()
livc.delegate = self
let suvc = SignUpViewController()
suvc.delegate = self
livc.signUpController? = suvc
return livc
}}
And this is my subclass of PFLogInViewController
class LogInViewController: PFLogInViewController {
static let forgotPassword = "Trouble Signing In?"
override func viewDidLoad() {
super.viewDidLoad()
let lv = self.logInView
self.facebookPermissions = ["public_profile", "email"]
self.fields = [PFLogInFields.UsernameAndPassword, .Facebook, .Twitter, .SignUpButton, .LogInButton, .PasswordForgotten, .DismissButton]
lv?.logo = UIImageView(image: UIImage(named: "myImage"))
lv?.logo?.contentMode = UIViewContentMode.ScaleAspectFit
lv?.emailAsUsername = true
self.view.backgroundColor = UIColor.whiteColor()
}
It might be a bug in Parse´s side, though. Any help would be appreciated!
This is a known issue - it looks like this bug was patched in the GitHub repo (see this commit) but not in the Cocoapod, if that's what you're using. I'm hoping they'll push the changes over soon :)
The reason for the bug in the old code is that the superclass is not updating the target/actions for the buttons when you set which fields you want in viewDidLoad. PFLoginViewController configures the target/actions in its viewDidLoad, but since you're setting the self.fields after calling super.viewDidLoad (which is what you should be doing, since you always call super first :) ), the field target/actions aren't re-updated. In other words, it looks like, the way PFLogInViewController was written, it only works if the fields don't change after its viewDidLoad is called :/. And since the default fields don't include a Facebook button, the Facebook button will never be set up. A simple solution is just to move self.fields =... before super.viewDidLoad().

This application is modifying the autolayout engine from a background thread swift2.0

I am using this simple code to extract some plain text from a website.
#IBAction func askWeather(sender: AnyObject) {
let url = NSURL(string: "http://www.weather-forecast.com/locations/" + userField.text! + "/forecasts/latest")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) -> Void in
if let urlContent = data{
let webContent = NSString(data: urlContent, encoding: NSUTF8StringEncoding)
let wArray = webContent?.componentsSeparatedByString("Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
let wCont = wArray![1].componentsSeparatedByString("</span>")
self.weatherResult.text = wCont[0]
}
else{
print("Sorry could not get weather information")
}
}
task.resume()
}
#IBOutlet var weatherResult: UILabel!
#IBOutlet var userField: UITextField!
And after i press the button to fetch the information nothing happens for several seconds(like 10-20) and then i get the correct result however i get this message in xcode:
This application is modifying the autolayout engine from a background
thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
I tried reading some posts on others having this problem but they were using threads like async etc. to run their code. Im not really sure what the problem is in my case.
Thank you!
I'm guessing that self.weatherResult.text = wCont[0] is modifying something like a UILabel or similar, in which case you're trying to change part of your user interface from a background thread – a big no-no.
Try code like this instead:
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
self.weatherResult.text = wCont[0]
}
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) -> Void in
There's your asynchronous thread. Right there. dataTaskWithURL runs in the background and will eventually call the callback function that you passed in. And that is done in the background.

Simple Swift Cocoa app with WebKit: Upload picture doesn't work

I decided to make my own FB chat app that simply shows https://messenger.com on a WebView after trying other 'freemium' apps.
My ViewController.swift has just a few lines of code that loads URL on the web view
import Cocoa
import WebKit
class ViewController: NSViewController {
#IBOutlet weak var webView: WebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "https://messenger.com")
let request = NSURLRequest(URL: url!);
webView.mainFrame.loadRequest(request);
}
override var representedObject: AnyObject? {
didSet {
// do nothing
}
}
}
Besides adding NSAppTransportSecurity key to info.plist to unblock HTTP traffic via HTTPS connection, I have not done any other settings.
Question
Please take a look at this image first.
Everything looks fine & working except two things.
Uploading image does not work - I labeled as 1 in the picture.
normally (as in other released apps or from web browsers) if you click that icon, it shows an explorer to upload a picture like below.
My app completely ignores user's click on that icon so I cannot upload any pictures to the chat. Interestingly, if I drag and drops the picture to the webview, it uploads fine.
Shared picture does not show up - I labeled as 2 in the picture.
again, from other browsers or released apps, it shows the pictures that I shared with participants like below. (of course I censored the pictures)
my app tries to load the pics, but does not display them. I can see it trying to load because I see circular progress indicator while loading.
Why?
I suspect that there might be a way to listen to the JavaScript that's triggered within the WebView and link to a file explorer or something like that?
This I have no idea. I'm logged into Messenger (basically Facebook), so I think session is not a problem here. Maybe some jQuery loading issue??
What should I do to solve these issues?
There is indeed a delegate method to open a new panel called runOpenPanelForFileButtonWithResultListener, documentation here.
In the delegate method, just create a new NSOpenPanel like this:
func webView(sender: WebView!, runOpenPanelForFileButtonWithResultListener resultListener: WebOpenPanelResultListener!, allowMultipleFiles: Bool) {
let openDialog = NSOpenPanel()
if (openDialog.runModal() == NSOKButton) {
let fileName: String = (openDialog.URL?.path)!
resultListener.chooseFilename(fileName) // Use chooseFilenames for multiple files
}
}
I just tried to create a WebView from Messagers App and images are loading well.
You should try to enable WebView options like "Autoload Images" or "Enable Animated Images" from interface builder (or by code).
This code works for me and what is nice here is that you make the download of image asynchronous. You can find more about this concept here: http://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1 and: http://www.raywenderlich.com/79150/grand-central-dispatch-tutorial-swift-part-2
Edited
I. You have to create new Class or a new iOS Swift File named ImageLoader with this content:
class ImageLoader {
var cache = NSCache()
class var sharedLoader : ImageLoader {
struct Static {
static let instance : ImageLoader = ImageLoader()
}
return Static.instance
}
func imageForUrl(urlString: String, completionHandler:(image: UIImage?, url: String) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {()in
var data: NSData? = self.cache.objectForKey(urlString) as? NSData
if let goodData = data {
let image = UIImage(data: goodData)
dispatch_async(dispatch_get_main_queue(), {() in
completionHandler(image: image, url: urlString)
})
return
}
var downloadTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlString)!, completionHandler: {(data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if (error != nil) {
completionHandler(image: nil, url: urlString)
return
}
if data != nil {
let image = UIImage(data: data)
self.cache.setObject(data, forKey: urlString)
dispatch_async(dispatch_get_main_queue(), {() in
completionHandler(image: image, url: urlString)
})
return
}
})
downloadTask.resume()
})
}
}
II. In your actual viewController you call the method 'imageForUrl' from ImageLoaded following this lines of code:
ImageLoader.sharedLoader.imageForUrl("http://upload.wikimedia.org/wikipedia/en/4/43/Apple_Swift_Logo.png", completionHandler:{(image: UIImage?, url: String) in
self.myImage.image = image!
})
I took the code from this link: https://teamtreehouse.com/community/does-anyone-know-how-to-show-an-image-from-url-with-swift
Edited for image loaded on webview
Here is the code. It works perfect for me:
override func viewDidLoad() {
super.viewDidLoad()
let myWebView:UIWebView = UIWebView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height))
myWebView.loadRequest(NSURLRequest(URL: NSURL(string: "https://scontent-vie1-1.xx.fbcdn.net/hphotos-xap1/v/t1.0-9/12144725_10204647881668565_4367944825116750386_n.jpg?oh=5ecdae91f5258ffe0e0355e176f8eb8a&oe=56B007CA")!))
self.view.addSubview(myWebView)
}

Cannot invoke initializer for type 'AVPlayer'

I'm having an issue getting my video to play via AVPlayer. For some reason I'm getting the error 'Cannot invoke initializer for type 'AVPlayer' with an argument list of type '(URL:NSURL)'
The weird thing is, this exact code snippet works in another one of my projects. I do have the AVKit and AVFoundation frameworks imported.
Well, I realized my dumb mistake. I had a local variable (player), so it died instantly before it could even play. I moved it up a level to be an instance variable so it persists as long as the view controller persists.
var MoviePlaying:Bool = false
let path = NSBundle.mainBundle().pathForResource("big_buck_bunny_720p_50mb", ofType:"mp4")
var player: AVPlayer = AVPlayer()
let playerController = AVPlayerViewController()
#IBAction func PlayTapped(sender: UIButton) {
let moviePlayerController = AVPlayerViewController()
let path = NSBundle.mainBundle().pathForResource("big_buck_bunny_720p_50mb", ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
let playerVC = AVPlayerViewController()
var player: AVPlayer = AVPlayer()
playerVC.player = AVPlayer(URL: url)
self.presentViewController(playerVC, animated: true, completion: nil)

(NSMenuItem): missing setter or instance variable

I am encountering a strange error:
2015-04-02 12:20:14.642 test[21167:257788] Failed to connect
(testApp) outlet from (test.AppDelegate) to (NSMenuItem): missing
setter or instance variable
inserted id: 122
I occured when a added a menuItem to a menu and connected a function to it.
I do not know what the Problem is. The app works fine but i don't think it is a smart idea to ignore the error.
What is meant by setter or instance variable? Why is it needed?
UPDATE: Here is the relevant code:
import Cocoa
import Foundation
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var statusMenu: NSMenu!
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
func applicationDidFinishLaunching(aNotification: NSNotification) {
let icon = NSImage(named: "statusIcon")
statusItem.image = icon
statusItem.menu = statusMenu
// Time for constant repeat
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "timerRepeat", userInfo: nil, repeats: true)
}
// Method to call the tracking core
func timerRepeat() {
//....
}
#IBAction func frontEnd(sender: NSMenuItem) {
var targetURL : String = NSBundle.mainBundle().resourcePath!
targetURL = targetURL + "/" + "front.app"
let workspace = NSWorkspace()
workspace.launchApplication(targetURL)
}
#IBAction func menuClicked(sender: NSMenuItem) {
NSApplication.sharedApplication().terminate(self)
}
}
You have a broken outlet in your xib file. Usually it happens when you set up an outlet to ivar or property which is later deleted or renamed not using Xcode's rename feature.
Also make sure that your custom view or view controller class is added to your target. (Project => Target Name => Build Phases => Compile Sources). It's possible that a file is in your project but not your target.
This happens because you at one point created an #IBOutlet for a storyboard element. You then later removed the code (reference) from your swift file.
I created an example where I create two extra #IBOutlets (I named them 'correctField' and 'incorrectField'- both are incorrect though) and connected them from my storyboard to my swift file.
I then removed the code from my swift file. This generates the log as shown in the following figure :
To remove this kind of log message, you can do the following:
Go to the 'storyboard' and select the storyboard elements you created connections (#IBOutlets) from.
Open the 'connection inspector' as showed in the figure below
Remove the Referencing Outlets which are incorrect (in my case it is the 'correctField' and 'incorrectField')
Done
This was done in xCode 11

Resources