정리가 필요한 카테고리(추후 정리)/C#,Unity

Xamarin] Xamarin.Forms 공식 가이드 따라 시작하기

TwinParadox 2017. 12. 3. 17:14
728x90

일전에 자마린에서 제공하는 공식 가이드를 따라 'Phoneword'라는 간단한 앱을 만들었는데, 전화번호를 입력 받아 통화를 거는 그 간단한 앱을 통해서 Xamarin.Android에서는 어떤 식으로 앱을 개발하는지 알아봤다. 이번에도 역시 해당 앱을 통해서 Xamarin.Forms에 대해서 경험해보도록 하자.





일단 새 프로젝트를 만들어야 한다.(Ctrl+Shift+N)

새 프로젝트 생성 폼에서 'Visual C#>Cross-Platform>Cross Platform App(Xamarin.Forms or Native)'를 선택하고 솔루션 이름을 'Phoneword'로 정해주고 적합한 위치에 프로젝트 경로를 지정해준 후 프로젝트를 생성하자.





곧장 프로젝트가 생성되어 코딩하는 것이 아니라 'New Cross Platform App - Phoneword'라는 다이얼로그가 뜨는데, 이 때 '빈 앱'을 선택하고, UI 기술에서는 'Xamarin.Forms'를 선택, 코드 공유 전략에서는 PCL(이식 가능한 클래스 라이브러리;Portable Class Library)를 선택해주고 다음으로 넘어가자.




안드로이드, iOS, UWP 등의 프로젝트를 생성하느라 시간이 다소 소요되고 나면, 'New Universal Windows Project(새 유니버설 Windows 프로젝트)' 다이얼로그가 뜬다. 여기서는 UWP 앱이 지원할 윈도우 10의 최소 버전과 타겟 버전을 선택해야 하는데, 간단한 예제 어플이기 때문에 심각하게 고민할 필요가 없다. 





프로젝트가 생성되고 나면, 솔루션 탐색기에서 Phoneword 솔루션을 선택하고, Nuget 패키지 관리를 선택하면 아래와 같은 것이 나온다. 솔루션 패키지 관리자에서 업데이트 탭을 선택, Xamarin.Forms 패키지를 가장 최신의 안정적인 릴리스 버전으로 업데이트 하도록 한다.




솔루션 탐색기로 돌아와서 이번에는 Phoneword 프로젝트의 'MainPage.xaml'을 더블 클릭해서 편집해보자. 해당 파일에 기본적인 코드가 작성되어 있는데, 이 코드를 지우고 아래 코드를 복사해 붙여놓도록 하자. 그리고 항상 작업 시에는 저장(CTRL+S)을 하는 것도 잊지 않도록 하자.

<?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="Phoneword.MainPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness">
            <On Platform="iOS" Value="20, 40, 20, 20" />
            <On Platform="Android, WinPhone, Windows" Value="20" />
        </OnPlatform>
    </ContentPage.Padding>
    <StackLayout>
      <Label Text="Enter a Phoneword:" />
      <Entry x:Name="phoneNumberText" Text="1-855-XAMARIN" />
      <Button x:Name="translateButon" Text="Translate" Clicked="OnTranslate" />
      <Button x:Name="callButton" Text="Call" IsEnabled="false" Clicked="OnCall" />
    </StackLayout>
</ContentPage>




'MainPage.xaml' 파일을 확장시킬 수 있는데, 확장시키면 'MainPage.xaml.cs'라는 C# 파일이 나타난다. 초기 코드가 적혀 있고, 이를 전부 지운 후 아래 코드로 대체한다. 이 코드에서 OnTranslate 메서드와, OnCall 메서드는 각각 Translate 버튼과 Call 버튼이 눌렸을 때 그에 맞는 작업을 하도록 작성되어 있다.

using System;
using Xamarin.Forms;

namespace Phoneword
{
    public partial class MainPage : ContentPage
    {
        string translatedNumber;

        public MainPage ()
        {
            InitializeComponent ();
        }

        void OnTranslate (object sender, EventArgs e)
        {
            translatedNumber = Core.PhonewordTranslator.ToNumber (phoneNumberText.Text);
            if (!string.IsNullOrWhiteSpace (translatedNumber)) {
                callButton.IsEnabled = true;
                callButton.Text = "Call " + translatedNumber;
            } else {
                callButton.IsEnabled = false;
                callButton.Text = "Call";
            }
        }

        async void OnCall (object sender, EventArgs e)
        {
            if (await this.DisplayAlert (
                    "Dial a Number",
                    "Would you like to call " + translatedNumber + "?",
                    "Yes",
                    "No")) {
                var dialer = DependencyService.Get<IDialer> ();
                if (dialer != null)
                    dialer.Dial (translatedNumber);
            }
        }
    }
}




'App.xaml' 파일을 확장시키면 아까와 마찬가지로 'App.xaml.cs'라는 C# 파일이 존재한다. 그 파일에 있는 기본 코드를 지우고, 아래 코드로 대체한다. App 클래스의 생성자는 앱이 시작했을 때 MainPage 클래스를 하나의 페이지인 것처럼 보여주는 역할을 수행하도록 설정되어 있다.

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Phoneword
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            MainPage = new MainPage();
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}




솔루션 탐색기에서 Phoneword 프로젝트를 한 번 클릭하고 새로운 항목을 추가(CTRL+SHIFT+A)를 통해 C# 클래스 파일을 하나 추가해주도록 하자. 클래스 파일의 이름은 'PhoneTranslator.cs'로 명명하고, 생성과 동시에 생기는 초기 코드를 전부 아래 코드로 대체한다. 이 클래스에서는 자판 문자를 입력 받으면 적절한 규칙에 따라서 이를 전화번호로 변환시켜주는 역할을 수행한다.

using System.Text;

namespace Core
{
    public static class PhonewordTranslator
    {
        public static string ToNumber(string raw)
        {
            if (string.IsNullOrWhiteSpace(raw))
                return null;

            raw = raw.ToUpperInvariant();

            var newNumber = new StringBuilder();
            foreach (var c in raw)
            {
                if (" -0123456789".Contains(c))
                    newNumber.Append(c);
                else
                {
                    var result = TranslateToNumber(c);
                    if (result != null)
                        newNumber.Append(result);
                    // Bad character?
                    else
                        return null;
                }
            }
            return newNumber.ToString();
        }

        static bool Contains(this string keyString, char c)
        {
            return keyString.IndexOf(c) >= 0;
        }

        static readonly string[] digits = {
            "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ"
        };

        static int? TranslateToNumber(char c)
        {
            for (int i = 0; i < digits.Length; i++)
            {
                if (digits[i].Contains(c))
                    return 2 + i;
            }
            return null;
        }
    }
}




동일한 작업을 한 번 더 해야 하는데, 이번에는 클래스 파일이 아닌 인터페이스 파일을 생성하고, 그 이름을 'IDialer.cs'로 하고 해당 파일의 코드를 아래 코드로 대체한다. 이 코드는 각각의 플랫폼(안드로이드, iOS 등)에서 변환된 전화번호로 발신하게끔 만들어진 Dial 메서드를 정의한다.

namespace Phoneword
{
    public interface IDialer
    {
        bool Dial(string number);
    }
}




솔루션 탐색기에서 Phoneword.iOS 프로젝트를 선택하고 새로운 항목을 추가(CTRL+SHIFT+A)를 통해서 클래스를 추가한다.  'Apple>Code>Class(An empty class declaration;빈 클래스 정의)'를 선택 후, 'PhoneDialer.cs'라는 이름으로 생성한다. 이 클래스 파일은 iOS에서 코드가 정상 작동하도록(변환된 전화번호로 다이얼하도록) 설계된 'Dial' 메서드를 구현하는 파일이다. 아래 코드 내용을 덮어쓰도록 한다.

using Foundation;
using Phoneword.iOS;
using UIKit;
using Xamarin.Forms;

[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.iOS
{
    public class PhoneDialer : IDialer
    {
        public bool Dial(string number)
        {
            return UIApplication.SharedApplication.OpenUrl (
                new NSUrl ("tel:" + number));
        }
    }
}




이번에는 안드로이드 프로젝트(Phoneword.Android)로 가보도록 하자. iOS와 동일하게 해당 프로젝트를 선택 후 새로운 항목 추가를 통해서 'Visual C#>Android>Class'를 선택, 'PhoneDialer.cs'라는 클래스 파일을 생성한다. 이 파일도 아래 코드를 통해서 안드로이드 플랫폼에서 우리가 원하고자 하는 동작을 하도록 만드는 파일이다.

using Android.Content;
using Android.Telephony;
using Phoneword.Droid;
using System.Linq;
using Xamarin.Forms;
using Uri = Android.Net.Uri;

[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.Droid
{
    public class PhoneDialer : IDialer
    {
        public bool Dial(string number)
        {
            var context = Forms.Context;
            if (context == null)
                return false;

            var intent = new Intent (Intent.ActionCall);
            intent.SetData (Uri.Parse ("tel:" + number));

            if (IsIntentAvailable (context, intent)) {
                context.StartActivity (intent);
                return true;
            }

            return false;
        }

        public static bool IsIntentAvailable(Context context, Intent intent)
        {
            var packageManager = context.PackageManager;

            var list = packageManager.QueryIntentServices (intent, 0)
                .Union (packageManager.QueryIntentActivities (intent, 0));

            if (list.Any ())
                return true;

            var manager = TelephonyManager.FromContext (context);
            return manager.PhoneType != PhoneType.None;
        }
    }
}




안드로이드 프로젝트에서 'Properties'를 더블클릭하고, 'Android Manifest' 탭을 선택한 후에, 퍼미션 요구 항목(Required permissions)에서 'CALL_PHONE' 퍼미션을 받을 수 있도록 선택한다. 이 퍼미션을 통해서 앱이 전화 기능을 사용할 수 있다.





이번에는 UWP 프로젝트에서(Phoneword.UWP) 새로운 항목 추가를 통해서 'Visual C#>Code>Class'를 선택, 'PhoneDialer.cs' 파일을 추가한다. 아래 코드를 덮어쓰고 저장하여 UWP에서도 우리가 하고자 하는 작업을 수행할 수 있도록 해주자.

using Phoneword.UWP;
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Calls;
using Windows.UI.Popups;
using Xamarin.Forms;

[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.UWP
{
    public class PhoneDialer : IDialer
    {
        bool dialled = false;

        public bool Dial(string number)
        {
            DialNumber(number);
            return dialled;
        }

        async Task DialNumber(string number)
        {
            var phoneLine = await GetDefaultPhoneLineAsync();
            if (phoneLine != null)
            {
                phoneLine.Dial(number, number);
                dialled = true;
            }
            else
            {
                var dialog = new MessageDialog("No line found to place the call");
                await dialog.ShowAsync();
                dialled = false;
            }
        }

        async Task<PhoneLine> GetDefaultPhoneLineAsync()
        {
            var phoneCallStore = await PhoneCallManager.RequestStoreAsync();
            var lineId = await phoneCallStore.GetDefaultLineAsync();
            return await PhoneLine.FromIdAsync(lineId);
        }
    }
}




UWP 프로젝트를 선택하고, 우클릭 후 '참조 추가(Add Reference)'를 선택하여 참조 관리 창을 띄우도록 한다. 'Universal Windows > Extension > Windows Mobile Extension for UWP'를 선택한다. 다시 UWP 프로젝트에서 'Package.appxmanifest'를 더블클릭 후, Capabilities 페이지에서 'Phone Call' 을 활성화시킨다. 안드로이드에서 퍼미션을 주는 작업과 유사하다고 보면 된다.






이 과정을 모두 마친 후 전체적으로 솔루션 빌드를 실시한다.(CTRL+SHIFT+B) 제대로 따라왔다면, Visual Studio의 상태창에서 성공적으로 빌드했음을 알려줄 것이다. 만약에 에러가 있다면, 이전 과정을 반복해야 한다. 물론 지난 안드로이드 과정에서도 그랬듯 공식 가이드에서 알려주지 않은 에러들이 존재할 수 있고 필자도 그러한 에러들이 있었으나, 따로 설명하도록 하겠다.



만일 솔루션 빌드까지 정상적으로 해냈다면, 이제 Phoneword.UWP 프로젝트를 '우클릭>시작 프로젝트로 설정'을 통해 시작 프로젝트로 설정해주고 Visual Studio의 툴바에서 로컬 머신으로 시작해보도록 하자. UWP, 윈도우 응용 프로그램 관련 개발이 처음인 사람들 혹은 개발자 모드를 건드리지 않은 사람들은 이 과정이 정상적으로 작동되지 않을 수 있다. 이 때는 개발자 모드를 활성화시켜야 한다.







위 화면처럼 UWP에서 성공적으로 동작한다면 잘 따라온 것이고 드디어 Xamarin.Forms를 이용한 첫 크로스 플랫폼 앱 개발을 해본 것이다.


각기 다른 플랫폼의 장비들(안드로이드, iOS)를 시작 프로젝트로 선택해 테스트해보면 되는데 iOS의 경우 윈도우 환경에서는 어렵고 별도의 맥 시스템을 이용해서 확인해볼 수 있다. 여기까지 따라왔다면 Xamarin.Forms를 통한 첫 앱을 개발한 것이고 이제 열심히 구글링을 하면서 자신들이 원하는 것을 만들어볼 차례다. 안타깝게도 한국에는 Xamarin.Forms에 대한 자세한 내용을 다룬 책이 없어서 온갖 정보를 영어로, 그것도 산발적으로 접할 수밖에 없다. 필자도 이 부분이 안타까워서 해결 방법을 찾아보고는 있지만, 현재 상황에서는 언어 장벽을 극복하여 무료로 풀린 책(쿡북 등)이나 스택 오버플로우 등의 조언을 구하는 것이 현실적이다.


Xamarin.Android를 할 때도 했던 이야기지만 간간히 안드로이드 스튜디오 등, 다른 언어에서 하나의 플랫폼을 타겟으로 만드는 간단한 예제들을 자마린으로 시도해보고 그에 따른 가이드를 만들어보려고 노력하고 있다. 물론, 지금 공부하고 있는 것이 좀 여유로워지면 말이다.







728x90