Model class gets the object but doesn't fulfill the TableView - ios

I know this is a common problem but I can't find the solution whether I searched for hours so I decided to open a new question. I'm getting "[UIRefreshControl copyWithZone:]: unrecognized selector sent to instance 0x102029400" problem. When I first open the view. Loading appears but It doesn't fulfill the table. However when I check model class, It gets the values from database.
In viewcontroller declaration;
model.delegate = self
model.refresh_history(sensor_name: send_item)
// Set up a refresh control.
mTableView.refreshControl = UIRefreshControl()
mTableView.refreshControl?.addTarget(model, action: #selector(model.refresh_history), for: .valueChanged)
Delegate;
extension ChooseHistoryViewController: ModelDelegate {
func modelUpdated() {
mTableView.refreshControl?.endRefreshing()
mTableView.reloadData()
}
func errorUpdating(_ error: NSError) {
let message: String
if error.code == 1 {
message = "Error"
} else {
message = error.localizedDescription
}
let alertController = UIAlertController(title: nil,
message: message,
preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
present(alertController, animated: true, completion: nil)
}
}
and my model class;
protocol ModelDelegate {
func errorUpdating(_ error: NSError)
func modelUpdated()
}
class Model{
var user: User
static let sharedInstance = Model()
var sensor_ecg: Sensor_ECG?
var delegate: ModelDelegate?
var history: [String] = []
init()
{
user = User()
}
#objc func refresh_history(sensor_name: String){
let parameters: Parameters = ["q" : "{\"member_id\" :\"\(user.id!)\"}", "apiKey": "2ABdhQTy1GAWiwfvsKfJyeZVfrHeloQI"]
Alamofire.request("https://api.mlab.com/api/1/databases/mysignal/collections/\(sensor_name)", method: .get, parameters: parameters,encoding: URLEncoding.default, headers: nil).responseJSON{ response in
let json = JSON(data: response.data!)
print(json)
if(response.response == nil) {
return
}
let history = json[0]["date"].string!
print(history)
}
DispatchQueue.main.async {
self.delegate?.modelUpdated()
}
}

You need to change
If you declared your method as "refresh_history:" (i.e. with a parameter), you need to add a colon to the "#selector" bit.
In other words, one line changes with one character:
mTableView.refreshControl?.addTarget(model, action: #selector(model.refresh_history(sensor_name:)), for: .valueChanged)

Related

Nil is not compatible with expected argument type 'UIImage'

This is my code,
self.plistModification(urlStr: urlString, uploadedImage:nil )
May I know how to assign an image value to nil.
my plistModification function is given below, the uploadedImage value (UIImage)changed every time whenever I call the function.
func plistModification(urlStr : String , uploadedImage : UIImage)
{
self.activityView.isHidden = false
SBNetworkManager().getDetails(sortType: "", URLString: urlStr, param: "") { (dictList, error) in
self.imageUpload(imageData: uploadedImage)
DispatchQueue.main.async {
self.activityView.isHidden = true
}
if self.flag == 1
{
GIDSignIn.sharedInstance().signOut()
}
}
else
{
DispatchQueue.main.async {
self.activityView.isHidden = true
if self.flag == 1
{
let alert = UIAlertController(title: "Alert", message: dictList!["status_message"]as? String, preferredStyle: .alert)
let alertAction = UIAlertAction(title: "Ok", style: .default, handler: { (action) in
GIDSignIn.sharedInstance().signOut()
})
alert.addAction(alertAction)
self.present(alert, animated: true, completion: nil)
}
}
}
You can add an empty image just like this
self.plistModification(urlStr: urlString, uploadedImage:UIImage() )
or You can set your method argument as optional like
func plistModification(urlStr: String, uploadedImage:UIImage?)
in the second option, you can pass nil value
self.plistModification(urlStr: urlString)
//OR
self.plistModification(urlStr: urlString, uploadedImage:imageData)
//it may be image or nil
func plistModification(urlStr: string, uploadedImage:UIImage? = nil){
}
Instead of passing new image object or nil to function, you can handle it at function level declare parameter as optional with default value. So when you need to pass nil then don't write than parameter in function call

Calling a function inside enum's function in the same class

I came across a scenario where i had to call a function inside enum's function in swift 3.
The scenario is as follows:
class SomeViewController: UIViewController {
enum Address {
case primary
case secondary
func getAddress() {
let closure = { (text: String) in
showAlert(for: "")
}
}
}
func showAlert(for text: String) {
let alertController = UIAlertController(title: text, message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title:NSLocalizedString("OK", comment:"OK button title"), style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
}
As you can see from the above code I get an error on line 10 (showAlert(for: ""))
The error is:
instance member showAlert cannot be used on type SomeViewController; did you mean to use a value of this type instead?
How can I call a function from enum's function then?
Alternative approach:
You can use a static method of SomeViewController to present the alert.
Example:
static func showAlert(for text: String)
{
let alertController = UIAlertController(title: text, message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title:NSLocalizedString("OK", comment:"OK button title"), style: .cancel, handler: nil))
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
}
Using it:
enum Address
{
case primary
case secondary
func getAddress()
{
let closure = { (text: String) in
SomeViewController.showAlert(for: "")
}
closure("hello")
}
}
override func viewDidLoad()
{
super.viewDidLoad()
let addr = Address.primary
addr.getAddress()
}
The enum does not know the instance and thus can not access its members. One approach to deal with this situation would be to inform the client of the enum that something went wrong.
class SomeViewController: UIViewController {
enum Address {
case primary
case secondary
func getAddress() -> String? {
//allGood == whatever your logic is to consider a valid address
if allGood {
return "theAddress"
} else {
return nil;
}
}
}
func funcThatUsesAddress() {
let address = Address.primary
guard let addressString = address.getAddress() else {
showAlert(for: "")
return
}
// use your valid addressString here
print(addressString)
}
func showAlert(for text: String) {
let alertController = UIAlertController(title: text, message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title:NSLocalizedString("OK", comment:"OK button title"), style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
}

How to use a UIAlertController in MVVM?

I have a VC with code to show an alert:
func showMessage() {
let alertView = UIAlertController(title: "TEST",
message: self.loginViewModel.errorText,
preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "Ok", style: .destructive, handler: nil))
present(alertView, animated: true, completion: nil)
}
and I have this login logic in my viewModel which needs to trigger this function:
func submitLoginRequest(userLogin: String, loginPassword: String, loginSecret: String, deviceToken: String) {
let userLogin = UserServices.init()
manager.userServicesApiRequest(url: Endpoints.login, request: userLogin) { (data, error) in
if let data = data {
let status = data["status"].stringValue
if status == "success" {
guard let userObject = UserProfileModel.init(data) else { return }
let encodedUserObject: Data = NSKeyedArchiver.archivedData(withRootObject: userObject)
UserDefaults.standard.set(encodedUserObject, forKey: "userProfile")
print("Login Succeeded") self.coordinatorDelegate?.loginViewModelDidLogin(viewModel: self)
} else {
self.errorText = data["reason"].stringValue
// here is where the controller needs calling!
}
}
I wanted to know how i should have them interact correctly to trigger the VC when the VM case is hit?

Custom Protocol Swift

I have a Protocol
import UIKit
import Alamofire
import SwiftyJSON
protocol RequestProtocol: class {
func RequestConnection(json: JSON, status: Int, Message: String)
}
class API: UIViewController {
var delegate: RequestProtocol?
var json: JSON = []
override func viewDidLoad() {
super.viewDidLoad()
}
func RequestConnection() {
Alamofire.request(variablesClass.url).responseJSON { (response) -> Void in
switch response.result {
case .success:
let result = response.result.value
if response.response?.statusCode == 200 {
self.json = JSON(result!)
DispatchQueue.main.async(execute: {
self.delegate?.RequestConnection(json: self.json, status: (response.response?.statusCode)!, Message: "\(self.json["Message"])")
})
} else {
self.json = JSON(result!)
DispatchQueue.main.async(execute: {
self.delegate?.RequestConnection(json: self.json, status: (response.response?.statusCode)!, Message: "\(self.json["Message"])")
})
}
break;
case .failure(let error):
print(error)
break;
}
}
}
}
I have it called in my Main View
class ViewController: UIViewController, RequestProtocol {
func RequestConnection(json: JSON, status: Int, Message: String) {
func showAlertView()
}
override func viewDidLoad() {
super.viewDidLoad()
var senderDelegateRequest = API()
var receiverDelegateViewController = ViewController()
senderDelegateRequest.delegate = receiverDelegateRequestRegistro
senderDelegateRequest.RequestConnection()
}
func showAlertView(){
let alertView = UIAlertController(title: "You need to log in first", message: "To access the special features of the app you need to log in first.", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "Login", style: .default, handler: { (alertAction) -> Void in
}))
alertView.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertView, animated: true, completion: nil)
}
Why the Protocol does'nt execute the function to display the alert.
There is some way that as soon as the protocol is executed it can execute some function
In your ViewController class you are conforming to the protocol RequestProtocol. This means you can be the delegate of API. You created an instance called receiverDelegateViewController but this instance isn't loaded yet. You should change:
senderDelegateRequest.delegate = receiverDelegateRequestRegistro
to
senderDelegateRequest.delegate = self
And the delegate in API should call RequestConnection, not your ViewController.

Proper way to Encode and Send String as a PFObject

I'm having an issue sending a string to Parse. Here's the line where the code stops:
Here's what my code looks like:
var friends: Array<AnyObject>?
var recipients: NSMutableArray?
var message: String!
var sendMessage:String!
override func viewDidLoad() {
super.viewDidLoad()
sendMessage = message
friends = []
recipients = []
let editButton = UIBarButtonItem(title: "Edit", style: UIBarButtonItemStyle.Plain, target: self, action: "edit")
navigationItem.rightBarButtonItem = editButton
tableView = UITableView(frame: view.bounds, style: UITableViewStyle.Plain)
tableView?.delegate = self
tableView?.dataSource = self
view.addSubview(tableView!)
let sendButton = [UIBarButtonItem(title: "Send", style: .Done, target: self, action: "sendPressed")]
toolbar.frame = CGRectMake(0, self.view.frame.size.height - 46, self.view.frame.size.width, 48)
toolbar.sizeToFit()
toolbar.setItems(sendButton, animated: true)
self.view.addSubview(toolbar)
}
func sendPressed () {
sendMessagetoParse()
}
func sendMessagetoParse(){
let messageString = message
let data = messageString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)! as NSData
// NSConcreteMutableData
let file = PFFile(name:message, data:data)
file.saveInBackground()
let sentMessage = PFObject(className:"Messages")
sentMessage["messages"] = file
sentMessage["username"] = PFUser.currentUser()?.username
sentMessage.setObject(self.recipients!, forKey: "recipientIds")
sentMessage.setObject(PFUser.currentUser()!.objectId!, forKey: "senderId")
sentMessage.setObject(PFUser.currentUser()!.username!, forKey: "senderName")
sentMessage.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
if (success) {
println("Message sent!")
I want to send the string object to my parse account. I've also used the method Parse provides,, i.e. dataUsingEncoding(NSUTF8StringEncoding) but that is also returning the same error. Any clue on the proper format or how I need to fix the code?
EDIT:
Okay, I'm no longer getting the error, but my only issue is that once I press 'send', it doesn't do anything. Here's my edited code:
func sendMessagetoParse(){
let messageString = message
if messageString != nil
{
let data = messageString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)! as NSData
// NSConcreteMutableData
let file = PFFile(name:message, data:data)
file.saveInBackground()
let sentMessage = PFObject(className:"Messages")
sentMessage["messages"] = file
sentMessage["username"] = PFUser.currentUser()?.username
sentMessage.setObject(self.recipients!, forKey: "recipientIds")
sentMessage.setObject(PFUser.currentUser()!.objectId!, forKey: "senderId")
sentMessage.setObject(PFUser.currentUser()!.username!, forKey: "senderName")
sentMessage.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
if (success) {
println("Message sent!")
var alert = UIAlertController(title: "Awesome!", message: "Message was Sent", preferredStyle: UIAlertControllerStyle.Alert);
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Cancel, handler: nil));
//show it
self.navigationController?.popToRootViewControllerAnimated(true)
} else {
println("error")
}
}
}
}
Here's code from the previous view controller, called ConfirmViewController, in which I tried to use a segue to pass a text label into the messageString into the current view controller, which is called FriendsViewController:
override func prepareForSegue(segue: (UIStoryboardSegue!), sender:
AnyObject!) {
if (segue.identifier == "addFriends") {
var mvc = segue!.destinationViewController as!
FriendsViewController;
mvc.sendMessage = messageView.text
}
}
With the segue set up in the ConfirmViewController I then set sendMessage equal to message in the viewDidLoad, as shown in my first edit.
Check your
let messageString
value. This runtime error you've got is probably a result of implicitly unwrapping optional value that is nil. You would want to execute your code inside an if statement like:
if let messageString != nil
{
////put your saving code here
}
Try this:
override func viewDidLoad() {
super.viewDidLoad()
// sendMessage = message
message = sendMessage

Resources