Disable the loading of Stylesheets in UIWebView and/or WKWebView - swift

I have absolutely tried everything with this one. I've read every apple article and no where can I find how to disable the loading of CSS (Styling) in the legacy UIWebView or the new WKWebView. I don't mind what web view I use just as long as it can accomplish this.
I've tried WKPreferences() and WKWebViewConfiguration and both have no member userStyleSheetEnabled.
I've referred myself to this apple article https://developer.apple.com/documentation/webkit/webpreferences/1536396-userstylesheetenabled?
Does anyone know the answer and how to achieve this on Swift 4?

The WebView class you referenced is very old and has been deprecated. If you need to add a webview to your app, use WKWebView instead. This answer works with iOS >= 11 and macOS >= 10.13.
What you need is adding WKContentRuleList to your WKWebView's configuration. They are similar to Safari content blockers (i.e. ad-blockers) that you may already have installed on your phone:
// This should ideally be in a file but we're using string for convenience
let jsonRuleList = """
[{
"trigger": {
"url-filter": ".*",
"resource-type": ["style-sheet"]
},
"action": {
"type": "block"
}
}]
"""
// Compile the content-blocking list
WKContentRuleListStore.default().compileContentRuleList(forIdentifier: "blockStyleSheet", encodedContentRuleList: jsonRuleList) { list, error in
guard error == nil else { print(error!.localizedDescription); return }
guard let list = list else { return }
// Add the stylesheet-blocker to your webview's configuration
let configuration = WKWebViewConfiguration()
configuration.userContentController.add(list)
// Adding the webview to the view. Nothing to see here
self.webView = WKWebView(frame: self.view.bounds, configuration: configuration)
self.view.addSubview(self.webView)
// Let's try Apple's website without CSS
let request = URLRequest(url: URL(string: "https://www.apple.com")!)
self.webView.load(request)
}
Result:
References
Customized Loading in WKWebView (WWDC video)
Creating Safari Content-Blocking Rules

Related

How to receive image dragged from Safari webpage with SwiftUI `.onDrop()`

I'm not able to receive image dragged from Safari webpage in my SwiftUI app on macOS. I suppose Safari uses different mechanics, because it works properly when it comes to Chrome and Firefox. I use .onDrop() but it results empty providers no matter what kind of UTType used. Below is my code but output no provider available.
So, what UTType it is when dragging image from Safari webpage, and how can I implement it in .onDrop()
.onDrop(of: [.url, .fileURL], isTargeted: $isTarget) { providers -> Bool in
if let provider = providers.first,
let identifier = provider.registeredTypeIdentifiers.first {
print(identifier)
provider.loadItem(forTypeIdentifier: identifier) { data, error in
// Do something here
}
} else {
print("no provider available")
}
return true
}

turn-by-turn navigation with google maps url scheme swift

How can I enable turn-by-turn navigation with google maps urlscheme?. I used below url in swift
comgooglemaps://?saddr=%f,%f&daddr=%f,%f&directionsmode=driving
but it only creates a route, the start navigation button is not present when the google maps app is activated.
am i missing something?
let kGoogleMapsSchema = "comgooglemaps://"
let path = "\(kGoogleMapsSchema)?saddr=%f,%f&daddr=%f,%f&directionsmode=driving"
guard let finalURLString = String(format: path, currentUserCoordinates!.latitude, currentUserCoordinates!.longitude, destinationCoordinates!.latitude, destinationCoordinates!.longitude, currentUserCoordinates!.latitude, currentUserCoordinates!.longitude).addingPercentEncoding(
withAllowedCharacters: .urlQueryAllowed) else {
return
}
guard let url = URL(string: finalURLString) else {
return
}
if #available(iOS 10, *) {
UIApplication.shared.open(url)
} else {
UIApplication.shared.openURL(url)
}
am i missing something?
I found the answer as only preview button was available instead of start navigation. Because I was giving start coordinates not of my current location. So in case it is my current coordinates it will work.
Google Maps, No Option for Starting the Navigation, Only Preview is there

Can't hide share button in USDZ + QLPreviewController

I got a project that involves a few USDZ files for the augmented reality features embedded in the app. While this works great, and we're really happy with how it performs, the built-in share button of the QLPreviewController is something that we'd like to remove. Subclassing the object doesn't have any effect, and trying to hide the rightBarButtonItem with the controller returned in delegate method still shows the button when a file is selected. The implementation of USDZ + QLPreviewController we're using is pretty basic. Is there a way around this issue?
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
let url = Bundle.main.url(forResource: models[selectedObject], withExtension: "usdz")! controller.navigationItem.rirButtonItems = nil.
// <- no effect return url as QLPreviewItem
}
#IBAction func userDidSelectARExperience(_ sender: Any) {
let previewController = QLPreviewController()
previewController.dataSource = self
previewController.delegate = self
present(previewController, animated: true)
}
This is the official answer from Apple.
Use ARQuickLookPreviewItem instead of QLPreviewItem. And set its canonicalWebPageURL to a URL (can be any URL).
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
guard let path = Bundle.main.path(forResource: "Experience", ofType: "usdz") else { fatalError("Couldn't find the supported input file.") }
let url = URL(fileURLWithPath: path)
if #available(iOS 13.0, *) {
let item = ARQuickLookPreviewItem(fileAt: url)
item.canonicalWebPageURL = URL(string: "http://www.google.com")
return item
} else { }
return url as QLPreviewItem
}
The version check is optional.
My approach is to add the QLPreviewController as an subview.
container is an UIView in storyboard.
let preview = QLPreviewController()
preview.dataSource = self
preview.view.frame = CGRect(origin: CGPoint(x: 0, y: -45), size: CGSize(width: container.frame.size.width, height: container.frame.size.height+45) )
container.addSubview(preview.view)
preview.didMove(toParent: self)
The y offset of the frame's origin and size may vary. This will ensure the AR QuickLook view to be the same size as the UIView, and hide the buttons (unfortunately, all of them) at the same time.
Instead of returning QLPreviewItem, use ARQuickLookPreviewItem which conforms to this protocol.
https://developer.apple.com/documentation/arkit/arquicklookpreviewitem
Then, assign a url that you would want to share (that will appear in share sheet) in canonicalWebPageURL property. By default, this property shares the file url (in this case, the USDZ file url). Doing so would not expose your file URL(s).
TLDR: I don't think you can.
I haven't seen any of the WWDC session even mention this and I can't seem to find any supporting developer documentation. I'm pretty sure the point of the ARKit QLPreviewController is so you don't have to do any actual coding on the AR side. I can see the appeal for this and for customisation in general, however, I'd suggest instead looking at some of the other ARKit projects that Apple has released and attempting to re-create those from the ground up as opposed to stripping this apart.
Please advise if this changes as I'd like to do something similar, especially within Safari.
I couldn't get to the share button at all to hide or disable it. Spent days to overcome this. I did rather unprofessional way of overcoming it. Subview QLPreviewController to a ViewController and subview a button or view on top of image view on top of share button and setting my company logo as image. It will be there all the time, even the top bar hides on full screen in AR mode. Not a clean solution. But works.

How to create apple watchOS5 complication?

I've never worked in WatchOS5 and want to develop a horizontal complication (Modular large) for AppleWatch, like "Heart Rate". The idea is that I would display heart rate data in a different way. Right now I want to deploy the complication on development watch.
I have created a new project with a checkbox for "complication" added. I see that this added a complications controller with timeline configuration placeholders.
There is also an storyboard with a bunch of empty screens. I'm not sure as to how much effort I need to put into an apple watch app before I can deploy it. I see this Apple doc, but it does not describe how to layout my complication. Some section seem to have missing links.
Can I provide one style of complication only (large horizontal - modular large)
Do I need to provide any iPhone app content beyond managing the
complication logic, or can I get away without having a view controller?
Do I control the appearance of my complication by adding something to the assets folder (it has a bunch of graphic slots)?
Sorry for a complete beginner project, I have not seen a project focusing specifically on the horizontal complication for watch OS 5
You should be able to deploy it immediately, though it won't do anything. Have a look at the wwdc video explaining how to create a complication: video
You can't layout the complication yourself, you can chose from a set of templates that you fill with data. The screens you are seeing are for your watch app, not the complication.
You don't have to support all complication styles.
The complication logic is part of your WatchKit Extension, so technically you don't need anything in the iOS companion app, I'm not sure how much functionality you have to provide to get past the app review though.
Adding your graphics to the asset catalog won't do anything, you have to reference them when configuring the templates.
Here's an example by Apple of how to communicate with the apple watch app. You need to painstakingly read the readme about 25 times to get all the app group identifiers changed in that project.
Your main phone app assets are not visible to the watch app
Your watch storyboard assets go in WatchKit target
Your programmatically accessed assets go into the watch extension target
Original answers:
Can I provide one style of complication only (large horizontal -
modular large) - YES
Do I need to provide any iPhone app content beyond
managing the complication logic, or can I get away without having a
view controller? YES - watch apps have computation limits imposed on them
Do I control the appearance of my complication by
adding something to the assets folder (it has a bunch of graphic
slots)? See below - it's both assets folder and placeholders
Modify the example above to create a placeholder image displayed on the watch (when you are selecting a complication while modifying the screen layout)
func getPlaceholderTemplate(for complication: CLKComplication, withHandler handler: #escaping (CLKComplicationTemplate?) -> Void) {
// Pass the template to ClockKit.
if complication.family == .graphicRectangular {
// Display a random number string on the body.
let template = CLKComplicationTemplateGraphicRectangularLargeImage()
template.textProvider = CLKSimpleTextProvider(text: "---")
let image = UIImage(named: "imageFromWatchExtensionAssets") ?? UIImage()
template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
// Pass the entry to ClockKit.
handler(template)
}else {
handler(nil);
return
}
}
sending small packets to the watch (will not send images!)
func updateHeartRate(with sample: HKQuantitySample){
let context: [String: Any] = ["title": "String from phone"]
do {
try WCSession.default.updateApplicationContext(context)
} catch {
print("Failed to transmit app context")
}
}
Transferring images and files:
func uploadImage(_ image: UIImage, name: String, title: String = "") {
let data: Data? = UIImagePNGRepresentation(image)
do {
let fileManager = FileManager.default
let documentDirectory = try fileManager.url(for: .cachesDirectory,
in: .userDomainMask,
appropriateFor:nil,
create:true)
let fileURL = try FileManager.fileURL("\(name).png")
if fileManager.fileExists(atPath: fileURL.path) {
try fileManager.removeItem(at: fileURL)
try data?.write(to: fileURL, options: Data.WritingOptions.atomic)
} else {
try data?.write(to: fileURL, options: Data.WritingOptions.atomic)
}
if WCSession.default.activationState != .activated {
print("session not activated")
}
fileTransfer = WCSession.default.transferFile(fileURL, metadata: ["name":name, "title": title])
}
catch {
print(error)
}
print("Completed transfer \(name)")
}

Load local web files & resources in WKWebView

Unlike with UIWebView and previous versions of WKWebView (iOS 10 & macOS 10.12), the default load operation for local files has moved from Bundle.main.path to Bundle.main.url. Similarly, loadFileURL has also become the default function to load local resources in WKWebView.
I know that .path and .url are entirely different and have both worked in the past – .path historically being the default-chosen method; however, it seems that the latest versions of Swift have broken most, if not all, .path solutions. The .path solutions seem to now flatten the directory hierarchy, putting all of the CSS, JS, and any other sub-directory contents, into one big directory. This causes loading errors when WKWebView attempts to load index.html, for example, with a linked, sub-folder stylesheet (ie. /css/style.css).
After seeing numerous questions and countless uncertain/broken answers to match, is there a quick and painless solution for implementing a WKWebView that can load local resources (including linked CSS/JS files), without any workarounds?
Updated for Swift 4, Xcode 9.3
This methods allows WKWebView to properly read your hierarchy of directories and sub-directories for linked CSS, JS and most other files. You do NOT need to change your HTML, CSS or JS code.
Solution (Quick)
Add the web folder to your project (File > Add Files to Project)
Copy items if needed
Create folder references *
Add to targets (that are applicable)
Add the following code to the viewDidLoad and personalize it to your needs:
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "website")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
Solution (In-Depth)
Step 1
Import the folder of local web files anywhere into your project. Make sure that you:
☑️ Copy items if needed
☑️ Create folder references (not "Create groups")
☑️ Add to targets
Step 2
Go to the View Controller with the WKWebView and add the following code to the viewDidLoad method:
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "website")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
index – the name of the file to load (without the .html extension)
website – the name of your web folder (index.html should be at the root of this directory)
Conclusion
The overall code should look something like this:
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.uiDelegate = self
webView.navigationDelegate = self
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "Website")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
}
}
If any of you have further questions about this method or the code, I'll do my best to answer!
This work For Me:
WKWebView *wkwebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
wkwebView.navigationDelegate = self;
wkwebView.UIDelegate = self;
[wkwebView.configuration.preferences setValue:#"TRUE" forKey:#"allowFileAccessFromFileURLs"];
NSURL *url = [NSURL fileURLWithPath:YOURFILEPATH];
[wkwebView loadFileURL:url allowingReadAccessToURL:url.URLByDeletingLastPathComponent];
[self.view addSubview:wkwebView];
I was facing the same issue my condition was, I am downloading some
HTML content from the server and save it to the Document directory and
show it inside the application. The same controller uses the LIVE URL
also I have to put the condition with url scheme. Tested on iOS 13 Xcode 11
if Url.scheme == "file" as String {
wkWebView.loadFileURL(Url, allowingReadAccessTo: Url)
}
else {
let request = URLRequest.init(url: Url, cachePolicy:.reloadIgnoringLocalCacheData, timeoutInterval:60)
wkWebView.load(request)
}
it worked perfect for me
1, Open project setting and got to Build Phases tab, open Link Binary with Libraries. Add Webkit framework.
2, Add the html to your project (Copy items if needed)
3, add this to your code:
#IBOutlet weak var webView: WKWebView!
4, viewDidLoad
viewDidLoad
It is possible to use resources from you project and even shared libraries inside html code with WKWebView.
For instance, I show how to load pic.png from bundle that contains ResourceContainingBundleClassNamed class.
NSSTring * htmlCode = #"<img src='pic.png'>";
NSBundle * bundle = [NSBundle bundleForClass:[ResourceContainingBundleClassNamed class]];
NSURL * base = bundle.resourceURL;
WKWebView * represent = [WKWebView new];
[represent loadHtmlString: htmlCode baseURL: base];
The magic is in resourceURL bundle property. If you just get path for desired file, then convert it to URL - no success.