Flutter와 iOS를 연결하는 메소드 채널을 만드는 방법에 대해 설명하겠습니다. 메소드 채널은 Flutter와 호스트 플랫폼 간의 통신을 가능하게 하는 방법입니다. 여기서는 Flutter 앱과 iOS 코드 간의 통신을 위해 사용됩니다.
기본 예제
Flutter 측에서 메소드 채널 생성:
Flutter에서 MethodChannel을 생성합니다. 이 채널은 고유한 이름을 가져야 합니다.
const methodChannel = MethodChannel('com.example.myapp/channel');
메소드 호출:
Flutter에서 메소드 채널을 사용하여 특정 메소드를 호출할 수 있습니다. 예를 들어, iOS에서 데이터를 요청할 수 있습니다.
String response = await methodChannel.invokeMethod('getData');
iOS 측에서 메소드 채널 처리:
Swift 또는 Objective-C를 사용하여 iOS 앱에서 메소드 채널을 처리합니다.
AppDelegate 클래스에 메소드 채널을 구현합니다.
Swift 예시:
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel(name: "com.example.myapp/channel",
binaryMessenger: controller.binaryMessenger)
methodChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// Handle method calls
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
메소드 콜 핸들링:
iOS에서 setMethodCallHandler를 사용하여 들어오는 메소드 호출을 처리합니다.
예를 들어, Flutter에서 getData 메소드를 호출하면 해당 메소드를 iOS에서 식별하고 처리할 수 있습니다.
응답 보내기:
처리 후, iOS에서 결과를 Flutter 앱으로 다시 보낼 수 있습니다. 이를 통해 양방향 통신이 가능해집니다.
메소드 채널을 사용하면 Flutter와 iOS 간에 복잡한 데이터를 효율적으로 주고받을 수 있습니다. 각 플랫폼의 특정 기능을 활용하거나, 플랫폼 간 데이터를 공유하는 데 유용합니다.
라이브러리 사용 예제
이 과정은 대체로 다음 단계로 이루어집니다:
- Flutter에서 사진 선택 요청을 보냅니다.
- iOS에서 사진 라이브러리에 접근하여 사진을 선택합니다.
- 선택된 사진을 Flutter로 전송합니다.
먼저, Flutter에서 시작하는 부분입니다.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const platform = MethodChannel('com.example.myapp/photo');
String _imagePath = '아직 이미지가 선택되지 않았습니다.';
Future<void> _getPhoto() async {
String imagePath;
try {
final String result = await platform.invokeMethod('getPhoto');
imagePath = result;
} on PlatformException catch (e) {
imagePath = "사진을 가져오는 데 실패했습니다: '${e.message}'.";
}
setState(() {
_imagePath = imagePath;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter와 iOS 사진 예제'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_imagePath),
RaisedButton(
onPressed: _getPhoto,
child: Text('사진 선택하기'),
),
],
),
),
);
}
}
이제 iOS에서 처리하는 부분입니다.
iOS (Swift) 측 코드:
import UIKit
import Flutter
import PhotosUI
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private var imageResult: FlutterResult?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let photoChannel = FlutterMethodChannel(name: "com.example.myapp/photo",
binaryMessenger: controller.binaryMessenger)
photoChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// Flutter로부터 'getPhoto' 메소드 콜을 받을 때
if call.method == "getPhoto" {
self?.imageResult = result
self?.showPhotoPicker(from: controller)
} else {
result(FlutterMethodNotImplemented)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func showPhotoPicker(from controller: UIViewController) {
var configuration = PHPickerConfiguration()
configuration.selectionLimit = 1
configuration.filter = .images
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
controller.present(picker, animated: true, completion: nil)
}
}
extension AppDelegate: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
guard let result = results.first, result.itemProvider.canLoadObject(ofClass: UIImage.self) else {
self.imageResult?("사진을 선택하지 않았습니다.")
return
}
result.itemProvider.loadObject(ofClass: UIImage.self) { (object, error) in
DispatchQueue.main.async {
if let image = object as? UIImage, let data = image.jpegData(compressionQuality: 1.0), let path = self.saveImageToDocumentDirectory(imageData: data) {
self.imageResult?(path)
} else {
self.imageResult?("이미지를 불러오는 데 실패했습니다.")
}
}
}
}
private func saveImageToDocumentDirectory(imageData: Data) -> String? {
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("temp_image.jpg")
fileManager.createFile(atPath: paths as String, contents: imageData, attributes: nil)
return paths
}
}
이 코드는 Flutter 앱에서 ‘사진 선택하기’ 버튼을 누르면 iOS의 사진 라이브러리에서 사진을 선택할 수 있게 해줍니다. 선택된 사진은 iOS 장치의 문서 디렉토리에 임시 파일로 저장되며, 그 경로가 Flutter로 전송되어 화면에 표시됩니다.
이 예제는 기본적인 구조를 제공합니다. 실제 애플리케이션에서는 사용자 권한 요청, 이미지 처리, 에러 처리 등 추가적인 요소가 필요할 수 있습니다.
extension AppDelegate: PHPickerViewControllerDelegate
:- 이 코드는
AppDelegate
클래스에PHPickerViewControllerDelegate
프로토콜을 채택하도록 확장합니다.PHPickerViewControllerDelegate
는 iOS 14 이상에서 제공하는 새로운 사진 선택기PHPickerViewController
의 대리자(delegate) 메소드를 정의합니다. - 대리자 메소드
picker(_:didFinishPicking:)
를 구현하여 사용자가 사진 선택기에서 사진을 선택하거나 선택기를 닫았을 때 호출되는 콜백을 제공합니다. 이를 통해 사용자가 선택한 이미지를 처리하고 결과를 Flutter 앱에 전달할 수 있습니다.
- 이 코드는
private func saveImageToDocumentDirectory(imageData: Data)
:- 이 함수는 선택된 이미지를 앱의 문서 디렉토리에 저장하는 데 사용됩니다.
PHPickerViewController
는 선택한 이미지의 실제 파일 경로를 제공하지 않으며, 대신UIImage
객체 또는 이미지 데이터를 제공합니다. - Flutter와의 통신에서 파일 경로를 전달하는 것이 일반적이기 때문에, 이미지 데이터를 임시 파일로 저장하고 해당 파일의 경로를 Flutter 측에 전달합니다.
- 이 방법을 사용하면 Flutter 앱에서 해당 경로에 접근하여 이미지를 표시하거나 다른 용도로 사용할 수 있습니다.
- 이 함수는 선택된 이미지를 앱의 문서 디렉토리에 저장하는 데 사용됩니다.
이렇게 확장과 도우미 함수를 사용하는 것은 iOS 개발에서 일반적인 패턴입니다. 이는 코드를 더욱 모듈화하고 읽기 쉽게 만들며, 특정 기능을 캡슐화하여 재사용을 용이하게 합니다. 선택된 이미지를 처리하고 Flutter와 효율적으로 통신하기 위해 이러한 방식을 사용하는 것입니다.
picker.delegate = self
코드에서 self
는 AppDelegate
인스턴스를 가리킵니다. 이 코드는 picker
의 대리자(delegate)로 AppDelegate
인스턴스를 설정합니다. 이 설정을 통해 picker
가 이벤트(예: 사용자가 사진을 선택하거나 취소하는 동작)를 발생시킬 때, 해당 이벤트를 처리하기 위한 메소드가 AppDelegate
인스턴스 내에서 호출됩니다.
이를 가능하게 하는 것이 extension AppDelegate: PHPickerViewControllerDelegate
부분입니다. 이 확장은 AppDelegate
클래스에 PHPickerViewControllerDelegate
프로토콜을 구현함으로써, AppDelegate
가 PHPickerViewControllerDelegate
의 요구사항을 충족하게 합니다. PHPickerViewControllerDelegate
프로토콜은 picker(_:didFinishPicking:)
같은 메소드를 정의하며, 이 메소드는 picker
이벤트에 반응하기 위해 필요합니다.
따라서, picker.delegate = self
를 설정함으로써, PHPickerViewController
인스턴스인 picker
는 사진 선택기 관련 이벤트가 발생할 때, AppDelegate
인스턴스에 정의된 메소드를 호출합니다. 이렇게 함으로써, 사용자가 사진을 선택하거나 사진 선택기를 닫을 때, 이에 대한 응답으로 AppDelegate
내의 대리자 메소드가 실행됩니다.
간단히 말해서, picker.delegate = self
는 사진 선택기의 이벤트를 AppDelegate
에서 처리할 수 있도록 연결하는 역할을 합니다. 그리고 extension AppDelegate: PHPickerViewControllerDelegate
는 이러한 이벤트 처리를 위한 메소드를 AppDelegate
에 추가합니다.
controller.present(picker, animated: true, completion: nil)
는 iOS 애플리케이션에서 새로운 뷰 컨트롤러를 모달 방식으로 표시하는 데 사용되는 코드입니다. 이 경우에는 PHPickerViewController
인스턴스인 picker
를 사용자에게 보여주기 위해 사용됩니다. 각 부분에 대해 자세히 설명드리겠습니다.
controller
:- 이것은
present
메소드를 호출하는 뷰 컨트롤러를 나타냅니다. 여기서controller
는AppDelegate
클래스 내에 정의된FlutterViewController
인스턴스를 참조합니다. 이는 Flutter와의 통합을 위한 기본 뷰 컨트롤러입니다.
- 이것은
present
:present
메소드는 한 뷰 컨트롤러에서 다른 뷰 컨트롤러를 모달 방식으로 표시하는 데 사용됩니다. 모달 방식은 현재 화면 위에 새로운 화면을 덮어서 표시하는 방식을 말합니다.
picker
:- 표시하려는 새 뷰 컨트롤러입니다. 이 경우
PHPickerViewController
의 인스턴스로, 사용자에게 사진을 선택할 수 있는 인터페이스를 제공합니다.
- 표시하려는 새 뷰 컨트롤러입니다. 이 경우
animated: true
:- 이 파라미터는 뷰 컨트롤러가 화면에 나타날 때 애니메이션 효과를 사용할 것인지를 지정합니다.
true
로 설정하면 부드럽게 화면에 나타나는 애니메이션 효과가 적용됩니다.
- 이 파라미터는 뷰 컨트롤러가 화면에 나타날 때 애니메이션 효과를 사용할 것인지를 지정합니다.
completion: nil
:- 뷰 컨트롤러의 표시가 완료된 후 실행할 콜백 함수를 지정합니다.
nil
로 설정된 경우, 표시가 완료된 후에 특별히 실행할 추가 작업이 없음을 의미합니다.
- 뷰 컨트롤러의 표시가 완료된 후 실행할 콜백 함수를 지정합니다.
이 코드 라인은 기본적으로 AppDelegate
의 FlutterViewController
에서 PHPickerViewController
를 사용자에게 보여주기 위해 사용됩니다. 사용자는 이 인터페이스를 통해 사진을 선택할 수 있으며, 선택이 완료되면 PHPickerViewControllerDelegate
의 메소드가 호출되어 선택된 사진을 처리할 수 있습니다.