Turn a String value to a URL value - ios

im redoing my ode after getting mixed up with some concepts of swift. Have in mind im new to swift.
In my project there are currently 2 ViewControllers, in the first one there is a UITextField and a UIButton. in the second one there is a UIWebView.
I know that the UIWebView only allows a URL type address, so i want the text introduced in the UITextField to be a URL, So how do i change the string introduced into a URL and display that value (URL introduced) in the UIWebView? Should i store that value in a global variable? I really tried all...

You can send it to the nextVC via Segue , or store it in defaults and read it there
let myUrl = URL(string:textfield.text)
self.performSegue(withIdentifier: "goToNext", sender:myUrl)
//
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let let next = segue.destination as? nextVC {
next.currentUrl = sender as! URL
}
}
//
class nextVC : UIViewController
{
var currentUrl:URL?
}

It is very important to check if the user has entered a valid url. you can check that in Second viewController while loading the request from url.
Here is the overall code :
First viewController :
var url: String!
url = URL(string:textfield.text) // Convert text to url
self.performSegue(withIdentifier: "yourIdentifier", sender:url) // Go from one VC to other.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let let vc = segue.destination as? nextVC {
vc.url = self.url
}
}
Second viewController :
var url: String! // Global var
if url.isUrl
{
webView.loadRequest(URLRequest(url: url!))
}
else
{
print("invalid url")
}
Validate URL string extension :
extension String {
var isUrl: Bool {
// for http://regexr.com checking
// (?:(?:https?|ftp):\/\/)(?:xn--)?(?:\S+(?::\S*)?#)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[#-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?
let schemes = URLSchemes.getAllSchemes(separetedBy: "|").replacingOccurrences(of: "://", with: "")
let regex = "(?:(?:\(schemes)):\\/\\/)(?:xn--)?(?:\\S+(?::\\S*)?#)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[#-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?"
let regularExpression = try! NSRegularExpression(pattern: regex, options: [])
let range = NSRange(location: 0, length: self.characters.count)
let matches = regularExpression.matches(in: self, options: [], range: range)
for match in matches {
if range.location == match.range.location && range.length == match.range.length {
return true
}
}
return false
}
var toURL: URL? {
let urlChecker: (String)->(URL?) = { url_string in
if url_string.isUrl, let url = URL(string: url_string) {
return url
}
return nil
}
if !contains(".") {
return nil
}
if let url = urlChecker(self) {
return url
}
let scheme = URLSchemes.detectScheme(urlString: self)
if scheme == .unknown {
let newEncodedString = URLSchemes.http.rawValue + self
if let url = urlChecker(newEncodedString) {
return url
}
}
return nil
}
}

Here is an example of how to do this.
Here is the gist:
ViewController:
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var urlTextView: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let webViewController = segue.destination as? WebViewController {
if let url = sender as? URL {
webViewController.urlToLoad = url
}
}
}
#IBAction func gotoURLButtonAction(_ sender: Any) {
let url = self.urlTextView.text ?? ""
if url.count > 0 {
if isProperHTTPUrl(str: url) {
self.performSegue(withIdentifier: "ViewControllerToWebViewController", sender: URL(string: url))
} else {
showError("not proper url format")
}
} else {
showError("must set url")
}
}
func isProperHTTPUrl(str:String) -> Bool {
let re = try! NSRegularExpression(pattern: "(?i)https?:\\/.*", options: [])
return re.numberOfMatches(in: str, options: .anchored, range: NSRange(location: 0, length: str.count)) > 0
}
}
WebViewController:
class WebViewController: UIViewController {
var urlToLoad:URL?
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let url = self.urlToLoad {
self.webView.loadRequest(URLRequest(url: url))
}
}
public func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
showError("Unable to load, \(error)")
}
}

Related

DispatchGroup with Asynchronous function

I use Alamofire for get a request. I have two UIViewControllers and I use prepare (segue) function for send the data between the both.
On my first view controller, I use Alamofire but when I use prepare (segue), all my informations are empty.
#IBAction func loginPage(_ sender: UIButton) {
let group = DispatchGroup()
Helper().alomofirePost(URL: "http://192.168.1.7/app_dev.php/login_check", Paramaters: paramaters) { contenuJSON in
if (contenuJSON["connected"].stringValue == "true") {
group.enter()
self.dashboad()
group.leave()
group.notify(queue: DispatchQueue.main) {
//print(self.image) // EMPTY
print(self.info[0]) // EMPTY FATAL ERROR INDEXT OUT OF RANGE
self.performSegue(withIdentifier: "Dashboard", sender: self)
}
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Dashboard" {
let success = segue.destination as! DashboardViewController
success.profil = self.image
}
}
func dashboad() {
// Other Function
//self.image = addPicProfil()
self.info = add_info(url: "http://192.168.1.7/app_dev.php/dashboard/info")
}
func add_info(url: String) -> [String] {
var info = [String]()
Helper().alomofireGet(URL: url) { contentJSON in
var content = contentJSON
print(content)
info.append(contentJSON["userFirstName"].stringValue)
info.append(contentJSON["countDevices"].stringValue)
info.append(contentJSON["earnedThisYearsEUR"].stringValue)
info.append(contentJSON["countCampaigns"].stringValue)
}
return (info)
}
In my Helper File I have :
func alomofireGet(URL: String, onCompletion:#escaping ((JSON) -> Void)) {
var contentJSON = JSON()
Alamofire.request(URL, method: .get).responseJSON() { (reponse) in
if reponse.result.isSuccess {
contentJSON = JSON(reponse.result.value!)
} else {
contentJSON = JSON(reponse.result.error!)
}
onCompletion(contentJSON)
}
}
func alomofirePost(URL: String, Paramaters: Dictionary<String, Any>, onCompletion: #escaping ((_ response: JSON) -> Void)) {
Alamofire.request(URL, method: .post, parameters: Paramaters).validate().responseJSON { (reponse) in
var contenuJSON = JSON()
if reponse.result.isSuccess {
contenuJSON = JSON(reponse.result.value!)
} else {
contenuJSON = JSON(reponse.result.error!)
}
onCompletion(contenuJSON)
}
}
You mess use DispatchQueue info is nil as you think that this
self.info = add_info(url: "http://192.168.1.7/app_dev.php/dashboard/info")
will add the asynchronous values appended but it will return an empty array , you need
func add_info(url: String,completion:#escaping(_ arr:[String]) -> ()) {
var info = [String]()
Helper().alomofireGet(URL: url) { contentJSON in
print(contentJSON)
info.append(contentJSON["userFirstName"].stringValue)
info.append(contentJSON["countDevices"].stringValue)
info.append(contentJSON["earnedThisYearsEUR"].stringValue)
info.append(contentJSON["countCampaigns"].stringValue)
completion(info)
}
}

Preform segue inside closure

I am trying to display the value of btc in a separate view controller but EthViewController is not changing if I set the label equal to btc inside of a closure.
func btcValue(completion: #escaping((String) -> ())){ //Added Line
let url = URL(string: "https://api.coindesk.com/v1/bpi/currentprice.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print ("Error!")
} else {
if let content = data {
do {
let myJson = try JSONSerialization.jsonObject(with: content) as! [String:Any]
if let rates = myJson["bpi"] as? [String:Any] {
if let currency = rates["USD"] as? [String:Any] {
if let btc = currency["rate"] as? String {
completion(btc) //Added Line
}
}
}
}
catch{
print(error)
}
}
}
}
task.resume()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let EthViewController = segue.destination as! EthViewController
btcValue { (btc) in
print(btc)
EthViewController.ethprice_string = btc
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
performSegue(withIdentifier: "segue_eth", sender: self)
}
Is there anyway that I can preform a segue from inside the closure or return the value btc outside of the closure? Any help is greatly appreciated.

Not able to get the cover , profile photo to display in screen

I am currently working on facebook login app.And i am trying to display user cover photo and user profile pic.
But here i was not able to pic the cover, profile photo and not able to populate in my image views.
here my code :
func getFacebookGraphAndSegue(token:String) {
guard let req = FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"email,name, first_name, last_name,id,cover,picture.width(100).height(100)"], tokenString: token, version: nil, httpMethod: "GET") else {
print("could not get Facebook user info")
return
}
req.start(completionHandler: { (connection, result, error) in
if(error == nil) {
print("result \(result)")
guard let userInfo = result as? [String: AnyObject] else {print("bad result");return}
let name = userInfo["name"] as? String ?? ""
// let cover = (userInfo["cover"] as? UIImage!)["source"]{
// var coverUrl = cover as? String
//
// }
self.performSegue(withIdentifier: "loginSegue", sender: userInfo)
}
else {
print("error \(error)")
}
})
}
//--------------------------------------------------------------------------------------------
// MARK: - Segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loginSegue" {
if let vc = segue.destination as? UserViewController {
if let info = sender as? [String: AnyObject] {
let email = info["email"] as? String ?? ""
let name = info["name"] as? String ?? ""
let firstName = info["first_name"] as? String ?? ""
let lastName = info["last_name"] as? String ?? ""
let lbl:String = "\nNAME:\(name)"
print(lbl)
vc.info = lbl
}
}
}
}
the commented line in above function is i tried to get the cover photo as well as profile pic and below i need to display.
In my next screen only i will display user name, profile pic,cover photo :
#IBOutlet weak var labelUserInfo: UILabel!
#IBOutlet weak var coverpageImage: UIImageView!
#IBOutlet weak var profilePicImage: UIImageView!
var info:String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.labelUserInfo.text = info
}
#IBAction func onLogout(_ sender: UIButton) {
//Facebook Logout
print("on Facebook Logout Tapped")
let loginManager = LoginManager()
loginManager.logOut()
self.performSegue(withIdentifier: "logouSegue", sender: nil)
}
please help me out. How can i get the cover , profile pic to display in my screen / image view.
thanks

Swift 3.0 iOS10 Passing Async data through a segue sender for Firebase

I have a log in page that collects a username and password. On submit, Its sends to the database to retrieve our servers access key. I do this through an asynchronous JSON POST using session.dataTask. When I retrieve the JSON Object I parse the key out of it. I want to pass it to the next page, retrieve a firebase token and then send both pieces of data back to the server for DB storage. I have created a "prepare for segue" function that collects the variable and passes it to a variable on the next page. I believe I am not setting up the sequence of events correctly or that the data isn't making it out of the Async container. Can someone have a look at these two files and see where I am getting it wrong?
Here is the first page I want to segue away from after making the REST web service call...
loginVC.swift:
import UIKit
class LoginVC: UIViewController {
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var validationBox: UITextView!
#IBAction func logInAction(_ sender: UIButton) {
guard let user = username.text, !user.isEmpty else {
validationBox.text = "Please enter valid credentials"
return
}
guard let pass = password.text, !pass.isEmpty else {
validationBox.text = "Please enter valid credentials"
return
}
let params = ["sUser": username.text!, "sPass": password.text!]
let url = URL(string: "restWebServiceURL")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else { return }
guard let data = data else { return }
do {
if let parsedJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
let parsedData = parsedJSON["d"] as! [String:Any]
let key = parsedData["key"] as! String
DispatchQueue.main.async {
print(key)
self.performSegue(withIdentifier: "FirebaseVC", sender: key)
}
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
func sayHello() {
print("Hello!")
}
func sayGoodbye() {
print("Goodbye!")
}
override func viewDidLoad() {
super.viewDidLoad()
validationBox.text = "Ready..."
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let FirebaseInit = segue.destination as? FirebaseVC {
if let sKey = sender as? String {
print("prepare - " + sKey)
FirebaseInit.sessionKey = sKey
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is the page I want to go to to receive the data access key ...
FirebaseVC.swift:
import UIKit
class FirebaseVC: UIViewController {
private var _sessionKey = String()
var sessionKey : String {
get { return _sessionKey }
set { _sessionKey = newValue }
}
#IBOutlet weak var sessionKeyTestBox: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
print(_sessionKey)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Feel free to suggest a better way to pass the data to the next page. Thanks...
It turns out I was correct in my assumption the the chain of events was off. Following the model suggested by #achrefGassoumi, I moved the datatask to a Singleton Service here:
import Foundation
struct CallWebService {
static let sharedInstance = CallWebService()
func logInToCaduceus(u: String, p: String, completion: #escaping (_ sKey: String) -> ()) {
let params = ["sUser": u, "sPass": p]
let url = URL(string: "https://telemed.caduceususa.com/ws/telemed.asmx/telemedLogin")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else { return }
guard let data = data else { return }
do {
if let parsedJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
let parsedData = parsedJSON["d"] as! [String:Any]
let key = parsedData["key"] as! String
DispatchQueue.main.async {
completion(key)
}
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
}
Then my two controllers look like this:
LoginVC
import UIKit
class LoginVC: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.destination.isKind(of: FirebaseVC.self)) {
let vc = segue.destination as! FirebaseVC
if let sKey = sender as? String {
vc.sessionKey = sKey
}
}
}
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var validationBox: UITextView!
#IBAction func logInAction(_ sender: UIButton) {
guard let user = username.text, !user.isEmpty else {
validationBox.text = "Please enter valid credentials"
return
}
guard let pass = password.text, !pass.isEmpty else {
validationBox.text = "Please enter valid credentials"
return
}
CallWebService.sharedInstance.logInToCaduceus(u: username.text!, p: password.text!, completion: {(sessionKey: String) -> Void in
print(sessionKey)
self.performSegue(withIdentifier: "FirebaseVC", sender: sessionKey)
}
)
}
override func viewDidLoad() {
super.viewDidLoad()
//validationBox.textAlignment = .center
validationBox.text = "Ready..."
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
AND THE receiving FirebaseVC
import UIKit
class FirebaseVC: UIViewController {
private var _sessionKey = String()
var sessionKey : String {
get { return _sessionKey }
set { _sessionKey = newValue }
}
#IBOutlet weak var sessionKeyTestBox: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
sessionKeyTestBox.text = _sessionKey
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Excuse my (non-swift) Javascript terminology but essentially I moved the data call into a service and then place a callback method in the service with the completion method to ensure the the performSegue doesn't fire until the data has been received and parsed out. So when i submit the log in form data to the server the segue doesn't fire until that async call has been completed.

How do I pass JSON data between controller in Swift 3.0?

I have these JSON data:
{"login":"ET001","email":"email#try.com"}
In Swift 3.0, I created two files which are LoginVC and ViewController.
ViewController can only be accessed after LoginVC verified the credentials. So far I managed to make the login access the ViewController page based on "success" JSON data from database.
But my next goal is to pass the JSON data "[login]" from LoginVC into ViewController.
In ViewController, I created UILabel "loginLbl" to display the JSON value from LoginVC.
How do update my code?
LoginVC.swift
import UIKit
class LoginVC: UIViewController {
#IBOutlet var _login: UITextField!
#IBOutlet var _pass: UITextField!
#IBOutlet var outputLbl: UILabel!
var login: String!
var pass: String!
override func viewDidLoad() {super.viewDidLoad()}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "tocey"{
if let destination = segue.destination as? ViewController {
destination.passedData = self.outputLbl.text
print("Sender value is : \(sender)")
}
}
}
#IBAction func loginData(_ sender: Any) {
login = _login.text
pass = _pass.text
if(login == "" || pass == "") {
return
}
else {
let url = URL(string: "http://localhost/login.php")
let session = URLSession.shared
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST"
let paramToLogin = "login=\(login!)&pass=\(pass!)"
request.httpBody = paramToLogin.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
if error != nil {
return
}
else {
do {
if let json = try JSONSerialization.jsonObject(with: data!) as? [String: String] {
DispatchQueue.main.async {
let success = Int(json["success"]!)
let loginvaluefromdb = json["login"]
if(success == 1){
self.outputLbl.text = loginvaluefromdb;
let abc = json["login"]
self.performSegue(withIdentifier: "tocey", sender: abc)
return
}
}
}
}
catch {
}
}
})
task.resume()
}
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet var loginLbl: UILabel!
var passedData: String!
override func viewDidLoad() {
super.viewDidLoad()
loginLbl.text = passedData
}
}
How to pass it into UILabel loginLbl?
Once you identify that login data is correct in your response you need to push your viewController in navigation controller and take one dictionary in your viewController and assign json to that dictionary.
if let json = try JSONSerialization.jsonObject(with: data!) as? [String: String] {
DispatchQueue.main.async {
let success = Int(json["success"]!)
let loginvaluefromdb = json["login"]
if(success == 1){
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "yourViwController") as! yourViwController
viewController.dictJson = json
self.navigationController?.pushViewController(viewController, animated: true)
}
if(success == 1){
self.outputLbl.text = loginvaluefromdb;
// Here you trigger the segue
self.performSegue(withIdentifier: "goToViewController", sender: loginvaluefromdb)
return
}
You need to pass the data in prepare for segue method :
Here is the editec code
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToViewController" {
if let destination = segue.destination as? ViewController {
// Here you will copy tha data you want to pass
destination.passedData = sender as? String
print("Sender Value: \(sender)")
}
}
}

Resources