Swift2 to Swift3 error - Cannot assign value of type 'NSDictionary?' to type 'AddressModel' - swift3

Moving an app from Swift2 to Swift3 and I've hit an error that I've been unable to fix after trying several different suggestions.
lazy var address: AddressModel? = {
[unowned self] in
var dict = self.getpayloadDict()
var model: AddressModel
model = dict
return model
}()
model = dict throws Cannot assign value of type 'NSDictionary?' to type 'AddressModel'
The AddressModel . . .
class AddressModel: Deserializable {
var City: String?
var State: String?
var PostalCode: String?
required init(data: [String: AnyObject]) {
City = data["City"] as! String?
State = data["State"] as! String?
PostalCode = data["PostalCode"] as! String?
}
}
Any help appreciated.

The error is supposed to occur also in Swift 2. It's pretty clear: getpayloadDict() returns a dictionary which doesn't match AddressModel.
You might create an AddressModel instance from the dictionary
lazy var address: AddressModel? = { // this closure does not cause a retain cycle
let dict = self.getpayloadDict()
return AddressModel(data: dict)
}()
Side note:
as! String? (force unwrap an optional to an optional) is horrible syntax. Use regular conditional downcast as? String. And please conform to the naming convention that variable names start with a lowercase letter.

Related

How to delete on a line in a list with Realm

I have a List structure that removes a line with ondelete, but while returning the result, the line is deleted but shows an error.
I think the problem is that the result is not updated correctly.
What code should I add to update the list?
List{
ForEach(datosRealm.aquaris, id: \.self) { item in
Text(item.nombre)
}.onDelete { (indexSet) in
let aqua = datosRealm.aquaris[indexSet.first!]
let realm = try! Realm()
try! realm.write{
realm.delete(aqua)
}
}
}
import SwiftUI
import RealmSwift
final class DadesRealm: ObservableObject {
#Published var aquaris: [Aquaris]
private var cargaToken: NotificationToken?
init() {
let realm = try! Realm()
aquaris = Array(realm.objects(Aquaris.self))
recargarDatos()
}
private func recargarDatos() {
let realm = try! Realm()
let personas = realm.objects(Aquaris.self)
cargaToken = personas.observe { _ in
self.aquaris = Array(personas)
}
}
deinit {
cargaToken?.invalidate()
}
}
class Aquaris: Object {
#objc dynamic var nombre = ""
#objc dynamic var litros = ""
#objc dynamic var tipoAcuario = ""
#objc dynamic var data : Date = Date()
#objc dynamic var id = UUID().uuidString
override static func primaryKey() -> String? {
return "id"
}
let mascota = List<Controls>()
}
Error:
libc++abi.dylib: terminating with uncaught exception of type
NSException
*** Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.' terminating with uncaught
exception of type NSException
The problem is taking a live updating Realm results object and casting it to an array, making those objects non-live updating (static)
In other words realm Results objects always reflect the current state of that data; if an object is deleted from Realm, the realm results object will also have that object removed. Likewise if an object is added then the results object will reflect that addition. (noting this is for objects that fit however the results objects was crafted; filtered, sorted etc)
So here's the issue
final class DadesRealm: ObservableObject {
#Published var aquaris: [Aquaris]
private var cargaToken: NotificationToken?
init() {
let realm = try! Realm()
aquaris = Array(realm.objects(Aquaris.self)) <- PROBLEM IS HERE
So you need to do one of two things
1 - Make aquaris a realm results object
#Published var aquaris: Results<Aquaris>? = nil
then
aquaris = realm.objects(Aquaris.self)
or
2 - If you must use that as an array, when the object is deleted from Realm, you also need to delete it from the array to keep them 'in sync'
I advise using suggestion 1 as it will make things easier in the long run.

Store dictionary in UserDefaults

This is a similar approach to Save dictionary to UserDefaults, however, it is intended for SwiftUI, not using a single line like set, so I want to store the value somewhere with a variable so I can call it easily. Also it's different because I'm asking for an initialization.
I have the following:
#Published var mealAndStatus: Dictionary
init() {
mealAndStatus = ["Breakfast": "initial", "Snack": "notSet", "Lunch": "notSet", "Snack2": "notSet", "Dinner": "notSet"]
if let storedDay = UserDefaults.standard.value(forKey: "mealAndStatus") {
mealAndStatus = storedDay as! Dictionary
}
}
1- How do I correctly store that dictionary in UserDefaults in SwiftUI?
2- That init, do I have to call it at the beginning of ContentView? Or can I leave it on the other swift file like that? Not sure how the init gets called.
I already made one with bool working:
#Published var startDay: Bool
init() {
startDay = true
if let storedDay = UserDefaults.standard.value(forKey: "startDay") {
startDay = storedDay as! Bool
}
}
but the dictionary doesn't seem to work. I need to initialize that dictionary and also store it in UserDefaults so I can access it later. Any help is appreciated.
This is the perfect solution I found for SwiftUI:
Store this somewhere, in my case I created a class just for UserDefaults:
#Published var mealAndStatus: [String: Date] =
UserDefaults.standard.dictionary(forKey: "mealAndStatus") as? [String: Date] ?? [:] {
didSet {
UserDefaults.standard.set(self.mealAndStatus, forKey: "mealAndStatus")
}
}
That above initializes the dictionary and also creates a variable to be easily called and use to update the value. This can be modified at lunch time and add new values, that way is initialized with whatever I want.
Furthermore, now on Apple Dev wwdc20 they announced a new way of handling UserDefaults with SwiftUI which may be even better than the above. The propery wrapper is called: #AppStorage.
Using JSONEncoder and JSONDecoder would help you convert to data any struct or dictionary that conforms to codable.
let arrayKey = "arrayKey"
func store(dictionary: [String: String], key: String) {
var data: Data?
let encoder = JSONEncoder()
do {
data = try encoder.encode(dictionary)
} catch {
print("failed to get data")
}
UserDefaults.standard.set(data, forKey: key)
}
func fetchDictionay(key: String) -> [String: String]? {
let decoder = JSONDecoder()
do {
if let storedData = UserDefaults.standard.data(forKey: key) {
let newArray = try decoder.decode([String: String].self, from: storedData)
print("new array: \(newArray)")
return newArray
}
} catch {
print("couldn't decode array: \(error)")
}
return nil
}
// You would put this where you want to save the dictionary
let mealAndStatus = ["Breakfast": "initial", "Snack": "notSet", "Lunch": "notSet", "Snack2": "notSet", "Dinner": "notSet"]
store(dictionary: mealAndStatus, key: arrayKey)
// You would put this where you want to access the dictionary
let savedDictionary = fetchDictionay(key: arrayKey)
On a side note, you probably shouldn't be using standard defaults for storing stuff like this. Storing it as a database, or saving it in a file especially with encryption on eith the database or the file might be a bit safer.

Argument of '#selector' does not refer

Anyone can help me?
This is my code:
fileprivate func datapicker(sender : UITextField){
let datePickerView:UIDatePicker = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.date
sender.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(datePickerValueChanged(sender: datePickerView, myText: sender)), for: UIControlEvents.valueChanged)
}
#objc func datePickerValueChanged(sender:UIDatePicker , myText : UITextField) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = DateFormatter.Style.medium
dateFormatter.timeStyle = DateFormatter.Style.none
myText.text = dateFormatter.string(from: sender.date)
}
#IBAction func fromdateAction(_ sender: UITextField) {
datapicker(sender: sender)
}
Selectors can't be called like that. You don't get to pass the arguments for a selector, the API does.
You have to pass in:
#selector(datePickerValueChanged)
This means that your datePickerValueChanged method can't take 2 arguments, because the API expects only one.
This means that you need a way to know which text field's date picker changed. One simple way to do this is to create a property called focusedTextField:
var focusedTextField: UITextField!
Set this to sender in your dataPicker method:
focusedTextField = sender
And set it to nil when the user ends editing
// in another method that is called when the text field ends editing
focusedTextField = nil
Now, you can remove the second argument from datePickerValueChanged and use focusedTextField instead.

Getting nil response while passing string to another view controller - Swift3

In FirstViewController i'm fetching the response from JSON and want to pass that fetched response to another view controller.Below is the code which i have used so far for parsing and passing the response.
FirstViewController
var fn:String! //globally declared variable
code i have tried for parsing in FirstViewController
do {
let detailsDictionary:NSDictionary = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! Dictionary<String, AnyObject> as NSDictionary
print(detailsDictionary)
let details = detailsDictionary["Data"] as! [[String:AnyObject]]
print(details)
for dtl in details
{
self.fn = dtl["Father_Name"] as? String ?? "NA"
print(self.fn) //here i'm getting the exact value from JSON
}
}
}
SecondViewController
In SecondViewController there is a Label called profile_name and want to set that parsed string(fn) as Label's text. for that i declared another variable as global.
var pname:String!
below is the code i have used to fetch the value from FirstViewController.
viewDidLoad()
{
let othervc = FirstViewController()
self.pname = othervc.fn
self.profile_name.text = self.pname
}
Problem : I tried my best efforts to get the desired output but i'm getting nil response.
Please Help.
In Second ViewController
let strName:String!
In First ViewController
let strOne = "This is for testing"
let objstory = self.storyboard?.instantiateViewController(withIdentifier: "yout Secoond ViewController Storybord ID") as! YourSecondViewControllerName
objstory.strNam = strOne
self.navigationController?.pushViewController(objstory, animated: true)
Your updated code just won't work.
let othervc = FirstViewController()
creates a new instance of FirstViewController (not the one that got the JSON).
You should be handling it something like this:
In FirstViewController
let fn = dtl["Father_Name"] as? String ?? "NA"
let svc = SecondViewController() // Or maybe instantiate from Storyboard, or maybe you already have a reference to it
svc.pname = fn
present(svc, animated: true, completion: nil)
Then in SecondViewController
override func viewDidLoad() {
super.viewDidLoad()
profile_name.text = pname
}
I'd suggest you take some time out and re-read Apple's View Controller programming guide.
Original Answer
The problem you have here…
vcvalue.profile_name.text = fn
is that profile_name is nil as the view for the view controller hasn't been loaded at this point.
You should handle this by creating a property in LeftSideMenuViewController
var name: String?
Then set
vcvalue.name = fn
And then in LeftSideMenuViewController
override func viewDidLoad() {
super.viewDidLoad()
profile_name.text = name
}
Also, some basic tips…
Don't force unwrap (!) apart from IBOutlets. You may have to write a bit more code, but you will reduce crashes.
Make #IBOutlets private - this will prevent you accidentally assigning to them as you are now
If you're overriding any viewWill/DidDis/Appear methods, you must call super at some point.
You need to re-read the section on switch/case
So this…
let a = indexPath.row
switch(a)
{
case 0 :
if(a == 0)
{
return 45
}
break
etc
could just be…
switch indexPath.row {
case 0...4:
return 45
case 5:
return 50
default:
break
}

Cannot call value of non function type '[String:AnyObject]'

I'm facing the issue in Swift 3
I have following piece of code:
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let datasFromJson = json["blog"] as? [[String:AnyObject]] {
for dataFromJson in datasFromJson{
if let title = dataFromJson("title")! as? String {
article.author = author
}
self.articles?.append(article)
}
}
I get this error when I try to cast title as string
Typo (brackets, not parentheses):
dataFromJson["title"] as? String // no exclamation mark after the closing bracket
Notes:
.mutableContainers is useless in Swift.
In Swift 3 JSON dictionary is [String:Any]
Is title used at all? Or is it another typo title vs. author?