Programmatically open ShareLink in SwiftUI - swift

Using ShareLink shows a Share button so it opens Share popup when user taps on that button.
ShareLink(item: data, subject: Text("Subject"), message: Text("Message"))
I would like to programmatically screenshot and then share on share popup. Is there any way to perform action when user taps on ShareLink button or programmatically open SharePopup like we used to do in Swift as UIActivityController ?

You can do this by wrapping a UIActivityController in UIViewControllerRepresentable.
In this example, I'm creating a URL on the click of a button, and then sharing it:
extension URL: Identifiable {
public var id: Self { self }
}
struct ContentView: View {
#State private var url: URL?
var body: some View {
Button {
// I'm creating a URL, this could be
// anything you want to share, e.g a screenshot
self.url = URL(string: "https://apple.com")
} label: {
Label("Share", systemImage: "square.and.arrow.up")
}
.sheet(item: $url, onDismiss: {
}, content: { url in
ActivityViewController(url: url)
})
}
}
struct ActivityViewController: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> UIActivityViewController {
UIActivityViewController(activityItems: [url], applicationActivities: nil)
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
}
}
// Just because sheet requires item: to be Identifiable
extension URL: Identifiable {
public var id: Self { self }
}

Related

Can't see buttons at top of webView

I can't see the buttons that should be on top of the web view that make it go back and forward. I don't know if this is a web view taking up the whole screen problem, or something else. I've tried making a new view with the buttons and putting the view in there. I don't know.
Here is my code:
import WebKit
import SwiftUI
struct Webview : UIViewRepresentable {
let request: URLRequest
var webview: WKWebView?
init(web: WKWebView?, req: URLRequest) {
self.webview = WKWebView()
self.request = req
}
func makeUIView(context: Context) -> WKWebView {
return webview!
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.load(request)
}
func goBack(){
webview?.goBack()
}
func goForward(){
webview?.goForward()
}
}
struct ContentView: View {
#State var searchURL = "https://google.com"
var body: some View {
VStack {
HStack {
//MARK: BACK BUTTON
Button(action: {
Webview(web: nil, req: URLRequest(url: URL(string: "\(searchURL.prefix(4) == "http" ? searchURL : "https://\(searchURL)")")!)).goBack()
}) {
Image("arrowshape.turn.up.backward")
}
//MARK: FOREWARD BUTTON
Button(action: {
Webview(web: nil, req: URLRequest(url: URL(string: "\(searchURL.prefix(4) == "http" ? searchURL : "https://\(searchURL)")")!)).goForward()
}) {
Image("arrowshape.turn.up.foreward")
}
}
Webview(web: nil, req: URLRequest(url: URL(string: "\(searchURL.prefix(4) == "http" ? searchURL : "https://\(searchURL)")")!))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The button action should not be a view in itself but should just update a variable that stores the URL to be displayed by the webview. However that is not required.
Take a look at this. Should cover all the scenarios you need without the need to manually manager URLs

How to authenticate with the Box SDK using SwiftUI?

I have been having a hard time trying to figure out how to authenticate with the Box API using SwiftUI.
As far as I understand, SwiftUI does not currently have the ability to satisfy the ASWebAuthenticationPresentationContextProviding protocol required to show the Safari OAuth2 login sheet. I know that I can make a UIViewControllerRepresentable to use UIKit within SwiftUI, but I can't get this to work.
I have figured out how to get the OAuth2 login sheet for Dropbox to appear and authenticate the client using SwiftUI.
The trick is to use a Coordinator to make the UIViewControllerRepresentable satisfy a protocol.
import SwiftUI
import BoxSDK
import AuthenticationServices
var boxSDK = BoxSDK(clientId: "<Client ID>", clientSecret: "<Client Secret>")
var boxClient: BoxClient
struct BoxLoginView: View {
#State var showLogin = false
var body: some View {
VStack {
Button {
showLogin = true
} label: {
Text("Login")
}
BoxView(isShown: $showLogin)
// Arbitrary frame size so that this view does not take up the whole screen
.frame(width: 40, height: 40)
}
}
}
/// A UIViewController that will present the OAuth2 Safari login screen when the isShown is true.
struct BoxView: UIViewControllerRepresentable {
typealias UIViewControllerType = UIViewController
let letsView = UIViewController()
#Binding var isShown : Bool
// Show the login Safari window when isShown
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
if(isShown) {
getOAuthClient()
}
}
func makeUIViewController(context _: Self.Context) -> UIViewController {
return self.letsView
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func getOAuthClient() {
boxSDK.getOAuth2Client(tokenStore: KeychainTokenStore(), context:self.makeCoordinator()) { result in
switch result {
case let .success(client):
boxClient = client
case let .failure(error):
print("error in getOAuth2Client: \(error)")
}
}
}
class Coordinator: NSObject, ASWebAuthenticationPresentationContextProviding {
var parent: BoxView
init(parent: BoxView) {
self.parent = parent
}
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
return parent.letsView.view.window ?? ASPresentationAnchor()
}
}
}

SwiftUI: Display file url from Array after user Picks file using DocumentPicker

I followed a tutorial on getting Url from a Document a user chooses and be able to display it on the View. My problem now is I want to add those Url's into an array. Then get the items from the array and print them onto the View. The way it works is the User presses a button and a sheet pops up with the files app. There the user is able to choose a document. After the user chooses the document the Url is printed on the View. To print the Url is use this
//if documentUrl has an Url show it on the view
If let url= documentUrl{
Text(url.absoluteString)
}
Issue with this is that when I do the same thing the
If let url= documentUrl
Is ran before the Url is even added to the array and the app crashes
Here is the full code
//Add the Urls to the array
class Article: ObservableObject{
var myArray:[String] = []
}
struct ContentView: View {
#State private var showDocumentPicker = false
#State private var documentUrl:URL?
#State var myString:URL?
#ObservedObject var userData:Article
// Func for onDismiss from the Sheet
func upload() {
// add the Url to the Array
DispatchQueue.main.async{
userData.myArray.append(documentUrl!.absoluteString)
}
}
var body: some View {
VStack{
//If have Url reflect that on the View
if let url = documentUrl{
//Works
Text(url.absoluteString)
//doesntwork
Text(userData.myArray[0])
}
}
Button(action:{showDocumentPicker.toggle()},
label: {
Text("Select your file")
})
.sheet(isPresented: $showDocumentPicker, onDismiss: upload )
{
DocumentPicker(url: $documentUrl)
}
}
}
The main thing I want to do the just display the ulrs into the view after the user chooses the document or after the sheet disappears. So if the user chooses 1 Url only one is printed. If another one is chosen after then 2 are show etc.
This is the documentPicker code used to choose a document
struct DocumentPicker : UIViewControllerRepresentable{
#Binding var url : URL?
func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
//initialize a UI Document Picker
let viewController = UIDocumentPickerViewController(forOpeningContentTypes: [.epub])
viewController.delegate = context.coordinator
print("1")
return viewController
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {
print("Swift just updated ")
print("2")
}
}
extension DocumentPicker{
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator:NSObject, UIDocumentPickerDelegate{
let parent: DocumentPicker
init(_ documentPicker: DocumentPicker){
self.parent = documentPicker
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first else{return}
parent.url = url
print("3")
}
}
}
Not sure if maybe I'm not approaching this the correct way? I looked at different tutorial but couldn't find anything.
Use .fileImporter presentation modifire (above ios 14)
.fileImporter(isPresented: $showDocumentPicker,
allowedContentTypes: [.image],
allowsMultipleSelection: true)
{ result in
// processing results Result<[URL], Error>
}
An observable object doesn't have a change trigger. To inform that the observable object has changed use one of the following examples:
class Article: ObservableObject {
#Published var myArray:[String] = []
}
or
class Article: ObservableObject {
private(set) var myArray:[String] = [] {
willSet {
objectWillChange.send()
}
}
func addUrl(url: String) {
myArray.append(url)
}
}
official documentation: https://developer.apple.com/documentation/combine/observableobject

Sheet and alert keeps refreshing my page for WKWebView SwiftUI

I am currently using WKWebView and I added a sheet and an alert inside my wkwebview. However, every time I close the sheet or close the alarm, My WkWebView keeps navigating(loading) back to the origin domain that I specified (for this case, google.com). I an wondering how I can fix this issue.
WebView
import SwiftUI
import WebKit
struct WebView : UIViewRepresentable {
let request: URLRequest
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.load(request)
}
}
Main View
struct MainView: View {
var webView: WebView = WebView(request: URLRequest(url: URL(string: "https://www.google.com")!))
var body: some View {
VStack(){
AlertView()
webView
SheetScreenView(webView: $webView)
}
}
AlertView()
...
Button(action: {
}, label: {}..alert(isPresented: $isBookmarked) {
Alert(title: Text(" \(webView.getURL())"), dismissButton: .default(Text("Ok")))
}
...
SheetScreenView
....
#State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
SheetView()
}
}
...
SheetView
struct SheetView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
Button("Press to dismiss") {
presentationMode.wrappedValue.dismiss()
}
.font(.title)
.padding()
.background(Color.black)
}
}
Your issue can be reproduced even without the sheet and alert by just rotating the device. The problem is that in updateView (which is going to get called often), you call load on the URLRequest. This is going to get compounded by the fact that you're storing a view in a var which is going to get recreated on every new render of MainView, since Views in SwiftUI are transient.
The simplest way to avoid this if your URL isn't going to change is to just call load in makeUIView:
struct WebView : UIViewRepresentable {
var request: URLRequest
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.load(request)
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
}
}
struct ContentView: View {
var body: some View {
WebView(request: URLRequest(url:URL(string: "https://www.google.com")!))
}
}
If your URL may change, you need a way to compare the previous state with the new one. This seems like the shortest (but certainly not only) way to do this:
struct WebView : UIViewRepresentable {
var url: URL
#State private var prevURL : URL?
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
if (prevURL != url) {
let request = URLRequest(url: url)
uiView.load(request)
DispatchQueue.main.async { prevURL = url }
}
}
}
struct ContentView: View {
var body: some View {
WebView(url: URL(string: "https://www.google.com")!)
}
}

A link with swift webview application target _blank is not working

I'm a beginner, sorry I did webview with swift, but a link with target _blank doesn't work
I've created a UIViewRepresentable class for the WKWebView from UIKit. The UIViewRepresentable class can be used to create and manage views(UIView) from UIKit in SwiftUI. There are two views in the project,
ContentView lists the urls from string array. Selecting an url navigates to the Detail view.
The Detail view shows the web page for the selected url using WKWebView.
Hope this helps. Here's the code.
import SwiftUI
import UIKit
import WebKit
struct ContentView: View {
var urls: [String] = ["https://www.stackoverflow.com", "https://www.yahoo.com"]
#State private var hideStatusBar = false
var body: some View {
NavigationView {
List {
ForEach(urls, id: \.self) { url in
VStack {
NavigationLink(destination: DetailView(url: url)) {
Text(url)
}
}
}
}
.navigationBarTitle("Main")
}
}
}
struct DetailView: View {
var url: String = ""
var body: some View {
VStack {
Webview(url: url)
Spacer()
}
.navigationBarHidden(true)
}
}
struct Webview: UIViewRepresentable {
var url: String
typealias UIViewType = WKWebView
func makeUIView(context: UIViewRepresentableContext<Webview>) -> WKWebView {
let wkWebView = WKWebView()
guard let url = URL(string: self.url) else {
return wkWebView
}
let request = URLRequest(url: url)
wkWebView.load(request)
return wkWebView
}
func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<Webview>) {
}
}