How to do login using MVVM? - swift

I am still quite new to iOS development and I am trying to teach myself good coding practices and design patterns in Swift starting with MVVM. I need to pass the data from a completion handle in my ServiceCall class to my ViewModel. I would like assistance in understanding how I can do it and also guidance in best practices using MVVM on my code.
This is what I have done so far:
Model
struct Login {
var appVersion: String ?
var deviceID : String ?
var deviceOS : String ?
var password : String ?
var username : String ?
}
Service Call / API Client
class LoginServiceCall : NSObject, URLSessionDelegate {
let viewResponse = ThrowResponse()
func requestLogin(request: URLRequest, requestObject: Login, completion: #escaping ([NSDictionary] ? ) -> Void) {
let searchParams = Login.init(appVersion: requestObject.appVersion, deviceID: requestObject.deviceID, deviceOS: requestObject.deviceOS, password: requestObject.password, username: requestObject.username)
var request = request
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: searchParams, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: {
data, response, error -> Void in
do {
let json = try JSONSerialization.jsonObject(with: data!) as ? Dictionary<String, Any>
//completion(json!)
// This is where i would like to pass the dictionary data
} catch {
DispatchQueue.main.async {
self.viewResponse.dismissLoader()
self.viewResponse.showFailureAlert(title: "Failure", message: "")
completion(nil)
}
}
})
task.resume()
}
}
View Controller
class LoginViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var loginButton: UIButton!
#IBOutlet var usernameOrEmailTextField: UITextField ?
#IBOutlet var passwordTextField : UITextField ?
var serviceBalance = 0.0
let defaults = UserDefaults.standard
var Reach : Reachability ? = Reachability()
var viewModel : LoginViewModel ?
override func viewDidLoad() {
super.viewDidLoad()
usernameOrEmailTextField?.delegate = self
passwordTextField?.delegate = self
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
usernameOrEmailTextField?.resignFirstResponder()
passwordTextField?.resignFirstResponder()
return (true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func loginButton(_ sender: Any) {
viewModel?.setLoginObject(withUsername: usernameOrEmailTextField?.text, withPassword : passwordTextField?.text)
}
#IBAction func forgotPasswordButton(_ sender: Any) {
self.performSegue(withIdentifier: "forgotPasswordSegue", sender: nil)
}
#IBAction func registerButton(_ sender: Any) {
self.performSegue(withIdentifier: "registerUserSegue", sender: nil)
}
}
ViewModel
class LoginViewModel: NSObject {
var Reach: Reachability? = Reachability()
var login: Login?
var homeViewDictionary: [NSDictionary]?
var APIClient: LoginServiceCall!
var request = URLRequest(url: URL(string: "http://myapiuser/login")!)
func setLoginObject(withUsername username: String?, withPassword password: String?){
login?.username = username
login?.password = password
login?.appVersion = self.getAppVersion()
login?.deviceID = self.getDeviceID()
login?.deviceOS = self.getDeviceOs()
APIClient.requestLogin(request: request, requestObject: login! { (AppDictionary) in
DispatchQueue.main.async {
self.homeViewDictionary = AppDictionary
}
}, completion: ())
}
func getAppVersion() -> String { return "0.2" }
func getDeviceID() -> String {
if let deviceid = UIDevice.current.identifierForVendor?.uuidString { return deviceid }
}
func getDeviceOs() -> String {
let systemVersion = UIDevice.current.systemVersion
let model = UIDevice.current.model
return systemVersion+" "+model
}
}

Related

How to send multiple images to Firebase and retrieve them in the UIImageView in Swift

I have an app that lets users choose multiple images. The problem is that it doesn't upload and save the image to the user in Firebase and retrieve the image.
This is my code:
import UIKit
import Photos
import Firebase
import BSImagePicker
class Downloadimages: UIViewController {
#IBOutlet weak var imgView: UIImageView!
var ref: DatabaseReference?
var SelectedAssets = [PHAsset]()
var PhotoArray = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func addImagesClicked(_ sender: Any) {
// create an instance
let vc = BSImagePickerViewController()
//display picture gallery
self.bs_presentImagePickerController(vc, animated: true,
select: { (asset: PHAsset) -> Void in
}, deselect: { (asset: PHAsset) -> Void in
// User deselected an assets.
}, cancel: { (assets: [PHAsset]) -> Void in
// User cancelled. And this where the assets currently selected.
}, finish: { (assets: [PHAsset]) -> Void in
// User finished with these assets
for i in 0..<assets.count
{
self.SelectedAssets.append(assets[i])
}
self.convertAssetToImages()
}, completion: nil)
let image = UIImagePickerController()
image.delegate = self as? UIImagePickerControllerDelegate & UINavigationControllerDelegate
image.sourceType = UIImagePickerControllerSourceType.photoLibrary
image.allowsEditing = false
self.present(image, animated: true)
{
//after its completed
}
}
#objc(imagePickerController:didFinishPickingMediaWithInfo:) func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage
{
imgView.image = image
}
else
{
//error
}
self.dismiss(animated: true, completion: nil)
let storageRef = Storage.storage().reference().child("myImage.png")
if let uploadData = UIImagePNGRepresentation(self.imgView.image!){
storageRef.putData(uploadData, metadata: nil, completion:
{
(metadata, error) in
if error != nil {
print("error")
return
}
print(metadata!)
//how do I put the download URL in the metadata into my database
}
)
}
}
func convertAssetToImages() -> Void {
if SelectedAssets.count != 0{
for i in 0..<SelectedAssets.count{
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
manager.requestImage(for: SelectedAssets[i], targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: option, resultHandler: {(result, info)->Void in
thumbnail = result!
})
let data = UIImageJPEGRepresentation(thumbnail, 0.7)
let newImage = UIImage(data: data!)
self.PhotoArray.append(newImage! as UIImage)
}
self.imgView.animationImages = self.PhotoArray
self.imgView.animationDuration = 3.0
self.imgView.startAnimating()
}
print("complete photo array \(self.PhotoArray)")
}
}
and this is my post code
import Foundation
class Post {
var id:String
var author:UserProfile
var text:String
var createdAt:Date
init(id:String, author:UserProfile,text:String,timestamp:Double) {
self.id = id
self.author = author
self.text = text
self.createdAt = Date(timeIntervalSince1970: timestamp / 1000)
}
static func parse(_ key:String, _ data:[String:Any]) -> Post? {
if let author = data["author"] as? [String:Any],
let uid = author["uid"] as? String,
let username = author["username"] as? String,
let photoURL = author["photoURL"] as? String,
let url = URL(string:photoURL),
let text = data["text"] as? String,
let timestamp = data["timestamp"] as? Double {
let userProfile = UserProfile(uid: uid, username: username, photoURL: url)
return Post(id: key, author: userProfile, text: text, timestamp:timestamp)
}
return nil
}
}
this is my userProfile
import Foundation
class UserProfile {
var uid:String
var username:String
var photoURL:URL
init(uid:String, username:String,photoURL:URL) {
self.uid = uid
self.username = username
self.photoURL = photoURL
}
}
and this is
import Foundation
import Firebase
class UserService {
static var currentUserProfile:UserProfile?
static func observeUserProfile(_ uid:String, completion: #escaping ((_ userProfile:UserProfile?)->())) {
let userRef = Database.database().reference().child("users/profile/\(uid)")
userRef.observe(.value, with: { snapshot in
var userProfile:UserProfile?
if let dict = snapshot.value as? [String:Any],
let username = dict["username"] as? String,
let photoURL = dict["photoURL"] as? String,
let url = URL(string:photoURL) {
userProfile = UserProfile(uid: snapshot.key, username: username, photoURL: url)
}
completion(userProfile)
})
}
}

Ensure phone number UITextField has a "+" prefix

I have several UITextField where a user can insert phone numbers into it. When I click the send button, it sends an automated message to the numbers listed. Everything works well but what I want to do is that when I click on the Send Button, I want it to check if the UITextFields that has text in it has a + symbol in front of the phone number listed before connecting with my server to send the automated message. How do I go about sending an alert to the user if the phone number listed does not have a + symbol?
ViewController:
class ViewController: UIViewController {
#IBOutlet weak var scrollviewcontact: UIScrollView!
#IBOutlet weak var viewcontact: UIView!
#IBOutlet weak var phonenumber: UITextField!
#IBOutlet weak var phonenumber1: UITextField!
#IBOutlet weak var phonenumber2: UITextField!
#IBOutlet weak var phonenumber3: UITextField!
var currentTextField: UITextField?
private let contactPicker = CNContactPickerViewController()
override func viewDidLoad() {
super.viewDidLoad()
phonenumber.textContentType = .telephoneNumber
phonenumber1.textContentType = .telephoneNumber
phonenumber2.textContentType = .telephoneNumber
phonenumber3.textContentType = .telephoneNumber
}
#IBAction func sendbutton(_ sender: Any) {
var numArray: Array<Any>
numArray = [phonenumber.text!, phonenumber1.text!, phonenumber2.text!, phonenumber3.text!]
let Url = String(format: "//URL")
guard let serviceUrl = URL(string: Url) else { return }
var request = URLRequest(url: serviceUrl)
request.httpMethod = "POST"
request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: numArray, options:[]) else {
return
}
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
print("json ", json)
} catch {
print(error)
}
}
}.resume()
}
extension ViewController: CNContactPickerDelegate {
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
let phoneNumberCount = contact.phoneNumbers.count
guard phoneNumberCount > 0 else {
dismiss(animated: true)
return
}
if phoneNumberCount == 1 {
setNumberFromContact(contactNumber: contact.phoneNumbers[0].value.stringValue)
}else{
let alertController = UIAlertController(title: "Select one of the numbers", message: nil, preferredStyle: .alert)
for i in 0...phoneNumberCount-1 {
let phoneAction = UIAlertAction(title: contact.phoneNumbers[i].value.stringValue, style: .default, handler: {
alert -> Void in
self.setNumberFromContact(contactNumber: contact.phoneNumbers[i].value.stringValue)
})
alertController.addAction(phoneAction)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: {
alert -> Void in
})
alertController.addAction(cancelAction)
dismiss(animated: true)
self.present(alertController, animated: true, completion: nil)
}
}
func setNumberFromContact(contactNumber: String) {
var contactNumber = contactNumber.replacingOccurrences(of: "-", with: "")
contactNumber = contactNumber.replacingOccurrences(of: "(", with: "")
contactNumber = contactNumber.replacingOccurrences(of: ")", with: "")
contactNumber = contactNumber.replacingOccurrences(of: " ", with: "")
currentTextField?.text = String(contactNumber)
}
func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
}
}
The goal that I'm trying to achieve is that when the Send Button is clicked, it checks which UITextField has text in it, and if it doesn't have a + as a prefix, an alert message should pop up.
You can try
let numArray = [phonenumber.text!, phonenumber1.text!, phonenumber2.text!, phonenumber3.text!]
guard numArray.filter { $0.hasPrefix("+") }.count == numArray.count else {
// alert with message make sure all textfields has + prefix
return
}
You can use the hasPrefix method. For example:
for num in numArray {
if num.hasPrefix("+") {
// do something
} else {
// do something else
}
}

How to pass data from view controller to data model in swift

I am building a simple app that talks to a web service.
I have used the delegates method to communicate data (from my model to view controller).
But I am not sure how to read the data from view controller (text_field.text) in my model. I need to do that so that I can pass the right parameter to my webservice
my view controller is:
import UIKit
class ViewController: UIViewController,HomeModelDelegate {
var homeModel = HomeModel()
#IBOutlet weak var loginid: UITextField!
#IBOutlet weak var pwd: UITextField!
#IBAction func submit(_ sender: UIButton) {
homeModel.chkpwd()
//Here viewcontroller is assigning itself to the homemodel's delegate property
homeModel.delegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
loginid.layer.cornerRadius=10
pwd.layer.cornerRadius = 10
}
func itemsDownloaded(locations: [Location]) {
loginid.text = locations[0].pwd
}
}
My model code is:
import UIKit
protocol HomeModelDelegate{
func itemsDownloaded(locations:[Location])
}
class HomeModel: NSObject
{
var delegate:HomeModelDelegate?
func chkpwd()
{
//Hit the webservice url
let x = ViewController()
let z = x.loginid
let serviceUrl = "http://www.xyz.in/webservice.php?loginid=(loginid.text)"
//download the json data
let url = URL(string: serviceUrl)
if let url = url {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url, completionHandler:
{ (data, response, error) in
if error == nil {
//succeeded
self.parseJson(data!)
}
else {
//failed
}
})
task.resume()
}
}
func parseJson(_ data:Data){
var locArray = [Location]()
do{
let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as! [Any]
for jsonResult in jsonArray{
let jsonDict = jsonResult as! [String:String]
let loc = Location(pwd: jsonDict["loginid"]!, loginid: jsonDict["pwd"]!)
locArray.append(loc)
//pass the location back to the delegate
delegate?.itemsDownloaded(locations: locArray)
}
}
catch{
print("An error occured")
}
}
}
Please try this :
import UIKit
class ViewController: UIViewController,HomeModelDelegate {
var homeModel = HomeModel()
#IBOutlet weak var loginid: UITextField!
#IBOutlet weak var pwd: UITextField!
#IBAction func submit(_ sender: UIButton) {
homeModel.z = loginid.text! // ASSIGNING z here
homeModel.chkpwd()
//Here viewcontroller is assigning itself to the homemodel's delegate property
homeModel.delegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
loginid.layer.cornerRadius=10
pwd.layer.cornerRadius = 10
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func itemsDownloaded(locations: [Location]) {
loginid.text = locations[0].pwd
}
}
Model :
import UIKit
protocol HomeModelDelegate{
func itemsDownloaded(locations:[Location])
}
class HomeModel: NSObject
{
var z:String = "" // INITIALIZING z
var delegate:HomeModelDelegate?
func chkpwd()
{
print(z) // CALLING z
//Hit the webservice url
let serviceUrl = "http://www.xyz.in/webservice.php?loginid=(loginid.text)"
//download the json data
let url = URL(string: serviceUrl)
if let url = url {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url, completionHandler:
{ (data, response, error) in
if error == nil {
//succeeded
self.parseJson(data!)
} else {
//failed
}
})
task.resume()
}
}
func parseJson(_ data:Data){
var locArray = [Location]()
do{
let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as! [Any]
for jsonResult in jsonArray{
let jsonDict = jsonResult as! [String:String]
let loc = Location(pwd: jsonDict["loginid"]!, loginid: jsonDict["pwd"]!)
locArray.append(loc)
//pass the location back to the delegate
delegate?.itemsDownloaded(locations: locArray)
}
} catch {
print("An error occured")
}
}
}

I get an error on the Swift user login screen

I've added screenshots below. I'm delighted to see you. Thank you
user login Screen
Error
override func viewDidLoad() {
super.viewDidLoad()
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnGirisYap(_ sender: Any) {
var pass : String!
I assign txtGirisSifre.text as variable "pass"
let arayüzPass = txtGirisSifre.text
var request = URLRequest(url: URL(string: "http://242.253.114.125:7001/WebApplicationo2/login")!)
request.httpMethod = "POST"
let postString = "user=emrekacan"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil
{
}
else
{
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let currencyRate = jsonResult as? NSArray
{
for i in 0..<currencyRate.count
{
if let gelenPass = (currencyRate[i] as? NSDictionary)?["pass"] as? String
{
pass = gelenPass
print(pass)
print(arayüzPass!)
}
}
If the password that is called web service is not empty in this section
if pass != nil {
self.login(pass1: pass , arayüzPass1: arayüzPass!)
}
}
}
catch
{
}
}
}
}
task.resume()
}
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
It gives an error after this section
func login(pass1 : String , arayüzPass1 : String!)
{
"SWRevealViewController" storyboard open
if pass1 == arayüzPass1 {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let resultViewController = storyBoard.instantiateViewController(withIdentifier: "RevealView") as! SWRevealViewController
self.present(resultViewController, animated:true, completion:nil)
}
}
}

JSON parsing error in Swift 2.0

I'm learning to code in Swift 2.0 and I got stuck while compiling it into simulator. The self.setLabels(data!) line displays an info Thread 1: EXC_BAD_INSTRUCTION. Can anyone help me with this? I'm doing a trial-and-error technique but no luck yet...
lass ViewController: UIViewController {
#IBOutlet weak var cityNameTextField: UITextField!
#IBOutlet weak var cityNameLabel: UILabel!
#IBOutlet weak var cityTempLabel: UILabel!
#IBAction func getWeatherDataClick(sender: AnyObject) {
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=" + cityNameTextField.text! + "")
}
override func viewDidLoad() {
super.viewDidLoad()
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=2de143494c0b295cca9337e1e96b00e0")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getWeatherData(urlString: String) {
let url = NSURL(string: urlString)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
self.setLabels(data!)
})
}
task.resume()
}
func setLabels(weatherData: NSData) {
let jsonResult = AnyObject? ()
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(weatherData, options: []) as? NSDictionary {
print(jsonResult)
}
} catch {
print(error)
}
if let name = jsonResult!["name"] as? String {
cityNameLabel.text = name
}
if let main = jsonResult!["main"] as? NSDictionary {
if let temp = main["temp"] as? Double {
cityTempLabel.text = String(format: "%.1f", temp)
}
}
};
}
First guess would be: data == nil. Your function: setLabels: is not prepared to receive nil argument. Try to change declaration of this function to:
func setLabels(weatherData: NSData?)
Or even better handle data == nil possibility before calling setLabels, in your NSURLSession block:
if let weatherData = data as? NSData {
//your data is not nil
//you can securely call setLabels
self.setLabels(weatherData)
} else {
//ooops sth goes wrong your data is nil, try to figure out why
}