-1

When I run the code below the attributedText is displayed in the console but does not show up in the UITextView. I save the attributedText in a global variable scrollViewText. Ive tried printing scrollViewText but it still shows up as a blank space in the console.

public static func getHonorsAdvisorsText(){

    Alamofire.request("https://honors.purdue.edu/json/honors-advisors.json").responseJSON { response in
        //check if result has value
        if let value = response.result.value {
            let json = JSON(value)
            let freshmenName = json["first_year_advisors"][0]["name"].string
            let freshmenAssignment = json["first_year_advisors"][0]["assignment"].string
            let freshmenEmail = json["first_year_advisors"][0]["email"].string
            let formattedString = NSMutableAttributedString()
            formattedString
                .bold("Freshmen Advisor", fontSize: 40)
                .bold("\n" + "Name: " , fontSize: 20)
                .normal(freshmenName!, fontSize: 20)
                .bold("\n" + "Assignment: " , fontSize: 20)
                .normal(freshmenAssignment!, fontSize: 20)
                .bold("\n" + "Email: " , fontSize: 20)
                .normal(freshmenEmail!, fontSize: 20)
            scrollViewText = formattedString
            print(formattedString)
        }
    }
}

I call the getHonorsAdvisorsText function in the ThirdViewController by using the code:

case 1:
AcademicsPage.getHonorsAdvisorsText()
textViewer.attributedText = scrollViewText
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Junaid Javed
  • 25
  • 1
  • 10

1 Answers1

1

Of course you will never get a response. You are running these 2 calls:

AcademicsPage.getHonorsAdvisorsText()
textViewer.attributedText = scrollViewText

HOWEVER, they run right after each other, but "getHonorsAdvisorsText" is asynchronous. (Meaning it doesn't finish right away) This means that when the attributedText is set, the value IS STILL EMPTY.

The easiest way to fix this is to remove that line, and instead change the getHonorsAdvisorsText function to:

public static func getHonorsAdvisorsText(){

    Alamofire.request("https://honors.purdue.edu/json/honors-advisors.json").responseJSON { response in
        //check if result has value
        if let value = response.result.value {
            let json = JSON(value)
            let freshmenName = json["first_year_advisors"][0]["name"].string
            let freshmenAssignment = json["first_year_advisors"][0]["assignment"].string
            let freshmenEmail = json["first_year_advisors"][0]["email"].string
            let formattedString = NSMutableAttributedString()
            formattedString
                .bold("Freshmen Advisor", fontSize: 40)
                .bold("\n" + "Name: " , fontSize: 20)
                .normal(freshmenName!, fontSize: 20)
                .bold("\n" + "Assignment: " , fontSize: 20)
                .normal(freshmenAssignment!, fontSize: 20)
                .bold("\n" + "Email: " , fontSize: 20)
                .normal(freshmenEmail!, fontSize: 20)
            scrollViewText = formattedString

            // SET THE TEXT HERE

            DispatchQueue.main.async {
               textViewer.attributedText = scrollViewText
            }

            print(formattedString)
        }
    }
}

Notice how the text is set within the "main" dispatch queue. This is because all interface changes must be done on the main queue.

Using a completion handler:

public static func getHonorsAdvisorsText(completionHandler : @escaping ((_ formattedString : NSMutableAttributedString) -> Void)){

    Alamofire.request("https://honors.purdue.edu/json/honors-advisors.json").responseJSON { response in
        //check if result has value
        if let value = response.result.value {
            let json = JSON(value)
            let freshmenName = json["first_year_advisors"][0]["name"].string
            let freshmenAssignment = json["first_year_advisors"][0]["assignment"].string
            let freshmenEmail = json["first_year_advisors"][0]["email"].string
            let formattedString = NSMutableAttributedString()
            formattedString
                .bold("Freshmen Advisor", fontSize: 40)
                .bold("\n" + "Name: " , fontSize: 20)
                .normal(freshmenName!, fontSize: 20)
                .bold("\n" + "Assignment: " , fontSize: 20)
                .normal(freshmenAssignment!, fontSize: 20)
                .bold("\n" + "Email: " , fontSize: 20)
                .normal(freshmenEmail!, fontSize: 20)
            scrollViewText = formattedString

            // SET THE TEXT HERE

            DispatchQueue.main.async {
               completionHandler(formattedString)
            }

            print(formattedString)
        }
    }
}

And you call it like this:

AcademicsPage.getHonorsAdvisorsText { (formattedString) in
    textViewer.attributedText = formattedString
}
Junaid Javed
  • 25
  • 1
  • 10
Pochi
  • 13,391
  • 3
  • 64
  • 104
  • 1
    This is not the best solution because it means the only use of this `getHonorsAdvisorsText` is to set a specific text view. The better solution is to setup this function with a completion handler. Then the function can be used by anything that needs the attributed string, not just one specific text view in one specific view controller. – rmaddy Jul 18 '17 at 01:25
  • The view controller that has the textView is set for a different class. How do I set the text of the textView from a different class – Junaid Javed Jul 18 '17 at 01:27
  • @rmaddy how do I add the completion handler to Alamofire code – Junaid Javed Jul 18 '17 at 01:28
  • @rmaddy that's true, I also thought about what he is trying to do, however the only effect this function has is updating a variable "scrollViewText = formattedString". Which is why it doesn't seem to be so bad to just update the interface as well as a side effect. – Pochi Jul 18 '17 at 01:31
  • @Pochi Simply "updating the interface as a side effect" is a terrible way to look at it. Never do such a thing. – rmaddy Jul 18 '17 at 01:34
  • @rmaddy pardon my ignorance but why is it a terrible way to look at it? isn't this what reactive programming does all the time? Granted it can't be applied to this case, because they are in different classes, if they were in the same class it could be attached to an optional variable. Then it will update it only if its hooked to something, if it isn't it won't. I see absolutely no harm in it, and kind of reminds me of some libraries that have ways to display their results if you hook something to them. – Pochi Jul 18 '17 at 01:41
  • @Pochi It is giving an error since `formattedString` is a NSMutableAttributedString that I can't use it as a string in `completionHandler(formattedString)` – Junaid Javed Jul 18 '17 at 01:48
  • @JunaidJaved Change the type from the function signature to "NSMutableAttributedString" (like i did on the updated answer just now) – Pochi Jul 18 '17 at 01:51