Currently learning Swift & iOS. I try to access with Parse a saved picture. However, I can't access it with getDataInBackground(block:).
Here's my code:
//
// ViewController.swift
// Instragram
//
// Created by Macbook Pro on 22.07.17.
// Copyright © 2017 Macbook Pro. All rights reserved.
//
import UIKit
import Parse
class ViewController: UIViewController {
#IBOutlet weak var picture: UIImageView!
#IBOutlet weak var senderLbl: UILabel!
#IBOutlet weak var recieverLbl: UILabel!
#IBOutlet weak var messageLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Creating tables and data in database
let imageData = UIImageJPEGRepresentation(picture.image!, 0.5)
let file = PFFile(name: "picture.jpg", data: imageData!)
let table = PFObject(className: "messages")
table["sender"] = "Akhmed"
table["reciver"] = "Bob"
table["picture"] = file
table["message"] = "Hello!"
table.saveInBackground {(success, error) -> Void in
if(success){
print("Saved successful")
} else {
print(error!)
}
}
//Recieving Data from the Server
let information = PFQuery(className: "messages")
information.findObjectsInBackground{(objects: [PFObject]?, error) -> Void in
if error == nil {
for object in objects!{
self.senderLbl.text = object["sender"] as? String
self.recieverLbl.text = object["reciver"] as? String
self.messageLbl.text = object["message"] as? String
object["picture"].getDataInBackground(...)
}
} else {
print(error!)
}
}
}
}
Down after I access the name, receiver and message string I try to access an image that has been saved on there server with:
object["picture"].getDataInBackground(block:)
However, Swift won't even autocorrect anymore after I've typed object["picture"]. I get also an error:
'Value of type "Any" has no Member 'getDataInBackground(block:)'
Any ideas what's wrong? It seems to me that Swift can't find the string picture even though the image is saved on the server under the string "picture".
You need to first cast it as a PFFfile object and then retrieve the actual image data with getDataInBackground function like this:
let imageFile = object["picture"] as? PFFile
imageFile?.getDataInBackground (block: { (data, error) -> Void in
if error == nil {
if let imageData = data {
self.myImage = UIImage(data:imageData)
}
}
})
Related
I'm really new into swift & currently learning API by doing a project that shows list of games from rawg.io referring to the website's doc. I created GameFeed.swift & GameDetail.swift to pull name, release date, and rating from it and working fine in my console.
GameFeed.swift :
struct GameFeed: Codable {
let results:[GameDetail]
}
GameDetail.swift :
struct GameDetail: Codable {
let name:String
let released:String
let rating:Double
}
Now i'm trying to put the results to a simple UIlabel like gameName.text, gameReleased.text & gameRating.text from ViewController.swift so it will be show in Main.Storyboard
i did research on google about how to show it to these UIlabel by using DispatchQueue.main.async but when i'm declaring it, it receiving error :
Value of type 'GameFeed' has no member 'name'
same error messages also happened to released & rating. This is my ViewController.Swift :
class ViewController: UIViewController {
#IBOutlet weak var gameName: UILabel!
#IBOutlet weak var gameReleased: UILabel!
#IBOutlet weak var gameRating: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Defining API Site
let urlString = "https://api.rawg.io/api/games"
let url = URL(string: urlString)
guard url != nil else {
return
}
// Calling API
let session = URLSession.shared
let dataTask = session.dataTask(with: url!){
(data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let gameFeed = try decoder.decode(GameFeed.self, from: data!)
print(gameFeed)
DispatchQueue.main.async {
self.gameName.text = gameFeed.name
self.gameReleased.text = gameFeed.released
self.gameRating.text = gameFeed.rating
}
}
catch {
print("Error Parsing JSON")
}
}
}
dataTask.resume()
}
}
What should i do to make it possible to parse the data to labels?
The GameFeed contains an Array of GameDetails. But you are trying to set a single GameDetail on those labels. You should first pull out a single GameDetail from that array, then assign it in a way you like.
DispatchQueue.main.async {
let gameDetail = gameFeed.results.first // <- This will return the first one
self.gameName.text = gameDetail?.name
self.gameReleased.text = gameDetail?.released
self.gameRating.text = gameDetail?.rating
}
I can't upload an image into Firebase Storage
I found this error in my output log
"An SSL error has occurred and a secure connection to the server
cannot be made."
and last output is
print("check: 4 >> Don't put image")
Then the least of the code doesn't execute therefore the image still not uploading
In the putData method i also use url parameter but also got the same problem
MY Full code is
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
class ViewController: UIViewController {
#IBOutlet weak var imageview: UIImageView!
#IBOutlet weak var emailTextView: UITextField!
#IBOutlet weak var passwordTextView: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func buttonAction(_ sender: Any) {
Auth.auth().createUser(withEmail: emailTextView.text!, password: passwordTextView.text!){ (result, error) in
if let _eror = error {
print(_eror.localizedDescription)
return
}
print("check: 1 >> After creating user\(result!)")
let uid = result?.user.uid
let storage = Storage.storage()
let storageRef = storage.reference(forURL: "gs://porate-chai-4deee.appspot.com").child("profile_image").child(uid!)
print("check: 2 >> \(storageRef)")
if let profileImage = self.imageview!.image, let imageData = profileImage.jpegData(compressionQuality: 0.1) {
print("check: 3 >> \(imageData)")
storageRef.putData(imageData, metadata: nil) { (metadata, error) in
if error != nil {
print("check: 4 >> Don't put image")
return
}
print("put image on firebase storage")
storageRef.downloadURL(completion: {(url, error) in
if error != nil {
print(error!.localizedDescription)
return
}
let downloadURL = url?.absoluteString
print(downloadURL!)
let ref = Database.database().reference()
print("seeref\(ref)")
let userReference = ref.child("tutor")
let newUserReference = userReference.child(uid!)
newUserReference.setValue([
"email": self.emailTextView.text!,
"profileimageurl": downloadURL!
])
})
}
}
}
}
}
My Firebase Storage Rules is
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
I also try this rules
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if true;
}
}
}
With the help of this answer
"flutter pub get" can't get dependency plugins on Windows
After 27-03-2020 the code still work but after 28-03-2020 have some problem storage.googleapis.com in Bangladesh.
To overcome this issue, use VPN tool and re-run your project.
I used Hotspot Sheild VPN and after that everything was good.
I use this
Hotspot Shield: Fastest
VPNwww.hotspotshield.com
Then the file again uplod
I am trying to retrieve certain data from my Firebase Database - the profile image. As you can see, this is from a UITableViewCell. I have an #IBOutlet for my imageView I want to cover.
As the view awakens, you can see that I go through, and make sure that I can get the information. I know how to retrieve data from Firebase, but not photo URLs, and then convert to the photo itself.
I'm not sure why it isn't working. I am getting an error, and will show it below. There is a possibility it is because of the URL unwrapping stuff, or as if the Firebase isn't formatted correctly, which I think it is, though.
Error Message : Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
import UIKit
import FirebaseAuth
import FirebaseDatabase
import Firebase
class ProfileCellControler: UITableViewCell {
#IBOutlet var name : UILabel!
#IBOutlet var rating : UILabel!
#IBOutlet var imageViewPro : UIImageView!
var databaseRefer : DatabaseReference!
var databaseHandle : DatabaseHandle!
override func awakeFromNib() {
super.awakeFromNib()
var urlString = ""
let urll = URL(string: urlString)!
databaseRefer = Database.database().reference()
let userID = Auth.auth().currentUser!.uid
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Name").observe(.value, with: { (data) in
print(String((data.value as? String)!))
self.name.text = "\(String((data.value as? String)!))"
print("Done")
})
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Stars").observe(.value, with: { (data) in
print(String((data.value as? String)!))
if ((String((data.value as? String)!)) == "N/A") {
self.rating.text = "No Rating"
} else {
self.rating.text = "\(String((data.value as? String)!)) ★"
}
print("Done")
})
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Image").observe(.value, with: { (data) in
print(String((data.value as? String)!))
print("Done \(String((data.value as? String)!))")
urlString = (String((data.value as? String)!))
})
ImageService.downloadImage(withURL: urll) { (image) in
self.imageViewPro.image = image
}
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
The string for the URL is found nil because you are creating the call to download the image for your url before the urll has been initialized with a value from the database in:
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Image").observe(.value, with: { (data) in
print(String((data.value as? String)!))
print("Done \(String((data.value as? String)!))")
urlString = (String((data.value as? String)!))
})
observe(.value, with: ) Is an asynchronous operation thus
ImageService.downloadImage(withURL: urll) { (image) in
self.imageViewPro.image = image
}
Is being called before observe(.value, with:) is resolved. I would recommend moving the callback for the download URL inside of the completion for .observe(:value, :with) or using grand central dispatch to control the flow better.
As a side note, I highly recommend SDWebImage for handling your image downloading needs as it is configurable with a default image for situations such as this when the image fails to load.
Import KingFisher to make your life easier and then..
Download string representation of image from Firebase asynchronically.
Assign downloaded image to imageView with .kf.setImage method.
I have two swift files in my project. One is called loadApi.swift and the other one is the default ViewController.swift
I intend to include all the functionalities of getting and parsing Json data in the loadApi.swift. Bellow is the code
import Foundation
var arr: Array = [String]()
func loadApi(){
var url = "http://api.wordnik.com:80/v4/word.json/love/definitions?limit=200&includeRelated=true&sourceDictionaries=ahd&useCanonical=true&includeTags=false&api_key=a2a73e7b926c924fad7001ca3111acd55af2ffabf50eb4ae5"
let session = NSURLSession.sharedSession()
let wordnik = NSURL(string: url)
//println(wordnik)
var task = session.dataTaskWithURL(wordnik!){
(data, response, error)-> Void in
if error != nil {
println("Internet error! Check your Internet Connection")
} else{
var error : NSError?
var vocabData = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &error) as NSArray
if error != nil{
println("Parse Json error!")
}else{
for items in vocabData{
let def = items["text"] as String
arr.append(def)
}
}
}
}
task.resume()
}
well, everything works fine and I can get and parse the Json data from the url and than append them into the "arr" Array. However, when I try to call the "arr" Array in the default ViewController.swift, I got this error saying "missing parameter #2 in call". Bellow is my code in the ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
loadApi()
println(arr)
//error saying "missing parameter #2 in call"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
How can I access the "arr" Array that contains all the strings I got from the Json data?
Changing the declaration of the array to var arr = [String]() should do the trick.
I am new to CloudKit and I am having trouble connecting the assets in my database to the ImageView and TextView in my app. In my CloudKit database, under record types, I created a record named "Companies". I then went to Public Data->Default Zone and then created a new instance of the record called "myCompany". In "myCompany" I have two assets, an image and a .txt file. I want to connect those assets to my app. I am not sure if I need to use CKQuery or what is the best approach. Any advice would be much appreciated. Below is what I have so far. Feel free to give feedback of what I have or if there's a better way, I would love to know. Thanks.
import UIKit
import CloudKit
class LearnViewController: UIViewController {
#IBOutlet weak var theImage: UIImageView!
#IBOutlet weak var theText: UITextView!
let myRecord = CKRecord(recordType: "Companies" )
var image: UIImage!
let database = CKContainer.defaultContainer().publicCloudDatabase
func loadCoverPhoto(completion:(photo: UIImage!) -> ()) {
dispatch_async(
dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)){
var image: UIImage!
let coverPhoto = self.myRecord.objectForKey("Picture") as CKAsset!
if let asset = coverPhoto {
if let url = asset.fileURL {
let imageData = NSData(contentsOfFile: url.path!)!
image = UIImage(data: imageData)
}
}
completion(photo: image)
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadCoverPhoto() { photo in
dispatch_async(dispatch_get_main_queue()) {
self.theImage.image = photo
}
}
You could get the record directly if you know the recordId by performing a fetch like:
database.fetchRecordWithID(CKRecordID(recordName: recordId), completionHandler: {record, error in
Or if you don't know the ID you should query for the record with something like the code below. just create the right NSPredicate. A query can return more than 1 record.
var query = CKQuery(recordType: recordType, predicate: NSPredicate(value: true))
var operation = CKQueryOperation(query: query)
operation.recordFetchedBlock = { record in
// is this your record...
}
operation.queryCompletionBlock = { cursor, error in
self.handleCallback(error, errorHandler: {errorHandler(error: error)}, completionHandler: {
// ready fetching records
})
}
operation.resultsLimit = CKQueryOperationMaximumResults;
database.addOperation(operation)
In your case when using the fetchRecordWithID option the code would look like this:
override func viewDidLoad() {
super.viewDidLoad()
database.fetchRecordWithID(CKRecordID(recordName: recordId), completionHandler: {record, error in
self.myRecord = record
loadCoverPhoto() { photo in
dispatch_async(dispatch_get_main_queue()) {
self.theImage.image = photo
}
}
}
}