how to show an image from URL from AFnetworking with Swift? - swift

This would be a super noob question but I have no idea how to convert URL to data and data to UIimage. The text labels are work really beautifully but the UI images are not working
I figured out there are some codes to convert them but don't know how to use them. could you help me out.
override func viewDidLoad() {
super.viewDidLoad()
let manager = AFHTTPSessionManager()
let url = mainURL + "sample.php"
manager.get(url, parameters: nil, progress: nil, success: { (task, res) in
guard let json = res as? [String: Any] else {
print ("not [String: Any]]")
return
}
if let array = json["data"] as? [Any] {
for i in 0 ..< array.count {
if let row = array [ i ] as? [String: Any] {
let model = Model(t: row ["title"] as! String,
n: row ["user"] as! String,
d: row ["regdate"] as! String,
imgUrl: row ["img"] as! String)
self.models.append(model)
}
self.tableView.reloadData()
}
}
}) { (task, error) in
print("error = \(error)")
}
self.tableView.estimatedRowHeight = 500
}
Model
class Model : NSObject {
//?
let data = try? Data(contetsOf: url!)
let img = UIImage(data: data!)
let imgView = UIImageView(image: img)
var title : String
var name : String
var date : String
var image : UIImage!
init(t: String, n: String, d: String, imgUrl: String){
title = t
name = n
date = d
image = UIImage(named: imgUrl)
}
and the outlets.
mainCell.titleLabel.text = m.title //labels work fine
mainCell.nameLabel.text = m.name
mainCell.mainImgView.image = m.image

Try this
Just store image url in model class not image. Update your model class with below code
class Model : NSObject {
var title : String
var name : String
var date : String
var image : String
init(t: String, n: String, d: String, imgUrl: String){
title = t
name = n
date = d
image = imgUrl
}
}
// past code inside cellforRowAt
// download image from image URL
let url = URL(string: image)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
mainCell.mainImgView.image = UIImage(data: data!)
}
}).resume()

Related

Swift. Data is loaded after then TableView

I have 2 files. First - TransactionsViewController. Second - GetTransactions. When I open TransactionsViewController the table from that view loads faster than the date from GetTransactions. Therefore, it is displayed blank. How to fix it?
Here is the code of TransactionsViewController viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
GetTransactions().getTransactions()
clientsCount = GetTransactions.transactions.count
setupNavBar()
createTable()
refreshSetup()
}
Here is the code of GetTransactions:
class GetTransactions {
static var transactionsArr = [[String : Any]]()
static var transactions = [TransactionInfo]()
let URL_GET_TRANSACTIONS = "https://mashkov.dev/sites/default/BankApp/Transactions/Transactions.php"
func getTransactions(){
GetTransactions.transactionsArr.removeAll()
AF.request(URL_GET_TRANSACTIONS).responseJSON{ (response) in
if (response.value as? [[String : Any]]) != nil {
GetTransactions.transactionsArr = response.value as! [[String : Any]]
}
self.convertData()
print(GetTransactions.transactions)
}
}
func convertData() {
GetTransactions.transactions.removeAll()
for transaction in GetTransactions.transactionsArr {
let cl = TransactionInfo(id: transaction["id"] as! String,
payee: transaction["payee_account_id"] as! String,
sender: transaction["sender_account_id"] as! String,
transDate: transaction["trans_date"] as! String,
amount: transaction["amount"] as! String,
isSuccessfully: Bool((transaction["isSuccessfully"] as! String)) ?? true)
GetTransactions.transactions.append(cl)
}
}
}
What you need is a closure / block to be passed as an argument
class GetTransactions {
static var transactionsArr = [[String : Any]]()
static var transactions = [TransactionInfo]()
let URL_GET_TRANSACTIONS = "https://mashkov.dev/sites/default/BankApp/Transactions/Transactions.php"
func getTransactions(completion block: () -> ()){
GetTransactions.transactionsArr.removeAll()
AF.request(URL_GET_TRANSACTIONS).responseJSON{ (response) in
if (response.value as? [[String : Any]]) != nil {
GetTransactions.transactionsArr = response.value as! [[String : Any]]
}
self.convertData()
print(GetTransactions.transactions)
block()
}
}
func convertData() {
GetTransactions.transactions.removeAll()
for transaction in GetTransactions.transactionsArr {
let cl = TransactionInfo(id: transaction["id"] as! String,
payee: transaction["payee_account_id"] as! String,
sender: transaction["sender_account_id"] as! String,
transDate: transaction["trans_date"] as! String,
amount: transaction["amount"] as! String,
isSuccessfully: Bool((transaction["isSuccessfully"] as! String)) ?? true)
GetTransactions.transactions.append(cl)
}
}
}
And you call it using,
GetTransactions().getTransactions {
clientsCount = GetTransactions.transactions.count
//reload your tableView here
}

Swift : Possible to overload randomVar as? [MyStruct]?

My server returns an array of photo informations in JSON like that :
"pics":[{"ID":182,"ID_member":39,"fn":"b69ea6f6c88b58c67a331aa3c5eaff81.jpg"}, ...]
I have a struct init function made to handle one photo json raw array (from type [String:Any]) :
init?(fromRaw _img:[String:Any]?)
{
guard
let img = _img,
let id = img["ID"] as? Int,
let idm = img["ID_member"] as? Int,
let fn = img["fn"] as? String
else
{
OOTLog.info("Warning : unable to init from photo raw array")
return nil ;
}
self.id = id
self.idMembre = idm
self.fileName = fn
}
My question is : lets say we have a json from server (of type [[String:Any]], array of n raw photos), is there any way to "overload" as? [Photo] with my init?(fromRaw:) within Photo struct, so we could just code :
guard let arrayPhoto = jsonRaw as? [Photo] else ..
Instead of :
guard let arrayPhotoRaw = jsonRaw as [[String:Any]] else ..
let photoArray:[Photo] = []
for p in jsonRaw {
guard let p = Photo(fromRaw:p) else { continue }
photoArray.append(p)
}
It's better to use
struct Root: Codable {
let pics: [Pic]
}
struct Pic: Codable {
let id, idMember: Int
let fn: String
enum CodingKeys: String, CodingKey {
case id = "ID"
case idMember = "ID_member"
case fn
}
}
let res = try! JSONDecoder().decode(Root.self, from:data)
print(res.pics)

Fetch data from multiple node in Firebase in Swift

I built my app to have news feed like Facebook. My problem is that I don't know how to fetch child images in Post and show it in a collectionView. Please show me how to do it. Appreciate any help.
Here is the db structure:
Posts
d7j3bWMluvZ6VH4tctQ7B63dU4u1:
20181112101928:
avatar: "https://platform-lookaside.fbsbx.com/platform/p..."
content: "Funny image"
images:
-LR4vaEIggkGekc-5ZME:
"https://firebasestorage.googleapis.com/v0/b/hon..."
-LR4vaENC-IsePibQYxY:
"https://firebasestorage.googleapis.com/v0/b/hon..."
name: "Thành Trung"
time: 1541992768776.3628
type: "Funny"
Here is my code:
func getDataFromPostFirebase() {
let getPostData = databaseReference.child("Posts")
getPostData.observe(.childAdded) { (snapshot) in
getPostData.child(snapshot.key).observe(.childAdded, with: { (snapshot1) in
getPostData.child(snapshot.key).child(snapshot1.key).observe(.value, with: { (snapshot2) in
self.arrayImageUrl = [String]()
if let dict = snapshot2.value as? [String : Any] {
guard let avatar = dict["avatar"] as? String else {return}
guard let content = dict["content"] as? String else {return}
guard let name = dict["name"] as? String else {return}
guard let time = dict["time"] as? Double else {return}
guard let type = dict["type"] as? String else {return}
if let images = dict["images"] as? [String : String] {
for image in images.values {
self.arrayImageUrl.append(image)
}
let newPost = Post(avatarString: avatar, contentString: content, nameString: name, timeDouble: time, typeString: type)
self.arrayPost.append(newPost)
DispatchQueue.main.async {
self.feedTableView.reloadData()
}
} else {
let newPost = Post(avatarString: avatar, contentString: content, nameString: name, timeDouble: time, typeString: type)
self.arrayPost.append(newPost)
DispatchQueue.main.async {
self.feedTableView.reloadData()
}
}
}
})
})
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayImageUrl.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! TrangChu_CollectionViewCell
cell.imgContent.layer.cornerRadius = CGFloat(8)
cell.imgContent.clipsToBounds = true
cell.imgContent.layer.borderWidth = 2
cell.imgContent.layer.borderColor = #colorLiteral(red: 0.4756349325, green: 0.4756467342, blue: 0.4756404161, alpha: 1)
let url = URL(string: arrayImageUrl[indexPath.row])
cell.imgContent.sd_setImage(with: url, completed: nil)
return cell
}
Model object
import Foundation
class Post {
var avatar : String
var content : String
var images : [String]?
var name : String
var time : Double
var type : String
init(avatarString : String, contentString : String, nameString : String, timeDouble : Double, typeString : String) {
avatar = avatarString
content = contentString
// images = imagesString
name = nameString
time = timeDouble
type = typeString
}
}
As what I've said your db is not well structured. I suggest you re structure it like this.
Posts
d7j3bWMluvZ6VH4tctQ7B63dU4u1:
avatar: "https://platform-lookaside.fbsbx.com/platform/p..."
content: "Funny image"
images:
-LR4vaEIggkGekc-5ZME: "https://firebasestorage.googleapis.com/v0/b/hon..."
-LR4vaENC-IsePibQYxY: "https://firebasestorage.googleapis.com/v0/b/hon..."
name: "Thành Trung"
time: 1541992768776.3628
type: "Funny"
timestamp: 1540276959924
I removed the timestamp node and transferred it along the children node. Now you can fetch the posts with this.
ref.child("Posts").observe(.childAdded) { (snapshot) in
var post = Post()
let val = snapshot.value as! [String: Any]
post.name = val["name"] as? String
self.ref.child("Posts").child(snapshot.key).child("images").observeSingleEvent(of: .value, with: { (snap) in
post.imagesString = [String]()
for image in snap.children.allObjects as! [DataSnapshot] {
post.imagesString?.append(image.value as! String)
print("images \(image.value)")
}
list.append(post)
print("post \(post)")
})
If you want to order the posts you can achieve it using queryOrderedByChild("timestamp")
Add this to access your images:
guard let images = dict["images"] as? [[String: Any]] { return }
let imagesString: [String] = []
for imageDict in images {
for key in imageDict.keys {
if let imageName = imageDict[key] as? String else {
// here you access your image as you want
imagesString.append(imageName)
}
}
}
Then when creating the post object you use imagesString that we created:
let newPost = Post(avatarString: avatar, contentString: content, imagesString: imagesString, nameString: name, timeDouble: time, typeString: type)
You can fetch the images values using this
ref.child("Posts").observe(.childAdded) { (snapshot) in
self.ref.child("Posts").child(snapshot.key).observe(.childAdded, with: { (snapshot1) in
self.ref.child("Posts").child(snapshot.key).child(snapshot1.key).child("images").observe(.childAdded, with: { (snap) in
let post = new Post()
for rest in snap.children.allObjects as! [DataSnapshot] {
//append images
post.imagesString.append(rest.value)
}
post.avatarString = snapshot1.value["avatar"] as? String
...
})
})
I suggest you change the structure of your db because its nested. Refer here

how to use random string to let or var to url link

how to use random string to let or var to url link
i want to make random string for url
let url = URL(string:"https://www.pallive.net/random.json")
or see the code when i change values in the site linke in the app do not changed,but if i chnage name of url it change
the code not reload if i change the value in json and keep same file
if i want to reload i have to change the name of file how to do some thange
auotmatic change the url and keep the orginal in the ftp server
import Foundation
class Episode
{
var title: String?
var description: String?
var thumbnailURL: URL?
var url: URL?
var episodes = [Episode]()
init(title: String, description: String, thumbnailURL: URL, createdAt: String, author: String)
{
self.title = title
self.description = description
self.thumbnailURL = thumbnailURL
}
init(espDictionary: [String : AnyObject])
{
self.title = espDictionary["title"] as? String
// description = espDictionary["description"] as? String
thumbnailURL = URL(string: espDictionary["thumbnailURL"] as! String)
self.url = URL(string: espDictionary["link"] as! String)
}
static func downloadAllEpisodes(completion: #escaping ([Episode]) -> ()) {
var episodes = [Episode]()
let url = URL(string:"https://www.pallive.net/random.json")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
completion(episodes)
}
else {
if let jsonData = data ,let jsonDictionary = NetworkService.parseJSONFromData(jsonData) {
let espDictionaries = jsonDictionary["episodes"] as! [[String : AnyObject]]
for espDictionary in espDictionaries {
let newEpisode = Episode(espDictionary: espDictionary)
episodes.append(newEpisode)
}
}
completion(episodes)
DispatchQueue.main.async(execute: {
completion(episodes)
})
}
}.resume()
}
func randomString(_ length: Int) -> String {
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.length)
var randomString = ""
for _ in 0 ..< length {
let rand = arc4random_uniform(len)
var nextChar = letters.character(at: Int(rand))
randomString += NSString(characters: &nextChar, length: 1) as String
}
return randomString
}
}

download and save a PFFile (UIImage) to show in a UIImageView : SWIFT

I have a large class called "Goal" in parse. This class has multiple elements, one of which is a PFFile, that is always a UIImage.
When I perform my query for the "Goal" class, I cannot figure out how to take the PFFile, and change it to a UIImage for use.
var query = PFQuery(className:"Goal")
let currentUser = PFUser.currentUser()!.username
query.whereKey("creator", equalTo: currentUser!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects?.count) goals for the TableView.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
let goalType = object["type"] as! String
let goalPeriod = object["period"] as! String
let goalCategory = object["category"] as! String
let goalShortDescription = object["shortDescription"] as! String
let goalLongDescription = object["longDescription"] as! String
let goalPointvalue = object["pointValue"] as! Int
let goalSharedSetting = object["shared"] as! Bool
let goalAdoptionCount = object["adoptionCount"] as! Int
let goalIsComplete = object["isComplete"] as! Bool
let goalSuccessImageData = object["image"] as! PFFile
goalSuccessImageData.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.imageQuery = image
}
}
}
let goalSuccessImage : UIImage = self.imageQuery
let goalObjectID = object.objectId
let goalSpreadCount = object["spreadCount"] as! Int
let goalSpreadTotal = object["spreadTotal"] as! Int
let goalTotalCompletions = object["totalCompletions"] as! Int
let thisGoal = GoalModel(period: goalPeriod, type: goalType, category: goalCategory, shortDescription: goalShortDescription, longDescription: goalLongDescription, pointValue: goalPointvalue, shared: goalSharedSetting, adoptionCount: goalAdoptionCount, isComplete: goalIsComplete, successImage: goalSuccessImage, goalID: goalObjectID!, spreadCount: goalSpreadCount, spreadTotal: goalSpreadTotal, totalCompletions: goalTotalCompletions ) as GoalModel
any tips on how to modify the "success image" part? I added a space before and after to make it easier to find.
Thank you in advance!
I'm using this way in my projects, if it help's you :
func performSave(sender: UIBarButtonItem){
affichageActivityIndicator()
let qos = Int(QOS_CLASS_USER_INITIATED.value)
dispatch_async(dispatch_get_global_queue(qos,0)) { () -> Void in
dispatch_async(dispatch_get_main_queue()){
if let updateObject = self.currentObject as PFObject? {
let imageData = UIImageJPEGRepresentation(imageToSave, 0.1)
let imageFile = PFFile(name:"image.png", data:imageData)
updateObject["imageFile"] = imageFile
// Save the data back to the server in a background task
updateObject.saveInBackgroundWithBlock{(success: Bool, error: NSError!) -> Void in
UIApplication.sharedApplication().endIgnoringInteractionEvents()
if success == false {
println("Error")
}
}
}
}
}
}