【问题标题】:Saving username information to a server instead of NSDefaults (iOS)将用户名信息保存到服务器而不是 NSDefaults (iOS)
【发布时间】:2017-02-07 17:01:08
【问题描述】:

我有一个服务器用于在我的应用程序中存储用户名和密码数据。在测试应用程序时,我使用 NSDefaults 将所有内容保存到本地设备,但现在应用程序已接近完全启动,我尝试将它们保存到服务器,因为这样对用户信息更安全。

当我将它保存到 NSDefaults 时,它的工作简单而短暂。但是,现在我正在尝试将数据发布到服务器并不断收到构建错误。我需要改变什么才能使它起作用?我是否不完全了解 POST 和 GET 的工作原理?谢谢。目前使用 Swift 2,不是我的选择,我更喜欢 3,但我的老板还不让我们更新它。

当前错误来自 POST USER DATA TO SERVER 部分,其中 xcode 声称 userNmeTxt 无法转换为 NSData。提前谢谢你。

编辑:第 87 行出现错误:“无法将 UITextField 类型的值转换为预期的参数类型 NSData!”

import UIKit

class UserNameViewController: AuthorizationViewController {

@IBOutlet weak var userNameTxt: UITextField!
@IBOutlet weak var continueBtn: UIButton!

var userModel: ProfileModel!

//MARK: - SYSTEMS METHODS

override func viewDidLoad() {
    super.viewDidLoad()
    userNameTxt.delegate = self
    userNameTxt.autocapitalizationType = .Sentences
    setEnabledButton()
}

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationController?.navigationBarHidden = false
    self.navigationItem.leftBarButtonItem  = getBackButton()
    self.title = ""
}

override func viewWillDisappear(animated: Bool) {
    self.navigationController?.navigationBarHidden = true
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    continueBtn.layer.cornerRadius = 10
}

override func popToRoot(sender:UIBarButtonItem){
    self.navigationController!.popViewControllerAnimated(true)
}

//MARK: - CHECK FOR AVALABILITY

func setEnabledButton(){
    if userNameTxt.text == "" {
        continueBtn.backgroundColor = UIColor.lightGrayColor()
    } else {
        continueBtn.backgroundColor = UIColor(colorLiteralRed: 63.0/255.0, green: 220.0/255.0, blue: 236.0/255.0, alpha: 1.0)
    }
    continueBtn.userInteractionEnabled =  userNameTxt.text != ""
}

//MARK: - POST USER DATA TO SERVER

func postData(url: String, params: Dictionary<String, String>, completionHandler: (data: NSData?, response: NSURLResponse?, error: NSError?) -> ()) {

    // Indicate download
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true

    let url = NSURL(string: "myPlaceholderURLgoesHere")!
    //        print("URL: \(url)")
    let request = NSMutableURLRequest(URL: url)
    let session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    // Verify downloading data is allowed
    do {
        request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
    } catch let error as NSError {
        print("Error in request post: \(error)")
        request.HTTPBody = nil
    } catch {
        print("Catch all error: \(error)")
    }

    // Post the data
    let task = session.dataTaskWithRequest(request) { data, response, error in
        completionHandler(data: userNameTxt, response: userModel, error: error)

        // Stop download indication
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        // Stop download indication

    }

    task.resume()

}

//MARK: - SEGUE

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "toPassword"{
        let controller = segue.destinationViewController as! PasswordViewController
        controller.userModel = userModel
    }
}

//MARK: - IB ACTIONS
@IBAction func continuePressed(sender: AnyObject) {
    userModel.userNickName = userNameTxt.text!
    performSegueWithIdentifier("toPassword", sender: self)
}
}

extension UserNameViewController: UITextFieldDelegate{
func textFieldDidEndEditing(textField: UITextField) {
    self.setEnabledButton()
}
}

【问题讨论】:

  • 如果您不知道,您可能需要考虑使用NSUbiquitousKeyValueStoredeveloper.apple.com/library/content/documentation/Cocoa/… 将首选项存储在 iCloud Key-Value 存储中
  • 首先,不要将密码存储在 NSUserdefaults 中,至少要单独加密。至少尝试保护用户的安全。此外,在服务器上存储凭据似乎很奇怪。当您注册帐户时,应存储凭据(使用凭据特定的盐进行加密保护:owasp.org/index.php/…)。当用户在设备上输入他们的凭据时,您应该点击 Web 服务来验证凭据,无需重新保存。
  • 如果您为了方便而保存凭据,则应将其存储在钥匙串中。

标签: ios swift xcode post user-data


【解决方案1】:

有几件事情你需要改变。

userNameTxt 不是用户名,它是包含用户名的 UITextField。你需要的文字是userNameTxt.text?

如果函数需要数据,则必须先将文本转换为数据

let task = session.dataTaskWithRequest(request) { data, response, error in
    completionHandler(data: userNameTxt.text?.data(using: .utf8), response: userModel, error: error)

【讨论】:

  • 必须稍微调整您的代码以适用于我们的代码,但这解决了问题!谢谢!这是我的第一份工作,我是这家初创公司唯一的 iOS 开发人员,所以最近我有点精神疲惫,甚至没有意识到我正在尝试使用实际的 TextField!
  • 乐于助人。祝你好运:-)
【解决方案2】:

我假设您必须将数据发送到服务器。

如果你没有也可以将数据保存在钥匙串访问中,请参阅:SO: Keychain Access

为了解决错误,请使用错误消息和代码行(如果可能)编辑问题。

我建议您将Alamofire 用于 POST/GET (REST)。要使用 Alamofire,您需要 Cocoapods 的基本知识。从长远来看会更好。

注意:发出请求时可能会出现两种错误结果。

1) 数据格式不正确或您的错误

2) 由于服务器端的后端错误导致服务器错误。

可以使用 POST 从您的设备发送数据,其中数据位于请求的 BODY 或 HEADER 中。通常它在正文中(alamofire 方法中的参数)。

这是一个例子:

import Alamofire 
...

// MARK:- Login Feature - Universal Met for login
internal static func loginWith(serverUrl: String, parameters: [String: AnyObject]?, headers: [String: String]?, notificationName: String, serviceType: LoginService)
{
    Alamofire.request(.POST, serverUrl, parameters: parameters, headers: headers).responseJSON
    { (response) in
        print("\n Login feature - \n")
        print(" Login url - \(serverUrl)\n")
        print(" Login parameters - \(parameters)\n")
        print(" Login notificationName - \(notificationName)\n")
        print(" Login response - \(response)\n")
        EXCDataParserH.parseResponseFrom(ServiceType.Login(type: serviceType),
            operation: nil,
            data: response.data,
            notification: notificationName)
    }
}

【讨论】:

  • 我给@Russell 提供了答案,因为他解决了我的问题中发布的问题,但是您的回复很棒!现在肯定会检查 Alamofire,因为它比我凌乱的服务器端代码要简单得多。非常感谢。
【解决方案3】:

不要在每次发出服务器请求时都编写整个内容,而是尝试执行以下操作:

import Foundation
import SystemConfiguration

class HTTPHelper{
class func httpPostDataDic(postURL:NSURL,postString:NSString,completionHandler:@escaping (NSDictionary?, NSError?) -> Void ) -> URLSessionTask{
    var responseResultData: NSDictionary = NSDictionary()
    let request = NSMutableURLRequest(url:postURL as URL);
    request.httpMethod = "POST";// Compose a query string
    request.httpBody = postString.data(using: String.Encoding.utf8.rawValue);
    print(request)

    let task = URLSession.shared.dataTask(with: request as URLRequest) {
        data, response, error in
        if error != nil
        {
            print("error=\(error)")
            completionHandler(nil, error as NSError?)
            return
        }
        let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
        if let responseString = responseString {
            print("responseString = \(responseString)")
        }
        do {
            let myJSON =  try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
            responseResultData=myJSON!
            completionHandler(responseResultData, nil)
        } catch {
            print(error)
        }
    }
    task.resume()
    return task


}
}

现在,每当您需要在 ViewController 类中发出服务器 POST 请求时,请执行以下操作:

//Requesting server
func requestServer() -> Void{
    let postvariable = "Value"

    let url = URL(string: "your url")!
    let postString = "postkey=\(postvariable)"
    HTTPHelper.httpPostDataDic(postURL: url as NSURL, postString: postString) {
        (responseResult, error) -> Void in
        DispatchQueue.main.async {
            if error != nil{
                print(error ?? "unknown")
            }
            else{
                print(responseResult ?? "unknown result")
//Parse your response
                self.parseResult(result: responseResult!);
            }
        }
    }


}

我可以问你一件我在你的问题中不明白的事情。 您将如何将登录凭据保存在服务器中?我的意思是,如果您将登录凭据保存在服务器中,您将如何验证用户对这些已保存凭据的访问权限?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-08
    • 2020-08-10
    • 2018-11-15
    • 1970-01-01
    • 2013-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多