import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate{
var webView: WKWebView!
lazy var progressbar: UIProgressView = UIProgressView();
deinit {
self.webView.removeObserver(self, forKeyPath: "estimatedProgress")
self.webView.scrollView.removeObserver(self, forKeyPath: "contentOffset")
}
override func viewDidLoad() {
super.viewDidLoad()
let webConfiguration = WKWebViewConfiguration();
webConfiguration.dataDetectorTypes = [.all]
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.translatesAutoresizingMaskIntoConstraints = false
self.webView.navigationDelegate = self
view.addSubview(webView)
self.webView.frame = self.view.frame
self.webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
webView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
])
self.webView.addSubview(self.progressbar)
self.setProgressBarPosition()
webView.scrollView.addObserver(self, forKeyPath: "contentOffset", options: .new, context: nil)
self.progressbar.progress = 0.1
self.progressbar.trackTintColor = .white
self.progressbar.progressTintColor = UIColor(red:244/255.0, green: 66/255.0, blue: 8/255.0, alpha: 1)
webView.addObserver(self, forKeyPath: "estimatedProgress",options: .new, context: nil)
let myURL = URL(string:"https://ya.ru")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
webView.scrollView.addSubview(refreshControl)
if #available(iOS 11.0, *){
webView.scrollView.contentInsetAdjustmentBehavior = .never;
}
webView.navigationDelegate = self
webView.allowsBackForwardNavigationGestures = true
}
func setProgressBarPosition(){
self.progressbar.translatesAutoresizingMaskIntoConstraints = false
self.webView.removeConstraints(self.webView.constraints)
self.webView.addConstraints([
self.progressbar.topAnchor.constraint(equalTo: self.webView.topAnchor, constant: self.webView.scrollView.contentOffset.y * -1),
self.progressbar.leadingAnchor.constraint(equalTo: self.webView.leadingAnchor),
self.progressbar.trailingAnchor.constraint(equalTo: self.webView.trailingAnchor)
])
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
switch keyPath{
case "estimatedProgress":
if self.webView.estimatedProgress >= 1.0 {
UIView.animate(withDuration: 0.3, animations: {() in
self.progressbar.alpha = 0.0
}, completion: { finished in
self.progressbar.setProgress(0.0, animated: false)
})
} else{
self.progressbar.isHidden = false
self.progressbar.alpha = 1.0
progressbar.setProgress(Float(self.webView.estimatedProgress), animated: true)
}
case "contentOffset":
self.setProgressBarPosition()
default:
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
@objc func reloadWebView(_ sender: UIRefreshControl){
webView.reload()
sender.endRefreshing()
}
func share(message: String, link: String){
if let link = NSURL(string: link){
let objectsToShare = [message, link] as [Any]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.present(activityVC, animated: true, completion: nil)
}
}
}
extension ViewController: WKNavigationDelegate{
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void){
guard
let url = navigationAction.request.url else{
decisionHandler(.cancel)
return
}
let string = url.absoluteString
if(string.contains("mailto:")){
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
return
}
if(string.contains("tel:")){
UIApplication.shared.open(url, options: [:], completionHandler: nil)
decisionHandler(.cancel)
return
}
if(string.contains("?share")){
let urlArr = string.components(separatedBy: "?")
let link = urlArr[0]
share(message: "", link: link)
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
}
https://josh.blog/2020/02/swiftui-webview-with-a-progress-bar
func share(message: String, link: String){
if let link = NSURL(string: link){
let objectsToShare = [message, link] as [Any]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.present(activityVC, animated: true, completion: nil)
}
}
share(message: "test", link: "https://ya.ru/")
https://stackoverflow.com/questions/37938722/how-to-implement-share-button-in-swift
Также в случае отсутствия gclid и utm определять источник по $_SERVER[‘HTTP_REFERER’] учитывая домены поисковых систем.
$seo_params=array('ga_cid','gclid','utm_source','utm_medium','utm_campaign','utm_content','utm_term');
$seo_cookies=$seo_cookies_default=array();
foreach($seo_params as $sp){
switch($sp){
case 'gclid':
$seo_cookies_default[$sp]='NULL';
break;
case 'utm_source':
$seo_cookies_default[$sp]='direct';
break;
default:
$seo_cookies_default[$sp]='none';
break;
}
}
$update_cookies=false;
if(isset($_GET['gclid'])&&$_GET['gclid']!=''){
$update_cookies=true;
foreach($seo_params as $sp){
switch($sp){
case 'gclid':
$seo_cookies[$sp]=$_GET[$sp];
break;
case 'utm_source':
$seo_cookies[$sp]='google';
break;
case 'utm_medium':
$seo_cookies[$sp]='cpc';
break;
default:
if(stristr($sp,'utm_')&&isset($_GET[$sp])&&$_GET[$sp]!=''){
$seo_cookies[$sp]=$_GET[$sp];
}
else{
$seo_cookies[$sp]='none';
}
break;
}
}
}
else{
$is_utm=false;
foreach($seo_params as $sp){
if(stristr($sp,'utm_')&&isset($_GET[$sp])&&$_GET[$sp]!=''){
$seo_cookies[$sp]=$_GET[$sp];
$is_utm=true;
$update_cookies=true;
}
else{
$seo_cookies[$sp]='none';
}
}
if(!$is_utm){
$referer=$_SERVER['HTTP_REFERER'];
if($referer!=''){
$parse_referer=parse_url($referer);
$referer_domain=$parse_referer['host'];
if($referer_domain!=''&&$referer_domain!=$_SERVER['HTTP_HOST']){
$update_cookies=true;
$organics=array('google','yandex','bing');
$is_organic=false;
foreach($organics as $organic){
if(stristr($referer_domain,$organic)){
$is_organic=true;
foreach($seo_params as $sp){
switch($sp){
case 'utm_source':
$seo_cookies[$sp]=$organic;
break;
case 'utm_medium':
$seo_cookies[$sp]='organic';
break;
default:
if(stristr($sp,'utm_')){
$seo_cookies[$sp]='none';
}
break;
}
}
}
}
if(!$is_organic){
foreach($seo_params as $sp){
switch($sp){
case 'utm_source':
$seo_cookies[$sp]=$referer_domain;
break;
case 'utm_medium':
$seo_cookies[$sp]='referral';
break;
default:
if(stristr($sp,'utm_')){
$seo_cookies[$sp]='none';
}
break;
}
}
}
}
}
}
}
if($_COOKIE['_ga']!=''){
$ga_cookie_split=explode('.',$_COOKIE['_ga']);
if($ga_cookie_split[2]&&$ga_cookie_split[3]){
$ga_cid=$ga_cookie_split[2].'.'.$ga_cookie_split[3];
$seo_cookies['ga_cid']=$ga_cid;
}
}
foreach($seo_params as $sp){
if($update_cookies||!isset($_COOKIE['local_'.$sp])||$sp=='ga_cid'){
$cv=($seo_cookies[$sp]!='')?$seo_cookies[$sp]:$seo_cookies_default[$sp];
setcookie('local_'.$sp,$cv,time()+31536000,'/',$_SERVER['HTTP_HOST']);
$_COOKIE['local_'.$sp]=$cv;
}
}
function add_seo_params_to_letter(){
global $seo_params;
$text='<hr/>';
foreach($seo_params as $sp){
$text.=$sp.': '.$_COOKIE['local_'.$sp]."<br/>";
}
return $text;
}
Функция add_seo_params_to_letter
используется для добавления в письма сохраненных данных.
Например в modx:
// /core/components/formit/model/formit/fihooks.class.php
public function email(array $fields=array()){
// ...
$message.=add_seo_params_to_letter();
// ...
}
Также в случае отсутствия gclid и utm определять источник по document.referrer
учитывая домены поисковых систем.
document.addEventListener("DOMContentLoaded",function(event){
var seo_params=new Array('ga_cid','gclid','utm_source','utm_medium','utm_campaign','utm_content','utm_term');
var seo_cookies=new Array();
var seo_cookies_default=new Array();
var params=new URLSearchParams(location.search);
seo_params.forEach((sp)=>{
switch(sp){
case 'gclid':
seo_cookies_default[sp]='NULL';
break;
case 'utm_source':
seo_cookies_default[sp]='direct';
break;
default:
seo_cookies_default[sp]='none';
break;
}
});
var update_cookies=false;
if(params.has('gclid')&¶ms.get('gclid')!=''&¶ms.get('gclid')!='none'){
update_cookies=true;
seo_params.forEach((sp)=>{
switch(sp){
case 'gclid':
seo_cookies[sp]=params.get(sp);
break;
case 'utm_source':
seo_cookies[sp]='google';
break;
case 'utm_medium':
seo_cookies[sp]='cpc';
break;
default:
if(sp.indexOf('utm_')!=-1&¶ms.has(sp)&¶ms.get(sp)!=''){
seo_cookies[sp]=params.get(sp);
}
else{
seo_cookies[sp]='none';
}
break;
}
});
}
else{
var is_utm=false;
seo_params.forEach((sp)=>{
if(sp.indexOf('utm_')&¶ms.has(sp)&¶ms.get(sp)!=''){
seo_cookies[sp]=params.get(sp);
is_utm=true;
update_cookies=true;
}
else{
seo_cookies[sp]='none';
}
});
if(!is_utm){
var referer=document.referrer;
if(referer!=''){
var referer_domain=referer.split('/')[2];
if(referer_domain!=''&&referer_domain!=location.hostname){
update_cookies=true;
var organics=new Array('google','yandex','ya.','bing');
var is_organic=false;
organics.forEach((organic)=>{
if(referer_domain.indexOf(organic)!=-1){
if(organic=='ya.'){
organic='yandex'
}
is_organic=true;
seo_params.forEach((sp)=>{
switch(sp){
case 'utm_source':
seo_cookies[sp]=organic;
break;
case 'utm_medium':
seo_cookies[sp]='organic';
break;
default:
if(sp.indexOf('utm_')){
seo_cookies[sp]='none';
}
break;
}
});
}
});
if(!is_organic){
seo_params.forEach((sp)=>{
switch(sp){
case 'utm_source':
seo_cookies[sp]=referer_domain;
break;
case 'utm_medium':
seo_cookies[sp]='referral';
break;
default:
if(sp.indexOf('utm_')!=-1){
seo_cookies[sp]='none';
}
break;
}
});
}
}
}
}
}
var ga_cookie=document.cookie.match(new RegExp('(^| )_ga=([^;]+)'));
if(ga_cookie[2]){
var ga_cookie_split=ga_cookie[2].split(".");
var ga_cid=ga_cookie_split[2]+'.'+ga_cookie_split[3];
seo_cookies['ga_cid']=ga_cid;
}
seo_params.forEach((sp)=>{
var sp_cookie=document.cookie.match(new RegExp('(^| )local_'+sp+'=([^;]+)'));
if(update_cookies||!sp_cookie||sp=='ga_cid'){
var cv=(seo_cookies[sp]!='')?seo_cookies[sp]:seo_cookies_default[sp];
document.cookie=encodeURIComponent('local_'+sp)+'='+encodeURIComponent(cv);
}
});
var links=document.getElementsByTagName("a");
for(i=0;i<links.length;i++){
var href=links[i].href;
if(href.includes(document.domain)&&!(href.includes('#'))){
seo_params.forEach((sp)=>{
var sp_cookie=document.cookie.match(new RegExp('(^| )local_'+sp+'=([^;]+)'));
if(sp_cookie[2]){
href+=(href.includes('?'))?"&":"?";
href+=sp+"="+sp_cookie[2];
}
})
links[i].href=href;
}
}
const url=new URL(window.location.href);
seo_params.forEach((sp)=>{
var sp_cookie=document.cookie.match(new RegExp('(^| )local_'+sp+'=([^;]+)'));
if(sp_cookie[2]&&!params.has(sp)){
url.searchParams.append(sp,sp_cookie[2]);
}
});
window.history.pushState(null,null,url);
});
ios webview pull to refresh
override func viewDidLoad() {
// after webView.load
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
webView.scrollView.addSubview(refreshControl)
}
@objc func reloadWebView(_ sender: UIRefreshControl){
webView.reload()
sender.endRefreshing()
}