Unable to initialize model in Swift - swift

I am looking to have a data model that is created by the user in the main View Controller and then passed into other view controllers via prepareForSegue.
However in my View Controller I am unable to use the model and I get errors for unwrapped optional values.
I have :
Class Collection: NSObject {
var id: String!
var image: String!
var apples: Int?
var oranges: Int?
var lemons: Int?
}
init(id: String, image: String, apples: Int, oranges: Int, lemons: Int) {
super.init()
self.id = id
self.photo = photo
self.apples = apples
self.oranges = oranges
self.lemons = lemons
}
View Controller:
var collection: Collection!
...
// if user selects a number ..
self.collection.oranges = usersSelection
self.collection.apples = usersSelection
etc
Can anyone tell me what I'm doing wrong please?
Thank you!

Define your model class like this:
class Collection: NSObject {
var id: String!
var image: String!
var apples: Int?
var oranges: Int?
var lemons: Int?
init(id: String, image: String, apples: Int?, oranges: Int?, lemons: Int?) {
super.init()
self.id = id
self.image = image
if let _ = apples{
self.apples = apples
}
if let _ = oranges{
self.oranges = oranges
}
if let _ = lemons{
self.lemons = lemons
}
}
}
Then implement in your ViewController like this:
class ViewController: UIViewController {
var collection: Collection!
override func viewDidLoad() {
collection = Collection(id: "user selection", image: "useselection", apples: nil, oranges: nil, lemons: nil)
// collection.image =
// colection.oranges =
// ........
}
}

Related

How to Use NSTableViewDiffableDataSource to Load Data with NSTableView

I'm trying to learn how to use NSTableViewDiffableDataSource to load data with NSTableView. I am able to use UITableViewDiffableDataSource and UICollectionViewDiffableDataSource to load data in iOS because I have found some examples online. But I am not able to use NSTableViewDiffableDataSource in Cocoa.
In the following case, I have a subclass of NSTableCellView named TestTableCellView, which shows three fields: First name, Last name, and his or her date of birth in String.
import Cocoa
class ViewController: NSViewController {
// MARK: - Variables
var dataSource: NSTableViewDiffableDataSource<Int, Contact>?
// MARK: - IBOutlet
#IBOutlet weak var tableView: NSTableView!
// MARK: - Life cycle
override func viewWillAppear() {
super.viewWillAppear()
let model1 = Contact(id: 1, firstName: "Christopher", lastName: "Wilson", dateOfBirth: "06-02-2001")
let model2 = Contact(id: 2, firstName: "Jen", lastName: "Psaki", dateOfBirth: "08-25-1995")
let model3 = Contact(id: 3, firstName: "Pete", lastName: "Marovich", dateOfBirth: "12-12-2012")
let model4 = Contact(id: 4, firstName: "Deborah", lastName: "Mynatt", dateOfBirth: "11-08-1999")
let model5 = Contact(id: 5, firstName: "Christof", lastName: "Kreb", dateOfBirth: "01-01-2001")
let models = [model1, model2, model3, model4, model5]
dataSource = NSTableViewDiffableDataSource(tableView: tableView, cellProvider: { tableView, tableColumn, row, identifier in
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "cell"), owner: self) as! TestTableCellView
let model = models[row]
cell.firstField.stringValue = model.firstName
cell.lastField.stringValue = model.lastName
cell.dobField.stringValue = model.dateOfBirth
return cell
})
tableView.dataSource = dataSource
guard let dataSource = self.dataSource else {
return
}
var snapshot = dataSource.snapshot()
snapshot.appendSections([0])
snapshot.appendItems(models, toSection: 0)
dataSource.apply(snapshot, animatingDifferences: true, completion: nil) // <--- crashing...
}
}
struct Contact: Hashable {
var id: Int
var firstName: String
var lastName: String
var dateOfBirth: String
}
Hmm... The application crashes with an error "Invalid parameter not satisfying: snapshot." A couple of days ago, I tested another example, which also crashed at the same line (dataSource.apply). I don't find many examples involving NSTableViewDiffableDataSource online. The only example I have found is this topic, which doesn't help. Anyway, what am I doing wrong? My Xcode version is 13.1. Thanks.
Create a snapshot like this and it should work:
guard let dataSource = self.dataSource else {
return
}
var snapshot = NSDiffableDataSourceSnapshot<Int, Contact>()
snapshot.appendSections([0])
snapshot.appendItems(models, toSection: 0)
dataSource.apply(snapshot, animatingDifferences: false)

Type 'American' does not conform to protocol 'Food'

have been fighting the code for some time now, and can't resolve the issue of: Type 'American' does not conform to protocol 'Food'
protocol Food {
var type: String { get }
var ingredient1: String { get }
var price: Int { get set}
func showHistory()
mutating func transfer()
init(type: String)
init(ingredient1: String)
}
struct American: Food {
let type: String
let ingredient1: String
var price: Int = 125
init(type: String, ingredient1: String) {
self.type = type
self.ingredient1 = ingredient1
}
func showHistory() {
print("American history")
}
mutating func transfer() {
print("transfering burgers")
}
}
I doubt you intended to separate your inits into two separate calls which is causing your error. You could solve this by implementing two separate inits as well but then you'd have to initialize both properties in separate inits which will give you an error
protocol Food {
var type: String { get }
var ingredient1: String { get }
var price: Int { get set}
func showHistory()
mutating func transfer()
init(type: String, ingredient1: String)
}
struct American: Food {
let type: String
let ingredient1: String
var price: Int = 125
init(type: String, ingredient1: String) {
self.type = type
self.ingredient1 = ingredient1
}
func showHistory() {
print("American history")
}
mutating func transfer() {
print("transfering burgers")
}
}

Swift MVVM Bind with Boxing

I am simply trying to create a weather application with WeatherViewController displaying the tableView with cells, and when the cell is tapped leads to WeatherDetailsViewController.
I am using the boxing way for binding and I am confused if I set Dynamic type in both the model and viewModel in the example below. You will know what I mean.
This is the Boxing Class
class Dynamic<T>: Decodable where T: Decodable {
typealias Listener = (T) -> ()
var listener: Listener?
var value: T {
didSet {
listener?(value)
}
}
func bind(listener: #escaping Listener) {
self.listener = listener
self.listener?(self.value)
}
init(_ value: T) {
self.value = value
}
private enum CodingKeys: CodingKey {
case value
}
}
This is the Weather Model Struct
struct Weather: Decodable {
let date: Dynamic<Int>
let description: Dynamic<String>
let maxTemperature: Dynamic<Double>
private enum CodingKeys: String, CodingKey {
case date = "time"
case description = "summary"
case maxTemperature = "temperatureMax"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
date = try Dynamic(container.decode(Int.self, forKey: .date))
description = try Dynamic(container.decode(String.self, forKey: .description))
maxTemperature = try Dynamic(container.decode(Double.self, forKey: .maxTemperature))
}
}
Here is my WeatherListViewModel & WeatherViewModel
Inside my WeatherViewModel I have assigned the type to be Dynamic but also in the model in order to bind in my WeatherDetailsViewController, is that right?
class WeatherListViewModel {
var weatherViewModels: [WeatherViewModel]
private var sessionProvider: URLSessionProvider
init(sessionProvider: URLSessionProvider) {
self.sessionProvider = sessionProvider
self.weatherViewModels = [WeatherViewModel]()
}
func numberOfRows(inSection section: Int) -> Int {
return weatherViewModels.count
}
func modelAt(_ index: Int) -> WeatherViewModel {
return weatherViewModels[index]
}
func didSelect(at indexPath: Int) -> WeatherViewModel {
return weatherViewModels[indexPath]
}
}
This is WeatherListViewModel Extension for network fetching where I initialize the WeatherViewModel
func fetchWeatherLocation(withLatitude latitude: CLLocationDegrees, longitude: CLLocationDegrees, completion: #escaping handler) {
sessionProvider.request(type: WeatherWrapper.self, service: WeatherService.specificLocation, latitude: latitude, longitude: longitude) { [weak self] result in
switch result {
case let .success(weatherWrapper):
let weathers = weatherWrapper.daily.weathers
self?.weatherViewModels = weathers.map {
return WeatherViewModel(weather: $0)
}
completion()
case let .failure(error):
print("Error: \(error)")
}
}
}
This is WeatherViewModel
struct WeatherViewModel {
private(set) var weather: Weather
var temperature: Dynamic<Double>
var date: Dynamic<Int>
var description: Dynamic<String>
init(weather: Weather) {
self.weather = weather
self.temperature = Dynamic(weather.maxTemperature)
self.date = Dynamic(weather.date)
self.description = Dynamic(weather.description)
}
}
Here is my WeatherDetailsViewController
Here I assign the binding to the labels respectively to get the changes
class WeatherDetailsViewController: UIViewController {
#IBOutlet private var imageView: UIImageView!
#IBOutlet private var cityLabel: UILabel!
#IBOutlet private var dateLabel: UILabel!
#IBOutlet private var descriptionLabel: UILabel!
#IBOutlet private var temperatureLabel: UILabel!
var viewModel: WeatherViewModel?
override func viewDidLoad() {
super.viewDidLoad()
setupVMBinding()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationItem.largeTitleDisplayMode = .never
}
private func setupVMBinding() {
if let viewModel = viewModel {
viewModel.date.bind {
self.dateLabel.text = $0.toString()
}
viewModel.temperature.bind {
self.temperatureLabel.text = "\($0)"
}
viewModel.description.bind {
self.descriptionLabel.text = $0.description
}
}
}
}
Question is, did I just repeat writing the type Dynamic in both model and viewModel? Is there a better way of doing this or am I on the right track. Sorry for the long code example.
I think you repeat writing Dynamic inside your Weather Model.
It does not need to be Dynamic type.
You can create a GenericDataSource
class GenericDataSource<T>: NSObject {
var data: Dynamic<T>?
}
Inside your View Model. This will Reference to your Weather Model without the need for creating dynamic type.
class WeatherViewModel {
var dataSource: GenericDataSource<Weather>?
....
}
Inside your View Controller
class WeatherDetailsViewController {
var viewModel: WeatherViewModel?
override func viewDidLoad() {
viewModel = ViewModel()
var dataSource = GenericDataSource<Weather>()
dataSource.data = Dynamic(Weather)
viewModel.dataSource = dataSource
setupVMBinding()
}
private func setupVMBinding() {
viewModel?.dataSource?.data?.bind {
self.dateLabel.text = $0.date
self.temperatureLabel.text = "\($0.maxTemperature)"
self.descriptionLabel.text = $0.description
}
}
}

Best and most efficient way to populate different type of data

I have two UICollectionView's and I am passing a unique type of data (Cats, Dogs) to the same UIViewController. Both types have unique properties like imageUrl, gender, age and etc. What would be the best way to populate this data to according Text Views?
fileprivate var isDog = false
var detailsData: Any? {
didSet {
if detailsData is Dog {
isDog = true
} else if detailsData is Cat {
isDog = false
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
isDog ? fillDogInfo(detailsData: detailsData as! Dog) : fillCatInfo(detailsData: detailsData as! Cat)
}
fileprivate func fillDogInfo(detailsData: Dog) {
contactButtonOutlet.isHidden = false
// Short Info
if let name = detailsData.name {
shortInfoMutableAttributedString.append("\(name)\n".customAttributedString(font: Font.header, textColor: UIColor.darkGray))
}
if let age = detailsData.age {
shortInfoMutableAttributedString.append("\(age)\n".customAttributedString(font: Font.header, textColor: UIColor.darkGray))
}
shortInfoTextView.attributedText = shortInfoMutableAttributedString
}
fileprivate func fillCatInfo(detailsData: Car) {
contactButtonOutlet.isHidden = true
if let name = detailsData.name {
shortInfoMutableAttributedString.append("\(name)\n".customAttributedString(font: Font.header, textColor: UIColor.darkGray))
}
if let gender = detailsData.gender {
shortInfoMutableAttributedString.append("\(gender)\n".customAttributedString(font: Font.header, textColor: UIColor.darkGray))
}
shortInfoTextView.attributedText = shortInfoMutableAttributedString
}
Is there any other way to populate this data with less code?
A classic case of Polymorphism, you should create a protocol and have the Dog and Cat classes implement it. Then you can just call the functions on this protocol instance.
Below is a simplified example. In this example, it does not make sense to make the distinction, since both Cat and Dog are the same class in terms of properties. This would become interesting if you want to make a distinction, like for example add a bark() function to only the Dog class
protocol Pet{
var name: String { get set}
func infoString() -> String
}
class Dog: Pet{
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func infoString() -> String {
return "Dog \(name) has age \(age)"
}
}
class Cat: Pet{
var name: String
var gender: String
init(name: String, gender: String) {
self.name = name
self.gender = gender
}
func infoString() -> String {
return "Cat \(name) has gender \(gender)"
}
}
fileprivate func fillPetInfo(pet: Pet){
textView.text = pet.infoString()
}
let dog = Dog(name: "Ruffer", age: 8)
fillPetInfo(pet: dog)

How I can access to one Struct property that implements two protocols?

Im just learning Swift 4 and I have some troubles trying to access a property of an struct that have to implement 2 protocols
here is my struct
struct FigureA {
static func load() -> Model {
return Model.make(
name: "FigureName",
status: "Painted",
image: UIImage(named: "FigureA"),
description: "Good figure")
}
}
here the protocol 1
protocol ListModel: class {
var name: String { get }
var status: String { get }
var image: UIImage? { get }
}
here the protocol 2
protocol DetailModel: ListModel {
var categoryName: String { get }
var modelDescription: String? { get }
}
And I want to get the access to the description of the Struct but I don't know how at all.
Can someone give me a bit of light.
Here is good start for you:
protocol BaseProtocol {
var id: Int { get set }
}
protocol PersonProtocol: BaseProtocol {
var firstName: String { get set }
var lastName: String { get set }
var name: String { get }
}
struct Person: PersonProtocol {
var id: Int
var firstName: String
var lastName: String
var name: String { return firstName + " " + lastName }
}
//≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Create Struct Instance & Print properties.
let person = Person(id: 1001, firstName: "Manish", lastName: "Rathi")
print(person.id)
print(person.firstName)
print(person.lastName)
print(person.name)
}
}
#HappyCoding 😊