Prevent open multiply tabs (Safari App Extension) - swift

I'm currently working on a Safari App Extension for specific site site (example.com for example) and configure toolbar item to show popover. But if safari have not tab with loaded site.com then this site must be loaded in new tab. Code in SafariExtensionHandler
override func popoverWillShow(in window: SFSafariWindow) {
if SafariExtensionHandler.thePage == nil {
let theURL = URL(string: "https://example.com")
window.openTab(with: theURL, makeActiveIfPossible: true)
}
}
static property thePage get its value when handler receive first message from script, but there need some time for page loading and message from script will be send. While the page is loading second click on extensions toolbar item open new tab with example.com, next click open next tab... Block new tab opening I tried like this
override func popoverWillShow(in window: SFSafariWindow) {
if SafariExtensionHandler.thePage == nil && loadingInProgress != true {
loadingInProgress = true
let theURL = URL(string: "https://example.com")
window.openTab(with: theURL, makeActiveIfPossible: true)
}
}
Because all tabs in Safari lives in sandbox opning new tab reset all property of SafariExtensionHandler to it default value.
How to prevent open multiply tabs.
Thank you.

Related

Black Screen while sharing on Facebook with UIActivityViewController?

Hi I am using UIActivityViewController to share content on social networks but facing one issue. The issue is when i select the share button the UIActivityViewController gets appear and I select the Facebook option.
I select the Facebook option in the sharing sheet -> Facebook sharing view appears with two options (Cancel and Next). When I select the Cancel button giving me another action sheet to discard or keep the post. When i select to discard the Action Sheet get disappear but the Facebook sharing view still keep showing and there is no way to go back.
Second when I press the next button in the Facebook sharing view -> another Facebook view gets appear with two buttons (Cancel and Share).... When I click the share button content get share on Facebook and black screen appears with a check in circle and text "Shared to Facebook!" .. And the application get stuck on this Screen.
Below is my code, Any help would be appreciated, I am also using the activityViewController!.completionWithItemsHandler but this closure not getting called in case of Facebook.
let url = "\(UserDefaults.baseURL)/pl/\(resource)/\(sharingId)"
// set up activity view controller
let imageToShare = [ url ]
activityViewController = UIActivityViewController(activityItems: imageToShare, applicationActivities: nil)
activityViewController!.popoverPresentationController?.sourceView = self.view // so that iPads won't crash
// exclude some activity types from the list (optional)
activityViewController!.excludedActivityTypes = [UIActivity.ActivityType.airDrop]
activityViewController!.completionWithItemsHandler = { (activityType: UIActivity.ActivityType?, completed:
Bool, arrayReturnedItems: [Any]?, error: Error?) in
if completed {
print("share completed")
self.dismiss(animated: true)
return
} else {
print("cancel")
self.dismiss(animated: true)
}
if let shareError = error {
print("error while sharing: \(shareError.localizedDescription)")
}
}
// present the view controller
self.present(activityViewController!, animated: true, completion: nil)

How to Make macOS App Window Hidden When Closed and Reopened With Menu Bar Item?

I am developing a macOS app (using Swift & Storyboard) which window behaves like the Adobe Creative Cloud app. And I could not find the optimal solution after hours of research.
This means:
When the app launches, the main window shows up with various menus on the status bar, an icon appears in the dock, and an icon appears in the status bar.
When the user clicks the red X, the main window and the icon in the dock are hidden.
The main app window can be reopened by clicking the status bar icon. And the dock icon reappears.
My storyboard looks like this:
I have tried the following:
By setting Application is agent (UIElement) to YES, I was able to close the main app window while keeping the app alive. However, the app icon does not show up in the dock, and there are no menus in the left side of the status bar.
I was able to launch a new app window by clicking the status bar icon. But doing so simply opens a whole new window regardless of whether a window is already being presented (I only want one window to show up).
let storyboard = NSStoryboard(name: "Main", bundle: nil)
guard let window = storyboard.instantiateController(withIdentifier: .init(stringLiteral: "main")) as? WindowController else { return }
window.showWindow(self)
Much appreciation for anyone who can help!
Don't use the Application is agent approach, but change the activationPolicy of the NSApp.
To dynamically hide the icon after closing the (last) window use this in your AppDelegate:
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
NSApp.setActivationPolicy(.accessory)
return false
}
And use something simular to this to initialise your menubar icon and activate the window including a dock icon:
class ViewController: NSViewController {
var status: NSStatusItem?
override func viewDidLoad() {
super.viewDidLoad()
status = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
status?.button?.title = "Test"
status?.button?.action = #selector(activateWindow(_:))
status?.button?.target = self
}
#IBAction func activateWindow(_ sender: AnyObject) {
NSApp.setActivationPolicy(.regular)
DispatchQueue.main.async {
NSApp.windows.first?.orderFrontRegardless()
}
}
}

How can I open URLs in watchOS?

Can a standalone Apple Watch app open a URL like we can in iOS:
UIApplication.sharedApplication().openURL(url!)
I want to display a message about an update when a new app is published. I want the user to go the URL for the update. Is there any good way?
The following answer works in watchOS 6.2 and above. It initiates a web authentication session, which opens a web view, similar to WKWebView on iOS. You can even swipe from the left to go back.
Keep in mind that it doesn't save cookies, so, for example, if you log in to a website and close it, you have to log in again when you open it back up.
import AuthenticationServices
import SwiftUI
struct ContentView: View {
var body: some View {
Button("Tap me") {
guard let url = URL(string: "https://example.com") else {
return
}
// Source: https://www.reddit.com/r/apple/comments/rcn2h7/comment/hnwr8do/
let session = ASWebAuthenticationSession(
url: url,
callbackURLScheme: nil
) { _, _ in
}
// Makes the "Watch App Wants To Use example.com to Sign In" popup not show up
session.prefersEphemeralWebBrowserSession = true
session.start()
}
}
}
I don't know if you're using SwiftUI or not, but the Link View is shown in the Documentation as available for watchOS. However using it in practice only prompts the user to open the link on their iPhone. As a standalone app since Safari is not available on watchOS you might want to consider other options to alert your user of an update.
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, World!")
Link("Apple.com", destination: URL(string: "https://www.apple.com")!)
}
}
}

How to test if the app is presenting a certain view controller?

I'm pretty new to XCode UI tests and I'm trying to run a test where I fill two text labels and then I press a button. After the button is pressed the app should make an URL call and be redirected to another view controller. I want to check if at the end of this operation the second view controller is displayed.
To test this, I have written the following test:
let app = XCUIApplication()
app.launch()
let ownerTextField = app.textFields["ownerTextField"]
ownerTextField.tap()
ownerTextField.typeText("UserA")
let repositoryTextField = app.textFields["repositoryTextField"]
repositoryTextField.tap()
repositoryTextField.typeText("AppB")
app.buttons["SearchButton"].tap()
XCTAssertTrue(app.isDisplayingResults)
Where isDisplayingResults is
extension XCUIApplication {
var isDisplayingResults: Bool {
return otherElements["resultView"].exists
}
}
I have set up the identifier of the View controller inside its swift file class:
override func viewDidLoad() {
super.viewDidLoad()
view.accessibilityIdentifier = "resultView"
...
Nonetheless to say, the test fails. How can I get a success?
It's so simple.
If after clicking the URL, Viewcontroller is presenting means your previous VC button doesn't exist on screen.
So Just check for previous VC button exists or not.
If app.buttons["SearchButton"].esists()
{ //write if code
} else {
// Write else code
}

How to communicate to the window controller from view controller of newly, programmatically created tab?

In my window controller, I implement:
#IBAction override func newWindowForTab(_ sender: Any?) {
if let wc = NSStoryboard.main?.instantiateInitialController() as? WindowController,
let window = wc.window {
self.window?.addTabbedWindow(window, ordered: .above)
window.makeKey()
}
}
In the view controller, I have this code:
let window = self.view.window?.windowController as? WindowController
Also tried:
let window = NSApp.mainWindow?.windowController as? WindowController
If I don't have any tabs, it's able to get the window controller. But on new tabs, it does grab the window controller.
Similarly, I've unsuccessfully tried sending an action to the WindowController:
NSApp.sendAction(#selector(WindowController.pageLabelChange), to: nil, from: label)
Works for the original window, but not for any newly created tabs.
How do the newly created view controller objects communicate with the window controller?
Edit:
For more context in how I am using this code: It's basically a PDFView that's embedded in a window. The window has a tool bar that displays the page number. Using any of the above code, I can set the current page number of the PDFView, but when there's a tab, it does not work. Using the .PDFViewPageChanged notification, I call my func
NSApp.sendAction(#selector(WindowController.pageLabelChange), to: nil, from: pdfView)
Edit 2:
I've created a GitHub with a test project that shows the problem I have. You should be able to see that when you launch the project, the + button will add a number to the textfield in the tab bar. But if you go to the View menu > Add tab, it creates a new tab, but the + does nothing.
You create new window controller on stack so it is destroyed right after return, that is the reason. You need to store somewhere reference to created tab window controller and manage it (the place of storage is up to your app logic).
Here is simple demo that makes code work
Tested with Xcode 11.4 / macOS 10.15.5
final class WindowController: NSWindowController {
#IBOutlet weak var testField: NSTextField!
var tabControllers = [WindowController]() // << storage for child controllers
#IBAction override func newWindowForTab(_ sender: Any?) {
if let wc = NSStoryboard.main?.instantiateInitialController() as? WindowController,
let window = wc.window {
self.window?.addTabbedWindow(window, ordered: .above)
window.makeKey()
tabControllers.append(wc) // keep reference in storage
// TODO: - you are responsible to manage wc, eg: remove
// from storage on window close.
}
}
}