Firebase storage image url not working - ios

Hi I'm a newbie developer from Taiwan.
I have started to use firebase(ios) storage for my app and I want to use ImageSliderShow to make a image slider view. After I got the image URL from firebase. I appended the url string into array then I ran the app. The image slider view works fine but the new image just showing nothing. Then I printed the url String before and after I appended into the array. It gave me two different string. Here is my code.
override func viewDidLoad() {
super.viewDidLoad()
let URLString = loadURL()
URLString.getURL(){
(result:String) in
self.ImageSliderView.backgroundColor = UIColor.white
self.ImageSliderView.slideshowInterval = 5.0
self.ImageSliderView.pageControlPosition = PageControlPosition.insideScrollView
self.ImageSliderView.pageControl.currentPageIndicatorTintColor = UIColor.lightGray
self.ImageSliderView.pageControl.pageIndicatorTintColor = UIColor.black
self.ImageSliderView.contentScaleMode = UIViewContentMode.scaleAspectFill
self.ImageSliderView.activityIndicator = DefaultActivityIndicator()
self.ImageSliderView.currentPageChanged = { page in
print("current page:", page)
}
print("Result: \(result)") ////print the result string before appended into array.
self.kingfisherSource.append(KingfisherSource(urlString: String(result))!)
print("alamoArray3: \(self.kingfisherSource[3].url)") ////print the result string after appended into array.
self.ImageSliderView.setImageInputs(self.kingfisherSource)
let recognizer = UITapGestureRecognizer(target: self, action: #selector(ResultViewController.didTap))
self.ImageSliderView.addGestureRecognizer(recognizer)
}
}
func didTap() {
let fullScreenController = ImageSliderView.presentFullScreenController(from: self)
// set the activity indicator for full screen controller (skipping the line will show no activity indicator)
fullScreenController.slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil)
}
getURL function:
class loadURL {
let storage = Storage.storage(url: "my-firebase-storage-bucket")
func getURL(completion:#escaping (_ result:String)->Void) {
self.storage.reference().child("images/breakfast/1/1.jpg").downloadURL { url, error in
if error != nil {
print("Firebase Image URL error: \(String(describing: error))")
} else {
print("Firebase Image URL: \(String(describing: url!))")
completion("\(String(describing: url))")
}
}
}
The output:
Firebase Image URL: https://firebasestorage.googleapis.com/v0/b/my-firebase-storage-bucket/o/images%2Fbreakfast%2F1%2F1.jpg?alt=media&token=15ee8094-ac50-4e93-adc0-200793181bfc
Result: Optional(https://firebasestorage.googleapis.com/v0/b/my-firebase-storage-bucket/o/images%2Fbreakfast%2F1%2F1.jpg?alt=media&token=15ee8094-ac50-4e93-adc0-200793181bfc)
alamoArray3: Optional(https://firebasestorage.googleapis.com/v0/b/my-firebase-storage-bucket/o/images%2Fbreakfast%2F1%2F1.jpg?alt=m ... 3181bfc)
The 3rd output just weird... Is it possible be an encoding issue?
And I apology about my bad English.

Finally I just figure it out. I let the type of result as URL and then it just works perfectly.

Related

How to resolve totally app freeze after DocuSign login

My app totally freeze after successful login to DocuSign
Here my code:
#IBAction private func signDocument(_ sender: UIButton) {
guard let hostURL = URL(string: Environment.current.docuSignHost) else { return }
isLoading = true
DSMManager.login(withEmail: Environment.current.docuSignUserID,
password: Environment.current.docuSignPass,
integratorKey: Environment.current.docuSignIntegratorKey,
host: hostURL) { [weak self] info, error in
self?.isLoading = false
if let error = error {
self?.error = error
} else {
self?.showDocuSign(info: info)
}
}
}
// MARK: - Helpers
private func showDocuSign(info: DSMAccountInfo?) {
guard let info = info else { return }
envelopManager.perform(with: info, presentingController: self)
}
final class EnvelopeManager {
private let envelopesManager = DSMEnvelopesManager()
private let templateManager = DSMTemplatesManager()
// MARK: - Lifecycle
func sync() {
envelopesManager.syncEnvelopes()
}
func perform(with config: DSMAccountInfo, presentingController: UIViewController) {
guard let path = Bundle.main.path(forResource: R.file.tgkCapitalPortfolioBAgreementPdf.name, ofType: "pdf") else { return }
let envelopDefinition = DSMEnvelopeDefinition()
envelopDefinition.envelopeName = "Some name"
envelopDefinition.emailSubject = "Please Sign Envelope on Document"
envelopDefinition.emailBlurb = "Hello, Please sign my Envelope"
let builder = DSMDocumentBuilder()
builder.addDocumentId("1")
builder.addName(R.file.tgkCapitalPortfolioBAgreementPdf.name)
builder.addFilePath(Bundle.main.path(forResource: R.file.tgkCapitalPortfolioBAgreementPdf.name,
ofType: "pdf")!)
let document = builder.build()
envelopDefinition.documents = [document]
let signHere = DSMSignHere()
signHere.documentId = document.documentId
signHere.pageNumber = 1
signHere.recipientId = "1"
signHere.frame = .init(originX: 100,
originY: 100,
width: 100,
height: 100,
originYOffsetApplied: 50)
signHere.tabId = "1"
let tabs = DSMTabs()
tabs.signHereTabs = [signHere]
let signer = DSMSigner()
signer.email = config.email
signer.name = config.userName
signer.userId = config.userId
signer.clientUserId = config.userId
signer.routingOrder = 1
signer.recipientId = "1"
signer.tabs = tabs
let signers: [DSMSigner] = [signer]
let recipients = DSMRecipients()
recipients.signers = signers
envelopDefinition.recipients = recipients
envelopDefinition.status = "created"
envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .offline) { [weak self] envelopID, error in
if let envelopID = envelopID {
print(envelopID)
self?.presentSigning(presenter: presentingController,
envelopeID: envelopID)
} else {
print(error.localizedDescription)
}
}
}
private func presentSigning(presenter: UIViewController, envelopeID: String) {
envelopesManager.resumeSigningEnvelope(withPresenting: presenter,
envelopeId: envelopeID) { (viewController, error) in
if let viewController = viewController {
print(viewController)
}
if let error = error {
print(error.localizedDescription)
}
}
}
}
All closures have a success status but the app freezes and any DocuSign view controllers are not showing.
But if in the second attempt I add the calling the logout before login - all works as expected
#IBAction private func signDocument(_ sender: UIButton) {
guard let hostURL = URL(string: Environment.current.docuSignHost) else { return }
isLoading = true
DSMManager.logout()
DSMManager.login(withEmail: Environment.current.docuSignUserID,
password: Environment.current.docuSignPass,
integratorKey: Environment.current.docuSignIntegratorKey,
host: hostURL) { [weak self] info, error in
self?.isLoading = false
if let error = error {
self?.error = error
} else {
self?.showDocuSign(info: info)
}
}
}
How to resolve this issue? How to make that my app will not freeze after any successful login?
Edit1
I have figured out that the total app freeze is occurring by the first app install. When I open an app that was already installed and I many times opened it earlier and go to DocuSign flow, call logout and finally call login- all works as expected - I don't have any app freeze. But if I don't call the logout method in this chain then my app freezes.
The problem was consistent in that DSMManager.setup() called earlier than applicationDidLaunch. After I moved it to applicationDidLaunch the problem was resolved
Thanks for posting the solution #igdev.
Yes, correct sequence to perform SDK setup is during applicationDidLaunch or afterwards, say during viewDidLoad() of any of the viewControllers (lazy sdk setup & initialization). As long as setup() is done prior to any of the sdk-calls such as login(withAccessToken:...) or cacheEnvelope(...). DocuSign SDK setup() establishes the core data and other essential components required for its functionality.
Typical sequence of calls made with DocuSign SDK.
Application Launch
Initialize DocuSign SDK with DocuSignSDK setup()
Login with DocuSignSDK login(with...). Login could be skipped if a prior SDK session is already established, for e.g. when getting signatures in offline-envelopes using downloaded templates.
Accessing TemplatesManager or EnvelopesManager functionality [Download templates, Sign Envelopes, Sync offline envelopes, etc] and other tasks such as handling DocuSign-SDK notifications to track events (e.g. Signing Completed Envelope Cached, etc).
Logout, it's not required, if needed can perform DSMManager.logout() when client user is logged out.

SLComposeServiceViewController refresh configurationItems?

I followed this guide on how to setup a SLComposeViewController, and it includes on how to set up configuration items.
I'm pulling from a MongoDB database to get the most recently modified item. Here is my configurationItems():
override func configurationItems() -> [Any]! {
let configurationItems = [editConfigurationItem]
return configurationItems
}
and here is my editConfigurationItem variable:
lazy var editConfigurationItem: SLComposeSheetConfigurationItem = {
func getFirstCategory(completion: #escaping(String) -> ()) {
DispatchQueue.main.async {
let email: String = customKeychainWrapperInstance.string(forKey: "email") ?? ""
let password: String = customKeychainWrapperInstance.string(forKey: "password") ?? ""
app.login(credentials: Credentials.emailPassword(email: email, password: password)) { (maybeUser, error) in
DispatchQueue.main.sync {
guard error == nil else {
print("Login failed: \(error!)");
return
}
guard let _ = maybeUser else {
fatalError("Invalid user object?")
}
print("Login succeeded!");
}
let user = app.currentUser!
let configuration = user.configuration(partitionValue: user.id)
let predata = try! Realm(configuration: configuration)
let unsortedData = predata.objects(Tiktoks.self)
let data = unsortedData.sorted(byKeyPath: "date", ascending: false)
let firstCategory = data[0].category
let firstCategoryId = data[0]._id
print(firstCategory)
print(firstCategoryId)
self.selectedId = firstCategoryId
completion(firstCategory)
}
}
print("done")
}
let item = SLComposeSheetConfigurationItem()!
item.title = "collection"
item.valuePending = true
getFirstCategory() { (firstCategory) in
item.valuePending = false
print("completed")
item.value = firstCategory // Using self as the closure is running in background
}
item.tapHandler = self.editConfigurationItemTapped
return item
}()
(Sorry for the messiness of the code, I'm new to Swift)
So far it works, but the item.value variable doesn't get updated in the UI. It "infinitely loads" until you click on the configuration item. When the configuration item is tapped to go to another view though, the actual variable from the database shows for a split second before showing the next view. When you go back from that other view, the actual variable is there.
It looks like to me that the configuration item isn't updating. I see that there is a reloadConfigurationItems(), but by putting that in my editConfigurationItem will cause a loop I would think (and it also errors out too). The documentation even says:
In particular, you don’t need to call this method after you change a configuration item property, because the SLComposeServiceViewController base class automatically detects and responds to such changes.
but it looks like it's not detecting the changes.
Is there a way to refresh item.value and item.valuePending?

getting names displaying from firebase

I am trying to display the contents of a firebase database. I know that I am reading them correctly as I am able to print them as they are read in. The problem is when I call the method to display them on screen, they are "out of range".
I know this means the the methods are being called simultaneously therefore the array is empty. I have tried the "Sleep()" method and doesn't work.
//arrays of names and descriptions
var Names:[String] = []
var Desctiptions: [String] = []
inital method
override func viewDidLoad() {
super.viewDidLoad()
getRestauraunt()
//create slides
scrollView.delegate = self
slides = createSlides()
setupSlideScrollView(slides: slides)
}
func getRestauraunt(){
let db = Firestore.firestore()
db.collection("Test").getDocuments { (snapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in snapshot!.documents {
let name = document.get("Name") as! String
let description = document.get("Description") as! String
//print("Names: ",name," Description: ",description)
self.Names.append(name)
self.Desctiptions.append(description)
}
}
}
}
create slides method
func createSlides() -> [Slide] {
//firebase link
let slide1:Slide = Bundle.main.loadNibNamed("Slide", owner: self, options: nil)?.first as! Slide
slide1.labelTitle.text = Names[0]
}
I would like if someone could show me how to get the 'createSlides()' method to wait until the 'getRestauraunts()' method has finished. Thank you
Just call it from the end of the getrestaurant()'s getDocuments closure
func getRestauraunt(){
//as before...
} else {
for document in snapshot!.documents {
let name = document.get("Name") as! String
let description = document.get("Description") as! String
self.Names.append(name)
self.Desctiptions.append(description)
}
self.createSlides()
}
}
}
As an aside, it might also be worth creating a simple Document struct with name and description properties, and just having the one array: [Document]

Changing UIImageView’s image doesn’t work

I've got a struct, which contains a static variable called userProfileImage, this struct is in a view controller called UserProfilePage.swift, here is the code:
struct UserProfile {
static let userProfileImage = UIImageView(image: #imageLiteral(resourceName: "profilePicture"))
}
class UserProfilePage: UIViewController {
// Some code that sets the userProfileImage to another image
}
The following code is in another swift file that has a struct called Downloads:
struct Downloads {
guard let profilePicURL = URL(string: profilePictureString) else {
UserProfile.userProfileImage.image = UIImage(named: "profilePicture")
print("Profile picture set to default profile picture")
return
}
// Some code
}
When profilePicURL is not empty, some code gets executed, but when it is empty (equal to ""), the code inside the else block gets executed. The problem is that the profile picture doesn't change, it just executes the print statement inside the else block. Does anyone know what's wrong with my code?
First of all, you have to change your userProfileImage. This Should be a var instead of **static let.
You can use if let statement with an async call in your code. Please try the following code.
let profilePictureString = "http://SOME URl STRING.."
if let profilePicURL = URL(string: profilePictureString){
// Plcae Your Network Code Here.
} else {
DispatchQueue.main.async {
UserProfile.userProfileImage.image = UIImage(named: "profilePicture")
print("Profile picture set to default profile picture")
}
}
You can call the setNeedsDisplay() for update the imageview
DispatchQueue.main.async {
UserProfile.userProfileImage.image = UIImage(named: "profilePicture")
UserProfile.userProfileImage.setNeedsDisplay()
}
use image literal
struct Downloads {
guard let profilePicURL = URL(string: profilePictureString) else {
UserProfile.userProfileImage.image = imageLiteral(resourceName: "profilePicture")
print("Profile picture set to default profile picture")
return
}
// Some code
}

Completion Handler is not working in viewDidLoad?

I'm using this library in my app for banners. I am trying to get the Link by parsing the JSON.
The Images are are not showing in the slideshow view. If I press the slideshow view, after that everything works fine. I thought that there was some issue with my completion handler.
But I can't solve it yet :)
#IBOutlet weak var slideshow: ImageSlideshow!
var transitionDelegate: ZoomAnimatedTransitioningDelegate?
var Banner : [AlamofireSource] = []
override func viewDidLoad() {
super.viewDidLoad()
Banners { (imagesource) in
if imagesource != nil {
self.bannershow()
}
}
}
func Banners(completionHandler: ([AlamofireSource]?) -> ()) -> (){
Alamofire.request(.GET, "http://46.420.116.11/mobileapp/gps/api.php?rquest=get_banners")
.responseJSON{ response in
if let data = response.result.value{
let json = JSON(data)
let count = json["image_path"].count
for index in 0...count-1 {
let image :String = json["image_path"][index].stringValue
let source : AlamofireSource = AlamofireSource(urlString: image)!
self.Banner.append(source)
}
completionHandler(self.Banner)
}
}
}
func bannershow(){
self.slideshow.backgroundColor = UIColor.whiteColor()
self.slideshow.slideshowInterval = 2.0
self.slideshow.contentScaleMode = UIViewContentMode.ScaleToFill
self.slideshow.setImageInputs(self.Banner)
let recognizer = UITapGestureRecognizer(target: self, action: "click")
self.slideshow.addGestureRecognizer(recognizer)
}
func click() {
let ctr = FullScreenSlideshowViewController()
ctr.pageSelected = {(page: Int) in
self.slideshow.setScrollViewPage(page, animated: false)
}
ctr.initialPage = slideshow.scrollViewPage
ctr.inputs = slideshow.images
self.transitionDelegate = ZoomAnimatedTransitioningDelegate(slideshowView: slideshow);
ctr.transitioningDelegate = self.transitionDelegate!
self.presentViewController(ctr, animated: true, completion: nil)
}
You probably have a threading problem. There is no guarantee that the Banners completion handler is called on the main thread. You need to step out to the main thread explicitly before doing anything that touches your properties or (especially) the interface.
I think your problem might be that you're expecting the images to be available immediately but they need to be downloaded before, so they won't be available immediately after your viewDidLoad method finished. That's why you should probably configure your slideshow in the viewDidLoad and not in your bannershow() method. Something like this might be an improvement:
#IBOutlet weak var slideshow: ImageSlideshow!
var bannerImages : [AlamofireSource] = []
override func viewDidLoad() {
super.viewDidLoad()
slideshow.backgroundColor = UIColor.whiteColor()
slideshow.slideshowInterval = 2.0
slideshow.contentScaleMode = UIViewContentMode.ScaleToFill
let recognizer = UITapGestureRecognizer(target: self, action: "click")
slideshow.addGestureRecognizer(recognizer)
getBanners { imagesource in
self.showBanner()
}
}
func getBanners(completionHandler: ([AlamofireSource]?) -> ()) -> (){
Alamofire.request(.GET, "http://46.420.116.11/mobileapp/gps/api.php?rquest=get_banners")
.responseJSON{ response in
if let data = response.result.value{
let json = JSON(data)
let count = json["image_path"].count
for index in 0...count-1 {
let image :String = json["image_path"][index].stringValue
let source : AlamofireSource = AlamofireSource(urlString: image)!
self.bannerImages.append(source)
}
}
completionHandler(self.bannerImages)
}
}
func showBanner() {
slideshow.setImageInputs(bannerImages)
}
Move the code to viewWillAppear.
override func viewWillAppear(animated: Bool) {
Banners { (imagesource) in
if imagesource != nil {
self.bannershow()
}
}
}
func Banners(completionHandler: ([AlamofireSource]?) -> ()) -> (){
Alamofire.request(.GET, "http://46.420.116.11/mobileapp/gps/api.php?rquest=get_banners")
.responseJSON{ response in
if let data = response.result.value{
let json = JSON(data)
let count = json["image_path"].count
for index in 0...count-1 {
let image :String = json["image_path"][index].stringValue
let source : AlamofireSource = AlamofireSource(urlString: image)!
self.Banner.append(source)
}
completionHandler(self.Banner)
}
}
}
You are executing for loop in Banners fun
for index in 0...count-1 {
let image :String = json["image_path"][index].stringValue
let source : AlamofireSource = AlamofireSource(urlString: image)!
self.Banner.append(source)
}
Replace this code in some other method and place an Optional
var image :String? = json["image_path"][index].stringValue
or place an thumb image, That will make you confirm that image is downloaded successfully or not .
Let me know if it works
Thanks, Happy Coding
Maybe you don't see images because you update them in a secondary thread, you have to perform image update in main thread;
In swift ( performSelectorOnMainThread() is not available), you may use something like this :
dispatch_async(dispatch_get_main_queue(), {
myslideshow.updateimage();
})
Not really sure, but I am guessing that since your images need to get downloaded, they are nil when you call self.slideshow.setImageInputs(self.Banner), which internally sets the image from the list to an imageview which is added inside the scrollView. So one way I can think of is use SDWebImage to set the image to the imageView so that it updates the respective imageViews once the image is ready(downloaded). I think you will need to use it in the InputSource.swift class in the setToImageView(_:) method. You will have to check this though, this is the only possible problem i could think of that might be causing your issue.

Resources