I am having an issue that only occurs with Swift, Webkit abd WebRTC. I am creating a webview of this url:
https://widgets.bingewave.com/webrtc/6b8bcc00-864d-4c97-8e15-0b3aed37946b
The complete code is here: https://github.com/BingeWave/bingewave-swift-video-app
The code for creating the Webview is this:
Webview
import SwiftUI
import WebKit
struct SwiftUIWebView: UIViewRepresentable {
let url: URL?
func makeUIView(context: Context) -> CustomWebView {
//Preferences
let prefs = WKWebpagePreferences()
prefs.allowsContentJavaScript = true
//Create a congif
let config = WKWebViewConfiguration()
//Add
config.defaultWebpagePreferences = prefs
return CustomWebView(
frame: .zero,
configuration: config
)
}
func updateUIView(_ uiView: CustomWebView, context: Context) {
guard let myURL = url else {
return
}
let request = URLRequest (url: myURL)
uiView.load(request)
}
CustomWebView.swift
//
// CustomWebView.swift
// BingWave Sample App Swift
//
// Created by Ayusma on 07/04/2022.
//
import WebKit
class CustomWebView: WKWebView {
//Change th Auth Token with that of a user
let AUTH_TOKEN = "xxxxxxx";
override func load(_ request: URLRequest) -> WKNavigation? {
guard let mutableRequest: NSMutableURLRequest = request as? NSMutableURLRequest else {
return super.load(request)
}
mutableRequest.setValue(AUTH_TOKEN, forHTTPHeaderField: "Authorization")
return super.load(mutableRequest as URLRequest)
}
}
ContentView
import SwiftUI
struct ContentView: View {
let BINGWAVE_URL = URL(string: "https://widgets.bingewave.com/webrtc/6b8bcc00-864d-4c97-8e15-0b3aed37946b")
var body: some View {
NavigationView{
SwiftUIWebView(url: BINGWAVE_URL)
.navigationTitle("BingWave Sample App")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The issue is the screen loads the view, I am able to join the video chat but my camera video black and if another user joins, their video is a black within the app. What is weird is they can see me and themsevles on their end, so the video is only black on the iOS app. And this works for other implements like a React Native Webview, it only fails for this implementation with Swift.
Am I missing anything?
Related
I have implemented the iOS Intune SDK in my app. The app applies the MAM policies and is working properly. I am trying to implement the Webview content restrictions using the IntuneMAMWebViewPolicyDelegate and isExternalURL as described here
The isExternalURL function is never called.
This is the view containing my WKWebView
import UIKit
import WebKit
import IntuneMAMSwift
class WebviewPage: UIViewController {
let webViewPolicyDelegage = WebViewPolicyClass.init();
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
IntuneMAMPolicyManager.instance().setWebViewPolicyDelegate(webViewPolicyDelegage, forWebViewer: webView!)
let webaddress = "https://www.google.com"
if let url = URL(string: webaddress) {
let urlRequest = URLRequest(url: url)
self.webView.load(urlRequest)
}
}
}
This is my delegate implementation
import IntuneMAMSwift
class WebViewPolicyClass: NSObject, IntuneMAMWebViewPolicyDelegate {
func isExternalURL(_ url: URL) -> Bool {
// TODO: Check if URL is external
return true
}
}
The app MAM policy is configured to require the managed Edge browser.
Any ideas why the delegate method is not called?
I'd like to be able to loop a live photo, for continuous playback.
So far, I'm trying to use the PHLivePhotoViewDelegate to accomplish this.
import Foundation
import SwiftUI
import PhotosUI
import iOSShared
struct LiveImageView: UIViewRepresentable {
let view: PHLivePhotoView
let model:LiveImageViewModel?
let delegate = LiveImageLargeMediaDelegate()
init(fileGroupUUID: UUID) {
let view = PHLivePhotoView()
// Without this, in landscape mode, I don't get proper scaling of the image.
view.contentMode = .scaleAspectFit
self.view = view
// Using this to replay live image repeatedly.
view.delegate = delegate
model = LiveImageViewModel(fileGroupUUID: fileGroupUUID)
guard let model = model else {
return
}
model.getLivePhoto(previewImage: nil) { livePhoto in
view.livePhoto = livePhoto
}
}
func makeUIView(context: Context) -> UIView {
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
guard let model = model else {
return
}
guard !model.started else {
return
}
model.started = true
view.startPlayback(with: .full)
}
}
class LiveImageLargeMediaDelegate: NSObject, PHLivePhotoViewDelegate {
func livePhotoView(_ livePhotoView: PHLivePhotoView, didEndPlaybackWith playbackStyle: PHLivePhotoViewPlaybackStyle) {
livePhotoView.stopPlayback()
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200)) {
livePhotoView.startPlayback(with: .full)
}
}
}
But without full success. It seems the audio does play again, but not the video. The livePhotoView.stopPlayback and the async aspect are just additional changes I was trying. I've tried it without those too.
Note that I don't want the user to have to manually change the live photo (e.g., see NSPredicate to not include Loop and Bounce Live Photos).
Thoughts?
ChrisPrince I tried your code and it works fine for me, I just add delegate and start playback inside of it and everything runs well and smoothly. I thought that there is no point in using stop playback because the function itself says that the playback ended.
func livePhotoView(_ livePhotoView: PHLivePhotoView, didEndPlaybackWith playbackStyle: PHLivePhotoViewPlaybackStyle) {
livePhotoView.startPlayback(with: .full)
}
Hi firebase I wanted to upload pdf doc style data from icloud by connecting to simulator. but I am getting a user access error. I don't want to have a user already in my application. What I did as in the firebase documentation is still not working.
also allow read, write: if true; I haven't tried it still does not allow access
Firebase;
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
SwiftUI contentview;
import SwiftUI
import MobileCoreServices
import Firebase
struct ContentView: View {
#State var show = false
#State var alert = false
var body: some View {
Button {
self.show.toggle()
} label: {
Text("Document Picker")
}
.sheet(isPresented: $show)
{
DocumentPicker(alert: self.$alert)
}
.alert(isPresented: $alert) {
Alert(title: Text("Opps Wonderful"), message: Text("OKey Upload"), dismissButton: .default(Text("OK")))
} }
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct DocumentPicker : UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return DocumentPicker.Coordinator(parent1: self)
}
#Binding var alert: Bool
func makeUIViewController(context: UIViewControllerRepresentableContext <DocumentPicker> ) -> UIDocumentPickerViewController {
let picker = UIDocumentPickerViewController(documentTypes: [String(kUTTypeItem)] , in: .open)
picker.allowsMultipleSelection = false
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocumentPicker>) {
}
class Coordinator: NSObject, UIDocumentPickerDelegate {
var parent : DocumentPicker
init (parent1: DocumentPicker)
{
parent = parent1
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
let bucket = Storage.storage().reference()
bucket.child((urls.first?.deletingPathExtension().lastPathComponent)!).putFile(from: urls.first!,metadata: nil ){
(_, err) in
if err != nil {
print ((err?.localizedDescription)!)
return
}
print("succeess")
self.parent.alert.toggle()
}
}
}
}
The security rules you're showing are for Cloud Firestore (a document database), while your code is using Cloud Storage (file storage). While both are part of Firebase, they each have their own API and their own security rules, and the security rules of one don't apply to the other.
You'll want to study the documentation on securing Cloud Storage, and then set the security rules for Cloud Storage in your project.
In SwiftUI, I have a network request running in scenedelegate, scenedidbecomeactive. I don't know which view the user will be on when the app becomes active, but I want to present an alert if the data in the network request changes. I simplified the code below, so it's easy to read...
func sceneDidBecomeActive(_ scene: UIScene) {
let customClass = CustomClass()
customClass.performNetworkRequest()
In CustomClass, i have...
func performNetWorkRequest() {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let d = data {
let response = try JSONDecoder().decode(DetailResponse.self, from: d)
DispatchQueue.main.async {
//Here is where I want to either present an alert, but I can't figure out how to.
//OR do i put a func in SceneDeletegate to present the alert on the window.rootviewcontroller and then just call that func from here?
}
Any help is much appreciated!
Paul has a point - here's a possible implementation:
// In CustomClass.swift
import Combine
class CustomClass : ObservableObject {
#Published var dataRecieved = PassthroughSubject<DetailResponse, Never>()
init() {
performNetWorkRequest()
}
func performNetWorkRequest() {
URLSession.shared.dataTask(with: url) { (data, response, error) in
let response = try JSONDecoder().decode(DetailResponse.self, from: data)
DispatchQueue.main.async {
self.dataRecieved.send(response)
}
}
.resume()
}
}
// In SomeView.swift
import SwiftUI
import Combine
struct ContentView: View {
#State var showAlert = false
var customClass = CustomClass()
var body: some View {
Text("Hello, World!")
.onReceive(customClass.dataRecieved) { _ in
self.showAlert = true
}
.alert(isPresented: $showAlert) {
// your alert
}
}
}
Notice I didn't mention the SceneDelegate in any of these - this approach (called MVVM) is more flexible, in my opinion - besides, the way it is set up, performNetWorkRequest() will be executed as soon as your view is initialized, anyway.
You can also tweak the PassthroughSubject - I didn't know if you needed the DetailResponse or not.
Hope this helped!
Edit:
I just reread your question and it seems that this implementation is at fault as you noted there was no way to know what view the user would be on in the case of a network change. In that case, you can feed the same instance of CustomClass in your SceneDelegate as an EnvironmentObject.
Hello I want to create an application with several webview that once they have been loaded once, I don't want them to be recreated anymore I want them to keep their instance.
I create in my application A navigation view which includes a list of 10 sites. When I click on a item of the list I want to display the corresponding webview with NavigationLink. It works. But the problem is that if I click on another item of the list and I will return to the previous one it returns to the home page, the webview loads again. I want the webview to be created only once and always stay alive in as long as the app is alive. I know that swiftui always refresh views is the problem, how can I prevent my webview from being refreshed by swiftUI?
In uitkit it's simple I create an array of wkwebview at launch of the app, I load all my webview url, in a singleton class. And depending on the item selected from my tableview I display the corresponding wkwebview. And even if I change the item all my webview are alive even if we don't see them.
struct WebView : UIViewRepresentable {
let request: URLRequest
func makeUIView(context: Context) -> WKWebView {
let web = WKWebView()
web.load(request)
return web
}
func updateUIView(_ uiView: WKWebView, context: Context) {
}
}
Here is simplified demo of possible approach. The idea is to cache created WKWebView objects by some identifiers and just reuse them in created (or recreated) thin-wrapper SwiftUI view representable.
Tested & works with Xcode 11.2 / iOS 13.2
struct WebView: UIViewRepresentable {
private static var cache: [Int: WKWebView] = [:]
// the only allowed entry point to create WebView, so have control either
// to create new instance of WKWebView or provide already loaded
static func view(with id: Int, request: URLRequest) -> WebView {
var web = cache[id] // it is UI thread so safe to access static
if web == nil {
web = WKWebView()
cache[id] = web
}
return WebView(with: web!, request: request)
}
private init(with web: WKWebView, request: URLRequest) {
self.web = web
self.request = request
}
private let web: WKWebView
private let request: URLRequest
func makeUIView(context: Context) -> WKWebView {
if web.url == nil { // just created, so perform initial loading
web.load(request)
}
return web
}
func updateUIView(_ uiView: WKWebView, context: Context) {
}
}
// Just simple test view
struct TestOnceLoadedWebView: View {
private let urls: [String] = [
"http://www.apple.com",
"http://www.google.com",
"http://www.amazon.com"
]
var body: some View {
NavigationView {
List(0 ..< urls.count) { i in
NavigationLink("Link \(i)", destination:
WebView.view(with: i, request:
URLRequest(url: URL(string: self.urls[i])!)))
}
}
}
}