이번 시간에 우리가 알아볼 내용은 모바일 (Android, iOS)에 Toast기능을 구현해보도록 하겠습니다.

안드로이드의 경우 이미 Toast 기능이 구현되어 있어서 유니티에 연결하는 것은 어렵지 않습니다.
하지만, iOS의 경우 Toast 기능이 없기 때문에 Toast기능부터 직접 만들어야하죠.
그래서 이번 문서에서는 추천하는 Toast라이브러리가 있어서 iOS에서는 해당 라이브러리를 소개하면서 연동 작업을 해보도록 하죠.

시작해요


폴더 구성

다음과 같이 폴더를 구성하도록 하겠습니다.
Plugins폴더에 Android, iOS 폴더를 구성하고, Scripts폴더에 Editor, Runtime 폴더를 구성하는 것이 핵심입니다.

Android

안드로이드의 경우 java또는 kotlin 언어를 통해 코드를 작성할 수 있습니다.
원하시는 언어로 NativeToast.java 또는 NativeToast.kt파일은 만들고 다음 내용을 추가합니다.

Java :

package com.unity3d.player;

import android.widget.Toast;

public class NativeToast
{
    void ShowToast(String message)
    {
        UnityPlayer.currentActivity.runOnUiThread(() ->
        {
            Toast toast = Toast.makeText(UnityPlayer.currentActivity, message, Toast.LENGTH_SHORT);
            toast.show();
        });
    }
}

Kotlin :

package com.unity3d.player

import android.widget.Toast

class NativeToast
{
    fun ShowToast(message: String)
    {
        UnityPlayer.currentActivity.runOnUiThread{
            val toast = Toast.makeText(UnityPlayer.currentActivity, message, Toast.LENGTH_SHORT)
            toast.show()
        }
    }
}

이 문서에서는 NativeToast.kt파일을 생성하여 코드 내용을 추가하고 유니티 프로젝트에 포함시켰습니다.

추가 설명

Q : runOnUiThread에서 Toast 메세지를 동작해야하나요?
A : Toast의 기능은 안드로이드의 메인(UI)쓰레드에서 동작시켜야 합니다.
유니티의 경우 또 다른 별도의 쓰레드에서 UnityPlayer를 동작시킵니다.
그러다보니 곧 바로 Toast 기능을 사용하면 문제를 일으킵니다.
이를 해결하기 위해선, 현재 UnityPlayer에서 동작하고 있는 UI 쓰레드에서 동작하도록 UnityPlayer.currentActivity.runOnUiThread를 사용하는 것이 바람직 합니다.

iOS

iOS의 경우 기본적으로 Objective-C/C++ 언어를 통해 플러그인을 작업할 수 있습니다.
하지만, Objective-C/C++의 경우 Swift 언어로 교체된지 오래입니다.

Swift 언어를 통해 플러그인을 제작하고 싶다면 두가지 방법으로 이를 해결할 수 있습니다.
1. Objective-C와 브릿지 처리를 통해 Swift언어를 연결하여 플러그인 구현하기
2. @_cdecl 비공식 어트리뷰트를 통해 Swift 플러그인 구현하기

대부분 iOS플러그인을 제작하는 방법은 1번 방법이 범용적으로 널리 쓰이고 있습니다.
하지만, 플러그인을 제작할 때 3개의 코드 파일이 따라오다보니까 굉장히 번거롭습니다.
그래서 저는 2번 방식을 좀 더 선호하는 편입니다.

Q : @_cdecl 어트리뷰트가 무엇인가요?
A : Swift에서 C 스타일의 함수 선언을 사용할 수 있게 해주는 특수한 속성입니다.
외부 C 라이브러리나 C를 사용한 어플리케이션과 Swift를 연동할 때 유용합니다.
하지만, 비공식 어트리뷰트이며 향후 Swift버전에서 언제 사라질지 알 수 없습니다.
현재 커뮤니티에서는 공식화를 논의한 흔적이 발견되기도 합니다.

Toast 라이브러리

iOS는 아시다시피 Toast기능이 원래 없습니다.
그래서, 이거를 직접 구현해줘야하는데.. 다들.. 마음 압니다.. 귀찮은거.. 😏

그래서 저는 Toast-Swift 라는 라이브러리를 추천합니다.

이 라이브러리 통해 유니티에서 Toast 메세지를 구현해보도록 합시다.

XCode에 플러그인 임포트 이론

위와 같은 플러그인을 사용하기 위해선, XCode의 경우 Pod, Package Dependencies 등의 방식이 있습니다. Pod의 경우 관리가 매우 번거롭기 때문에 이를 개선한 Package Dependencies를 활용하도록 하겠습니다.

Package Dependencies의 경우 프로젝트에서 찾아볼 수 있습니다.

+ 버튼을 누르고 검색창에 플러그인 깃 링크를 입력해주면 해당 플러그인을 임포트 할 수 있습니다.

플러그인을 Objective-C, Swift에서 사용하기 위해선 Unity-iPhone, UnityFramework 타겟에 사용할 프레임워크/라이브러리를 추가해야합니다. 🫤

+을 누르면 Toast 플러그인이 식별됩니다. Toast 플러그인을 선택하고 Add를 눌러서 추가할 수 있습니다.


다만, 유니티의 경우 iOS빌드를 할 때마다 XCode의 세팅이 대부분 리셋되기 때문에 위처럼 세팅하더라도 빌드를 다시 하면 이 세팅을 다시 해줘야합니다.
그래서, 이를 해결하기 위해 iOS의 경우 유니티에서 빌드가 끝난 시점에 XCode 프로젝트 세팅을 수정하여 프레임워크/라이브러리를 추가 및 등록하는 작업을 진행해야합니다.

Unity 빌드 끝난 시점 XCode 수정하기

유니티 Editor폴더에 PostProcessBuild_iOS.cs파일을 생성합니다.

다음과 같이 코드를 작성합니다.
Package Dependencies에 라이브러리를 추가하고, Unity-iPhone, UnityFramework등록해주는 코드가 작성되어 있습니다.

#if UNITY_IOS
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using System.IO;

public class PostProcessBuild_iOS
{
    [PostProcessBuild]
    public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
    {
        if (target == BuildTarget.iOS)
        {
            string pbxProjectPath = PBXProject.GetPBXProjectPath(pathToBuiltProject);

            PBXProject pbxProject = new PBXProject();
            pbxProject.ReadFromFile(pbxProjectPath);

            string unityIphoneTargetGuid = pbxProject.GetUnityMainTargetGuid();
            string unityFrameworkTargetGuid = pbxProject.GetUnityFrameworkTargetGuid();
            
            string packageFrameworkName = "Toast";
            string packageUrl = "https://github.com/scalessec/Toast-Swift.git";
            string version = "5.1.1";

            var toastGuid = pbxProject.AddRemotePackageReferenceAtVersion(packageUrl, version);
            pbxProject.AddRemotePackageFrameworkToProject(unityIphoneTargetGuid, packageFrameworkName, toastGuid, false);
            pbxProject.AddRemotePackageFrameworkToProject(unityFrameworkTargetGuid, packageFrameworkName, toastGuid, false);

            File.WriteAllText(pbxProjectPath, pbxProject.WriteToString());
        }
    }
}
#endif

Swift 플러그인 코드 작성

iOS 플러그인을 개발하는 파이프라인은 여러 방식이 있습니다. 그 중 제가 사용하는 진행 방식은 다음과 같습니다.

  1. 유니티에서 XCode 프로젝트를 생성한 뒤, 해당 프로젝트에서 swift 코드를 작성하고 코드 파일을 다시 유니티로 가져와서 C# 파일을 구성한 뒤 다시 빌드하여 플러그인을 진행하는 방식
  2. XCode에서 Storyboard 방식으로 새로운 프로젝트를 생성하고 플러그인 기능을 모두 구현한 뒤, 유니티에서 동작할 수 있도록 몇몇 부분만 유니티 API로 변경하여 마이그레이션 하는 방식

저는 1과 2번 방식을 혼합해서 제작을 하는데요. 이번 문서에서는 1번 방식으로 진행하도록 하겠습니다.

1번 방식의 경우, XCode 프로젝트를 빌드하기 전에 먼저 iOS폴더에 비어있는 swift 코드 파일 1개를 미리 생성하고 진행합니다. 저는, NativeToast.swift 파일을 생성하였습니다.

이렇게 할 경우, Libraries폴더에 유니티 Plugins폴더에서 세팅한 폴더 구조를 그대로 가져오면서 swift 코드를 작성할 수 있습니다.

코드 영역에서 오른쪽 마우스를 하면 Create Code Snippet...을 식별할 수 있습니다. 이것을 클릭합니다. (단축키 : Cmd + Shift + L)

검색 필드에 class를 검색하여 swift-subclass를 찾아서 선택해줍니다.

Public 접근 제한자 추가클래스 이름을 지정하고 NSObject를 상속받도록 해줍니다.

싱글턴을 작성합니다. (swift는 싱글턴 변수 명으로 shared를 주로 사용합니다.)

Toast 라이브러리를 사용하기 위해 import를 해주고, MakeToast함수를 미리 작성해둡니다.

Swift-Toast 리포지토리에서 Document를 보면 다음과 같이 작성하면 된다는 것을 아랐습니다~ (무적의 380도로 조지진 않습니다.)

GetAppController().rootView를 통해 유니티 iOS의 View에 접근할 수 있으며, .makeToast(message : String)를 작성하여 MakeToast() 메서드 작성을 마무리합니다.

Objective-C++ 브릿지 방식

브릿지는 Objective-C/C++를 사용해서 브릿지를 만들 수 있습니다.
Objective-C는 확장자가 .m 이며, Objective-C++는 .mm 입니다.

이 문서에서는 C++방식으로 설명합니다.

클래스, 프로퍼티, 변수, 함수에 @objc 어트리뷰트를 추가하여 Objective-C++가 인식할 수 있도록 해줍니다.

Objective-C++파일을 하나 만듭니다. 이름은 NativeToastBridge라고 하겠습니다.
XCode에서는 Objective-C++파일을 바로 만들 순 없어서 Objective-C를 생성 후 이름 변경하여 확장자를 .mm으로 변경합니다.



기존 내용은 지우고, Create Code Snippet.. 기능을 사용하여 #ifdef__cplusplus Guar 템플릿을 임포트 합니다. (단축키 : cmd + Shift + L)

UnityFramework-Swift.h를 임포트합니다. 이 녀석을 통해 Swift로 작성된 코드를 불러올 수 있습니다.

다음과 같이 작성하여 Swift에서 작성한 MakeToast와 연결합니다.
유니티에서 Objective-C++와 통신할 때 String 자료형으로 통신할 수 없기 때문에 const char*로 인자를 설정하고 NSString으로 String형태로 변환합니다.

C#과 Objective-C++ 그리고 Swift와 호환되는 자료형

C#Objective-C++Swift디테일
Stringconst char *String[NSString] stringWithUTF8String으로 캐스팅 요구
boolboolBool or CBool
intintInt32 or CIntC의 int는 32비트로 정의됩니다. Swift의 경우 Int32가 유사합니다.
floatfloatFloot or CFloat
longlongInt64 or CLongLongSwift는 long이 없습니다. C의 long과 유사한 Int64를 사용합니다.
doubledoubleDouble or CDouble

Swift 브릿지 방식

@_cdecl을 사용해서 swift를 브릿지 형태로 구성할 수 있습니다.

NativeToast.swift는 다음과 같이 구성되어있는 상태에서 작업을 진행합니다.

Swift파일을 하나 만듭니다. 이름은 NativeToastBridge라고 하겠습니다.


다음과 같이 작성하여 Swift에서 작성한 MakeToast와 연결합니다.
유니티에서 Swift와 통신할 때 String 자료형으로 통신할 수 없기 때문에 UnsafePointer<CChar>로 인자를 설정하고 String 형태로 변환해서 사용합니다.

C#과 Swift 브릿지와 호환되는 자료형

C#Swift디테일
StringUnsafePointer<CChar>String(cString:)으로 캐스팅 요구
boolBool or CBool
intInt32 or CInt
floatFloat or CFloat
longInt64 or CLongLong
doubleDouble or CDouble

유니티에 가져오기

Java 또는 Kotlin 코드를 Android 폴더에 넣고,
XCode에서 작성한 NativeToast와 Bridge코드 파일을 유니티 iOS 폴더에 가져옵니다.
(기존에 유니티에서 임시로 만든 swift파일은 제거했습니다.)

유니티에서 연동 코드 작성하기

Runtime 폴더에 NativeToast.cs파일을 생성합니다.

NativeToast.cs에는 다음과 같이 작성합니다.

#if UNITY_IPHONE
using System.Runtime.InteropServices;
#endif

#if UNITY_ANDROID
using UnityEngine.Android;
#endif

using UnityEngine;


public class NativeToast : MonoBehaviour
{
#if UNITY_IOS
    [DllImport ("__Internal")]
    private static extern void MakeToast(string message);
#endif

    /// <summary>
    /// Show a Toast message from Android.
    /// </summary>
    /// <param name="message">the message you want to show</param>
    public static void ShowToast(string message)
    {
#if UNITY_EDITOR
        Debug.Log(message);
#elif UNITY_ANDROID
        AndroidJavaObject activity = new AndroidJavaObject("com.unity3d.player.NativeToast");
        activity.Call("ShowToast", message);
#elif UNITY_IOS
		MakeToast(message);
#endif
    }
}

여기에 작성된 MakeToast는 Bridge코드에서 작성한 함수와 이름을 동일하게 해야합니다.
@_cdecl로 작성한 경우 괄호안에 작성한 이름과 매치하면 됩니다.
(Tip : 인자의 이름은 맞추지 않아도 동작하는듯 합니다.🤔)

[DllImport("__Internal")]
private static extern void MakeToast(string message);

사용해보기

테스트용 코드를 만들고 Button에 연결한 뒤, 빌드를 하여 잘 동작하는지 체크합니다.

후원

이 내용이 맛있었다면 저에게 맛있는 커피를 후원해주세요! ☕

토스 익명 송금 : 후원하기
계좌 송금 : 토스뱅크 1000-0586-4766 (김한용)

profile
유니티를 통한 스페셜 테크닉만 다루는 독특한 개발자

0개의 댓글