Xamarin.Tips – iOS Shadow on Transparent UIView

I ran into another interesting requirement that took some creativity to solve. At a basic level, on iOS, we needed to have a UIView with a fully transparent background that also had a dropshadow (acting more as a box shadow). With its offset being to the bottom right, meaning the shadow should not be visible from the top or left sections of the view.

I know you might be thinking, that sounds like a ridiculous requirement! Why would someone ever do that? It actually gives a pretty cool magnifying look on the large image behind the view, like the UIView was layered above the image and focusing a portion of the large image. I can’t share screenshots, but I would suggest giving the look a try!

But to get to the point, if you haven’t yet, try setting Shadow details on the Layer of a UIView that has a UIColor.Clear background color. You’ll notice that nothing shows up. The next logical thought is to just add another layer that applies the shadow, then add that layer as a sublayer to the view’s main layer. However, that will simply add a gray frame behind yours (since iOS’s shadows act as drop shadows, not box shadows).

At the brink of a mental breakdown, I came up with a solution that worked well for our situation without having to add extra views to act as the shadows or any other wacky workaround (although I will say this workaround is quite wacky).

I did this in Xamarin.Forms, so this is in a Custom renderer for a Frame, but the same thing applies to any UIView. The basic premise is to add two new layers for the bottom portion of the shadow and the right portion.

    public class TransparentFrameRenderer : FrameRenderer
    {

        protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
        {
            base.OnElementChanged(e);
            if(e.NewElement != null)
            {
            }
        }
        public override void Draw(CGRect rect)
        {
            Layer.InsertSublayer(CreateShadowLayer(new CGRect(rect.Width + 2, 3, 4, rect.Height + 2)), 0);
            Layer.InsertSublayer(CreateShadowLayer(new CGRect(3, rect.Height + 2, rect.Width, 4.0f)), 0);
            base.Draw(rect);

        }

        private CALayer CreateShadowLayer(CGRect rect)
        {
            var shadowLayer = new CALayer();
            shadowLayer.BackgroundColor = UIColor.Black.CGColor;
            shadowLayer.ShadowOpacity = 0.8f;
            shadowLayer.ShadowOffset = new CGSize(0, 0);
            shadowLayer.ShadowColor = UIColor.Black.CGColor;
            shadowLayer.ShadowPath = UIBezierPath.FromRect(rect).CGPath;

            return shadowLayer;
        }
    }

Note the helper method to create a layer with the shadow details set to what we need, and inserting two sublayers that set the frame for the layer. These are specific values I used in our situation, but you can easily play with them and also see how the same thing can be applied if your shadow needs to be applied to all four sides (if your light perspective is perfectly perpendicular to the view), inverse of what we have here, or if you need larger or smaller shadows.

This is NOT a complete solution, or a real reusable control, but more of a general approach to rendering shadows around a control with a transparent background.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Xamarin.Controls – Creating your own Markdown TextBlock in UWP

After talking about using the MarkdownTextView I created and how to accomplish rendering Markdown in iOS and Android without a WebView, I received a few requests to do it for UWP as well.

Check these previous posts out:

  1. Xamarin.Controls – MarkdownTextView
  2. Xamarin.Controls – Creating Your Own Android Markdown TextView
  3. Xamarin.Controls – Creating Your Own iOS Markdown UILabel

Let’s dive in to rendering markdown into a TextBlock in UWP. We’ll break it down into a few steps:

  1. Parse a markdown string into an html string
  2. Create a Behavior for the TextBlock
  3. Parse the html into relevant Span tags in the behavior
  4. Use the new Behavior in our XAML or C#

 

Parsing Markdown

This is traditionally the most difficult part. However, our community is awesome and open sourced a Markdown processor with an MIT license (so use it freely!).

I won’t put the actual code in here because it is overwhelmingly long, but here is a link to it:

https://github.com/SuavePirate/MarkdownTextView/blob/master/src/Forms/SPControls.MarkdownTextView/SPControls.MarkdownTextView/Markdown.cs

Note that this is portable, so you can use it in a PCL without a problem and share it between your platforms.

Now that we have our means of processing the Markdown, let’s create some extension methods to make it easier to parse and do some extra processing like cleaning up our tags, line breaks, etc.

#region MARKDOWN STYLES
private const string ORIGINAL_PATTERN_BEGIN = "<code>";
private const string ORIGINAL_PATTERN_END = "</code>";
private const string PARSED_PATTERN_BEGIN = "<font color=\"#888888\" face=\"monospace\"><tt>";
private const string PARSED_PATTERN_END = "</tt></font>";

#endregion

public static string ToHtml(this string markdownText)
{
    var markdownOptions = new MarkdownOptions
    {
        AutoHyperlink = true,
        AutoNewlines = false,
        EncodeProblemUrlCharacters = false,
        LinkEmails = true,
        StrictBoldItalic = true
    };
    var markdown = new Markdown(markdownOptions);
    var htmlContent = markdown.Transform(markdownText);
    var regex = new Regex("\n");
    htmlContent = regex.Replace(htmlContent, "
");

    var html = htmlContent.HtmlWrapped();
    var regex2 = new Regex("\r");
    html = regex.Replace(html, string.Empty);
    html = regex2.Replace(html, string.Empty);
    return html;
}

///
<summary>
/// Wrap html with a full html tag
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public static string HtmlWrapped(this string html)
{
    if (!html.StartsWith("<html>") || !html.EndsWith("</html>"))
    {
        html = $"<html><body>{html}</body></html>";
    }
    return html;
}

///<summary>
/// Parses html with code or pre tags and gives them proper
/// styled spans so that Android can parse it properly
/// </summary>
/// <param name="htmlText">The html string</param>
/// <returns>The html string with parsed code tags</returns>
public static string ParseCodeTags(this string htmlText)
{
    if (htmlText.IndexOf(ORIGINAL_PATTERN_BEGIN) < 0) return htmlText;
    var regex = new Regex(ORIGINAL_PATTERN_BEGIN);
    var regex2 = new Regex(ORIGINAL_PATTERN_END);

    htmlText = regex.Replace(htmlText, PARSED_PATTERN_BEGIN);
    htmlText = regex2.Replace(htmlText, PARSED_PATTERN_END);
    htmlText = htmlText.TrimLines();
    return htmlText;
}

public static bool EqualsIgnoreCase(this string text, string text2)
{
    return text.Equals(text2, StringComparison.CurrentCultureIgnoreCase);
}

public static string ReplaceBreaks(this string html)
{
    var regex = new Regex("
");
    html = regex.Replace(html, "\n");
    return html;
}

public static string ReplaceBreaksWithSpace(this string html)
{
    var regex = new Regex("
");
    html = regex.Replace(html, " ");
    return html;
}

public static string TrimLines(this string originalString)
{
    originalString = originalString.Trim('\n');
    return originalString;
}

Now we can properly parse markdown to html:

var markdown = "# Hello *World*";
var html = markdown.ToHtml();
// html = "<h1>Hello <strong>World</strong></h1>"

Create a Behavior

I’m going to take some inspiration from Shawn Kendrot and his post here.

The first thing Shawn does is implement a base Behavior class, so let’s go ahead and use that here:

    // WinRT Implementation of the base Behavior classes
    public abstract class Behavior<T> : Behavior where T : DependencyObject
    {
        protected T AssociatedObject
        {
            get { return base.AssociatedObject as T; }
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            if (this.AssociatedObject == null) throw new InvalidOperationException("AssociatedObject is not of the right type");
        }
    }

    public abstract class Behavior : DependencyObject, IBehavior
    {
        public void Attach(DependencyObject associatedObject)
        {
            AssociatedObject = associatedObject;
            OnAttached();
        }

        public void Detach()
        {
            OnDetaching();
        }

        protected virtual void OnAttached()
        {

        }

        protected virtual void OnDetaching()
        {

        }

        protected DependencyObject AssociatedObject { get; set; }

        DependencyObject IBehavior.AssociatedObject
        {
            get { return this.AssociatedObject; }
        }
    }

Now we can create our actual Behavior implementation to handle the html parsing. If you want the entire file, you can find it in a gist here. Or keep reading so we can break it down.

HtmlTextBehavior.cs

public class HtmlTextBehavior : Behavior<TextBlock>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += OnAssociatedObjectLoaded;
        AssociatedObject.LayoutUpdated += OnAssociatedObjectLayoutUpdated;
    }
 
    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
        AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated;
    }
 
    private void OnAssociatedObjectLayoutUpdated(object sender, object o)
    {
        UpdateText();
    }
 
    private void OnAssociatedObjectLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        UpdateText();
        AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
    }
 
    private void UpdateText()
    {
        if (AssociatedObject == null) return;
        if (string.IsNullOrEmpty(AssociatedObject.Text)) return;

        string text = AssociatedObject.Text;

        // Just incase we are not given text with elements.
        string modifiedText = string.Format("<div>{0}</div>", text);

        // reset the text because we will add to it.
        AssociatedObject.Inlines.Clear();
        try
        {
            var element = XElement.Parse(modifiedText);
            ParseText(element, AssociatedObject.Inlines);
        }
        catch (Exception)
        {
            // if anything goes wrong just show the html
            AssociatedObject.Text = text;
        }
        AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated;
        AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
    }
    
    /// <summary>
    /// Traverses the XElement and adds text to the InlineCollection.
    /// </summary>
    /// <param name="element"></param>
    /// <param name="inlines"></param>
    private static void ParseText(XElement element, InlineCollection inlines)
    {
        if (element == null) return;

        InlineCollection currentInlines = inlines;
        var elementName = element.Name.ToString().ToUpper();
        switch (elementName)
        {
            case ElementA:
                var link = new Hyperlink();
                var href = element.Attribute("href");
                if(href != null)
                {
                    try
                    {
                        link.NavigateUri = new Uri(href.Value);
                    }
                    catch (System.FormatException) { /* href is not valid */ }
                }
                inlines.Add(link);
                currentInlines = link.Inlines;
                break;
            case ElementB:
            case ElementStrong:
                var bold = new Bold();
                inlines.Add(bold);
                currentInlines = bold.Inlines;
                break;
            case ElementI:
            case ElementEm:
                var italic = new Italic();
                inlines.Add(italic);
                currentInlines = italic.Inlines;
                break;
            case ElementU:
                var underline = new Underline();
                inlines.Add(underline);
                currentInlines = underline.Inlines;
                break;
            case ElementBr:
                inlines.Add(new LineBreak());
                break;
            case ElementP:
                // Add two line breaks, one for the current text and the second for the gap.
                if (AddLineBreakIfNeeded(inlines))
                {
                    inlines.Add(new LineBreak());
                }

                Span paragraphSpan = new Span();
                inlines.Add(paragraphSpan);
                currentInlines = paragraphSpan.Inlines;
                break;
            // TODO: Add ElementH1 - ElementH6 handlers here. They should behave the same way as ElementP with increased font size.
            case ElementLi:
                inlines.Add(new LineBreak());
                inlines.Add(new Run { Text = " • " });
                break;
            case ElementUl:
            case ElementDiv:
                AddLineBreakIfNeeded(inlines);
                Span divSpan = new Span();
                inlines.Add(divSpan);
                currentInlines = divSpan.Inlines;
                break;
        }
        foreach (var node in element.Nodes())
        {
            XText textElement = node as XText;
            if (textElement != null)
            {
                currentInlines.Add(new Run { Text = textElement.Value });
            }
            else
            {
                ParseText(node as XElement, currentInlines);
            }
        }
        // Add newlines for paragraph tags
        if (elementName == ElementP)
        {
            currentInlines.Add(new LineBreak());
        }
    }
    /// <summary>
    /// Check if the InlineCollection contains a LineBreak as the last item.
    /// </summary>
    /// <param name="inlines"></param>
    /// <returns></returns>
    private static bool AddLineBreakIfNeeded(InlineCollection inlines)
    {
        if (inlines.Count > 0)
        {
            var lastInline = inlines[inlines.Count - 1];
            while ((lastInline is Span))
            {
                var span = (Span)lastInline;
                if (span.Inlines.Count > 0)
                {
                    lastInline = span.Inlines[span.Inlines.Count - 1];
                }
            }
            if (!(lastInline is LineBreak))
            {
                inlines.Add(new LineBreak());
                return true;
            }
        }
        return false;
    }
}

That’s a lot of stuff to look at. Let’s break it down. The core of the processing is in the UpdateText and ParseText methods.

Essentially what we are doing here is some layout management, and then parsing the html (parsed into an XElement). It’s different from the approaches we took in Android and iOS where there are native APIs that can parse HTML automatically. But if you looked at the Android post I made before, you’ll remember the extra TagHandler we created that took unsupported html element types and parsed them into Spans for proper formatting. What we did in that situation is a simplified version of what we accomplish here. We take relevant html tags, and create Spans, Bolds, HyperLinks and other relevant types in order to be able to render it within our final TextBlock.

There are a few element types in the parsing that are not included, but can be pretty easily added. For example, code, pre, h1h6, etc. However, these can be easily added to the switch statement! Most of these are going to be handled the same way that ElementP does. So you can add them, and adjust the FontSize or FontFamily of your Span!

Applying the Behavior

Now that we have our HtmlTextBehavior built out, we can apply it to our actual TextBlock!

In XAML:

<TextBlock Text="{Binding MyHtmlText}" FontSize="20" TextWrapping="Wrap">
    <Interactivity:Interaction.Behaviors>
        <local:HtmlTextBehavior />
    </Interactivity:Interaction.Behaviors>
</TextBlock>

Now we can render out MyHtmlText property from our ViewModel directly in our TextBlock!

Xamarin.Tips – Super Simple Sqlite

Thinking about locally storing data and entity structures can be intimidating. You might have a complex data structure in your backend server structure and are trying to match that type of data in your mobile apps. Perhaps you’ve just never done it before and need a solution quickly. You might be over thinking it!

Let’s break it down into some straightforward steps.

  1. Create a Xamarin PCL or Shared project. It doesn’t matter if it’s Xamarin.Forms or native.
  2. Install this nuget package in your PCL or Shared project: https://www.nuget.org/packages/sqlite-net-pcl/
  3. Install the same nuget package in your Android, iOS, UWP, and any other project.
  4. Create your model in your PCL or Shared project. In this case, we will use a basic example model:
    public class Profile
    {
        public string Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Handle { get; set; }
        public DateTime CreatedDate { get; set; }
    }
    
  5. Create a DbContext class in your PCL or Shared project:
    public class DbContext
    {
        public static string LocalFilePath; // Set this before creating from platform project
        public SQLiteAsyncConnection Database { get; }
        /// <summary>
        /// Initialized a new DbContext
        /// </summary>
        public DbContext()
        {
            Database = new SQLiteAsyncConnection(LocalFilePath + "localdb.db3");
        }
    
        /// <summary>
        /// Creates a table for a given type in sql lite
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task<CreateTablesResult> CreateTableAsync<T>() where T : new()
        {
        return await Database.CreateTableAsync<T>();
        }
    
        /// <summary>
        /// Gets a table by it's type from the db.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public AsyncTableQuery<T> Set<T>() where T : new()
        {
            return Database.Table<T>();
        }
    }
    
  6. Create a GenericRepository Class:
    public class GenericSqliteRepository<T> : IGenericRepository<T> where T : new()
    {
        protected readonly DbContext _context;
        public GenericSqliteRepository(DbContext context)
        {
            _context = context;
        }
        public virtual async Task InitializeAsync()
        {
            await _context.CreateTableAsync<T>();
        }
        public virtual async Task AddAsync(T entity)
        {
            await _context.Database.InsertOrReplaceAsync(entity);
        }
        public virtual async Task AddRangeAsync(IEnumerable<T> entities)
        {
            await _context.Database.InsertAllAsync(entities);
        }
    
        public virtual async Task<T> FindAsync(Expression<Func<T, bool>> predicate)
        {
            return await _context.Set<T>().Where(predicate).FirstOrDefaultAsync();
        }
    
        public virtual async Task<IEnumerable<T>> GetAsync(Expression<Func<T, bool>> predicate)
        {
            return await _context.Set<T>().Where(predicate).ToListAsync();
        }
    
        public virtual async Task<IEnumerable<T>> GetAsync(int skip, int take)
        {
            return await _context.Set<T>().Skip(skip).Take(take).ToListAsync();
        }
    
        public virtual async Task<IEnumerable<T>> GetAsync(Expression<Func<T, bool>> predicate, int skip, int take)
        {
            return await _context.Set<T>().Where(predicate).Skip(skip).Take(take).ToListAsync();
        }
    
        public virtual async Task RemoveAsync(T entity)
        {
            await _context.Database.DeleteAsync(entity);
        }
    
        public virtual async Task UpdateAsync(T entity)
        {
            await _context.Database.UpdateAsync(entity);
        }
    }
    
  7. Create a ProfileRepository Class:
    public class ProfileRepository : GenericSqliteRepository<Post>
    {
        public ProfileRepository(DbContext context) : base(context)
        {
        }
    }
    
  8. Set your file path in your platform code:
    1. Android
      DbContext.LocalFilePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
      
    2. iOS
      var docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
      string libFolder = Path.Combine(docFolder, "..", "Library", "Databases");
      
      if (!Directory.Exists(libFolder))
      {
          Directory.CreateDirectory(libFolder);
      }
      DbContext.LocalFilePath = libFolder;
      
  9. Use your repository in your shared code or platform code or wherever you want.
    var repo = new ProfileRepository(new DbContext());
    await repo.InitializeAsync();
    ...
    await repo.AddAsync(new Profile {...});
    ...
    var profiles = await repo.GetAsync(0, 10);
    ...
    var profile = await repo.FindAsync(p => p.Id == "foo");
    ...
    
  10. Start storing your things!

This is obviously a simple situation that doesn’t cover all needs, but it’s a place to start for complex data models. Build repositories for each of your types. Control your queries that are very specific in those model-specific repositories.

Look out for a follow up post about some patterns and tips to use for complex and large data sets!

Xamarin.Tips – Calling Platform-Specific Code from a Portable Class Library – Xamarin.Forms DependencyService

For those who just want code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable

For those who just want the video: [VIDEO] Xamarin.Tips: Calling Platform-Specific Code from a PCL (Xamarin.Forms DependencyService)

As we develop our applications to be cross-platform, we try to share as much code between different platforms as possible. Many times, though, we have a need to call platform-specific code from that shared code; whether it be something such as playing a sound file, handling hardware readings, or using a third-party SDK that isn’t abstracted to be shareable.

In these examples, we will look at implementing parts of the HockeyApp SDK that are specific to the platform you are running against such as showing the Feedback control to ask your users to report bugs or give feedback.

There are a few ways to accomplish this, but in this post, we will look at using the Xamarin.Forms DependencyService.

The Xamarin.Forms DependencyService allows us to register implementations in our platform code as the implementation of an interface defined in our shared code. We can then use the Get() method from the DependencyService to get a reference to the platform code in our shared code.

To start, in our shared code, we will create an interface that will be implemented on our platforms:

public interface IHockeyService
{
    void Init();
    void GetFeedback();
}

 

Now in our platform projects, we will implement our IHockeyService. The important thing to note is the assembly level attribute tag we put over our namespace. This tag is what registers our implementation to the Dependency Service!

iOS:

[assembly: Xamarin.Forms.Dependency(typeof(HockeyService))]
namcespace YOUR_NAMESPACE
{
    public class HockeyService : IHockeyService
    {
        public HockeyService()
        {
        }

        public void GetFeedback()
        {
            BITHockeyManager.SharedHockeyManager.FeedbackManager.ShowFeedbackListView();
        }

        public void Init()
        {
            var manager = BITHockeyManager.SharedHockeyManager;
            manager.Configure("HOCKEY_APP_ID");
            manager.StartManager();
        }
    }
}

Android:

[assembly: Xamarin.Forms.Dependency(typeof(HockeyService))]</pre>
namcespace YOUR_NAMESPACE
{
    public class HockeyService : IHockeyService
    {
        private const string HOCKEYAPP_KEY = "HOCKEY_APP_ID";
        private readonly Android.App.Application _androidApp;
        private readonly Activity _context;

        public HockeyAppService(Activity context, Android.App.Application androidApp)
        {
            _context = context;
            _androidApp = androidApp;
        }

        public void GetFeedback()
        {
            FeedbackManager.ShowFeedbackActivity(_context.ApplicationContext);
        }

        public void Initialize()
        {
            CrashManager.Register(_context, HOCKEYAPP_KEY);
            MetricsManager.Register(_androidApp, HOCKEYAPP_KEY);
            UpdateManager.Register(_context, HOCKEYAPP_KEY);
            FeedbackManager.Register(_context, HOCKEYAPP_KEY);
        }
    }
}

Now we need to initialize the HockeyApp manager from our shared code.

App.xaml.cs

public App ()
{
    DependenyService.Get<IHockeyService>().Init();
}

Now that our service is registered to the DependencyService, we can make calls to it from our Shared code. In this example, we will add a button to a Xamarin.Forms page with a click handler that calls the service:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="YourNamespace.MainPage">

    <Button Text="Get feedback with the dependency service!"
            VerticalOptions="Center"
            HorizontalOptions="Center"
            Clicked="Feedback_Clicked"/>

</ContentPage>

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void Feedback_Clicked(object sender, EventArgs e)
    {
        DependencyService.Get<IHockeyService>().GetFeedback();
    }
}

Now our results will show that our shared code call to the service successfully shows the HockeyApp UI

simulator-screen-shot-feb-15-2017-1-47-26-pm

Xamarin.Tips – Calling Platform-Specific Code from a Portable Class Library – The Singleton Method

For those who just want code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable

For those who just want the video: [VIDEO] Xamarin.Tips: Calling Platform-Specific Code from a PCL (Singleton Method)

As we develop our applications to be cross-platform, we try to share as much code between different platforms as possible. Many times, though, we have a need to call platform-specific code from that shared code; whether it be something such as playing a sound file, handling hardware readings, or using a third-party SDK that isn’t abstracted to be shareable.

In these examples, we will look at implementing parts of the HockeyApp SDK that are specific to the platform you are running against such as showing the Feedback control to ask your users to report bugs or give feedback.

There are a few ways to accomplish this, but in this post, we will look at using the Singleton Method.

The Singleton Method is where we create a universal Singleton in our shared code that is initialized in our platform code, and then called from our shared code.

To start, in our shared code, we will create an interface that will be implemented on our platforms:

public interface IHockeyService
{
    void Init();
    void GetFeedback();
}

Then we create our actual Singleton in our shared code:

public class HockeyManager
{
    public static IHockeyService Current;
}

Now in our platform projects, we will implement our IHockeyService:

iOS:

public class HockeyService : IHockeyService
{
    public HockeyService()
    {
    }

    public void GetFeedback()
    {
        BITHockeyManager.SharedHockeyManager.FeedbackManager.ShowFeedbackListView();
    }

    public void Init()
    {
        var manager = BITHockeyManager.SharedHockeyManager;
        manager.Configure("HOCKEY_APP_ID");
        manager.StartManager();
    }
}

Android:

public class HockeyService : IHockeyService
{
    private const string HOCKEYAPP_KEY = "HOCKEY_APP_ID";
    private readonly Android.App.Application _androidApp;
    private readonly Activity _context;

    public HockeyAppService(Activity context, Android.App.Application androidApp)
    {
        _context = context;
        _androidApp = androidApp;
    }

    public void GetFeedback()
    {
        FeedbackManager.ShowFeedbackActivity(_context.ApplicationContext);
    }

    public void Initialize()
    {
        CrashManager.Register(_context, HOCKEYAPP_KEY);
        MetricsManager.Register(_androidApp, HOCKEYAPP_KEY);
        UpdateManager.Register(_context, HOCKEYAPP_KEY);
        FeedbackManager.Register(_context, HOCKEYAPP_KEY);
    }
}

Now we need to initialize our Singleton from our platform-specific code. In iOS, we will do this in our AppDelegate, and in Android – our MainActivity:

iOS

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    HockeyManager.Current = new HockeyService();
    global::Xamarin.Forms.Forms.Init();
    LoadApplication(new App());

    return base.FinishedLaunching(app, options);
}

Android:

protected override void OnCreate(Bundle bundle)
{
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;
    base.OnCreate(bundle);
    HockeyManager.Current = new HockeyService(this, Application);
    global::Xamarin.Forms.Forms.Init(this, bundle);
    LoadApplication(new App());
}

Now we need to initialize the HockeyApp manager from our shared code.

App.xaml.cs

public App ()
{
    HockeyManager.Current.Init();
}

Now that our Singleton is created, we can make calls to it from our Shared code. In this example, we will add a button to a Xamarin.Forms page with a click handler that calls the service:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="YourNamespace.MainPage">

    <Button Text="Get feedback with a singleton!"
            VerticalOptions="Center"
            HorizontalOptions="Center"
            Clicked="Feedback_Clicked"/>

</ContentPage>
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void Feedback_Clicked(object sender, EventArgs e)
    {
        HockeyManager.Current.GetFeedback();
    }
}

Now our results will show that our shared code call to the Singleton successfully shows the HockeyApp UI

simulator-screen-shot-feb-15-2017-1-47-26-pm

Xamarin.Android Continuous Integration with Visual Studio Team Services

Have you or someone you know been physically harmed or emotionally scarred by the stress of setting up Continuous Integration on VSTS for your Xamarin.Android project? If your answer is yes, you are entitled to no compensation, but this blog post might help.

Easily one of the most frustrating experiences I’ve had with CI so far. It seemed that after I worked through one issue breaking the build, another two popped up. It’s like fighting the Hydra of builds.

Of course everything built fine on my local machine as well as on my other developers’ machines! So WTF VSTS???

Let’s look at some common errors you might have run into. If none of these cover your problems, leave a comment and we can try to sort it out after.

“Major version 51 is newer than 50, the highest major version supported by this compiler.”

Maybe you’ve run into this problem during your actual builds in the past. Basically the issue is that the latest Android Support Libraries build with a newer version of the compiler. The fix is simple – in VSTS, set your JDK version explicitly to 8.

  1. In your build definition, go to your Build Xamarin.Android project step
    vstsdroid
  2. Scroll down the the JDK options section
  3. Select JDK Version 
  4. Select JDK 8 from the JDK Version dropdown
    VSTSJDK.PNG
  5. Save
  6. Run

 

“java.lang.OutOfMemoryError. Consider increasing the value of $(JavaMaximumHeapSize)”

The error is at least pretty explanatory. You ran out of memory in your Java Heap.

The solution is to increase your Java Heap explicitly:

  1. In your IDE (VS or XS), go to your Android project
  2. Open the properties for your project
  3. Set your configuration to Release (or whatever configuration your VSTS build runs as)
  4. Go to the Android Options Tab
  5. Go to the Advanced tab
  6. Go down to the Advanced Android Build Settings
  7. Set the Java Max Heap Size to something larger such as “1G”

javaheap

 

These errors can be common and are only applicable to basic Android builds. This does not cover Continuous Deployment to services such as HockeyApp or Visual Studio Mobile Center.

Don’t forget to leave a comment if you’ve run into any different issues!

[Video] Xamarin.Flux

Learn how to use the Flux design pattern with Xamarin.

Source code: https://github.com/SuavePirate/Xamarin.Flux

[Video Series] Xamarin.Tips: Calling Platform-Specific Code from a PCL

Learn how to use 4 methods to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.

  1. Singleton Method
  2. Xamarin.Forms Dependency Service
  3. Service Locator
  4. Dependency Injection

 

Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable 

[VIDEO] Xamarin.Tips: Calling Platform-Specific Code from a PCL (All 4 Methods)

Using all 4 methods to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.

 

Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable 

[VIDEO] Xamarin.Tips: Calling Platform-Specific Code from a PCL (Dependency Injection)

Using Dependency Injection to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.

 

Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable