مشاوره رایگان
دریافت لینک های دانلود دریافت پستی درب منزل مبلغ کل: تومان
جهت دریافت کد تخفیف به کانال تلگرام مراجعه و یا با پشتیبان آنلاین هماهنگ نمایید

Cloud Backend در iOS با استفاده از Parse - بخش دوم


جلسه بیست و نهم : افزودن Cloud Backend به اپ iOS با استفاده از Parse – بخش دوم

این مقاله ، بخش دوم از آموزش Cloud Backend است. در بخش اول از این سری آموزشی ، مقدمه ای در مورد خدمات (Backend BaaS) ارائه نمودیم و اپ Recipe را برای استفاده از این نوع خدمات ، تغییر دادیم. به جای ذخیره سازی دستور پخت ها به صورت محلی و در داخل اپ ، دستور پخت ها را به Parse backend ، انتقال دادیم. در حال حاضر ، اپ Recipe به Parse متصل است و دستور پخت ها ، از cloud دانلود می شوند.

اگر بخش اول از این سری آموزشی را مطالعه نموده اید ، اکنون باید درک پایه ای از Parse SDK را کسب کرده باشید. در بخش اول ، نحوه بازیابی اشیاء از Parse را آموزش دادیم. در این بخش ، نحوه ذخیره سازی دستور پخت های جدید در Parse backend را نشان خواهیم داد. البته ، نحوه حذف دستور پخت از cloud را نیز یاد خواهید گرفت.

ما کار بر روی پروژه Xcode را که در بخش اول ساخته ایم ، ادامه خواهیم داد. از این رو ، اگر پروژه و تمرین جلسه قبل را انجام نداده اید ، اکیداً توصیه می شود که آموزش جلسه قبل را مطالعه نمایید.

بیایید کار را شروع کنیم.

 

شروع کار

قبل از این که وارد بخش کدنویسی شویم ، بیایید به قابلیت های جدیدی که قرار است اعمال کنیم ، نگاه سریعی بیاندازیم. قبلاً ، ما یک اپ Recipe ساخته ایم که دستور پخت ها را از Parse بازیابی کرده و آنها را با استفاده از کلاس PFQueryTableViewController ، نمایش می دهد. در این بخش ، ما دو قابلیت دیگر را به اپ اضافه خواهیم کرد که عبارتند از :

  • ایجاد دستور پخت جدید و ذخیره کردن آن در cloud
  • حذف نمودن دستور پخت موجود از cloud

طراحی رابط کاربری

در رابط کاربری اصلی اپ ، تغییرات زیادی را اعمال نکرده ایم. با این حال ، برای این که به کاربر این امکان را فراهم نماییم که بتواند دستور پخت جدیدی را وارد نماید ، یک view controller جدید ، اضافه خواهیم کرد. View controller جدید با نام “New Recipe” با ضربه زدن بر روی کلید “+” در رابط کاربری اصلی ، فعال می شود.  از طریق صفحه جدید ایجاد شده ، کاربر می تواند اطلاعات دستور پخت را که شامل نام ، مدت زمان پخت ، مواد اولیه و تصویر دستور پخت است ، تکمیل نماید.

از آن جایی که نمی خواهیم بر روی مسائل مربوط به طراحی تمرکز کنیم ، در اینجا ، نحوه ساخت رابط کاربری را نشان نخواهیم داد. view controller جدید ، یعنی  “New Recipe”  یک زیر کلاس از UITableViewController می باشد و با استفاده از static table view ایجاد می شود. البته ، ما شما را تشویق می کنیم که رابط کاربری را خودتان بسازید ، اما ، برای صرفه جویی در زمان ، می توانید پروژه را از اینجا download the Xcode project from here  دانلود نمایید (تست شده در Xcode 4.6.3). فایل را از حالت زیپ خارج کرده و آن را باز نمایید. بقیه این آموزش را به ساخت تمپلت کد ، اختصاص خواهیم داد.

قبل از ادامه کار ، به خاطر داشته باشید که application ID و client key را در فایل RecipeBookAppDelegate.m ، به ID های Parse مربوط  به پروژه خود ، تغییر دهید.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    [Parse setApplicationId:@"<change to your application ID>"
                  clientKey:@"<change to your client key>"];
 
    return YES;
}

برداشتن عکس تصویر دستور پخت از Photo Library

در ابتدا ، ما تابع “Add Photo”  از view controller مربوط به New Recipe را پیاده سازی خواهیم کرد. هنگامی که کار بر روی تصویر “Add Photo”  ضربه می زند ، اپ photo library را برای انتخاب کردن  تصویر دستور پخت ، بالا خواهد آورد. iOS SDK کلاس UIImagePickerController را برای گرفتن عکس از دوربین (UIImagePickerController) و دسترسی به photo library را فراهم می کند. بیایید ، ببینیم که چگونه اپ ، media browser را بالا می آورد.

ایجاد یک Media browser

تعریف KUTTypeImage ، که بعداً از آن استفاده خواهیم کرد ، در فریم ورک MobileCoreServices ، وجود دارد. بنابراین ، این فریم ورک را به پروژه Xcode خود اضافه می کنیم و عبارت import زیر را ، در ابتدای فایل NewRecipeViewController.m  اضافه می کنیم:

#import <MobileCoreServices/UTCoreTypes.h>

در ادامه ، کد زیر را برای ایجاد متد “showPhotoLibrary:” ، اضافه می کنیم:

- (void)showPhotoLibary
{
    if (([UIImagePickerController isSourceTypeAvailable:
          UIImagePickerControllerSourceTypeSavedPhotosAlbum] == NO)) {
        return;
    }
    
    UIImagePickerController *mediaUI = [[UIImagePickerController alloc] init];
    mediaUI.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    
    // Displays saved pictures from the Camera Roll album.
    mediaUI.mediaTypes = @[(NSString*)kUTTypeImage];
    
    // Hides the controls for moving & scaling pictures
    mediaUI.allowsEditing = NO;
    
    mediaUI.delegate = self;
    
    [self.navigationController presentModalViewController: mediaUI animated: YES];
}

در کد بالا ، ما ابتدا ، image picker را پیکربندی کرده و source type را به UIImagePickerControllerSourceTypePhotoLibrary تنظیم می کنیم. این کار ، یک media browser را نمایش می دهد که دسترسی به آلبوم تصاویر دستگاه و از جمله ، قابلیت دسترسی به دوربین را نیز فراهم می سازد. به طور پیش فرض ، photo library ، شامل عکس و ویدیوهای ذخیره شده می باشد. در این مورد ،  اپ Recipe ما ، فقط به کاربر اجازه انتخاب تصویر دستور پخت از کتابخانه را خواهد داد. بنابراین ، نوع رسانه را به kUTTypeImage ، محدود می کنیم. مابقی کد ، خود توضیح (self explanatory) است.

هنگامی که کاربر ، “AddPhoto” را ضربه می زند ، که اولین ردیف static table view است ، متد “”showPhotoLibrary: را برای نمایش media browser ، فراخوانی می کنیم. بنابراین ، کد زیر را در همان فایل پیاده سازی شده، اضافه می کنیم:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
        [self showPhotoLibary];
    }
}

اگر اپ را اجرا کنیم و سعی کنیم یک دستور پخت جدید را اضافه کنیم ، اپ ، تصاویر را برای انتخاب کردن نشان می دهد. با این حال ، اپ به انتخاب ما ، عکس العمل نشان نمی دهد ؛ زیرا ، پروتکل UIImagePickerControllerDelegate  ، پیاده سازی نشده است.

 

نکته : اگر از شبیه ساز iPhone برای تست اپ استفاده می کنید ، photo library احتمالاً خالی است. اضافه کردن عکس به آلبوم تصاویر کار ساده ای است. فقط کافی است هر کدام از تصاویر را که تمایل داریم ، از Finder به شبیه ساز درگ نماییم. با این کار ، تصویر موردنظر به طور خودکار در Safari نشان داده می شود. به سادگی روی عکس ضربه زده و نگه می داریم تا در داخل photo library ذخیره شود.

پیکربندی نماینده UIImagePickerController

برای عکس العمل نشان دادن به انتخاب کاربر ، ما باید یک نماینده پیاده سازی کنیم که مطابق با پروتکل های UIImagePickerControllerDelegate و UINavigationControllerDelegate باشد. بدیهی است که NewRecipeViewController باید به عنوان شیء نماینده تنظیم شود. در فایل NewRecipeViewController.h رابط کاربری را مطابق کد زیر تغییر می دهیم:

@interface NewRecipeViewController : UITableViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate>

فایل NewRecipeViewController.m را باز کرده و کد زیر را به آن اضافه می کنیم:

- (void) imagePickerController: (UIImagePickerController *) picker didFinishPickingMediaWithInfo: (NSDictionary *) info {
 
    UIImage *originalImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage];
    self.recipeImageView.image = originalImage;
    
    [picker dismissViewControllerAnimated:YES completion:nil];    
}

هنگامی که کاربر تصویر را از آلبوم انتخاب می کند ، متد didFinishPickingMediaWithInfo  از نماینده فراخوانی می شود و یک شیء NSDictionary حاوی رسانه (تصویر یا ویدیو) و داده های مربوطه را منتقل می نماید. بدون قابلیت editing که غیر فعال است ، می توانیم تصویر انتخاب شده از info dictionary را با استفاده از کلید UIImagePickerControllerOriginalImage ، به دست بیاوریم. تصویر انتخاب شده ، سپس به image view مربوط به newrecipe ، تخصیص می یابد.

نکته: اگر قابلیت editing ، فعال باشد ، می توان به تصویر ویرایش شده از طریق کلید UIImagePickerControllerEditedImage ، دسترسی یافت.

اگر اپ را دوباره اجرا کنیم ، باید بتوانیم تصویر دستور پخت را انتخاب نماییم.

هندل کردن فیلد متن

اگر از تمپلت source code  که ما ساخته ایم ، استفاده می کنید ، فیلدهای متنی (text field) را ما به صورت از پیش ساخته شده در تمپلت قرار داده  و آنها را به کدهای مربوطه ، ارتباط داده ایم. فیلدهای متنی برای اطلاعات مجموعه دستور پخت ها استفاده می شوند ، که حاوی نام دستور پخت ، مدت زمان آماده سازی ، مواد اولیه است که توسط کاربر تکمیل می شوند. فیلد مواد اولیه یک رشته comma-delimited است که هر آیتم ، با کاما از بقیه جدا می شود. برای ساده نگه داشتن اپ demo ، هیج اعتبار سنجی برای ورودی ها انجام نمی شود.

ما هم چنین ، UITextFieldDelegate  را در تمپلت پروژه پیاده سازی کردیم. شیء UITextField هنگامی که رخدادهای خاص رخ می دهد ، به متد UITextFieldDelegate مناسب ، پیام می دهد. در مورد این اپ ، ما می خواهیم که صفحه کلید را هنگامی که کاربر کلید بازگشت را ضربه می زند پنهان نماید. این کار را به راحتی می توان ، با پیاده سازی متد textFieldShouldReturn ، زمانی که کاربر کلید بازگشت در صفحه کلید را فشار می دهد انجام داد.

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

آپلود کردن دستور پخت در Parse Cloud

با تنظیمات اصلی که در رابط کاربری اعمال کردیم ، اکنون می توانیم وارد بخش اصلی این آموزش بشویم. هنگامی که کاربر کلید “Save” را ضربه می زند ، اپ متد “save:” را فراخوانی کرده و دستور پخت جدید را از جمله تصویر دستور پخت را در Parse cloud آپلود می نماید. Parse SDK یک روش ساده برای آپلود کردن داده فراهم می نماید. ذخیره سازی داده در Parse حول شیء PFObject انجام می شود. همان طور که قبلاً اشاره کرده ایم ، هر شیء PFObject حاوی جفت های key-value از داده های سازگار  با JSON است.

در فایل “NewRecipeViewController.m” متد “save:” را با کدهای زیر ، پیاده سازی می کنیم:

- (IBAction)save:(id)sender {
    // Create PFObject with recipe information
    PFObject *recipe = [PFObject objectWithClassName:@"Recipe"];
    [recipe setObject:_nameTextField.text forKey:@"name"];
    [recipe setObject:_prepTimeTextField.text forKey:@"prepTime"];
    
    NSArray *ingredients = [_ingredientsTextField.text componentsSeparatedByString: @","];
    [recipe setObject:ingredients forKey:@"ingredients"];
    
    // Recipe image
    NSData *imageData = UIImageJPEGRepresentation(_recipeImageView.image, 0.8);
    NSString *filename = [NSString stringWithFormat:@"%@.png", _nameTextField.text];
    PFFile *imageFile = [PFFile fileWithName:filename data:imageData];
    [recipe setObject:imageFile forKey:@"imageFile"];
    
    // Show progress
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.mode = MBProgressHUDModeIndeterminate;
    hud.labelText = @"Uploading";
    [hud show:YES];
 
    // Upload recipe to Parse
    [recipe saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
        [hud hide:YES];
        
        if (!error) {
            // Show success message
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Upload Complete" message:@"Successfully saved the recipe" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [alert show];
            
            // Notify table view to reload the recipes from Parse cloud
            [[NSNotificationCenter defaultCenter] postNotificationName:@"refreshTable" object:self];
            
            // Dismiss the controller
            [self dismissViewControllerAnimated:YES completion:nil];
 
        } else {
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Upload Failure" message:[error localizedDescription] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [alert show];
 
        }
        
    }];
}

برای ذخیره کردن Recipe object در cloud ، ابتدا یک PFObject را با استفاده از کلاس “Recipe” ایجاد می کنیم. همان طور که در کد بالا (خط 3-8) مشاهده می شود ، PFObject بسیار شبیه به NSMutableDictionary ، عمل می کند.

برای تصویر دستور پخت ، به سادگی می توانیم آن را با تبدیل کردن به NSData و سپس با استفاده از  PFFile ذخیره نماییم. در اینجا ، ما تصویر اصلی را به داده JPEG ، با استفاده از UIImageJPEGRepresentation برمی گردانیم. هنگامی که PFObject را پیکربندی کردیم ، می توانیم متد “”saveInBackground: را برای ذخیره کردن دستور پخت فراخوانی نماییم. این متد ، به طور خودکار ، تصویر را آپلود کرده و دستور پخت را ذخیره می نماید. اما برای هندل کردن بهتر عملیات آپلود ، ما متد جایگزین دیگری ، به نام “”saveInBackgroundWithBlock: را فراخوانی می کنیم. می توانیم منطق اضافی دیگری را نیز برای اپ فراهم کنیم ، که بعد از انجام کامل عملیات ذخیره سازی ، اجرا خواهد شد. در اپ Demo ، می توانیم یک پیام هشدار را برای بیان تکمیل عملیات ذخیره سازی نمایش دهیم ، که در ادامه آن ، عملیات notify کردن view controller اصلی برای آپلود مجدد دستور پخت ها صورت می گیرد.

نمایش دادن Indicator آپلود با MBProgressHUB

اکنون ، شما باید درک بهتری را در مورد کار با اشیاء به دست آورده باشید. اما ، خطوط کدی  که بعد از PFObject می آید (یعنی خطوط 16-20) ، چه هستند؟ همان طور که می دانید ، آپلود کردن تصویر دستور پخت ، زمان بر است. همیشه بهتر است که به کاربر اجازه دهیم ، از نحوه پیشرفت کار اطلاع یابد. iOS SDK برای توسعه دهندگان ، ساختار UIActivityIndicatorView را فراهم می کند که از آن برای نمایش یک spinner استفاده می نمایند. اما در این جلسه ، اجازه دهید یک کلاس بسیار جذاب دیگر را که توسط Matej Bukovinski توسعه داده شده است ، به شما معرفی نماییم. MBProgressHUD یک کلاس منبع باز (open source) است که یک HUD شفاف با یک Indicator (نشانگر)  را نمایش می دهد. می توانید این کلاس را به طور رایگان از Github دانلود نمایید. اگر هنوز تعجب می کنید که این چه کلاسی است ، کافی است نگاهی به صفحات زیر ، بیاندازید:

کلاس MBProgressHUD کاربرد بسیار ساده ای دارد. فقط کافی است دو فایل  MBProgressHUD.h و  MBProgressHUD.m را به پروژه Xcode خود اضافه نماییم. برای راحتی شما ، ما هر دو کلاس را در تمپلت پروژه قرار داده ایم. فقط چند خط کد برای راه اندازی HUD نیاز هست. ابتدا فایل هدر MBProgressHUD را import می کنیم:

#import "MBProgressHUD.h"

سپس HUD را با view جاری ، مقداردهی اولیه می کنیم. کلاس MBProgressHUD انواع مختلف HUD را فراهم می کند. ما فقط از حالت MBProgressHUDModeInDeterminate استفاده می کنیم و برچسب را به “Uploading” تغییر می دهیم. خط آخر به UHO می گوید که به طور پیش فرض ، نمایش آن را مخفی نماید.

MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeInDeterminate;
hud.labelText = @"Uploading";
[hud show:YES]; 

برای مخفی کردن HUD ، می توانیم کد زیر را اضافه نماییم:

[hud hide:YES];

تست اپ

ما به شما ، نحوه استفاده از MBProgressHUD را ، در حالی که دستور پخت ها در Parse cloud ذخیره می شوند ، نشان دادیم. بسیار خوب! اکنون زمان اجرای اپ فرا رسیده است. سعی می کنیم یک دستور پخت جدید را اضافه کرده و آن را در Parse cloud ذخیره نماییم. در طول انجام عملیات آپلود دستور پخت ، ما باید HUD را مشاهده نماییم که نشان می دهد عملیات آپلود در حال اجرا است. پس از تکمیل کار ، table view از Recipe Book view controller عملیات refresh را انجام داده و دستور پخت جدید را از Parse دریافت کرده و لود می نماید.

 

حذف کردن دستور پخت از Parse Cloud

اگر آموزش UITableView را دنبال کرده باشید ، باید نحوه حذف یک سطر از table view را بدانید. به سادگی با پیاده سازی متد  “”commitEditingStyle: با کد زیر می توانیم این کار را انجام دهیم:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Remove the row from data model
    PFObject *object = [self.objects objectAtIndex:indexPath.row];
    [object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
        [self refreshTable:nil];
    }];
}

همانند ذخیره سازی داده در  Parse cloud ، شیء PFObject انواع متدها را برای delete کردن ، فراهم می نماید. متد “deleteInBackground:” به کاربر این امکان را می دهد که شیء را از cloud در backend حذف نماید. در اینجا ، ما از متد جایگزین به نام “deleteInBackgroundWithBlock:” استفاده می کنیم ، که اجازه می دهد یک callback را هنگامی که عملیات delete تکمیل شد ، اجرا کنیم.

 

خلاصه

از طریق این سری آموزشی ، امیدواریم ، توانسته باشید نحوه استفاده راحت از Parse SDK ، برای ذخیره کردن داده ها در cloud را یاد گرفته باشید. اگر فکر می کنید که وصل کردن اپ به cloud کار بسیار سخت و پیچیده ای است ، دوباره فکر کنید! Parse ، هم چنین ، همانند سایر ارائه دهندگان خدمات BaaS (مانند StackMob) ، فرآیند توسعه کل backend را ساده نموده است. امیدواریم که این سری آموزشی ، الهام بخش شما ، برای افزودن backend به اپ iOS تان باشد و به عنوان یک نقطه شروع برای شما ، در استفاده از سایر راه حل های BaaS عمل نماید. ما منتظر دیدن اپ cloud شما هستیم!

فصلِ: 3 , تعداد قسمت ها: 179 , سطح: صفر تا صد

این فصل در یک نگاه:

در این دوره آموزشی ، برنامه نویسی اندروید و ios را با استفاده از زامارین خواهیم آموخت. همچنین به صورت کاملا پروژه محور ، اپلیکیشن فروشگاهی مشابه دیجی کالا را پیاده سازی خواهیم کرد. در انته…
فصلِ: 5 , تعداد قسمت ها: 221 , سطح: صفر تا صد
موضوعات: آموزش IOS

این فصل در یک نگاه:

در این فصل:-نمایش notification به کاربر زمانی که اتفاقاتی از قبیل لایک کردن پست و ... میافتد(این مبحث مبحثی مهم بوده و ج…

  توضیحات کلی دوره: با سلام خوش اومدین به حرفه ای ترین دوره ی Swift ابتدای کار ممکن است سوالی برای شما پیش آید: -من نیازی به ساخت اپلیکیشن اجتماعی ندارم چرا باید این دوره را ببینم؟ ج…
فصلِ: 7 , تعداد قسمت ها: 159 , سطح: صفر تا صد
موضوعات: آموزش IOS

این فصل در یک نگاه:

اپل نویس حرفه ای شوید... با این پکیج کسب درآمد میلیونی کنید....   در این پکیج به نکات ریز و درشت زیادی پرداختیم که خیلی خیلی میتونه به شما کمک کنه که تبدیل به برنامه نویس حرفه ای شوید... …
فصلِ: 12 , تعداد قسمت ها: 126 , سطح: صفر تا صد
موضوعات: آموزش IOS

این فصل در یک نگاه:

فصل 16 فصل آخرمون هست و میایم توی این فصل یه سری مباحث کوچیکی که جامونده و کارهای نهایی برای انتشار برنامه توی مایکت های…

با سلام نکته:هیچ نیازی به mac و یا iphone نیست... نکته:هیچ نیازی به بلد بودن برنامه نویسی از قبل نیست... مباحثی که توی این دوره مرور میکنیم میتونه ما رو از سطح صفر برنامه نویسی ios به صد …
فصلِ: 6 , تعداد قسمت ها: 194 , سطح: صفر تا صد

این فصل در یک نگاه:

در این فصل آپدیت های مربوط به دوره را قرار میدهیم…

با سلام و خسته نباشد خدمت کلیک سایتی های عزیز در ادامه با توضیحات مختصری درمورد دوره ی react native با ما همراه باشید: React Native چیست؟ قطعا یکی از آرزوهای برنامه نویسان این میباشد که ب…
فصلِ: 5 , تعداد قسمت ها: 51 , سطح: صفر تا صد

این فصل در یک نگاه:

اموزش پرداخت درون برنامه ای بازار در یونیتی-اموزش خرید سکه در بازی-اموزش خرید مصرفی و غیر مصرفی…

توضیحات کلی مجموعه: آموزش مقدماتی تا پیشرفته یونیتی(ساخت بازی توپ چرخنده-اموزش بازی دوبعدی-اموزش بازی سه بعدی اول شخص شوتر-اموزش بازی سه بعدی رالی (ماشین سواری))   سرفصلهای دوره: نصب ی…
فصلِ: 2 , تعداد قسمت ها: 68 , سطح: صفر تا صد

این فصل در یک نگاه:

آموزش ساخت یک فروشگاه کوچک-آموزش کار با bottom navigation -آموزش کار با تب بار-آموزش کار با scrollview - آموزش کار با ان…

توضیحات کلی مجموعه: آموزش دوره فلاتر از پایه تا پیشرفته(این دوره به دوره متخصص فلاتر تغییر پیدا کرد) درسال های گذشته تعدا زیادی فریمورک معرفی شد که هرکدام تجربه خاص خودش رو داشت،اما فلاتر …
فصلِ: 5 , تعداد قسمت ها: 25 , سطح: صفر تا صد

این فصل در یک نگاه:

فصل پنجم…

توضیحات کلی مجموعه: درباره گیت: لذت پیش بردن پروژه های برنامه نویسی خود را با بهترین ورژن کنترل سیستم جهان تجربه کنید کامل ترین دوره ی آموزشی گیت و گیت هاب به همراه مثال های کاربردی گی…

تولید شده توسط کلیک سایت

پشتیبانی آنلاین
آماده پاسخگویی هستیم
انتخاب تصویر جهت ارسال:
در حال ضبط صدا

(جهت توقف و یا لغو ضبط از دکمه های زیر استفاده کنید)

توقف و ارسال :
لغو ضبط
در حال حاضر تمامی کارشناسان آفلاین هستند. همواره می توانید با شماره تلگرام / واتساپ 09010005000 به صورت آنلاین با ما در ارتباط باشید. جهت ورود به واتساپ کلیک کنید
0 پیام جدید
پشتیبان در حال تایپ ...
ارسال تصویر ضبط صدا
0 کارشناسان آنلاین می باشند
این گفت و گو توسط پشتیبان به اتمام رسید