تطوير الإضافات على منصة iOS في كوردوفا
يقدم هذا القسم تفاصيل عن كيفية تنفيذ شيفرات الإضافات الأصلية (native plugin code) على منصة iOS. قبل قراءة هذه الصفحة، راجع صفحة دليل تطوير الإضافات للحصول على نظرة عامة على بنية الإضافات وواجهة JavaScript الخاصة بها.
يواصل هذا القسم تطوير مثال الإضافة echo الوارد في دليل تطوير الإضافات، والذي يربط الاتصال بين المعرض webview والمنصة الأصلية (native platform).
يتم تنفيذ إضافات iOS على هيئة صنف من لغة Objective-C، والذي يوسع الصنف CDVPlugin
.
لكي يُربط المعامل service
المُمرر إلى التابع exec
الذي يخص JavaScript مع صنف في لغة Objective-C، فيجب تسجيل كل أصناف الإضافة على هيئة وسم <feature>
في الملف config.xml
.
إعداد صنف الإضافة
يُستخدم الجزء من الإضافة المكتوب بلغة JavaScript التابعَ cordova.exec
على النحو التالي:
exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);
هذا سيُرسل طلبية (request) من UIWebView
إلى الجانب الأصلي (native side) من منصة iOS، ثم يستدعي التابع action
على الصنف service
فعليًا، مع تمرير الوسائط المعطاة في المصفوفة args
.
حدِّد الإضافة على هيئة وسم <feature>
في الملف config.xml
الخاص بتطبيق Cordova-iOS، مع استخدام الملف plugin.xml
لإدراج هذا الوسم تلقائيًا، كما هو موضح في دليل تطوير الإضافات:
<feature name="LocalStorage">
<param name="ios-package" value="CDVLocalStorage" />
</feature>
يجب أن تتطابق الخاصية name
في الوسم feature
مع قيمة المعامل service
الذي مررته سابقا إلى الدالة exec
التي تخص JavaScript. كما يجب أن تتطابق الخاصية value
مع اسم صنف الإضافة في Objective-C. ويجب أن تساوي الخاصية name
في الوسم <param>
دائمًا القيمة "ios-package"
.
إذا لم تتبع هذه الإرشادات، فقد يتم تصريف (compile) الإضافة، ولكن قد لا تتمكن كوردوفا من الوصول إليها.
تهيئة ودورة الحياة الإضافات
يتم إنشاء نسخة (instance) واحدة من كائن الإضافة خلال دورة حياة (lifetime) المعرض UIWebView
. لا يتم إنشاء نسخ للإضافات حتى تتم الإشارة إليها أولًا عبر استدعاء من JavaScript، إلا في حال إضافة الوسم <param>
مع الخاصيتين name="onload"
و value="true"
في الملف config.xml
على النحو التالي:
<feature name="Echo">
<param name="ios-package" value="Echo" />
<param name="onload" value="true" />
</feature>
يجب أن تستخدم الإضافاتُ التابع pluginInitialize
في مرحلة الانطلاق (startup logic).
لإضافات الطلبيات الطويلة (long-running requests)، أو النشاطات الخلفية (background activity)، مثل قارئات الوسائط، أو المستمعات (listeners)، أو النشاطات ذات الحالة الداخلية (internal state)، يجب أن تنفِّذ التابع onReset
لأجل إلغاء تلك الطلبيات الطويلة، أو للتنظيف بعد تلك الأنشطة. يتم تشغيل التابع onReset
عند انتقال UIWebView
إلى صفحة جديدة أو عند تحديث الصفحة، وهو ما يؤدي إلى إعادة تحميل JavaScript.
كتابة إضافات iOS
يرسل استدعاء JavaScript طلبية إضافة (plugin request) إلى الجانب الأصلي (native side)، وتُعيّن إضافة Objective-C المقابلة في الملف config.xml
. وأي شيء يتم إرساله إلى الإضافة عبر الدالة exec
سيُمرّر إلى التابع action
الخاص بصنف بالإضافة.
فيما يلي توقيع (signature) تابع الإضافة:
- (void)myMethod:(CDVInvokedUrlCommand*)command
{
CDVPluginResult* pluginResult = nil;
NSString* myarg = [command.arguments objectAtIndex:0];
if (myarg != nil) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Arg was null"];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
لمزيد من التفاصيل، اطلع على الصفحات CDVInvokedUrlCommand.h و CDVPluginResult.h و CDVCommandDelegate.h.
أنواع الرسائل CDVPluginResult في منصة iOS
يمكنك استخدام CDVPluginResult
لإعادة مجموعة متنوعة من أنواع النتائج إلى استدعاءات JavaScript، باستخدام توابع تتبع النمط التالي:
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAs...
يمكنك إنشاء الأنواع String
و Int
و Double
و Bool
و Array
و Dictionary
و ArrayBuffer
و Multipart
. يمكنك أيضًا استبعاد أي وسيط لأجل إرسال حالة، أو لإعادة خطأ، أو يمكنك حتى اختيار عدم إرسال أي نتيجة للإضافة، وفي هذه الحالة لن يُطلق أيٌّ من تلك الاستدعاءات.
تذكر ما يلي عند التعامل مع القيم المعادة المعقدة:
messageAsArrayBuffer
تتوقعNSData*
، وتحوله إلىArrayBuffer
في استدعاء جافاسكريبت. وبالمثل، أي كائنArrayBuffer
ترسله JavaScript إلى الإضافة سيُحوّل إلىNSData*
.messageAsMultipart
تتوقع مصفوفةNSArray*
تحتوي على أيٍّ من الأنواع المدعومة الأخرى، وترسل المصفوفة بأكملها باعتبارها الوسيطarguments
إلى رد نداء JavaScript. بهذه الطريقة، تُسلسل (serialized) كل الوسائط أو تُفك (deserialized) بحسب الضرورة، لذلك من الآمن إعادةNSData*
كسلسلة متعددة (multipart)، ولكن ليس على هيئة مصفوفة أو قاموس.
الإضافة echo مثالًا لإضافة iOS
لمطابقة واجهة الميزة echo المقدمة في صفحة الإضافات، استخدم الملف plugin.xml
لإدراج مواصفات feature
في الملف config.xml
الخاص بالمنصة المحلية:
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="Echo">
<param name="ios-package" value="Echo" />
</feature>
</config-file>
</platform>
ثم أضف الملفين Echo.h
و Echo.m
إلى المجلد Plugins
الموجود في مجلد تطبيق Cordova-iOS:
/********* Echo.h Cordova Plugin Header *******/
#import <Cordova/CDVPlugin.h>
@interface Echo : CDVPlugin
- (void)echo:(CDVInvokedUrlCommand*)command;
@end
/********* Echo.m Cordova Plugin Implementation *******/
#import "Echo.h"
#import <Cordova/CDVPlugin.h>
@implementation Echo
- (void)echo:(CDVInvokedUrlCommand*)command
{
CDVPluginResult* pluginResult = nil;
NSString* echo = [command.arguments objectAtIndex:0];
if (echo != nil && [echo length] > 0) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echo];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
@end
تُوسّع عمليات الاستيراد (import) في الجزء العلوي من الملف الصنفَ CDVPlugin
. في هذه الحالة، لا تدعم الإضافة سوى إجراء (action) واحد، وهو echo
. تُستخلص السلسلة النصية echo
عن طريق استدعاء التابع objectAtIndex
للحصول على المعامل الأول الموجود في المصفوفة arguments
، والذي يتوافق مع المعاملات المُمررة من قبل الدالة exec()
التي تخص JavaScript.
تتحقق الشيفرة من أن المعامل لا يساوي القيمة nil
أو سلسلة نصية فارغة؛ فإن كان الأمر كذلك، فستعيد PluginResult
مع الحالة "ERROR
" وإلا فستعيد PluginResult
مع الحالة "OK
"، مع تمرير السلسلة النصية الأصلية echo
. وأخيرًا، تُرسل النتيجة إلى التابع self.commandDelegate
، والذي ينفذ دوال ردود نداء النجاح أو الفشل (success or failure callbacks) الخاصة بالتابع exec
على جانب JavaScript. في حال استدعاء دالة رد نداء النجاح، فسيُمرر إليها المعامل echo
.
التكامل مع منصة iOS
يوفر الصنف CDVPlugin
توابع أخرى يمكن إعادة تعريفها من قبل الإضافة. على سبيل المثال، يمكنك إمساك الأحداث pause
و resume
وإنهاء التطبيق (app terminate) و handleOpenURL
. ألقِ نظرةً على الصنفين CDVPlugin.h و CDVPlugin.m للمزيد من الإرشادات.
المهام الفرعية
تنفّذ توابع الإضافة عادةً في نفس المهمة الفرعية (thread) التي تشتغل عليها الواجهة الرئيسية. إن كانت الإضافة تتطلب قدرًا كبيرًا من المعالجة، أو تتطلب القيام باستدعاء مُعطِّل (blocking call)، فعليك استخدام مهمة فرعية خلفية (background thread). اطلع على الشيفرة التالي مثلًا:
- (void)myPluginMethod:(CDVInvokedUrlCommand*)command
{
// هنا command.arguments تحقق من
[self.commandDelegate runInBackground:^{
NSString* payload = nil;
// Some blocking logic...
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
// آمن في المهام الفرعية sendPluginResult استخدام
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
تنقيح إضافات iOS
لتنقيح (debug) الأخطاء في شيفرة Objective-C، سيكون عليك استخدام المُصحِّح المدمج في Xcode. أما بالنسبة إلى JavaScript، فيمكنك ربط Safari بالتطبيق عبر محاكي أو جهاز iOS.
أخطاء شائعة
- لا تنسَ إدراج خريطة الإضافة (plugin's mapping) إلى الملف
config.xml
. وإلا فسيُسجّل خطأ في وحدة تحكم Xcode. - لا تنسَ إضافة كل المضيفات التي تتصل بها في القائمة البيضاء. وإلا فسيُسجّل خطأ في وحدة تحكم Xcode.