I came across the following UITableViewController code and wanted to implement the associated classes and functions that go with them in order for me to learn a little bit more about Swift.
I'm not sure what the implementation of api.getRooms() looks like. I think it may be a closure, but I'm not entirely sure?
My question is what would api.getRooms() be returning considering there's {} usage? If anyone could explain to me what's going on that would be greatly appreciated.
api.getRooms(User.currentUser()!) { (roomsObj, error) in
if let rooms = roomsObj as? Array<Room> {
self.rooms = rooms
self.tableView.reloadData()
if (viaPullToRefresh) {
self.refreshControl.endRefreshing()
}
}
}
PullViewController.swift
class PullViewController: UITableViewController {
var rooms = Array<Room>()
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
// Custom initialization
assert(User.currentUser())
}
override func viewDidLoad() {
self.refreshControl = UIRefreshControl()
self.refreshControl.addTarget(self, action: Selector("refreshInvoked"), forControlEvents: UIControlEvents.ValueChanged)
refresh()
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return rooms.count
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath:NSIndexPath!) -> UITableViewCell! {
var cell: UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
if !cell {
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier:"Cell")
}
let room = rooms[indexPath.row]
cell!.textLabel.textColor = UIColor.blackColor()
cell!.textLabel.text = "\(room.name)(\(room.messageCount))"
return cell
}
func refreshInvoked() {
refresh(viaPullToRefresh: true)
}
func refresh(viaPullToRefresh: Bool = false) {
let api = API()
api.getRooms(User.currentUser()!) { (roomsObj, error) in
if let rooms = roomsObj as? Array<Room> {
self.rooms = rooms
self.tableView.reloadData()
if (viaPullToRefresh) {
self.refreshControl.endRefreshing()
}
}
}
}
}
User.swift
class User {
init() {
}
class func currentUser() -> Bool {
return true
}
}
Room.swift
class Room {
var name: String
var messageCount: Int
init() {
}
}
API.swift (not sure whether this is implemented correctly).
class API {
init() {
}
func getRooms(user: User) -> (Array<Room>, String) { // ??
// ??
}
}
In you room class, you did not initialize the name and message count variable, in swift only optional variable can be nil
class Room {
var name: String
var messageCount: Int
init(name:String, messageCount:Int) {
self.name = name
self.messageCount = messageCount
}
}
also assert need to evaluate to bool so
assert(User.currentUser() != nil)
1) You are sending assert a void since your currentUser method doesn't return anything, so assert wouldn't know if this is good or bad. Therefore you need to make currentUser return a BOOL or something else if you want to use it like this, but it needs some sort of BOOL result to tell if it is asserting correctly or not. Hopefully that makes sense.
2)You are trying to feed your getRooms function a lambda function instead of running a function after the results.
--Update--
If you are wanting a completion lambda then you'll want to write getRooms like this:
func getRooms(user: User, completion: ((Array<Room>,String?) -> Void)?) -> Array<Room> {//String because I have no idea what type they want for errors
var room = Room()
room.messageCount = 0
room.name = "Room1"
var rooms = Array<Room>()
rooms.append(room)
completion?(rooms,nil)
return rooms
}
or something along those lines
This is how I implemented the api.getRooms() function:
API.swift
class API {
func getRooms(user: User, completion: (Array<Room>, String) -> ()) {
var room = Room(name: "Room1", messageCount: 0)
var rooms = Array<Room>()
rooms.append(room)
completion(rooms, "error")
}
}
PullViewController.swift
func refresh(viaPullToRefresh: Bool = false) {
let api = API()
if let user = User.currentUser() {
api.getRooms(user) { (roomsObj, error) in
self.rooms = roomsObj
self.tableView.reloadData()
if (viaPullToRefresh) {
self.refreshControl.endRefreshing()
}
}
}
}
Related
I'm new in swift 5.
I have a trouble to call a function present in viewcontroller file :
func getJsonResultFromUrl (urlSrc: String, completion:#escaping (Arts)->()) {
if let url = URL(string: urlSrc) {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
var returnValue: Arts?
let decoder = JSONDecoder()
returnValue = try decoder.decode(Arts.self, from: data)
completion(returnValue!)
} catch {
fatalError("Couldn't parse \(url) as \(Arts.self):\n\(error)")
}
}
task.resume()
}
}
from another file with class :
import UIKit
class ListStockArticle: UITableViewController {
var datacell: (Arts) -> () = {_ in } // = 0
var url: String = ""
var nombreDeLigne: Int?
init() {
let urlPrefixLocal = "http://urlmasqued"
let urlPrefixRemote = "http://urlmasqued"
if self.url.validURL {
} else {
self.url = urlPrefixRemote + ajax
}
print(self.url)
let k: () = getJsonResultFromUrl(urlSrc: url, completion: (Arts) -> () )
print(k)
////// -> I have error : Editor placeholder in source file
/* getJsonResultFromUrl(urlSrc: self.url) { k in Arts()
// self.datacell = Arts
self.nombreDeLigne = k.count
}
*/
super.init(nibName:nil, bundle:nil) // *
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20 //self.nombreDeLigne
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "Test"
return cell
}
}
The URL and get data from url works fine, but how can I call the function from another page ? The idea is to get a list of product, and create a cell per products.
I will really appreciate help, this sounds strange for me.
Regards
Try this: Singleton class
class SampleController {
private static var privateSharedInstance: SampleController?
static var sharedInstance: SampleController {
if privateSharedInstance == nil {
privateSharedInstance = SampleController()
}
return privateSharedInstance ?? SampleController()
}
private init() {
}
func getJsonResultFromUrl () {
}
}
Usage:
override func viewDidLoad() {
super.viewDidLoad()
SampleController.sharedInstance.getJsonResultFromUrl()
}
searching in the textField of the tableView.
I need to search the names from the tableView. So for that i have tableView and textField in the UIViewController.
And i need to search from the Api. For that i have used the Alamofire method to fetch the data.
I need to search. Now i implement to display the names from the api to tableView And i got the output. But i need to implement searching .how to do in the mvvm.
my model:-
class SearchModel: NSObject {
var restaurantname :String!
init?(dictionary :JSONDictionary) {
guard let name = dictionary["name"] as? String else {
return
}
self.restaurantname = name
}
}
My viewmodel:-
class SearchViewModel: NSObject {
var datasourceModel:SearchDataSourceModel
init(withdatasource newDatasourceModel: SearchDataSourceModel) {
datasourceModel = newDatasourceModel
}
func datafordisplay(atindex indexPath: IndexPath) -> SearchModel{
return datasourceModel.dataListArray![indexPath.row]
}
func numberOfRowsInSection(section:Int) -> Int {
return (datasourceModel.dataListArray?.count)!
}
func loadData(completion :#escaping (_ isSucess:Bool) -> ()){
loadFromWebserviceData { (newDataSourceModel) in
if(newDataSourceModel != nil) {
self.datasourceModel = newDataSourceModel!
completion(true)
}
else {
completion(false)
}
}
}
//}
func loadFromWebserviceData(completion :#escaping (SearchDataSourceModel?) -> ()) {
//with using Alamofire ..............
Alamofire.request("http://www.example.com").validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON{ response in
switch response.result {
case .success(let data):
print("success",data)
let result = response.result
if let wholedata = result.value as? [String:Any]{
if let data = wholedata["data"] as? Array<[String:Any]>{
// print(data["name"] as! String)
print(data)
print(response)
let newDataSource:SearchDataSourceModel = SearchDataSourceModel(array: data)
completion(newDataSource)
// }
}
}
// case .failure(let data):
// print("fail",data)
case .failure(let encodingError ):
print(encodingError)
// if response.response?.statusCode == 404 {
print(encodingError.localizedDescription)
completion(nil)
// }
}
}}
}
my DataSourcemodel:-
class SearchDataSourceModel: NSObject {
var dataListArray:Array<SearchModel>? = []
init(array :Array<[String:Any]>?) {
super.init()
var newArray:Array<[String:Any]> = []
if array == nil {
// newArray = self.getJsonDataStored44()
}
else {
newArray = array!
}
var datalist:Array<SearchModel> = []
for dict in newArray {
let model = SearchModel(dictionary: dict)
datalist.append(model!)
}
self.dataListArray = datalist
}
}
my viewController class:-
class SearchViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet private weak var tableView: UITableView!
private var searchViewModel :SearchViewModel!
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?, withViewModel viewModel:SearchViewModel) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
searchViewModel = viewModel
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
// tableView.dataSource = self
// filteredData = data
searchViewModel.loadData { (isSuccess) in
if(isSuccess == true) {
self.tableView .reloadData()
}
else {
}
}
// self.tableView .reloadData()
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchViewModel.numberOfRowsInSection(section: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "searchcell"
var cell: SearchCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? SearchCell
if cell == nil {
tableView.register(UINib(nibName: "SearchCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? SearchCell
}
cell.setsearchData(search: searchViewModel.datafordisplay(atindex: indexPath))
// cell.name.text = searchViewModel.datafordisplay(atindex: indexPath)
// cell.name.text = filteredData[indexPath.row]
// cell.setsearchData(search: searchViewModel.datafordisplay(atindex: indexPath))
return cell
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
tableViewCell:-
class SearchCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func setsearchData(search:SearchModel) {
self.name.text = search.restaurantname
}
}
This is my code .Now how to implement the searching here.
There are t way of searching.
when you enter text and press the search button.
When You type then search without using the search button.
A solution for 1:
You need to call a method on the search button
Search button.
func Search() {
predicate = NSPredicate(format: "Self.YourSearchListName beginsWith[c]%#", SearchName)
GetsearchList = YourArrayForSearch.filtered(using: predicate) as NSArray
YourTable.reloadData()
}
2nd Solution
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == self.YourTextFieldName{
Search()
}
}
for 2nd Solution don't forget to set the delegate.
filteredContactListArray is the array holding the filtered data to reload the tableview when user start searching for particular text.
#IBOutlet var searchCityTextField: UITextField!
var filteredSearchArray :SearchViewModel!
within in Viewdidload() function
searchCityTextField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
UItextfieldDelegate Method
func textFieldDidChange(textField: UITextField) {
if textField.text == "" {
filteredSearchArray = searchViewModel// contactListArray is the actual array with all the list of data.
citiesTableView.reloadData()
}else{
filterContentForSearchText(textField.text!)
}
}
func filterContentForSearchText(searchText: String) {
filteredSearchArray = NSMutableArray(array:searchViewModel. dataListArray.filter({(ele:AnyObject) -> Bool in
return (ele as! searchViewModel).cityName.lowercased().contains(searchText.lowercased())
}))
citiesTableView.reloadData()
}
UItableviewDelegate Method
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredContactListArray.count
}
I'm new at swift closures.
I have this code and i can't extract the values from the forloop into the arrays outside the closure.
Sorry about this question but i have already searched stackflow but without success in this specific case.
Thanks in advance.
var namesArray: [String] = []
var imagesArray: [String] = []
var timesArray: [String] = []
var placesArray: [String] = []
func getData(){
//DATA SOURCE
// Create request for user's Facebook data
let request = FBSDKGraphRequest(graphPath: cmp.pageId, parameters: ["fields": "events"])
// Send request to Facebook
request.startWithCompletionHandler {
(connection, result, error) in
if error != nil {
// Some error checking here
print(error.debugDescription)
} else
if let pageData = result["events"] {
print(result["events"]!!["data"]!![0]["name"])
if let eventDetails = pageData!["data"] {
// EVENT DETAILS FETCH
for var i = 0; i < eventDetails!.count; i++
{
let fetchedEventName = eventDetails![i]["name"] as? String!
let fetchedEventTime = eventDetails![i]["start_time"] as? String!
if eventDetails?[i]["place"]??["name"] != nil {
if let fetchedEventPlace = eventDetails?[i]["place"]??["name"] {
self.placesArray.append(fetchedEventPlace! as! String)
}
} else {
self.placesArray.append("Lisbon")
}
self.namesArray.append(fetchedEventName!)
self.timesArray.append(fetchedEventTime!)
}
print("Name of event: \(self.namesArray)")
}
} else {
print("Error.")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
getData()
}
EDIT: I want to show the fetched result into a tableview that's already set.
Heres the tableview code.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("previewCell") as? PreviewCell {
var img: UIImage!
let url = NSURL(string: imagesArray[indexPath.row])!
if let data = NSData(contentsOfURL: url){
img = UIImage(data: data)
} else {
img = UIImage(named: "ant")
}
cell.configureCell(img, title: namesArray[indexPath.row], time: timesArray[indexPath.row], place: placesArray[indexPath.row])
return cell
} else {
return PreviewCell()
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return namesArray.count
}
func addNewEvent(name: String, image: String, time: String, place: String)
{
namesArray.append(name)
imagesArray.append(image)
timesArray.append(time)
placesArray.append(place)
}
You are already getting the number in to the arrays so what you need to do is just reload the tableview after the for loop.
//end of for-loop
self.tableView.reloadData()
The reason for this is the asynchronous execution of the request.startWithCompletionHandler. The code inside that block will most likely execute after your tableView already loaded and it therefor needs a reload after the data has been fetched
In case if you need to return data from closure you can define completion on your getData() method like this
func getData(completion:([DataType]) -> Void) {
//process data
let dataArray:DataType = []
completion(dataArray)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
getData { (completionData) -> Void in
// process returned data
}
}
Hope this helps
So here is my Problem.. I've retrieved the user data from _User class in Parse and showing it in the app as follows:
var data:NSMutableArray = NSMutableArray()
func loadData() {
data.removeAllObjects()
var userQuery = PFUser.query()
userQuery?.orderByAscending("createdAt")
userQuery?.findObjectsInBackgroundWithBlock({ (objects, erroe) -> Void in
if let objects = objects {
for object in objects {
if let user = object as? PFUser {
if user.objectId != PFUser.currentUser()?.objectId {
self.data.addObject(object)
}
self.tableView.reloadData()
}
}
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("users", forIndexPath: indexPath) as! userListTableViewCell
let userData:PFObject = self.data.objectAtIndex(indexPath.row) as! PFObject
// Usernames and gender..
myCell.fullName.text = userData.objectForKey("fullName") as! String!
myCell.genderLabel.text = userData.objectForKey("gender") as! String!
// Profile pictures..
let profilePics = userData.objectForKey("profilePicture") as! PFFile
profilePics.getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
myCell.dp.image = downloadedImage
}
}
return myCell
}
Now I want to add a follow button to follow a particular user and want to save that data in Parse. So my questions are:
How can I add the follow button effectively which would not mess up things if there are so many users.. I've tried giving them tag like myCell.followButton.tag = indexPath.row but it dint work well. so I want to know about some other way for achieving my goal.
What is the best possible way to save the follower list in Parse.. I'm thinking to make a class named Followers having the columns user : the user being followed and follower where we can add the PFUser.currentUser().objectId . Is there anything better than this or this is a nice method to do it?
here is the screen shot of my userListTableViewController..
Here you can see the followButton which i've already connected to userListTableViewCell.. Please help me out.. Thanks for your time..
Lets make that button you want a UILabel instead and add a UIGestureRecognizer to it, one like this
Create a swift file named PFUserTapGestureRecognizer.swift
import UIKit
class PFUserTapGestureRecognizer: UITapGestureRecognizer
{
var tapper: PFUserTapper
init(id: Int, onTap: (id: Int) -> Void)
{
self.tapper = PFUserTapper()
self.tapper.id = id
self.tapper.onTap = onTap
super.init(target: self.tapper, action: Selector("userTapped"))
}
}
class PFUserTapper : NSObject
{
var id = 0
var onTap: ((idUse: Int) -> Void)?
func userTapped()
{
onTap?(idUser: id)
}
}
Now, when you load your cell in your view controller where you are loading your UITableView, in the delegate tableView:cellForRowAtIndexPath:, do this:
// Check if you are following that user already
// if im NOT following this user then
{
let idUser = userData.objectForKey("id") as! Int
myCell.labelFollow.tag = idUser
myCell.labelFollow.text = "Follow"
addFollowBehavior(follow: true, idUser: idUser, label: myCell.labelFollow)
}
// else if i'm following the user
{
myCell.labelFollow.text = "Unfollow"
addFollowBehavior(follow: false, idUser: idUser, label: myCell.labelFollow)
}
// else.. you should consider the case when you click Follow and it takes time to get an answer from your service
And create this function
func addFollowBehavior(follow follow: Bool, idUser: Int, label: UILabel)
{
if let recognizers = label.gestureRecognizers {
for recognizer in recognizers {
label.removeGestureRecognizer(recognizer as! UIGestureRecognizer)
}
}
label.addGestureRecognizer(PFUserTapGestureRecognizer(idUser,
onTap: { (idUser) -> Void in
// THIS IS WHERE YOU SAVE TO PARSE, WEB SERVICE OR WHATEVER
// if follow then
// Do Follow
// else
// UnFollow...
// On the callback write this to protect from reused cells:
if label.tag = idUser {
myCell.labelFollow.text = follow ? "Unfollow" : "Follow" // NOW (UN)FOLLOWING!
addFollowBehavior(follow: !follow, idUser: idUser, label: myCell.labelFollow)
}
}
)
}
I didn't had the chance to test it but I have a very similar implementation on a requirement like that and it works perfectly
Note: this works if the follow item is an UIImageView or whatever inherits from UIView for a cooler look, instead of changing the text, you change the UIImage
i trie to find parse users via UISearchBar and display results in a table view.
When i heat search, nothing happened...
My idea was to use the searchText.text as a query to PFUser.
Should i try to use searchBar with search display controller instead ?
Any idea ?
import UIKit
class AllFriendsTableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate {
#IBOutlet var SearchDisplay: UISearchDisplayController!
var userList:NSMutableArray = NSMutableArray()
func searchBarShouldBeginEditing(searchBar: UISearchBar!) -> Bool {
return true
}
func searchBarShouldEndEditing(searchBar: UISearchBar!) -> Bool {
return true
}
func searchBar(searchBar: UISearchBar!, textDidChange searchText: String!) {
loadUser()
}
#IBOutlet var searchText: UISearchBar!
func loadUser () {
userList.removeAllObjects()
var findUser:PFQuery = PFUser.query()
findUser.whereKey("username", equalTo: searchText.text)
findUser.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
if !(error != nil) {
// The find succeeded.
println("succesfull load Users")
// Do something with the found objects
for object in objects {
self.userList.addObject(object)
println("users added to userlist")
}
self.tableView.reloadData()
} else {
// Log details of the failure
println("error loadind user ")
println("error")
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
if tableView == searchDisplayController.searchResultsTableView {
return userList.count
}
else {
return 0
}
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell: AllFirendsTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)as AllFirendsTableViewCell
let users:PFObject = self.userList.objectAtIndex(indexPath!.row) as PFObject
var findUserName:PFQuery = PFUser.query()
findUserName.whereKey("username", containsString: searchText.text)
findUserName.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!) -> Void in
if !(error != nil) {
if let user:PFUser = users as? PFUser {
cell.userNameTextField.text = user.username
println("user exist")
// define avatar poster
if let avatarImage:PFFile = user["profileImage"] as? PFFile {
avatarImage.getDataInBackgroundWithBlock{(imageData:NSData!, error:NSError!)-> Void in
if !(error != nil) {
let image:UIImage = UIImage(data: imageData)
cell.avatarImage.image = image as UIImage
cell.avatarImage.layer.cornerRadius = 24
cell.avatarImage.clipsToBounds = true
}
}
}
else {
cell.avatarImage.image = UIImage(named: "Avatar-1")
cell.avatarImage.layer.cornerRadius = 24
cell.avatarImage.clipsToBounds = true
}
}
}
}
return cell
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
navigationController.navigationBar.hidden = false
loadUser()
}
override func viewDidAppear(animated: Bool) {
}
override func viewDidLoad() {
// Do any additional setup after loading the view, typically from a nib.
}
}
I suspect this might be due to you attempting to reload the tableview from the background thread that your network request occurs on. UI updates should happen from the main thread.
In your loadUser() function try doing your tableview reload from the main thread, as follows:
NSOperationQueue.mainQueue().addOperationWithBlock({
self.tableView.reloadData()
})
That may fix your issue.