Fetch Images from the url

In order to retrieve images from the URL, I initially made some modifications to the 'EventsData' class. I incorporated the 'IFetchData' interface because this class is responsible for fetching an icon from the provided URL. I added a few more variables, such as 'IconSprite', and implemented the data missing from the Interface. The updated code now appears as follows:

/// <summary>
/// Data class which will be fetched from the server
/// </summary>
[Serializable]
public class EventsData : IFetchData<Sprite>
{
    /// <summary>
    /// Event Title
    /// </summary>
    [JsonProperty("Event Title")]
    public string EventTitle;
    /// <summary>
    /// Icon url where icon will be downloaded
    /// </summary>
    [JsonProperty("Icon")]
    public string IconUrl;
    /// <summary>
    /// Icon sprite will be placed here after downloading
    /// </summary>
    public Sprite IconSprite;

    /// <summary>
    /// Type of reward
    /// </summary>
    [JsonProperty("Reward Type")]
    public RewardType RewardType;
    /// <summary>
    /// Amount given in the reward
    /// </summary>
    [JsonProperty("Reward Amount")]
    public int RewardAmount;
    /// <summary>
    /// Description of the event
    /// </summary>
    public string Description;

    /// <summary>
    /// Hexcode of the title color
    /// </summary>
    [JsonProperty("Title Color"), HideInInspector]
    public string TitleColorHex;

    /// <summary>
    /// Hexcode of the body color
    /// </summary>
    [JsonProperty("Body Color"), HideInInspector]
    public string BodyColorHex;
    /// <summary>
    /// Time of the event
    /// </summary>
    [JsonProperty("Event Time")]
    public TimeSpan EventTime;

    /// <summary>
    /// Event Type
    /// </summary>
    [JsonProperty("Event Type")]
    public EventType EventType;

    /// <summary>
    /// Color type of the title
    /// </summary>
    public Color TitleColor;
    /// <summary>
    /// Color type of the body
    /// </summary>
    public Color BodyColor;

    /// <summary>
    /// Downaloaded data which is sprite
    /// </summary>
    public Sprite _downloadedData
    {
        get => IconSprite;
    }
    
    /// <summary>
    /// Calls when initialize
    /// </summary>
    public void Init()
    {
        TitleColor = ParseColor(TitleColorHex);
        BodyColor = ParseColor(BodyColorHex);
    }

    /// <summary>
    /// Parse Hexcode to the color type
    /// </summary>
    /// <param name="HexCode">Hexcode to convert</param>
    /// <returns>Color Type of the given hexcode</returns>
    Color ParseColor(string HexCode)
    {
        Color myColor = new Color();
        ColorUtility.TryParseHtmlString(HexCode, out myColor);
        return myColor;
    }

    /// <summary>
    /// Fetch Icon from the given url
    /// </summary>
    /// <param name="url">icon url</param>
    /// <param name="OnProgress">Raise when downloading is in progress</param>
    /// <param name="OnCompleted">Raise when downlaoding is completed</param>
    /// <returns></returns>
    public IEnumerator FetchData(string url, Action<float, string> OnProgress, Action<bool, Sprite> OnCompleted)
    {
        UnityWebRequest webRequest = UnityWebRequestTexture.GetTexture(url);

        webRequest.SendWebRequest();

        while (!webRequest.isDone)
        {
            OnProgress?.Invoke(webRequest.downloadProgress, EventTitle);
            yield return null;
        }

        switch (webRequest.result)
        {
            case UnityWebRequest.Result.ConnectionError:
                OnCompleted?.Invoke(false, null);
                Debug.LogError("Error Downloading Image: " + webRequest.error);
                break;
            case UnityWebRequest.Result.DataProcessingError:
                OnCompleted?.Invoke(false, null);
                Debug.LogError("Error Downloading Image: " + webRequest.error);
                break;
            case UnityWebRequest.Result.ProtocolError:
                OnCompleted?.Invoke(false, null);
                Debug.LogError("Error Downloading Image: " + webRequest.error);
                break;
            case UnityWebRequest.Result.Success:
                // texture downloaded
                Texture2D myTexture = ((DownloadHandlerTexture)webRequest.downloadHandler).texture;
                // creating sprite from the downloaded texture
                IconSprite = Sprite.Create(myTexture, new Rect(0, 0, myTexture.width, myTexture.height), new Vector2(0.5f, 0.5f));
                OnCompleted?.Invoke(true, IconSprite);
                break;
        }
    }
}

The implementation for downloading the image is now complete. The only remaining task is to display a loading indicator while the image is being downloaded. To accomplish this, I made modifications to the 'EventItemUI' by incorporating a loading pool and managing callbacks from the image loading coroutine.

/// <summary>
/// Attached to event Item prefab
/// </summary>
public class EventItemUI : MonoBehaviour
{
    /// <summary>
    /// Fetch the loading pool of image
    /// </summary>
    [Inject]
    private EventItemLoadingPool _loaderPool;

    /// <summary>
    /// Data to show on this item
    /// </summary>
    [SerializeField]
    private EventsData _data;
    /// <summary>
    /// Loading parent of image
    /// </summary>
    [SerializeField]
    private Transform _loaderParent;
    /// <summary>
    /// UI Title BG
    /// </summary>
    [Header("UI")]
    [SerializeField]
    private Image _titleBg;
    /// <summary>
    /// UI Background BG
    /// </summary>
    [SerializeField]
    private Image _backgroundBg;
    /// <summary>
    /// UI Icon
    /// </summary>
    [SerializeField]
    private Image _icon;
    /// <summary>
    /// Title Text
    /// </summary>
    [SerializeField]
    private TextMeshProUGUI _title;
    /// <summary>
    /// Time left
    /// </summary>
    [SerializeField]
    private TextMeshProUGUI _time;
    /// <summary>
    /// Description of the item
    /// </summary>
    [SerializeField]
    private TextMeshProUGUI _description;
    /// <summary>
    /// Button text which shows Active, Coming Soon
    /// </summary>
    [SerializeField]
    private TextMeshProUGUI _buttonText;

    /// <summary>
    /// Reference of image loader
    /// </summary>
    private EventItemLoading _loader;
    /// <summary>
    /// Status of image loading
    /// </summary>
    private LoadingStatus _loadStatus;

    /// <summary>
    /// Calls when item is spawned
    /// </summary>
    /// <param name="parent">Parent to be of this parent</param>
    /// <param name="data">data to show on this item</param>
    public void Setup(Transform parent, EventsData data)
    {
        _data = data;

        _loadStatus = LoadingStatus.InProgress;
        // load icon image
        StartCoroutine(_data.FetchData(_data.IconUrl, OnProgress, OnCompleted));
        ImageLoadingProgress();

        RectTransform transform = GetComponent<RectTransform>();
        transform.parent = parent;

        SetupUI();
    }

    /// <summary>
    /// Setup UI on the item
    /// </summary>
    void SetupUI()
    {
        _titleBg.color = _data.TitleColor;
        _backgroundBg.color = _data.BodyColor;
        _title.text = _data.EventTitle;
        _description.text = _data.Description;
        _time.text = string.Format("TimeLeft: \t {0}d {1}h {2}m {3}s", _data.EventTime.Days, _data.EventTime.Hours, _data.EventTime.Minutes, _data.EventTime.Seconds);
        _buttonText.text = _data.EventType.ToString();
        _icon.sprite = _data.IconSprite != null ? _data.IconSprite : null;

        if(_data.EventType == EventType.ComingSoon)
        {
            _time.gameObject.SetActive(false);
        }
        else if (_data.EventType == EventType.Expired)
        {
            _buttonText.transform.parent.gameObject.SetActive(false);
            _time.text = _data.EventType.ToString();
        }
    }

    /// <summary>
    /// Loading Progress of Image
    /// </summary>
    void ImageLoadingProgress()
    {
        if (!_loader && _loadStatus == LoadingStatus.InProgress)
            _loader = _loaderPool.Spawn(_loaderParent);
        else if (_loader && _loadStatus != LoadingStatus.InProgress)
            _loaderPool.Remove(_loader);
    }

    /// <summary>
    /// Calls when progress is on the way
    /// </summary>
    /// <param name="progress">float value from 0 - 1</param>
    /// <param name="info">text of the laoding</param>
    void OnProgress(float progress, string info)
    {
        if (_loader)
            _loader.ShowProgress(progress, "Loading Image...");
    }

    /// <summary>
    /// Calls when the downloading is done
    /// </summary>
    /// <param name="isDone">is download successfull</param>
    /// <param name="data">data to download in this case the sprite</param>
    void OnCompleted(bool isDone, Sprite data)
    {
        _loadStatus = isDone ? LoadingStatus.Completed : LoadingStatus.Fail;

        if (!isDone)
            return;

        SetupUI();

        ImageLoadingProgress();
    }

    /// <summary>
    /// Item Pool
    /// </summary>
    public class Pool : MonoMemoryPool<Transform, EventsData, EventItemUI>
    {
        protected override void Reinitialize(Transform p1, EventsData p2, EventItemUI item)
        {
            base.Reinitialize(p1, p2, item);
            item.Setup(p1, p2);
        }
    }
}

Following all these steps, the final result appears like this.

Last updated