I am having the following situation.
I am using a WebkitView to display some session information. In most places it's generated when the ViewController comes to life, but I have one particular view in which I have to reload it on user interactions with buttons that change to next or previous month.
I have the following code:
// This is my connection to the storyboard element
#IBOutlet weak var areaNotasMes: WKWebView!
After that I have a function that does the URL handling:
func actualizarUrlNotasMes(fechaNueva: Date) {
let formateadorAdicional = DateFormatter()
formateadorAdicional.dateFormat = "yyyyMM"
let url = URL(string: "https://myurl/\(formateadorAdicional.string(from: fechaNueva))")
areaNotasMes.load(URLRequest(url: url!))
}
And I have a button event that uses the function with the next YEAR_MONTH combination like this:
#IBAction func mesSigPressed(_ sender: UIButton) {
// Some more code to get the fechaActual variable
actualizarUrlNotasMes(fechaNueva: fechaActual)
// Some more code
}
So, basically what's happening is that it loads the first time I open the ViewController, but when I click on the button to change to the next month, the date gets changed, correctly generated and passed to the function. It generates the correct URL, but when the areaNotasMes.load(...) function runs, nothing refreshes on the WebKitView. I tried adding areaNotasMes.reload(), but it also did nothing. Do I have to like destroy and recreate the WebKitView each time I change the URL? Or am I not handling the URL change correctly?
Also, the console pops up some of these messages from time to time, not always:
[BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C5.1:2][0x7fea2d201de0] get output frames failed, state 8196
TIC Read Status [5:0x0]: 1:57
No you don't need to re-initialize the webview when changing the URL. I can't tell the issue with the information you provided. I would advise that you set the ViewController as navigationDelegate of the webview and implement these method to try to diagnose the issue :
func webView(WKWebView, didStartProvisionalNavigation: WKNavigation!)
func webView(WKWebView, didFail: WKNavigation!, withError: Error)
//Called when an error occurs during navigation.
func webView(WKWebView, didFailProvisionalNavigation: WKNavigation!, withError: Error)
Documentation here
I think I actually managed to fix this in the most random way. I thought maybe there's some reload function (except for .reload()) so using the auto completion of syntax, I came up with the following command:
areaNotasMes.reloadInputViews()
Apparently .reloadInputViews() is a void function inherited from UIView which updates the custom input and accessory views when the object is the first responder. You can check it here: reloadInputViews()
It does send some Error Domain=NSURLErrorDomain Code=-999 "(null)" although it actually works as supposed and refreshes the WebKitView information.
I hope this helps someone else.
Related
Update: this problem is more focused now, and not on quite the same topic. I've asked this question as a follow-on
ORIGINAL QUESTION:
I am getting a crash on a subclassed WKWebView-provisioned app.
ProcessAssertion::acquireSync Failed to acquire RBS assertion 'ConnectionTerminationWatchdog' for process with PID=87121, error: Error Domain=RBSServiceErrorDomain Code=1 "target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit" UserInfo={NSLocalizedFailureReason=target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit}
The problem is, that I can't tell if this is related or not. The actual error on crash is
Thread 1: EXC_BAD_ACCESS (code=1, address=0xbdb2dfcf0470)
Which I was assuming was something running off the end of an array. This makes some sense: I'm selecting from a table that filters out some entries from the data source; but I've checked that carefully; there is no point when a row index greater than the actual rows is accessed (and yes, I'm accounting for the difference between count and index).
The main change here is that I previously had a UIView that acted as a container for a number of CAShapeLayers. I also wanted to overlay text view, but with the proviso that this be via a WKWebView. With two separate views, I would have to either have the CAShapeLayer objects in front of, or behind the WebView. I was seeking a fix to that.
What I have done is substitute a WKWebView for the original UIView. I can add the CAShapes to it, so it performs the original function. It also can, presumably, display the html. And the original suggestion in this answer to a question I asked is what I am working towards. The idea being that it would allow the effect sought, with shapes in front of or behind the html elements.
But the error is thrown after the DidSelect call on the table:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
Tracker.track("getting row \(indexPath.row)")
let ptv = tableView as? NovilloTableView
if ptv!.uiType == .textTable {
let gp = Projects.currentProject?.getPaths(type: PaletteView.getCurrentPane())
GitPaths.currentGitPath = gp![indexPath.row]
NotificationCenter.default.post(name: NNames.updateWebText.nn(), object: nil)
return
}
let svgs = Projects.currentProject!.getPaths(type : PaletteView.getCurrentPane())
var gitPath = svgs[indexPath.row]
var gitPaths = GitPaths.getMediaBoundingBoxes(paths: [gitPath])
guard let pathArrays = gitPath.parseForRegBeziers() else { return }
let rslt = pathArrays.0
let regBeziers = pathArrays.1
gitPath.boundingBox = gitPath.getBoundsParamsForPaths(src: regBeziers.isEmpty ? rslt : regBeziers)
GitPaths.currentGitPath = gitPath
// Tracker.track("sending notification")
NotificationCenter.default.post(name: NNames.updateMedia.nn(), object: nil,
userInfo: ["path" : gitPath])
Tracker.track("completed didSelect")
return
}
In other words, the interaction has ended; but I get this crash, even though I can se that the expected result (the rendering of the shapes into the view) has been completed correctly. It seems to be happening right after everything has completed, and no amount of breakpoints has shown anything else to be happening.
This is confusing to me, and I have hit a limit on what I know how to do to dig further into this. Given the nature of web connections, I've wondered if it's some asynchronous issue that I can't debug sequentially; but that's guesswork without any direct evidence.
I suspect that there is a possible configuration problem with the WebView that shows up once I interact with it, by changing its contents. I'm not actually trying to get it to load anything when it crashes, it's only performing its original function as a container for the CAShapeLayers, so I'm confused.
The main view that contains the WKWebView (which is subclassed, to support a function to determine if it should display the web content, and which I've commented out), is set as the delegate for the WKWebView, and that seems to be fine, though there are no actual protocol functions added to that view, not sure if that matters.
The other detail is that the WebView when it does load web content is only loading local text, and not connected to any services. This testing is happening in the Simulator, and I've come across advice elsewhere to allow for background processes that include enabling Background Fetch, etc., but this has done nothing to change the situation...
EDIT: this is the extent of the configuration of the subclassed WKWebView: maybe this is the issue?
mediaDisplay = NovilloWebView()
mediaPane.addSubview(mediaDisplay)
mediaDisplay.navigationDelegate = self
mediaDisplay.uiDelegate = self
mediaDisplay.backgroundColor = .clear
I have what I think should be a very simple operation. I just want to have an entry in a Firebase DB changed when I press a button. I already checked that there's nothing wrong with the button.
Here is my FireBase entry, by the way:
This code works fine in displaying text to the console (so, no problem with the button or connection, or variable, for that matter):
#IBAction func test(_ sender: Any) {
print("Username: \(requestUsername)")
}
My simple IBAction function gets a SIGABRT every time. I tried to look at other S.O. questions on the best way to update values, and to try to follow their suggestions, so I'm not sure if my firebase code is messed up, or something else. Right now, "ref" is declared inside the IBAction, but I've also tried declaring it globally, with the same result.
#IBAction func test(_ sender: Any) {
var ref = FIRDatabase.database().reference()
ref.child("Sell_Request").child(requestUsername).child("Request_Made").setValue("true")
}
Update:
For what it's worth, I'm able to get the correct Firebase entry with this code. I'm still not sure how to update specific fields. If anyone has any ideas, I would appreciate it.
ref.child("Sell_Request").queryOrdered(byChild: "name").queryEqual(toValue: requestUsername).observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot)
})
}
Here a simple issue I am facing while starting to experiment with WatchKit and complications.
I created a simple app which is showing a complication with a public string “Y” and by clicking it, the Apple Watch app is shown with a simple switch.
I wrote my code in getPlaceholderTemplateForComplication in ComplicationController.swift and added a switch IBAction in InterfaceController.swift.
By changing the value of the switch, the public string cycles between “N” and “Y”. I would like to have it changed in the complication as well. However I am noticing that the complication stays as it is initially at “Y”.
I found a similar question about forcing complication updates, but it was related to a TimeLine complication func and not placeHolder.
func updateComplication() {
let complicationServer = CLKComplicationServer.sharedInstance()
for complication in complicationServer.activeComplications {
complicationServer.reloadTimelineForComplication(complication)
}
}
It is not clear to me on where and how to use this in my case.
As suggested I worked on getCurrentTimelineEntryForComplication .
In order to test a ModularComplication only, I used:
switch complication.family {
case .ModularSmall:
let modularSmallTemplate =
CLKComplicationTemplateModularSmallRingText()
modularSmallTemplate.textProvider =
CLKSimpleTextProvider(text: stringa)
modularSmallTemplate.fillFraction = 0.95
modularSmallTemplate.ringStyle = CLKComplicationRingStyle.Closed
let template = CLKComplicationTimelineEntry(
date: NSDate(), complicationTemplate: modularSmallTemplate)
handler(template)
default:
handler(nil)
}
I have the switch IBAction in InterfaceController.swift.
I am encountering issues in using:
func updateComplication() {
let complicationServer = CLKComplicationServer.sharedInstance()
for complication in complicationServer.activeComplications {
complicationServer.reloadTimelineForComplication(complication)
}
}
Where do I have to write the above mentioned func, in order to be able to call it from inside the IBAction?
If I write it in ComplicationController.swift, by calling it from the IBAction in InterfaceController.swift as:
ComplicationController.updateComplication()
I get the error “Missing argument for parameter #1 in call”,
while if I write it in InterfaceController and call it in the IBAction, although the build is successful, when running the App and changing the value of the switch I get the following error: “fatal error: unexpectedly found nil while unwrapping an Optional value” on the line:
for complication in complicationServer.activeComplications
of func updateComplication.
It's not the placeholder text that you want to update. The placeholder template is a static template that's displayed in the selection screen for your complication while you are customizing your watch face. It's only called once to get the placeholder text, and doesn't get called when a timeline is reloaded.
The complication's current (actual, live) timeline entry is provided by getCurrentTimelineEntryForComplication. This function is where you should use your model's on state of the switch to create a CLKTextProvider containing a "N" or "Y" for the active complication family.
Finally, you should call updateComplication() from your switch IBAction to reload your complication timeline, which would update the current entry to show the new state of the switch on the watch face.
Since your complication is updated manually, you should return a nil updateDate in getNextRequestedUpdateDateWithHandler to avoid scheduling your complication for any regular updates.
Update for your edited question:
You should include the updateComplication function in your interface controller, as your complication controller isn't meant to be instantiated by you.
In regard to the "Unexpectedly found nil while unwrapping an Optional value" error, this is an issue that has been mentioned on the Apple Developer Forums.
If you are already running watchOS 2.1, you could try the latest watchOS beta to see if the issue has been fixed yet for 2.2. You should also file a bug report and dupe radar 22947535.
I have a Swift application that has data I want to save using the methods described in this question. Now, I need to know what is the proper way to link these actions to the File -> Save/Save As menu item and the File -> Open menu item. This isn't a document-based application.
I'm running Xcode 6.4 on OS X 10.10.4.
Create an IBAction function and link it to the XIB via Interface Builder.
Create an open/save panel in that function and let the user select the file name and location, use the returned NSURL array for saving/loading path. (after converted to required object type, of course.)
There are lots of example codes almost everywhere, either Objective-C or Swift.
In Swift 3, it may seem odd because it's using 'First Responder' but all you have to do is add the following code to your NSViewController class that is set as the Custom Class on a storyboard. It does not have to be connected like other #IBAction functions.
class Test: NSViewController {
#IBAction func saveDocument(_ sender: Any?) {
// code to execute for save functionality
// following line prints in debug to show function is executing.
// delete print line below when testing is completed.
Print("save")
}
#IBAction func openDocument(_ sender: Any?) {
// code to execute for open functionality here
// following line prints in debug to show function is executing.
// delete print line below when testing is completed.
print("open")
}
}
I am trying to use swift's shouldPerformSegueWithIdentifier() method, but it accepts 2 arguments. These are (identifier: String!, sender:AnyObject)
My main goal is to execute the code when pressing a login button in my storyboard, and depending of a series of checks return TRUE or FALSE, depending if whether the correct username and password were provided. So here are my questions:
What am I supposed to use as the identifier? Apple's documentation it explains that the identifier is a string that identifies the triggered segue. So suppose that my segue had the name of loginSegueProcess. How could I use it in my ViewController tat is assigned to my UIView? The thing is that I declare the method in my code and it requires me to specify both arguments (identifier & sender). How could I provide the arguments?
Will this method actually fulfill my needs? By that I mean if it will indeed stop the segue transition whenever my Login button is clicked and depending on whether the correct credentials were provided it is going to take you to the next View or it will show, say for example, an AlertView.
Finally, I was thinking that the performSegueWithIdentifier(args) method would help me as well. Does anybody know the difference between them?
Thanks a lot in advance!
isn't it what you want to do?
override func shouldPerformSegueWithIdentifier(identifier: String!, sender: AnyObject!) -> Bool {
if identifier == "LoginSuccessSegue" { // you define it in the storyboard (click on the segue, then Attributes' inspector > Identifier
var segueShouldOccur = /** do whatever you need to set this var to true or false */
if !segueShouldOccur {
println("*** NOPE, segue wont occur")
return false
}
else {
println("*** YEP, segue will occur")
}
}
// by default, transition
return true
}
You may not invoke shouldPerformSegueWithIdentifier() method by yourself. It will be automatically called just before transition to the next view giving a chance to determine wether the transition should take place or. You may conditionally return YES/NO from this method. If your condition does't involve any sever call,a simple logical checking this method will be enough for you.
performSegueWithIdentifier() is used to invoke a segue programmatically. Consider the above case with a network call, you may return NO from shouldPerformSegueWithIdentifier() initially since authentication is going on. After getting the response from server if it success you can call the segue to execute with performSegueWithIdentifier (Here the identifier is the ID you have given in the storyboard). Before make sure you are supposed to return YES from shouldPerformSegueWithIdentifier().
Now a third case if your segue is connecting from the login button(You have to connect it from the controller itself). The checking of shouldPerformSegueWithIdentifier is no more required. You can just call the segue with performSegueWithIdentifier() after getting the success response from server