I have global array named newSystems which contains objects of following System class:
class System: NSObject, NSCoding {
var systemView: UIViewController?
private let _systemName: String!
private let _systemType: String!
let _systemImplForCustomerID: String!
init(name: String, type: String, systemID: String) {
_systemName = name
_systemType = type
_systemImplForCustomerID = systemID
systemView = System.setSystemView(_systemType: type)
}
private static func setSystemView(_systemType: String) -> UIViewController{
var sysView: UIViewController!
switch _systemType {
case "Room":
sysView = mainStoryboard.instantiateViewController(withIdentifier: "View1") as! UINavigationController
case "Kitchen":
sysView = mainStoryboard.instantiateViewController(withIdentifier: "View2") as! KitchenVC
default:
break;
}
return sysView
}
}
Now in TabBarController I am assigning the systemView property to viewControllers array:
for system in newSystems {
if let newView = system.systemView {
self.viewControllers?.insert(newView, at: 0)
}
}
So now TabBarController shows 2 items in tabBar, both are accessible and works fine.
But now in Room I would like to use _systemImplForCustomerID
Example of RoomVC:
class RoomVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = UICollectionViewCell()
*** Here I need the _systemImplForCustomerID ***
return cell
}
My question is how should I get _systemImplForCustomerID property? Or which way should I follow to have it there. As temporary solution I have created function which filtering global array newSystems and works fine, but is it correct solution?
func findCustomerSystemImplID(view: UIViewController) -> String? {
var id: String?
let currentSystem = newSystems { $0.systemView?.view.accessibilityIdentifier == view.view.accessibilityIdentifier }
if let sysID = currentSystem.first?.customerSystemImplID {
id = sysID
}
return id
}
Related
I faced such problem. When I launch the ios application, I get a white screen and the data that I take from Firebase is not displayed. How can i fix this problem? I would be grateful for your favorite recommendations for solving my problem
This is my ViewController
class ViewController: UIViewController {
#IBOutlet weak var cv: UICollectionView!
var channel = [Channel]()
override func viewDidLoad() {
super.viewDidLoad()
self.cv.delegate = self
self.cv.dataSource = self
let db = Firestore.firestore()
db.collection("content").getDocuments() {( quarySnapshot, err) in
if let err = err {
print("error")
} else {
for document in quarySnapshot!.documents {
if let name = document.data()["title"] as? Channel {
self.channel.append(name)
}
if let subtitle = document.data()["subtitle"] as? Channel {
self.channel.append(subtitle)
}
}
self.cv.reloadData()
}
}
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return channel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ContentCell
let channel = channel[indexPath.row]
cell.setup(channel: channel)
return cell
}
}
This is my Model
struct Content {
let contents: [Channel]
}
struct Channel {
let title: String
let subtitle: String
}
This is my Cell
class ContentCell: UICollectionViewCell {
#IBOutlet weak var channelText: UILabel!
#IBOutlet weak var subtitle: UITextView!
func setup(channel: Channel) {
channelText.text = channel.title
subtitle.text = channel.subtitle
}
}
The data retrieved from Firestore can't just magically be cast to your custom type (Channel); it's a simple dictionary. You eighter need to use Codable or do it manually like so:
I can't tell how exactly to convert it as you have not shared the structure of your data in Firestore, but I assume this will work:
db.collection("content").getDocuments() { (snapshot, error) in
if let error = error {
print("error: \(error.localizedDescription)")
} else if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
if let title = data["title"] as? String,
let subtitle = data["subtitle"] as? String {
self.channel.append(Channel(title: title, subtitle: subtitle))
}
}
}
self.cv.reloadData()
}
When i run the code and click the segue to description view controller appear error in Debugging .Could not cast value of type '__NSCFString' (0x10354a248) to 'UIImage' (0x104c42b48).
in main view controller error got this Thread 1: signal SIGABRT
MainViewController.swift
import UIKit
import Alamofire
import AlamofireImage
import SDWebImage
class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView?
var foods: [[String: Any]] = [[String: Any]]()
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request("https://api.myjson.com/bins/1bnsyj").responseJSON { (response) in
if let responseValue = response.result.value as! [String: Any]? {
print(responseValue)
if let responseFoods = responseValue["items"] as! [[String: Any]]? {
self.foods = responseFoods
self.tableView?.reloadData()
}
}
else {
print("error : \(String(describing: response.result.error))")
}
}
// Do any additional setup after loading the view.
}
// MARK: - UITableViewDataSource & UITableViewDelegate
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return foods.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FoodTableViewCell") as! FoodTableViewCell
if foods.count > 0 {
let eachFood = foods[indexPath.row]
cell.lblFoodName?.text = (eachFood["name"] as? String) ?? ""
cell.lblDescription?.text = (eachFood["description"] as? String) ?? ""
let url = NSURL(string: self.foods[indexPath.row]["photoUrl"]! as! String)
cell.imageViewFood?.af_setImage(withURL: url! as URL, placeholderImage: nil, filter: nil,runImageTransitionIfCached: true, completion: nil)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "DetailsViewController") as! DetailsViewController
vc.foodnameseg = foods[indexPath.row]["name"] as! String
vc.descriptionsegue = foods[indexPath.row]["description"] as! String
vc.imagefoodsegue = foods[indexPath.row]["photoUrl"] as! UIImage
self.navigationController?.pushViewController(vc, animated: true)
}
}
DetailsViewController.swift
import UIKit
class DetailsViewController: UIViewController {
var foodnameseg : String = ""
var descriptionsegue : String = ""
var imagefoodsegue = UIImage()
#IBOutlet weak var imagefood: UIImageView!
#IBOutlet weak var lblNameSegue: UILabel!
#IBOutlet weak var descriptionSegue: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
lblNameSegue.text = foodnameseg
descriptionSegue.text = descriptionsegue
imagefood.image = imagefoodsegue
}
}
This line is the culprit:
vc.imagefoodsegue = foods[indexPath.row]["photoUrl"] as! UIImage
You are trying to cast the String as an UIImage.
One solution to this is to add another key to foods[indexPath.row] called "photos" and make sure that it is a UIImage when you're assigning it.
Or you could make a food struct like so:
struct Food
{
var name: String
var photoURL: URL
var photo: UIImage?
}
Replace your foods array's type to [Food]. This should provide more safety in the code and you won't have to force-cast everytime you want to get a photo.
i am new in swift.
i have make a collectionview in nib file and i have a subview of that in main view controller.
i want show array in collectionview but i could not.
first i make a model of day:
struct Days {
let day: String
let Image: String
let temp: Double
}
then in daycell:
class DayCell: UICollectionViewCell {
#IBOutlet weak var lblDay: UILabel!
#IBOutlet weak var imgWeather: KBImageView!
#IBOutlet weak var lblTemp: UILabel!
func updateViews(day: Days) {
lblDay.text = day.day
imgWeather.setImageWithKingFisher(url: day.Image)
lblTemp.text = String(day.temp)
}
}
then in public class, i get json data with alamofire and decode that and put them in my model:
public class Publics {
static let instance = Publics()
func showInfo(code: String, completion: #escaping ([Days]) -> Void) {
let DaysUrl = "http://api.openweathermap.org/data/2.5/forecast?id=\(code)&appid=3e28385cde03f6ee26c83b629ca274cc"
Alamofire.request(DaysUrl, method: .get, parameters: nil, encoding: URLEncoding.httpBody).responseJSON { response in
if let data = response.data {
do {
self.myJson = try JSONDecoder().decode(JsonForecast.Response.self, from: data)
let counter = (self.myJson?.list.count)! - 1
let myDay1 = self.myJson?.list[counter-32]
let myDay2 = self.myJson?.list[counter-24]
let myDay3 = self.myJson?.list[counter-16]
let weekDay1 = self.getDate(date: self.getDayOfWeek((myDay1?.dt_txt)!)!)
let weekDay2 = self.getDate(date: self.getDayOfWeek((myDay2?.dt_txt)!)!)
let weekDay3 = self.getDate(date: self.getDayOfWeek((myDay3?.dt_txt)!)!)
let DaysArray = [
Days(day: weekDay1, Image: (myDay1?.weather[0].icon)!, temp: (myDay1?.main?.temp)!) ,
Days(day: weekDay2, Image: (myDay2?.weather[0].icon)!, temp: (myDay2?.main?.temp)!) ,
Days(day: weekDay3, Image: (myDay3?.weather[0].icon)!, temp: (myDay3?.main?.temp)!)
]
completion(DaysArray)
} catch {
print(error)
}
}
}
}
till here i do not have problem but now i want to show DaysArray in collectionview but i can not and my collectionview class is below:
class DayCollection: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet var contentView: UIView!
#IBOutlet weak var collectionDay: UICollectionView!
var days = [Days]()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DayCell", for: indexPath) as! DayCell
Publics.instance.showInfo(code: "112931") { result in
self.days = result
print(self.days)
DispatchQueue.main.async {
self.collectionDay.reloadData()
}
}
let day = days[indexPath.item]
cell.updateViews(day: day)
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return days.count
}
override func awakeFromNib() {
super.awakeFromNib()
self.collectionDay.dataSource = self
self.collectionDay.delegate = self
self.collectionDay.register(UINib(nibName: "DayCell", bundle: nil), forCellWithReuseIdentifier: "DayCell")
}
}
what should i do in mainVC class?
(maybe i should i use from protocol delegate or no?)
First of all if you want to have constants in a struct declare them as constants. private(set) is horrible.
struct Days {
let day: String
let dImage: String
let temp: Double
}
And never ever declare struct members as implicit unwrapped optionals which are initialized with non-optional values in an init method. The init method in a struct is not needed anyway.
You have to add a completion handler
public func showInfo(code: String, completion: #escaping ([Days]) -> Void) {
...
let daysArray = [
Days(day: weekDay1, Image: (myDay1?.weather[0].icon)!, temp: (myDay1?.main?.temp)!) ,
Days(day: weekDay2, Image: (myDay2?.weather[0].icon)!, temp: (myDay2?.main?.temp)!) ,
Days(day: weekDay3, Image: (myDay3?.weather[0].icon)!, temp: (myDay3?.main?.temp)!)
]
completion(daysArray)
}
Then in the class of the collection view add a data source array
var days = [Days]()
and get the data
Publics.instance.showInfo(code: "Foo") { result in
self.days = result
DispatchQueue.main.async {
self.collectionDay.reloadData()
}
}
and return days.count in numberOfItemsInSection
Further force unwrap the cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DayCell", for: indexPath) as! DayCell
If the code crashes you made a design mistake. With the optional binding the code doesn't crash but you don't see anything and you don't know why
and get a day
let day = days[indexPath.item]
cell.updateViews(day)
I find myself stuck upon the implementation of a model for getting data from a firebase database.
I'm not sure what I've done so far is correct but as far as my knowledge of swift is concerned (I'm new to swift) I think I've followed the right path.
So I have a collection view which get the data from a firebase database.
The database structure is like so:
-SwimManager
--SwimmingPools
---SwimPoolName 1
-----Capacity: "2000"
-----PhotoUrl: "https//www.test"
---SwimPoolName 2
-----Capacity: "3000"
-----PhotoUrl: "https//www.test"
I'll show the code for the view controller, the model and the cell.
Here's my ViewController:
#IBOutlet weak var collectionView: UICollectionView!
var swimRef = Database.database().reference().child("SwimmingPools")
var swimmingPools = [SwimmingPool]()
verride func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
getSwimPoolInfo()
}
func getSwimPoolInfo() {
fishRef.observeSingleEvent(of: .value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let swimNameFb = snap.key
let value = snap.value
let swim = Fish(swimName: swimNameFb, photoUrl: "")
self.swimmingPools.append(swim)
// Not sure how to add the picture
}
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SwimCell", for: indexPath) as? SwimCell {
let swim: SwimminPool!
swim = swimmingPools[indexPath.row]
cell.configureCell(swim)
return cell
} else {
return UICollectionViewCell()
Model:
class SwimmingPool {
private var _swimName: String!
private var _photourl: String!
private var _capacity: String!
var swimName: String {
if _swimName == nil {
_swimName = ""
}
return _swimName
}
...............
init(swimName: String, photoUrl: String) {
self._SwimName = swimName
self._photourl = photoUrl
}
func getData() {
//perform action the get the data from the single swimmingPool (e.g. swimPoolName 1)
}
}
And finally, here's the cell:
class SwimCell: UICollectionViewCell {
#IBOutlet weak var swimThumb: UIImageView!
#IBOutlet weak var swimNameLbl: UILabel!
var swim: SwimmingPool!
func configureCell(_ swim: SwimmingPool) {
self.swim = swim
swimNameLbl.text = self.swim.swimName.capitalized
var url = URL(string: self.swim.photoUrl)
if url == nil {
url = URL(string: "")
}
swimThumb.sd_setImage(with: url)
}
}
In the Viewcontroller the func getSwimPooInfo is triggered after viewDidLoad and so the array swimminPools is empty... Honestly it seems I cannot figure where my mistake is....
Thx!
I'm working on MVVM architecture in Swift with UITableView. For this, I have created sample table view.
Can any one please suggest whether I am going correct or any other improvements need to do?
The following are the classes for this architecture.
ViewController - Contains UITableView and its delegate and datasource methods.
class ViewController: UIViewController {
let PRODUCT_CELL_IDENTIFIER = "ProductCellIdentifier"
#IBOutlet weak var productTableView: UITableView!
var productViewModel: ProductViewModel = ProductViewModel()
}
//UITableView Delegate Methods
extension ViewController {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productViewModel.numberOfRowsInSection()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: PRODUCT_CELL_IDENTIFIER) as! ProductTableViewCell
let product = productViewModel.productsArray[indexPath.row]
cell.productName.text = product.name
cell.productQuantity.text = "\(product.quantity)"
return cell
}
}
ProductViewModel: - This is ViewModel class.
class ProductViewModel: NSObject {
var productsArray = Array<Product>()
override init() {
let product1 = Product(name: "Prodcut1", image_url: "", quantity: 2)
let product2 = Product(name: "Prodcut2", image_url: "", quantity: 3)
productsArray.append(product1)
productsArray.append(product2)
}
func numberOfRowsInSection() -> Int {
return productsArray.count
}
}
Product - This is the model class
class Product: NSObject {
var name: String
var image_url: String
var quantity: Int
init(name: String, image_url: String, quantity: Int) {
self.name = name
self.image_url = image_url
self.quantity = quantity
}
}
ProductTableViewCell - This is UITableViewCell class
class ProductTableViewCell: UITableViewCell {
#IBOutlet weak var productQuantity: UILabel!
#IBOutlet weak var productName: UILabel!
#IBOutlet weak var productImageView: UIImageView!
}
You are doing good job, but you can even improve you product model with adding following function to get array of direct models. It is very useful when you have create array from web Api response.
class Product : NSObject
{
var imgUrl : String!
var name : String!
var quantity : Int!
init(dictionary: [String:Any])
{
imgUrl = dictionary["img_url"] as? String
name = dictionary["name"] as? String
quantity = dictionary["quantity"] as? Int
}
init(name: String, image_url: String, quantity: Int)
{
self.name = name
self.imgUrl = image_url
self.quantity = quantity
}
public class func modelsFromArray(array:[[String:Any]]) -> [Product]
{
var models:[Product] = []
for item in array
{
models.append(Product.init(dictionary:item))
}
return models
}
}
With Usage Like
let product1 = Product(name: "Prodcut1", image_url: "", quantity: 2) //Normal Case
let productList:[[String:Any]] =
[
["name":"Jaydeep","img_url":"xyz","quantity":1],
["name":"Jaydeep","img_url":"xyz","quantity":2],
["name":"Jaydeep","img_url":"xyz","quantity":3],
["name":"Jaydeep","img_url":"xyz","quantity":4],
["name":"Jaydeep","img_url":"xyz","quantity":5],
["name":"Jaydeep","img_url":"xyz","quantity":6]
]
//Assign Direct Dictionary to Get Array Of Models
/* Very useful when productList is dictionary from server response*/
let productArray:[Product] = Product.modelsFromArray(array: productList)
And Also your Cell Class is Improved By
class ProductTableViewCell: UITableViewCell {
#IBOutlet weak var productQuantity: UILabel!
#IBOutlet weak var productName: UILabel!
#IBOutlet weak var productImageView: UIImageView!
func setProductData(product:Product)
{
self.productName.text = product.name
self.productQuantity.text = "\(product.quantity)"
}
}
Usage:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: PRODUCT_CELL_IDENTIFIER) as! ProductTableViewCell
let product = productViewModel.productsArray[indexPath.row]
cell.setProductData(product:product)
return cell
}
MVVM in iOS can be easily implemented without using third party dependencies. For data binding, we can use a simple combination of Closure and didSet to avoid third-party dependencies.
public final class Observable<Value> {
private var closure: ((Value) -> ())?
public var value: Value {
didSet { closure?(value) }
}
public init(_ value: Value) {
self.value = value
}
public func observe(_ closure: #escaping (Value) -> Void) {
self.closure = closure
closure(value)
}
}
An example of data binding from ViewController:
final class ExampleViewController: UIViewController {
private func bind(to viewModel: ViewModel) {
viewModel.items.observe(on: self) { [weak self] items in
self?.tableViewController?.items = items
// self?.tableViewController?.items = viewModel.items.value // This would be Momory leak. You can access viewModel only with self?.viewModel
}
// Or in one line:
viewModel.items.observe(on: self) { [weak self] in self?.tableViewController?.items = $0 }
}
override func viewDidLoad() {
super.viewDidLoad()
bind(to: viewModel)
viewModel.viewDidLoad()
}
}
protocol ViewModelInput {
func viewDidLoad()
}
protocol ViewModelOutput {
var items: Observable<[ItemViewModel]> { get }
}
protocol ViewModel: ViewModelInput, ViewModelOutput {}
final class DefaultViewModel: ViewModel {
let items: Observable<[ItemViewModel]> = Observable([])
// Implmentation details...
}
Later it can be replaced with SwiftUI and Combine (when a minimum iOS version of your app is 13)
In this article, there is a more detailed description of MVVM
https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3