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
}
Related
Below is my main view controller. The user selects images of clothing which are then categorized using CoreML and given a filename. Then, data is saved to Realm. When I call the function loadClothing(), the array is empty even though items were added during func detect. Any help is much appreciated!
import UIKit
import PhotosUI
import RealmSwift
import CoreML
import Vision
class ViewController: UIViewController, PHPickerViewControllerDelegate {
#IBOutlet weak var shoesImageView: UIImageView!
#IBOutlet weak var shirtImageView: UIImageView!
#IBOutlet weak var pantsImageView: UIImageView!
var documentsUrl: URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
let realm = try! Realm()
var clothing: Results<Clothing>?
override func viewDidLoad() {
super.viewDidLoad()
loadClothing()
let clothingArray = Clothing()
print(clothingArray)
}
#IBAction func addClothesButton(_ sender: UIBarButtonItem) {
pickPhotos()
}
#IBAction func randomizeButton(_ sender: UIBarButtonItem) {
loadClothing()
let clothingArray = Clothing()
print(clothingArray)
shirtImageView.image = load(fileName: clothingArray.shirtImages.randomElement()!)
pantsImageView.image = load(fileName: clothingArray.pantsImages.randomElement()!)
shoesImageView.image = load(fileName: clothingArray.shoesImages.randomElement()!)
}
//MARK: - PHPickerViewController
#objc func pickPhotos() {
var config = PHPickerConfiguration()
config.selectionLimit = 25
config.filter = PHPickerFilter.images
let pickerViewController = PHPickerViewController(configuration: config)
pickerViewController.delegate = self
self.present(pickerViewController, animated: true, completion: nil)
}
// MARK: - PHPickerViewControllerDelegate
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
for result in results {
result.itemProvider.loadObject(ofClass: UIImage.self) {(object, error) in
if let image = object as? UIImage {
DispatchQueue.main.async {
guard let fileName = result.itemProvider.suggestedName else {
fatalError("Could not retrieve file name.")
}
print(fileName)
guard let ciImage = CIImage(image: image) else {
fatalError("Could not convert to CI Image.")
}
self.detect(image: ciImage, fileName: fileName)
}
}
}
}
}
// MARK: - Core ML
func detect(image: CIImage, fileName: String) {
guard let model = try? VNCoreMLModel(for: ClothingClassifier(configuration: MLModelConfiguration()).model) else {
fatalError("Loading CoreML Model failed.")
}
let request = VNCoreMLRequest(model: model) { (request, error) in
guard let results = request.results as? [VNClassificationObservation] else {
fatalError("Model failed to process image.")
}
let newClothing = Clothing()
if let firstResult = results.first {
let uiImage = UIImage(ciImage: image)
if firstResult.identifier.contains("shirts") {
newClothing.shirtImages.append(fileName)
} else if firstResult.identifier.contains("pants"){
newClothing.pantsImages.append(fileName)
} else if firstResult.identifier.contains("shoes") {
newClothing.shoesImages.append(fileName)
}
self.save(clothing: newClothing)
print(newClothing)
}
}
let handler = VNImageRequestHandler(ciImage: image)
do {
try handler.perform([request])
}
catch {
print(error)
}
}
// MARK: - Data Manipulation Methods
func save(clothing: Clothing) {
do {
try realm.write {
realm.add(clothing)
}
} catch {
print("Error saving uploaded clothing. \(error)")
}
}
func loadClothing() {
clothing = realm.objects(Clothing.self)
print("loaded")
}
private func load(fileName: String) -> UIImage? {
let fileURL = documentsUrl.appendingPathComponent(fileName)
do {
let imageData = try Data(contentsOf: fileURL)
return UIImage(data: imageData)
} catch {
print("Error loading image : \(error)")
}
return nil
}
}
Clothing Class
import Foundation
import RealmSwift
class Clothing: Object {
let shirtImages = List<String>()
let pantsImages = List<String>()
let shoesImages = List<String>()
}
I've encountered this issue a few times where I am trying to either pass a function from one swift file, to another, and I get the error, "Cannot find '_' in scope". I am wondering why this occurs and how to fix it? I have tried creating a public function, public func fetchJokesAPI() , along with making the class of this function an Observable object so that I am able to pass this function throughout my swift files. Why is the compiler not finding my function in the scope? Below is some code to further give reference to the issue.
//Model
import Foundation
struct DecodingError: Error{}
struct JokesModel: Codable {
let type: String
let value: Value
}
// MARK: - Value
struct Value: Codable {
let id: Int
let joke: String
}
class JokesAPI {
var session: URLSession?
// MARK: Getting random jokes from API endpoint
func fetchJokes() {
guard let url = URL(string: "https://api.icndb.com/jokes/random/") else {
print("Cannot generate URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print(error.localizedDescription)
return
}
guard (response as? HTTPURLResponse) != nil else {
print("No Response")
return
}
struct JokesResponse: Decodable {
let data: [JokesModel]
}
guard let data = data, let dataString = String(data: data, encoding: .utf8)
else {
print("No Data")
return
}
do {
let jokeResponse = try JSONDecoder().decode(JokesResponse.self, from: data)
print(jokeResponse.data)
} catch {
print("Error decoding joke response:", error)
}
}
task.resume()
}
}
// Another Swift File that is a Coco touch file.
import UIKit
import Foundation
class HomeViewController: UIViewController {
private let service = JokesAPI()
private var joke: [JokesModel]?
#IBOutlet var tellAeAJokeButton: UIButton!
#IBOutlet var jokeLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
service.fetchJokes()
}
#IBAction func didPressBtn() {
// MARK: Actions for both fetching a joke from the API, & Text-Speech using AI Voice
service.fetchJokes()
joke = [JokesModel]()
jokeLabel.text = JokesModel().joke
// Value of type 'JokesModel' has no member 'joke'
// Missing argument for parameter 'from' in call
You are getting the error because you need an instance of the class to call your function on.
To get your code working you can do something like this where your viewController takes a service the JokesWebService like so, the better option would be to have the service on a viewModel if you already have one but if not this should do the trick to get you started:
import UIKit
class HomeViewController: UIViewController {
#IBOutlet var tellAeAJokeButton: UIButton!
private var service: JokesWebService!
override func viewDidLoad() {
super.viewDidLoad()
service = JokesWebService()
}
#IBAction func didPressBtn() {
service.fetchJokesAPI()
}
}
I'm coming from a javascript background and am finding it difficult to understand how to store the response from a simple GET request in SWIFT.
I have an empty array named plants declared in my View Controller. The response from my GET request returns an array of plant names (strings). How can I assign the response array to the array plants?
The setup of my code looks like this:
class MyPlantsViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var addPlantTextField: UITextField!
var plants: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: CGRect.zero)
getAllPlants()
}
func getAllPlants() {
// Create URL
let url = URL(string: ".....com/api/plants")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("error: \(error)")
} else {
if let response = response as? HTTPURLResponse {
print("statusCode: \(response.statusCode)")
}
if let data = data {
<<..... I have tried lots of things here......>>
}
}
}
task.resume()
}
......
You can use JSONDecoder to decode list of string as below,
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("error: \(error)")
} else {
do {
self.plants = try JSONDecoder().decode([String].self, from: data!)
} catch {
print(error)
}
}
}
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
}
}
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")
}
}
}