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

جلسه پانزدهم آموزش iOS: آشنایی با Core Data - داده های پایدار


در این جلسه ، ما مقدمه ای کوتاه در مورد Core Data ، ارائه داده و با هم یک اپ ساده ، با استفاده از Core Data ، خواهیم ساخت.

در آموزش این جلسه ، در مورد پایداری (persistence) در آیفون (یا سایر دستگاه های تحت iOS) صحبت می کنیم. مفهوم پایداری به این معنا است که ، داده هایی که در اپ ما قرار دارند ، طی راه اندازی های مختلف اپلیکیشن ، در جای خود باقی بمانند. مفهوم پایداری ، به کاربران اجازه می دهد ، داده های پایدار را ذخیره کرده و آنها را بازیابی نمایند؛ به طوری که ، هر بار که از برنامه خود استفاده می نمایند ، مجبور نباشند ، اطلاعات خود را دوباره وارد نمایند. چندین روش ، برای ذخیره سازی داده ها در دستگاه های iOS ، وجود دارند، اما اکثر آنها ، برای ذخیره داده های پیچیده ، مناسب نیستند. این روش ها ، معمولاً برای ذخیره تنظیمات ، یا پیش بارگذاری (preload) برخی از داده ها ، مانند “Property List” و “Archiving Objects” ، مورد استفاده قرار می گیرند. به همین دلیل است که ، ما به سراغ Core Data خواهیم رفت، تا ببینیم ، به چه صورت می توانیم از آن برای مدیریت داده ها در پایگاه داده ، استفاده نماییم.

هدف از این آموزش ، فراهم آوردن مقدمه عملی ، جهت آشنایی با چارچوب Core Data ، می باشد. برای بهره مندی بیشتر از این آموزش ، انتظار داریم که آموزش های مربوط به Storyboard و UITableView را مطالعه کرده باشید.

Core Data یک پایگاه داده نیست!

زمانی که ما از داده پایدار ، صحبت می کنیم ، چیزی که به ذهن افراد می آید ، مفهوم پایگاه داده (database) می باشد. اگر با Oracle یا MySQL ، آشنایی داشته باشید ، می دانید که پایگاه داده رابطه ای (relational database) داده ها را در فرمی به شکل جدول ، با چندین سطر و ستون ، ذخیره می کند ، و معمولاً از طریق پرس و جوهای ( SQL Queries ) ، دسترسی به داده ها را تسهیل می نماید. نباید Core Data را با database اشتباه گرفت. اگر چه SQLite database ، به طور پیش فرض ، مخزن پایدار (persistent store) ، برای Core Data در آیفون می باشد، با این حال ، Core Data ، پایگاه داده رابطه ای نیست. در واقع ، Core Data ، یک چارچوب (framework) است که به توسعه دهنده گان اجازه می دهد ، داده ها را در پایگاه داده به شیوه شیء گرا ، ذخیره (یا بازیابی) نمایند. از طریق Core Data ، به سادگی می توانیم ، اشیاء درون اپ را به رکوردهای جدول در پایگاه داده ، نگاشت (map) کنیم ، بدون این که با SQL ، آشنایی داشته باشیم.

برای نشان دادن این مفهوم ، بیایید ساخت یک اپ را با استفاده از Core Data ، شروع کنیم. نام این اپ ، My Store ، است. این اپ ، بسیار ساده بوده و تمام دستگاه هایی را که آنها را به همراه نام ، ورژن و کارخانه سازنده ، جمع آوری کرده ایم ، ذخیره می کند.

 

ساخت یک اپ ساده با Core Data

ابتدا ، بیایید یک پروژه با Core Data ، ایجاد می کنیم. Xcode را باز کرده و یک پروژه جدید ، ایجاد می کنیم. تمپلت Empty Application را مطابق شکل زیر ، انتخاب می کنیم.

در صفحه بعد ، MyStore را به عنوان نام پروژه ، وارد می کنیم. iPhone را در بخش Device Family ، انتخاب می کنیم. باید اطمینان حاصل شود که گزینه های Use Story boards ، Use Core Data و Use Automatic Reference Counting ، به حالت انتخاب شده باشد. کلید Next و Create را فشار می دهیم.

Core Data Stack

قبل از شروع کار روی پروژه ، ابتدا باید Core Data Stack (پشته Core Data) را بشناسیم:

Managed Object Model – طرحی (schema) را که در اپ استفاده می کنیم ، توصیف می کند.  اگر با پایگاه داده آشنایی داشته باشید ، متوجه می شوید که این طرح مربوط به پایگاه داده می باشد. با این حال ، طرح ، به وسیله مجموعه ای از اشیاء (که به عنوان entity ها شناخته می شوند) ، نمایش داده می شوند. در Xcode در حقیقت Managed Object Model ، در یک فایل با پسوند . xcdatamodeld ، تعریف می شود. می توانیم از visual editor ، برای تعریف entity ها و ویژگی ها و همچنین روابط آنها ، استفاده کنیم.

Persistent Store Coordinator – SQLite مخزن پایدار پیش فرض در iOS ، می باشد. با این حال ، Core Data ، به توسعه دهندگان اجازه می دهد که مخزن های متعددی را که حاوی entity های مختلف می باشند ، راه اندازی کنند. Persistent Store Coordinator ، مسئول مدیریت مخزن های مختلف شیء پایدار است و اشیاء را در مخزن ها ، ذخیره می کند. هنگامی که از Core Data ، استفاده می کنیم ، به طور مستقیم با Persistent Store Coordinator ، ارتباط نداریم.

Managed Object Context – این بخش را ، به عنوان یک “scratch pad” در نظر بگیرید که حاوی اشیائی است که با داده های مخزن پایدار، ارتباط دارد. وظیفه آن ، مدیریت اشیاء ایجاد شده و برگردانده شده با استفاده از Core Data ، می باشد. در میان اجزای پشته ( Core Data Stack ) در اصل ، Managed Object Context ، یکی از مواردی است که ما در اغلب موارد با آن سر و کار داریم به طور کلی ، هر زمان که نیاز به واکشی و ذخیره اشیاء در مخزن پایدار داشته باشیم ، context ، اولین مؤلفه ای است که ما با آن ارتباط خواهیم داشت.

احتمالاً ، تصویر زیر می تواند دید بهتری در مورد Core Data Stack ، به شما بدهد:

تعریف Managed Object Model

بیایید به سراغ ساخت اپ برویم. اولین قدم ، باز کردن Data Model به نام MyStore.xcdatamodeld ، و تعریف object model ، می باشد. در اینجا ، ما یک entity برای Device ، تعریف خواهیم کرد که از آن برای ذخیره اطلاعات دستگاه در پایگاه داده ، استفاده خواهیم کرد. برای ایجاد یک entity ، روی کلید “+” در در پایین و گوشه سمت چپ editor view ، کلیک کرده و نام entity را Device ، قرار می دهیم.

هنگامی که یک entity جدید ایجاد کردیم ، نیاز داریم که صفت ها را به آن ، اضافه کنیم. روی کلید “+” ، در بخش attributes ، کلیک می کنیم ، تا این کار را انجام دهیم. سه صفت به نام های name ، version  و company ، اضافه می کنیم. نوع آنها را به رشته (string) ، تنظیم می نماییم.

طراحی نمودن رابط کاربری

کار بعدی که نیاز داریم انجام دهیم ، ایجاد Storyboard است ، که viewهای اپ ما را ، تعریف می کند. به منوی File > New > New File رفته و Storyboard را در تمپلت User Interface ، انتخاب می کنیم. روی کلید Next کلیک کرده و برای گزینه device family ، مقدار iPhone را انتخاب کرده ، سپس روی کلید create ، کلیک می کنیم.

پس از این کار، باید اطمینان یابیم که “Storyboard” را که تازه ایجاد کرده ایم ، به عنوان Storyboard اصلی در تنظیمات پروژه ، قرار گرفته است.

همچنین فراموش نکنیم که تمام کدهای تولید شده در متد

(BOOL)application:application didFinishLaunchingWithOptions:launchOptions

، در داخل فایل AppDelegate ، را حذف کنیم. این متد باید مانند حالت زیر ، ساده باشد:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return YES;
}

به Storyboard رفته و رابط کاربری را مطابق تصویر زیر می سازیم:

ابتدا ، یک Table View Controller  ، درگ کرده و آن را در Navigation Controller جاسازی می کنیم. یک کلید (button) به بخش بالا و سمت راست از navigation bar ، درگ می کنیم و شناسه (identifier) آن را “Add” ، قرار می دهیم. این کار باعث خواهد شد که کلید به طور خودکار به کلید “+” ، تغییر می یابد. سپس ، prototype cell را انتخاب کرده و سبک (style) آن را به “Right Detail” تغییر می دهیم.

یک View Controller را به Storyboard ، درگ کرده و یک Navigation Bar ، به بالای صفحه ، اضافه می کنیم. سپس ، دو کلید را به داخل navigation bar ، درگ می کنیم. یکی از کلیدها را “Cancel” و دیگری را “Save”  ، نامگذاری می کنیم. در content view ، سه فیلد متنی ، اضافه می کنیم و صفت های حفره (placeholder) را “Name” ، “Version” و “Company” ، نامگذاری می کنیم.

زمانی که ، کاربر روی کلید “+” ، در table view controller ، ضربه بزند ، detail view ، نشان داده خواهد شد. در نهایت ، کلید کنترل را فشار داده و نگه می داریم ، سپس روی کلید “+” ، کلیک کرده و آن را به داخل detail view controller ، درگ می کنیم. “Model” را به عنوان Segue action ، انتخاب می کنیم؛ تا table view controller و detail view controller ارتباط پیدا کنند.

ایجاد View Controller Class

یک کلاس جدید با کلیک راست کردن در پوشه MyStore > New File > Objective-C class ، ایجاد می کنیم و نام کلاس را DeviceViewController ، قرار می دهیم. این کلاس را به عنوان یک زیر کلاس از UITableViewController قرار می دهیم. به Storyboard رفته و Table View Controller را انتخاب می کنیم و آن را با کلاس  DeviceViewController ، مرتبط می کنیم.

هنگامی که کار انجام شد ، مراحل مشابهی را برای ایجاد یک کلاس جدید به نام DeviceDetailViewControllerUIViewController ، تکرار می کنیم. دوباره ، به Storyboard رفته و کلاس سفارشی detail view controller را به عنوان “DeviceDetailViewController” ، قرار می دهیم.

در نهایت ، UITextFields را به فایل header ، DeviceDetailViewController متصل می کنیم و دو متد action ، را برای کلیدهای save و cancel ، به ترتیب ، ایجاد می کنیم.

کدها باید شبیه مورد زیر باشند:

@property (weak, nonatomic) IBOutlet UITextField *nameTextField;
@property (weak, nonatomic) IBOutlet UITextField *versionTextField;
@property (weak, nonatomic) IBOutlet UITextField *companyTextField;
 
- (IBAction)cancel:(id)sender;
- (IBAction)save:(id)sender;

غواصی در داخل Core Data

پس از ساخت رابط کاربری ، زمان آن است که وارد جزئیات Core Data شویم. ظاهراً ، دو کار داریم که باید آنها را پیاده سازی کنیم:

  1. ذخیره سازی اطلاعات در Detail View Controller
  2. واکشی اطلاعات دستگاه ها از داخل مخزن پایدار (یعنی پایگاه داده SQLite) و پر کردن داده ها در Table View Controller.

پیاده سازی های فوق را مرحله به مرحله ، انجام خواهیم داد.

ذخیره سازی اطلاعات دستگاه

ابتدا ، نیاز داریم که DeviceDetailViewController را پیاده سازی کنیم؛ تا به کاربر اجازه دهیم ، اطلاعات دستگاه را در پایگاه داده ، اضافه نماید. فایل DeviceDetailViewController.m را باز کرده و کد زیر را بعد از @implementation DeviceDetailViewController ، اضافه می کنیم.

- (NSManagedObjectContext *)managedObjectContext {
    NSManagedObjectContext *context = nil;
    id delegate = [[UIApplication sharedApplication] delegate];
    if ([delegate performSelector:@selector(managedObjectContext)]) {
        context = [delegate managedObjectContext];
    }
    return context;
}

به خاطر دارید که در هنگام ایچاد پروژه ، ما گزینه Data Core را انتخاب نموده ایم. با این کار ، Xcode به طور خودکار ، یک managed object context ، در AppDelegate ، تعریف می کند. این متد ، به ما اجازه می دهد ، managed object context را از AppDelegate ، بازیابی کنیم. بعداً ، ما از context ، برای ذخیره سازی داده های دستگاه ، استفاده خواهیم کرد.

سپس ، بخش های “save” و “cancel” را پیاده سازی خواهیم کرد. کد الزامی زیر را ، اضافه می کنیم:

	- (IBAction)cancel:(id)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}
 
- (IBAction)save:(id)sender {
    NSManagedObjectContext *context = [self managedObjectContext];
    
    // Create a new managed object
    NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:@"Device" inManagedObjectContext:context];
    [newDevice setValue:self.nameTextField.text forKey:@"name"];
    [newDevice setValue:self.versionTextField.text forKey:@"version"];
    [newDevice setValue:self.companyTextField.text forKey:@"company"];
    
    NSError *error = nil;
    // Save the object to persistent store
    if (![context save:&error]) {
        NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
    }
    
    [self dismissViewControllerAnimated:YES completion:nil];
}

هنگامی که کاربر کلید “Cancel” را ضربه می زند ، ما انتظار داریم که اپ ، detail view controller را ببندد. در خط 2 از کد بالا ، متد dismissViewControllerAnimated ، برای بستن current view controller با انیمیشن ، اجرا می شود.

برای متد “save” ، ابتدا ، managed object context را در اختیار می گیریم. هر شیء که Core Data ، ذخیره می کند ، از شیء NSManagedObject ، ارث بری می نماید. بنابراین ، ما ابتدا ، یک نمونه از شیء NSManagedObject را برای entity ، “Device” که در object model تعریف کرده ایم ، ایجاد می کنیم. کلاس NSEntityDescription ، یک متد به نام “insertNewObjectForEntityForName”را  برای توسعه دهنده ، ارائه می دهد ، تا بتواند یک شیء managed object را ایجاد نماید. هنگامی که managed object (یعنی newDevice) ایجاد شد ، می توانیم صفت ها (names ، version ، company) را با استفاده از ورودی کاربر ، تنظیم نماییم. در آخر، متد “save” از context را فراخوانی می کنیم تا شیء در داخل پایگاه داده ، ذخیره شود.

اکنون ، می توانیم کلید Run را کلیک نموده و اپ خود را اجرا نماییم. با ضربه زدن بر روی کلید “+” ، Detail View باز شده و یک دستگاه جدید ، ذخیره می کنیم. دستگاه جدید هنوز در جدول ، نمایش داده نمی شود. بیایید ببینیم به چه نحوه می توانیم اطلاعات دستگاه را از پایگاه داده ، واکشی نماییم.

 

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

فایل DeviceViewController.m ، را باز می کنیم ، خصیصه “devices” را اضافه می کنیم تا بتوانیم اطلاعات تمام دستگاه های دریافتی را ذخیره نماییم.

@interface DeviceViewController ()
@property (strong) NSMutableArray *devices;
 
@end

دوباره ، کد زیر را بعد از “@implementation DeviceViewController” ، اضافه می کنیم تا بتوانیم  managed object context را در اختیار بگیریم.

- (NSManagedObjectContext *)managedObjectContext
{
    NSManagedObjectContext *context = nil;
    id delegate = [[UIApplication sharedApplication] delegate];
    if ([delegate performSelector:@selector(managedObjectContext)]) {
        context = [delegate managedObjectContext];
    }
    return context;
}

سپس ، متد viewDidAppear را اضافه می کنیم.

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    // Fetch the devices from persistent data store
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Device"];
    self.devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
    
    [self.tableView reloadData];
}

مشابه کاری که در Detail View Controller ، انحام دادیم ، ابتدا managed object context را در اختیار می گیریم. برای واکشی اطلاعات دستگاه از پایگاه داده ، کد بالا ، یک نمونه (instance) جدید از NSFetchRequest را ایجاد می کند و entity ، Device را تنظیم می نماید. سپس ، متد “executeFetchRequest” را برای بازیابی تمام اطلاعات دستگاه ها از پایگاه داده ، فراخوانی می کند. اگر با پایگاه داده های رابطه ای آشنا باشید، نمونه موردنظر مانند دستور SELECT ، عمل می کند.

نکته: متد viewDidAppear ، متدی است که به طور خودکار ، در هر بار که یک صفحه view ، نمایش داده می شود ، فراخوانی می شود. این متد ، برخلاف متد viewDidLoad ، که در هنگام بارگذاری کنترلر ، فراخوانی می شود ، عمل می نماید.

 

پر کردن اطلاعات دستگاه در Table View

از آن جایی که تمایل داریم ، داده ها را در table view ، نمایش دهیم ، نیاز هست که data source را برای این داده ها ، پیاده سازی کنیم. برای انجام این کار ، کد زیر را اضافه می کنیم:



	- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return self.devices.count;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    // Configure the cell...
    NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
    [cell.textLabel setText:[NSString stringWithFormat:@"%@ %@", [device valueForKey:@"name"], [device valueForKey:@"version"]]];
    [cell.detailTextLabel setText:[device valueForKey:@"company"]];
    
    return cell;
}

اگر قبلاً از UITableViewController ، استفاده کرده باشید ، می دانید که کد بالا ، یک روش ساده ، برای نمایش دادن داده در داخل table view ، می باشد. اگر کد بالا را بررسی کنید ، متوجه خواهید شد که NSMangedObject ، تقریباً شبیه NSDictionary ، می باشد. وظیفه آن ، جمع آوری تمام صفت های entity (یعنی Device) می باشد و به سادگی می توانیم از متد “valueForKey” ، برای مقدار دهی صفت ، استفاده نماییم.

سعی می کنیم ، اپ را اجرا کرده و آن را تست نماییم. اگر همه چیز ، درست باشد ، اپ باید شبیه تصویر زیر عمل کند. سعی می کنیم یک دستگاه جدید ، اضافه کنیم. اطلاعات دستگاه باید به طور خودکار در table view ، پر شود.

 

موضوع جلسه بعد چیست؟

این جلسه ، آموزش طولانی بود ، اما ، سعی کردیم به دقت ، مراحل پیاده سازی را شرح دهیم ؛ به طوری که هر کسی ، می تواند نحوه انجام مراحل را پیگیری نماید. همان طور که می بینید ، از طریق استفاده از Core Data ، مجبور نیستید دیگر نگران SQL ، برای ذخیره سازی داده های پایگاه داده ، باشید. همه چیز در پشت صحنه ، انجام می شود. این آموزش ، اولین سری ، از آموزش های Core Data ، می باشد. در جلسات بعد ، ما بیشتر درباره ارتباط اشیاء ، صحبت خواهیم کرد.

فصلِ: 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 کارشناسان آنلاین می باشند
این گفت و گو توسط پشتیبان به اتمام رسید