MessageKit messages keep switching sides - ios

I am implementing a Chat between driver and user in a cab app. I am getting the message from API and displaying it in chat but the chat flips depending on the ID of the last msg
I just want to add the incoming msg on the left side of the screen.
import Foundation
import MessageKit
class ChatViewController: MessagesViewController {
private lazy var loader:UIView = {
return createActivityIndicator(UIScreen.main.focusedView ?? self.view)
}()
#IBOutlet weak var topView: UIView!
let imageView : UIImageView = {
let iv = UIImageView()
iv.image = UIImage(named:"backgroundImg")
iv.contentMode = .scaleAspectFill
return iv
}()
var userId = NSNumber()
var providerId = NSNumber()
var rideId = NSNumber()
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
member = Member(name: "User", color: .headingGold)
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messageInputBar.delegate = self
messagesCollectionView.messagesDisplayDelegate = self
var topSafeAreaHeight: CGFloat = 0
var bottomSafeAreaHeight: CGFloat = 0
let window = UIApplication.shared.windows[0]
if #available(iOS 11.0, *) {
let safeFrame = window.safeAreaLayoutGuide.layoutFrame
topSafeAreaHeight = safeFrame.minY
bottomSafeAreaHeight = safeFrame.maxY
} else {
topSafeAreaHeight = 10
}
self.topView.frame = CGRect(x: 10, y: topSafeAreaHeight, width: self.view.frame.size.width-20, height: 60)
self.topView.layer.cornerRadius = 10
self.messagesCollectionView.contentInset = UIEdgeInsets(top: topView.frame.maxY, left: 0, bottom: 70, right: 0)
messagesCollectionView.backgroundView = imageView
func backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
return isFromCurrentSender(message: message) ? UIColor.myGold : UIColor.headingGold
}
getChatHistory()
self.timer = Timer.scheduledTimer(timeInterval: 6.0, target: self, selector: #selector(getChatHistory), userInfo: nil, repeats: true)
}
override func viewDidAppear(_ animated: Bool) {
view.bringSubviewToFront(topView)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
timer.invalidate()
}
#objc func getChatHistory(){
self.messages = []
let request = NSMutableURLRequest(url: NSURL(string: "\(ServiceConstants.baseUrl+ServiceConstants.MD_GET_CHAT)?request_id=\("\(self.rideId)")&user_id=\(self.userId)")! as URL)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")
let tokenStr:NSString = UserDefaults.standard.value(forKey: "tokentype") as! NSString
let accessStr:NSString = UserDefaults.standard.value(forKey: "accesstoken") as! NSString
let str = NSString(format: "%# %#", tokenStr, accessStr) as String
request.addValue(str, forHTTPHeaderField: "Authorization")
let session = URLSession.shared
// self.loader.isHidden = false
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
// DispatchQueue.main.async {
// self.loader.isHidden = true
//
// }
if (error != nil) {
print(error!)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse!)
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
print(jsonResult)
let messagesArray = jsonResult["messages"] as! NSArray
for dictVal in messagesArray
{
if let str = dictVal as? [String:AnyObject]
{
let msg = str["message"] as! String
let type = str["type"] as! String
self.member = Member(name: type, color: .myGold)
let newMessage = Message(
member: self.member,
text: msg,
messageId: UUID().uuidString)
// self.messageCallback(newMessage)
self.messages.append(newMessage)
}
}
DispatchQueue.main.async{
self.messagesCollectionView.reloadData()
self.messagesCollectionView.scrollToBottom(animated: true)
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
})
dataTask.resume()
}
func sendMsg(msg:String){
let parameters:[String:String] = ["user_id":"\(userId)","provider_id":"\(providerId)","request_id":"\(rideId)","type":"up","message":"\(msg)"]
let url = URL(string: ServiceConstants.baseUrl + ServiceConstants.MD_SEND_CHAT_MSG)!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")
let tokenStr:NSString = UserDefaults.standard.value(forKey: "tokentype") as! NSString
let accessStr:NSString = UserDefaults.standard.value(forKey: "accesstoken") as! NSString
let str = NSString(format: "%# %#", tokenStr, accessStr) as String
request.addValue(str, forHTTPHeaderField: "Authorization")
do{
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
}catch let error
{
print(error.localizedDescription)
}
self.loader.isHidden = false
let task = session.dataTask(with: request, completionHandler: {data, response, error in
DispatchQueue.main.async {
self.loader.isHidden = true
}
print(response as Any)
guard error == nil else{return}
guard let data = data else{return}
do{
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any]
{
print(json)
}
}catch let error
{
print(error.localizedDescription)
}
})
task.resume()
}
var messages: [Message] = []
var member: Member!
#IBAction func backBtnAction(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
extension ChatViewController: MessagesDataSource {
func numberOfSections(
in messagesCollectionView: MessagesCollectionView) -> Int {
return messages.count
}
func currentSender() -> Sender {
return Sender(id: member.name, displayName: member.name)
}
func messageForItem(
at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> MessageType {
return messages[indexPath.section]
}
func messageTopLabelHeight(
for message: MessageType,
at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> CGFloat {
return 12
}
func messageTopLabelAttributedText(
for message: MessageType,
at indexPath: IndexPath) -> NSAttributedString? {
return NSAttributedString(
string: message.sender.displayName,
attributes: [.font: UIFont.systemFont(ofSize: 12)])
}
}
extension ChatViewController: MessagesLayoutDelegate {
func heightForLocation(message: MessageType,
at indexPath: IndexPath,
with maxWidth: CGFloat,
in messagesCollectionView: MessagesCollectionView) -> CGFloat {
return 0
}
}
extension ChatViewController: MessagesDisplayDelegate {
func configureAvatarView(
_ avatarView: AvatarView,
for message: MessageType,
at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) {
let message = messages[indexPath.section]
let color = message.member.color
avatarView.backgroundColor = color
}
}
extension ChatViewController: MessageInputBarDelegate {
func messageInputBar(
_ inputBar: MessageInputBar,
didPressSendButtonWith text: String) {
sendMsg(msg: text)
member = Member(name: "up", color: .myGold)
let newMessage = Message(
member: member,
text: text,
messageId: UUID().uuidString)
messages.append(newMessage)
inputBar.inputTextView.text = ""
messagesCollectionView.reloadData()
messagesCollectionView.scrollToBottom(animated: true)
}
}
struct Member {
let name: String
let color: UIColor
}
struct Message {
let member: Member
let text: String
let messageId: String
}
extension Message: MessageType {
var sender: Sender {
return Sender(id: member.name, displayName: member.name)
}
var sentDate: Date {
return Date()
}
var kind: MessageKind {
return .text(text)
}
}
This is my class for chatViewController and this is my first experience with messageKit. I might be missing something simple or trivial..

The main function that determines alignment is,
func currentSender() -> Sender {
return Sender(id: member.name, displayName: member.name)
}
In this case, member.name is the identifier to recognize own messages.
The primary provider of that data is initialized as,
member = Member(name: "User", color: .headingGold)
But as you insert a new message it got changed to causing logic for alignment to change,
member = Member(name: "up", color: .myGold) // why change global variable?
In this case, if a member with name: "up" sends a text it switches sides. Better refactor the entire codebase and maintain better id for current and counterpart users.
Follow, official Doc implementation for reference.

Related

how to use codable and decodable for the following json?

I have a following sample json file I want to do it with modal classes but am not sure how to do this in code.
{
"countryCodes": [{
"country_code": "AF",
"country_name": "Afghanistan",
"dialling_code": "+93"
},
{
"country_code": "AL",
"country_name": "Albania",
"dialling_code": "+355"
},
{
"country_code": "DZ",
"country_name": "Algeria",
"dialling_code": "+213"
},
{
"country_code": "AS",
"country_name": "American Samoa",
"dialling_code": "+1"
}
]
}
My modal class is as follows
import Foundation
class CountryModal : NSObject, NSCoding{
var countryCodes : [CountryCode]!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
countryCodes = [CountryCode]()
if let countryCodesArray = dictionary["countryCodes"] as? [[String:Any]]{
for dic in countryCodesArray{
let value = CountryCode(fromDictionary: dic)
countryCodes.append(value)
}
}
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if countryCodes != nil{
var dictionaryElements = [[String:Any]]()
for countryCodesElement in countryCodes {
dictionaryElements.append(countryCodesElement.toDictionary())
}
dictionary["countryCodes"] = dictionaryElements
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
countryCodes = aDecoder.decodeObject(forKey: "countryCodes") as? [CountryCode]
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if countryCodes != nil{
aCoder.encode(countryCodes, forKey: "countryCodes")
}
}
}
and
import Foundation
class CountryCode : NSObject, NSCoding{
var countryCode : String!
var countryName : String!
var diallingCode : String!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
countryCode = dictionary["country_code"] as? String
countryName = dictionary["country_name"] as? String
diallingCode = dictionary["dialling_code"] as? String
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if countryCode != nil{
dictionary["country_code"] = countryCode
}
if countryName != nil{
dictionary["country_name"] = countryName
}
if diallingCode != nil{
dictionary["dialling_code"] = diallingCode
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
countryCode = aDecoder.decodeObject(forKey: "country_code") as? String
countryName = aDecoder.decodeObject(forKey: "country_name") as? String
diallingCode = aDecoder.decodeObject(forKey: "dialling_code") as? String
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if countryCode != nil{
aCoder.encode(countryCode, forKey: "country_code")
}
if countryName != nil{
aCoder.encode(countryName, forKey: "country_name")
}
if diallingCode != nil{
aCoder.encode(diallingCode, forKey: "dialling_code")
}
}
}
How do I use these models to get the value of to say India +91 code? I am new to swift so I am clueless as to how to use these models.
the method I know is simple method of ditionary
fileprivate func loadNames(Country: String ) {
// //var countryCodes : [CountryCode]!
// var dict = [Coordinator]?.self
if let filePath = Bundle.main.path(forResource: "CountryCode", ofType: "json") {
if let jsonData = NSData(contentsOfFile: filePath) {
do {
// countryCodes = try? JSONDecoder().decode(CountryCode.self, from: jsonData as Data)
var countryCodes = try JSONSerialization.jsonObject(with: jsonData as Data, options: []) as? [String: Any]
// let countryCodes = try? JSONDecoder().decode(Welcome.self, from: jsonData as Data)
print ("country d ",countryCodes!)
let countryArray = countryCodes!["countryCodes"] as? NSArray
if countryArray!.count > 0
{
for countryElement in countryArray!
{
let dict = countryElement as! NSDictionary
if (dict["country_code"] as! String).description == countryCodeLocale
{
print("found ",(dict["dialling_code"] as! String ).description)
country_codeText.text = (dict["dialling_code"] as! String ).description
phone_NumberText.text = "99133131602"
return
}
}
}
} catch {
print(error.localizedDescription)
}
}
}
}
func fetchValues()
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Testing")
request.returnsObjectsAsFaults = false
do
{
let result = try context.fetch(request)
for data in result as! [NSManagedObject]
{
self.nameArr.append(data.value(forKey: "name") as! String)
self.lastNameArr.append(data.value(forKey: "lastname") as! String)
}
self.tableView.reloadData()
} catch {
print("Failed")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.nameArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? testTVCell
cell?.nameLbl.text = self.nameArr[indexPath.row]
cell?.lastNameLbl.text = self.lastNameArr[indexPath.row]
cell?.deleteBtn.tag = indexPath.row
return cell!
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
#IBAction func deleteAction(_ sender: UIButton) {
// self.nameArr.remove(at: sender.tag)
// self.lastNameArr.remove(at: sender.tag)
// self.tableView.reloadData()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Testing")
fetchRequest.returnsObjectsAsFaults = false
let index = sender.tag
do
{
if let result = try? context.fetch(fetchRequest)
{
for object in result as! [NSManagedObject]
{
print(object.value(forKey: "name") as! String)
if ((object).value(forKey: "name") as! String).description == nameArr[index]
{
print(object.value(forKey: "name") as! String)
context.delete(object )
self.lastNameArr.remove(at: index)
self.nameArr.remove(at: index)
DispatchQueue.main.async
{
self.tableView.reloadData()
}
break
}
}
}
}
catch
{
print("error")
}
let _ : NSError! = nil
do {
try context.save()
self.tableView.reloadData()
} catch {
print("error : \(error)")
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Deleted")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Testing")
fetchRequest.returnsObjectsAsFaults = false
let index = indexPath.row
do
{
if let result = try? context.fetch(fetchRequest)
{
for object in result as! [NSManagedObject]
{
print(object.value(forKey: "name") as! String)
if ((object).value(forKey: "name") as! String).description == nameArr[index]
{
print(object.value(forKey: "name") as! String)
context.delete(object )
DispatchQueue.main.async
{
}
}
}
}
}
catch
{
print("error")
}
let _ : NSError! = nil
do {
try context.save()
} catch {
print("error : \(error)")
}
self.lastNameArr.remove(at: index)
self.nameArr.remove(at: index)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
#IBAction func submitTapped(_ sender: Any)
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Testing", in: context)
let newUser = NSManagedObject(entity: entity!, insertInto: context)
newUser.setValue(self.nameTxt.text, forKey: "name")
newUser.setValue(self.lastNameTxt.text, forKey: "lastname")
do
{
try context.save()
}
catch
{
print("Failed saving")
}
}
func apiCall()
{
let urlString = "https://example.org"
let url = NSURL(string: urlString)
let request = NSMutableURLRequest(url: url! as URL)
activityView.startAnimating()
self.view.addSubview(activityView)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
if error != nil
{
DispatchQueue.main.async
{
let alert = UIAlertController(title: "Network Connection Lost", message: "Please try again", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .cancel, handler: { Void in
self.activityView.stopAnimating()
})
alert.addAction(ok)
self.present(alert, animated: true, completion: nil)
}
return
}
do
{
let result = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as AnyObject
print(" JSON : ", result)
let a = result as! NSArray
let b = a[0] as! NSDictionary
print("name ", (b["Name"] as! String).description)
var nameArray = [String]()
for element in a
{
nameArray.append(((element as! NSDictionary)["Name"] as! String).description)
}
print("nameArray ", nameArray)
DispatchQueue.main.async
{
self.activityView.stopAnimating()
self.activityView.isHidden = true
}
}
catch
{
print("Error -> \(error)")
DispatchQueue.main.async
{
self.activityView.stopAnimating()
let alert = UIAlertController(title: "Alert?", message: error as? String,preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alert.addAction(ok)
self.present(alert, animated: true, completion: nil)
return
}
}
}
task.resume()
}
}
let indexpath = NSIndexPath(row: sender.tag, section: 0)
let cell = tableView.cellForRow(at: indexpath as IndexPath) as? testTVCell
if (self.tableView.isEditing) {
cell?.deleteBtn.setTitle("Edit", for: .normal)
self.tableView.setEditing(false, animated: true)
} else {
cell?.deleteBtn.setTitle("Done", for: .normal)
self.tableView.setEditing(true, animated: true)
}
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let urlString = appDelegate.serverUrl + "queryClassifiedList"
let url = NSURL(string: urlString)
let request = NSMutableURLRequest(url: url! as URL)
let spinner = JHSpinnerView.showDeterminiteSpinnerOnView(self.view)
spinner.progress = 0.0
self.view.addSubview(spinner)
request.httpMethod = "POST"
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")
var dict : [AnyHashable: Any] = [
"channel" : appDelegate.channel,
"device" : appDelegate.deviceId,
"classifiedtype" : self.classifiedType,
"startnum" : startNum,
"endnum" : endNum
]
if UserDefaults.standard.value(forKey: "categoryClassifiedFilter") as? Int != nil && UserDefaults.standard.value(forKey: "categoryClassifiedFilter") as? Int != -1
{
let categoryRow = (UserDefaults.standard.value(forKey: "categoryClassifiedFilter") as? Int)!
dict["categoryid"] = categoryRow
}
if UserDefaults.standard.value(forKey: "cityRowFilter") as? Int != nil && UserDefaults.standard.value(forKey: "cityRowFilter") as? Int != -1
{
let cityId = (UserDefaults.standard.value(forKey: "cityRowFilter") as? Int)!
dict["city"] = cityId
}
let jsonData: Data? = try? JSONSerialization.data(withJSONObject: dict, options: [])
request.httpBody = jsonData
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let urlString = appDelegate.serverUrl + "queryClassifiedList"
let url = NSURL(string: urlString)
let request = NSMutableURLRequest(url: url! as URL)
let spinner = JHSpinnerView.showDeterminiteSpinnerOnView(self.view)
spinner.progress = 0.0
self.view.addSubview(spinner)
request.httpMethod = "POST"
var bodyData : String!
bodyData = "channel=" + appDelegate.channel + "&device=" + appDelegate.deviceId + "&Classifiedtype=" + self.classifiedType.description + "&Startnum=" + startNum.description + "&Endnum=" + endNum.description
if UserDefaults.standard.value(forKey: "categoryClassifiedFilter") as? Int != nil && UserDefaults.standard.value(forKey: "categoryClassifiedFilter") as? Int != -1
{
let categoryRow = (UserDefaults.standard.value(forKey: "categoryClassifiedFilter") as? Int)!
bodyData = bodyData + "&Categoryid=" + categoryRow.description
}
if UserDefaults.standard.value(forKey: "cityRowFilter") as? Int != nil && UserDefaults.standard.value(forKey: "cityRowFilter") as? Int != -1
{
let cityId = (UserDefaults.standard.value(forKey: "cityRowFilter") as? Int)!
bodyData = bodyData + "&city=" + cityId.description
}
print("bodyData : ", bodyData)
request.httpBody = bodyData.data(using: String.Encoding.utf8)
}
but I want modal class method implementation. Can someone help?
You can try
struct Root: Codable {
let countryCodes: [CountryCode]
}
struct CountryCode: Codable {
let countryCode, countryName, diallingCode: String
enum CodingKeys: String, CodingKey {
case countryCode = "country_code"
case countryName = "country_name"
case diallingCode = "dialling_code"
}
}
let res = try? JSONDecoder().decode(Root.self,from:jsonData)
print(res.countryCodes)
res.countryCodes.forEach {
if $0.countryCode == "DZ" {
print($0.diallingCode)
}
}

Swift4: Want to use the variable of UIViewController with UIButton custom class

Currently, I am applying custom class SAFavoriteBtn to UIButton.
I wrote the code to get the API when the button was pressed within that class, I assigned the parameters to get the API data to the variables of UIViewController, I want to use the variable in SAFavoriteBtn. In this case, how should pass the value?
And this pattern is using segue?
UIViewController
class StoreViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate, UICollectionViewDelegateFlowLayout,UITableViewDelegate, UITableViewDataSource {
var store_id = ""
var instaId = ""
var store = [Store]()
var photoPath = [Store.photos]()
var tag = [Store.tags]()
var selectedImage : UIImage?
let defaultValues = UserDefaults.standard
#IBOutlet weak var imageCollectionView: UICollectionView!
#IBOutlet weak var mainImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var locationLabel: UILabel!
#IBOutlet weak var UIView: UIView!
#IBOutlet weak var tagCollectionView: UICollectionView!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//Collectiopn DetaSources
imageCollectionView.dataSource = self
imageCollectionView.delegate = self
tagCollectionView.dataSource = self
tagCollectionView.delegate = self
tableView.dataSource = self
tableView.delegate = self
//Navigation Color
self.navigationController!.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController!.navigationBar.shadowImage = UIImage()
navigationController!.navigationBar.topItem!.title = ""
navigationController!.navigationBar.tintColor = UIColor.white
//UIView Shadow
let shadowPath = UIBezierPath(rect: UIView.bounds)
UIView.layer.masksToBounds = false
UIView.layer.shadowColor = UIColor.black.cgColor
UIView.layer.shadowOffset = .zero
UIView.layer.shadowOpacity = 0.2
UIView.layer.shadowPath = shadowPath.cgPath
//Request API
let url = URL(string: "http://example.com/store/api?store_id=" + store_id)
let request = URLRequest(url: url!)
let session = URLSession.shared
let encoder: JSONEncoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
encoder.outputFormatting = .prettyPrinted
session.dataTask(with: request){(data, response, error)in if error == nil,
let data = data,
let response = response as? HTTPURLResponse{
let decoder: JSONDecoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
do {
let json = try decoder.decode(Store.self, from: data)
self.store = [json]
self.photoPath = json.photos
self.tag = json.tags
if let imageURL = URL(string: "http://example.com/photos/" + json.photos[0].path){
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
self.mainImage.image = image
}
}
}
}else if let imageURL = URL(string: "http://example.com/photos/" + json.photos[0].path.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!){
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
self.mainImage.image = image
}
}
}
}
DispatchQueue.main.async {
self.nameLabel.text = json.name
self.locationLabel.text = json.location
self.tableView.reloadData()
self.imageCollectionView.reloadData()
self.tagCollectionView.reloadData()
}
} catch {
print("error:", error.localizedDescription)
}
}
}.resume()
print(store)
//print(defaultValues.string(forKey: "id"))
// Image Collection view Layout
let itemSize = UIScreen.main.bounds.width/3.62 - 3.62
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
layout.itemSize = CGSize(width: itemSize, height: itemSize)
layout.minimumInteritemSpacing = 1
layout.minimumLineSpacing = 1
imageCollectionView.collectionViewLayout = layout
// Tag Collection View
let tagLayout = UICollectionViewFlowLayout()
tagLayout.minimumLineSpacing = 1
tagLayout.minimumInteritemSpacing = 1
tagLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
tagLayout.itemSize = CGSize(width: 80, height: 24)
tagCollectionView.collectionViewLayout = tagLayout
//status bar color
self.setNeedsStatusBarAppearanceUpdate()
}
override var prefersStatusBarHidden: Bool {
return false
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return UIStatusBarStyle.lightContent
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// UI coner redius
let uiViewPath = UIBezierPath(roundedRect: UIView.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 8, height: 8))
let uiViewMask = CAShapeLayer()
uiViewMask.path = uiViewPath.cgPath
UIView.layer.mask = uiViewMask
navigationController!.navigationBar.topItem!.title = " "
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//Collection
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.imageCollectionView{
let imageCell:UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell",for: indexPath)
let imageView = imageCell.contentView.viewWithTag(1) as! UIImageView
let textLabel = imageCell.contentView.viewWithTag(2) as! UILabel
let instaBtn = imageCell.contentView.viewWithTag(3) as! UIButton
instaBtn.tag = indexPath.row
if photoPath.count > indexPath.row{
if collectionView == self.imageCollectionView{
let url : String = "http://example.com/photos/" + photoPath[indexPath.row].path
let imageURL = URL(string: url)
print(url)
if imageURL != nil {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL!)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
imageCell.layer.masksToBounds = true;
imageCell.layer.cornerRadius = 3
imageView.image = image
textLabel.text = self.photoPath[indexPath.row].username
print(self.photoPath[indexPath.row].username)
}
}
}
}else{
let encodeURL : String = "http://example.com/photos/" + photoPath[indexPath.row].path
let url = URL(string: encodeURL.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!)
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
imageCell.layer.masksToBounds = true;
imageCell.layer.cornerRadius = 3
imageView.image = image
textLabel.text = self.photoPath[indexPath.row].username
print(self.photoPath[indexPath.row].username)
}
}
}
}
}
}
instaBtn.addTarget(self, action: #selector(self.instaBtnTapped), for: UIControlEvents.touchUpInside)
imageCell.addSubview(instaBtn)
return imageCell
//Tag collection view
}else if collectionView == self.tagCollectionView{
let tagCell:UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "TagCell",for: indexPath)
let tagLabel = tagCell.contentView.viewWithTag(2) as! UILabel
if tag.count > indexPath.row{
tagLabel.text = tag[indexPath.row].name
}
tagCell.layer.cornerRadius = 12
return tagCell
}else{
return UICollectionViewCell()
}
}
//tapped instaBtn jump insta user page function
#objc func instaBtnTapped(sender: UIButton){
instaId = photoPath[sender.tag].username
let url = URL(string: "https://www.instagram.com/"+instaId+"/")
UIApplication.shared.open(url!, options: [ : ], completionHandler: nil)
print (sender.tag)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return photoPath.count > 0 ? 3 : 0
}
func tagcollectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tag.count > 0 ? tag.count : 0
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return store.count > 0 ? 1 : 0
case 1 :
return store.count > 0 ? 1 : 0
case 2 :
return store.count > 0 ? 1 : 0
default:
return 0
}
}
//Collection view tap
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == self.imageCollectionView{
let url : String = "http://example.com/photos/" + photoPath[indexPath.row].path
let imageURL = URL(string: url)
print(url)
if imageURL != nil {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL!)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
self.mainImage.image = image
}
}
}
}else{
let encodeURL : String = "http://example.com/photos/" + photoPath[indexPath.row].path
let url = URL(string: encodeURL.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!)
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
self.mainImage.image = image
}
}
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
switch indexPath.section {
case 0 :
//Price Cell
guard let priceCell = tableView.dequeueReusableCell(withIdentifier: "priceCell", for: indexPath) as? priceTableViewCell else { return UITableViewCell()}
if let price:String = store[indexPath.row].price{
priceCell.priceLabel.text! = price
}else{
priceCell.priceLabel.text! = "-"
}
return priceCell
case 1 :
//timeCell
guard let timeCell = tableView.dequeueReusableCell(withIdentifier: "timeCell", for: indexPath) as? timeTableViewCell else{return UITableViewCell()}
if let time:String = store[indexPath.row].open_time{
timeCell.timeLabel.text! = time
}else{
timeCell.timeLabel.text! = "-"
}
return timeCell
case 2 :
//closedayCell
guard let closedayCell = tableView.dequeueReusableCell(withIdentifier: "closedayCell", for: indexPath) as? closedayTableViewCell else { return UITableViewCell() }
if let closeday:String = store[indexPath.row].closed_day{
closedayCell.closedayLabel.text! = closeday
}else{
closedayCell.closedayLabel.text! = "-"
}
return closedayCell
default :
print("Default Selected")
}
return cell
}
#IBAction func moreImageBtn(_ sender: Any) {
let store_id = self.store_id
self.performSegue(withIdentifier: "toStorePhotoViewController", sender: store_id)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toStorePhotoViewController"{
let storePhotoViewController = segue.destination as! StorePhotoViewController
storePhotoViewController.store_id = sender as! String
}
}
//This is SAFavoriteBtn
//Bookmark Button
#IBAction func bookmarkBtn(_ sender: SAFavoriteBtn) {
}
#IBAction func locationBtn(_ sender: Any) {
let lat = store[0].lat
let lng = store[0].lng
if UIApplication.shared.canOpenURL(URL(string:"comgooglemaps://")!){
let urlStr : String = "comgooglemaps://?daddr=\(lat),\(lng)&directionsmode=walking&zoom=14"
UIApplication.shared.open(URL(string:urlStr)!,options: [:], completionHandler: nil)
}else{
let daddr = String(format: "%f,%f", lat, lng)
let urlString = "http://maps.apple.com/?daddr=\(daddr)&dirflg=w"
let encodeUrl = urlString.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)!
let url = URL(string: encodeUrl)!
UIApplication.shared.open(url,options: [:], completionHandler: nil)
}
}
}
SAFavoriteBtn
import UIKit
class SAFavoriteBtn: UIButton {
var isOn = false
let defaultValues = UserDefaults.standard
//Want to use the variable of UIViewController with UIButton custom class in this part
var storeId = ""
override init(frame: CGRect) {
super.init(frame:frame)
initButton()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initButton()
}
func initButton() {
setImage(UIImage(named:"bookmark.png"), for: UIControlState.normal)
addTarget(self, action: #selector(SAFavoriteBtn.buttonPressed), for: .touchUpInside)
}
#objc func buttonPressed() {
activateBtn(bool: !isOn)
}
func activateBtn(bool : Bool){
isOn = bool
//UI
let image = bool ? "bookmark_after.png" : "bookmark.png"
setImage(UIImage(named: image), for: UIControlState.normal)
//API
bool ? favorite() : deleteFavorite()
}
func favorite(){
let user_id = defaultValues.string(forKey: "userId")
let url = URL(string: "http://example.com/api/store/favorite?")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
let postParameters = "user_id=" + user_id! + "&store_id=" + storeId
request.httpBody = postParameters.data(using: .utf8)
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if error == nil, let data = data, let response = response as? HTTPURLResponse {
print("Content-Type: \(response.allHeaderFields["Content-Type"] ?? "")")
print("statusCode: \(response.statusCode)")
print(String(data: data, encoding: .utf8) ?? "")
}
}.resume()
print("favorite")
}
func deleteFavorite(){
let user_id = defaultValues.string(forKey: "userId")
let url = URL(string: "http://example.com/api/store/favorite?")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
let postParameters = "user_id=" + user_id! + "&store_id=" + storeId
request.httpBody = postParameters.data(using: .utf8)
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if error == nil, let data = data, let response = response as? HTTPURLResponse {
print("Content-Type: \(response.allHeaderFields["Content-Type"] ?? "")")
print("statusCode: \(response.statusCode)")
print(String(data: data, encoding: .utf8) ?? "")
}
}.resume()
print("delete")
}
}

App crash on iPhone 6, but work on iPhone 6 simulator

I don't understand why my application works well on the Simulator but instead when I run on iPhone crashes. What can be?
import UIKit
import CoreData
class HomeTableViewController: UITableViewController{
var arrayEntity : [Questions] = []
override func viewDidLoad(){
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
if (CoreDataController.shared.checkIfIsEmpty() == true) { getJSON() }
else { arrayEntity = CoreDataController.shared.loadQuestion()!
//reload della tableview quando l'array si modifica
self.tableView.reloadData()
}
}
struct ProvaREST{
var userId: Int = 0
var id: Int = 0
var title: String = ""
var body: String = ""
static func parsereviewJSONData(data: Data) -> [ProvaREST] {
var provaREST = [ProvaREST]()
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
// Parse JSON data
if let reviews = jsonResult as? [Dictionary<String, Any>] {
for review in reviews {
var newReview = ProvaREST()
newReview.userId = review["userId"] as! Int
newReview.id = review["id"] as! Int
newReview.title = review["title"] as! String
newReview.body = review["body"] as! String
provaREST.append(newReview)
}
}
} catch let err {
print(err)
}
return provaREST
}
}
func getJSON(){
var reviews = [ProvaREST]()
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
guard let URL = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
var request = URLRequest(url: URL)
request.httpMethod = "GET"
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
if (error == nil) {
// Parse JSON Data
if let data = data {
reviews = ProvaREST.parsereviewJSONData(data: data)
//Core data saving
for i in 0 ..< reviews.count{
CoreDataController.shared.addQuestion(userID: reviews[i].userId, id: reviews[i].id, title: reviews[i].title, body: reviews[i].body)
}
//quando finisco di aggiungere, tengo in memoria (per zone fuori copertura) ed estraggo i dati per la tableview
self.arrayEntity = CoreDataController.shared.loadQuestion()!
self.tableView.reloadData()
}
} else {
// Failure connection
print("Nessuna connessione")
}
})
task.resume()
session.finishTasksAndInvalidate()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayEntity.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = arrayEntity[indexPath.row].body
cell.detailTextLabel?.text = String(arrayEntity[indexPath.row].id)
return cell
}
}
and
import Foundation
import UIKit
import CoreData
class CoreDataController {
static let shared = CoreDataController()
private var context: NSManagedObjectContext
private init() {
let application = UIApplication.shared.delegate as! AppDelegate
self.context = application.persistentContainer.viewContext
}
func addQuestion(userID: Int, id: Int, title: String, body: String) {
let entity = NSEntityDescription.entity(forEntityName: "Questions", in: self.context)
let newQuestion = Questions(entity: entity!, insertInto: self.context)
newQuestion.userid = Int32(userID)
newQuestion.id = Int32(id)
newQuestion.title = title
newQuestion.body = body
do {
try self.context.save()
} catch{
print("Errore")
}
}
func loadQuestion() -> [Questions]? {
print("Recupero libri:")
let fetchRequest: NSFetchRequest<Questions> = Questions.fetchRequest()
do {
let array = try self.context.fetch(fetchRequest) as [Questions]
guard array.count > 0 else {print("[EHY!] Non ci sono elementi da leggere "); return array}
return array
}catch let err{
print(err)
return nil
}
}
func checkIfIsEmpty() -> Bool{
let fetchRequest: NSFetchRequest<Questions> = Questions.fetchRequest()
do {
let array = try self.context.fetch(fetchRequest)
guard array.count > 0 else { return true }
return false
}catch{
print("Errore nella fetchRequest")
return false
}
}
}
The coredatacontroller class is the class that handles database operationsThe coredatacontroller class is the class that handles database operationsThe coredatacontroller class is the class that handles database operations

I can not get the json data to display in my second UIViewController

I have the following two functions in my first ViewController. They load a UITableView with over 300 rows. I call the loadRemoteData function inside the ViewDidLoad. Everything works fine in the first ViewController.
// MARK: - parseJSON
func parseJSON(data: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let rootDictionary = json as? [NSObject: AnyObject], rootResults = rootDictionary["results"] as? [[NSObject: AnyObject]] {
for childResults in rootResults {
if let firstName = childResults["first_name"] as? String,
let lastName = childResults["last_name"] as? String,
let bioguideId = childResults["bioguide_id"] as? String,
let state = childResults["state"] as? String,
let stateName = childResults["state_name"] as? String,
let title = childResults["title"] as? String,
let party = childResults["party"] as? String {
let eachLegislator = Legislator(firstName: firstName, lastName: lastName, bioguideId: bioguideId, state: state, stateName: stateName, title: title, party: party)
legislators.append(eachLegislator)
}
}
}
} catch {
print(error)
}
}
// MARK: - Remote Data configuration
func loadRemoteData() {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let url = "https://somedomain.com/legislators?order=state_name__asc,last_name__asc&fields=first_name,last_name,bioguide_id"
if let url = NSURL(string: url) {
let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if let error = error {
print("Data Task failed with error: \(error)")
return
}
if let http = response as? NSHTTPURLResponse, data = data {
if http.statusCode == 200 {
dispatch_async(dispatch_get_main_queue()) {
self.parseJSON(data)
self.tableView.reloadData()
}
}
}
})
task.resume()
}
}
In the second ViewController, I want to display more information about the individual listed in the cell that is tapped, for that I use a different URL such as https://somedomain.com/legislators?bioguide_id=\"\(bioguideId)\" which provides me with a lot more detail. (The data being requested from the JSON Dictionary is different)
The code I use in the second ViewController is just like shown above with the only difference being the URL. I can print the url coming from the previous ViewController and it is displayed in the console log but no json data is shown.
I would appreciate any help.
Thanks
Below is the code for my second ViewController:
import UIKit
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var bioguideId: String?
var currentLegislator: Legislator? = nil
var currentLegislatorUrl: String?
let reuseIdentifier = "Cell"
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var tableView: UITableView!
// MARK: - parseJSON
private func parseJSON(data: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let rootDictionary = json as? [NSObject: AnyObject],
rootResults = rootDictionary["results"] as? [[NSObject: AnyObject]] {
for childResults in rootResults {
if let firstName = childResults["first_name"] as? String,
let lastName = childResults["last_name"] as? String,
let bioguideId = childResults["bioguide_id"] as? String,
let state = childResults["state"] as? String,
let stateName = childResults["state_name"] as? String,
let title = childResults["title"] as? String,
let party = childResults["party"] as? String {
currentLegislator = Legislator(firstName: firstName, lastName: lastName, bioguideId: bioguideId, state: state, stateName: stateName, title: title, party: party)
}
}
}
} catch {
print(error)
}
}
// MARK: - Remote Data configuration
func loadRemoteData(url: String) {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let url = currentLegislatorUrl
if let url = NSURL(string: url!) {
let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if let error = error {
print("Data Task failed with error: \(error)")
return
}
print("Success")
if let http = response as? NSHTTPURLResponse, data = data {
if http.statusCode == 200 {
dispatch_async(dispatch_get_main_queue()) {
self.parseJSON(data)
self.tableView.reloadData()
}
}
}
})
task.resume()
}
}
func loadImage(urlString:String) {
let imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if (error == nil && data != nil) {
func display_image() {
self.imageView.image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
print(currentLegislatorUrl!)
loadRemoteData(currentLegislatorUrl!)
loadImage("https://theunitedstates.io/images/congress/225x275/\(bioguideId!).jpg")
self.title = bioguideId
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath)
cell.textLabel!.text = currentLegislator?.firstName
return cell
}
}
Thanks to Adam H. His comment made me reevaluate the URL I was using and by adding additional operators, now the data is shown in my second ViewController.

Swift iOS 9 UITableView reloadData after CoreData finished

I try to update my tableview after my REST-call is finished and all entities are stored in CoreData. I tried dispatch_asynch but the tableview doesn't reload when expected. I am using a UIRefreshControl to trigger refreshing. Usually, the correct tableview data is displayed after 2 refreshes. I have no idea why.
I can confirm that my tableview instance IS NOT nil.
#IBAction func refreshTriggered(sender: UIRefreshControl) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.evMan.retrieveAndSaveEvents(self.pubMan.getAllPublishers())
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}
}
This is my retrieveAndSaveEvents method from my 'EventManager' evMan:
func retrieveAndSaveEvents(forPublishers: [PUBLISHERS]) {
for publisher in forPublishers {
let pubId = publisher.id as Int
let str = "/publishers/\(pubId)/events"
// resty is an instance of my REST-api wrapper
self.resty.GET(self.server, route: str, onCompletion: {json in
let result = json.asArray
for var i = 0; i < result!.count; i++ {
if !self.isIDAlreadyInDB(json[i]["id"].asInt!) {
let newEv = NSEntityDescription.insertNewObjectForEntityForName("EVENTS", inManagedObjectContext: self.context!) as! EVENTS
newEv.headline = json[i]["headline"].asString!
newEv.id = json[i]["id"].asInt!
newEv.content = json[i]["content"].asString!
newEv.ofPublisher = publisher
do {
try self.context!.save()
} catch _ {
}
print("New Event from \(publisher.name): \(newEv.headline)")
}
}
})
}
}
FYI, here's my cellForRowAtIndexPath: (I am using a Custom UITableViewCell)
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("eventCell") as? EventCell
let curEv = evMan.getEvents()[indexPath.row]
cell?.infoLabel.text = curEv.ofPublisher?.name
cell?.myImageView.image = UIImage(named: "icon.png")
cell?.detailLabel.text = curEv.headline
cell?.timeLabel.attributedText = NSAttributedString(string: self.dateFormatter.stringFromDate(curEv.updatedAt))
cell?.contentView.backgroundColor = UIColor.clearColor()
cell?.backgroundColor = UIColor(white: 1.0, alpha: 0.5)
return cell!
}
Here is my REST-wrapper:
class REST: NSObject {
// Basic Auth
let sConfig = ServerConfig()
var username = "rest"
var password = "resttest"
override init() {
username = sConfig.getUserLogin().user
password = sConfig.getUserLogin().pass
}
func GET(server: String, route: String, onCompletion: (JSON) -> Void) {
let route = server+route
makeHTTPGetRequest(route, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
func makeHTTPGetRequest(path: String, onCompletion: ServiceResponse) {
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
let loginString = NSString(format: "%#:%#", username, password)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
let loginvalue = "Basic " + base64LoginString
// add Headers
request.addValue(loginvalue, forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
let json:JSON = JSON(data: data!)
onCompletion(json, error)
})
task.resume()
}
}
#IBAction func refreshTriggered(sender: UIRefreshControl) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.evMan.retrieveAndSaveEvents(self.pubMan.getAllPublishers())
})
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}
Now, you cannot guarantee reloadData() happens after self.evMan.retrieveAndSaveEvents(self.pubMan.getAllPublishers()) , because they are not happen in the same queue. And probably, reloadData() happens before retrieve.
Option 1:
You should put the block :
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
at the end of func retrieveAndSaveEvents(self.pubMan.getAllPublishers())
Option 2 :
#IBAction func refreshTriggered(sender: UIRefreshControl) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.evMan.retrieveAndSaveEvents(self.pubMan.getAllPublishers())
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
})
}
EDIT:
I did not see there is another queue in retrieveAndSaveEvents
So, put the
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
at the end of:
self.resty.GET(self.server, route: str, onCompletion: {json in
let result = json.asArray
for var i = 0; i < result!.count; i++ {
if !self.isIDAlreadyInDB(json[i]["id"].asInt!) {
let newEv = NSEntityDescription.insertNewObjectForEntityForName("EVENTS", inManagedObjectContext: self.context!) as! EVENTS
newEv.headline = json[i]["headline"].asString!
newEv.id = json[i]["id"].asInt!
newEv.content = json[i]["content"].asString!
newEv.ofPublisher = publisher
do {
try self.context!.save()
} catch _ {
}
print("New Event from \(publisher.name): \(newEv.headline)")
}
}
})

Resources