<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های محمد صادق پنادگو</title>
        <link>https://virgool.io/feed/@mspanadgoo</link>
        <description>برنامه نویس iOS</description>
        <language>fa</language>
        <pubDate>2026-06-07 03:13:14</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/367544/avatar/QaMsMk.png?height=120&amp;width=120</url>
            <title>محمد صادق پنادگو</title>
            <link>https://virgool.io/@mspanadgoo</link>
        </image>

                    <item>
                <title>۵ اصلSOLID در زبان برنامه‌نویسی Swift</title>
                <link>https://virgool.io/@mspanadgoo/5-solid-principles-in-swift-iav1tan1fqsf</link>
                <description>اصول SOLID، پنج اصل در برنامه نویسی شئ‌گرا را بیان می‌کند:Single Responsibility Principle (اصل تک وظیفه‌ای)Open/Closed Principle (اصل باز/بسته)Liskov Substitution Principle (اصل جانشینی لیسکُو)Interface Segregation (تفکیک رابط)Dependency Inversion (وارونگی وابستگی)۱- اصل تک وظیفه‌ای (Single Responsibility Principle)این اصل بیان می‌کند که هر ماژول باید یک وظیفه و دلیل مشخص برای تغییر داشته‌باشد. اصل تک وظیفه‌ای با یک زیربنای کوچک و یک مورد مشخص مانند کلاس و یا یک آبجکتی که تنها یک هدف و کارائي دارد، شروع می‌شود. این اصل به شما کمک می‌کند که کلاس‌هایتان را تا حد امکان تمیز نگاه دارید.یک مثال: class Handler { 
    func handle() {        
        let data = requestDataToAPI()        
        let array = parse(data: data)        
        saveToDatabase(array: array)    
    }   
    private func requestDataToAPI() -&gt; Data {        
        // Network request and wait the response    
    } 
    private func parseResponse(data: Data) -&gt; [String] {        
        // Parse the network response into array  
    }  
    private func saveToDatabase(array: [String]) {        
        // Save parsed response into database   
    }
}
در مثال بالا کلاس Handler وظایف مختلفی مانند برقراری یک فراخوانی شبکه‌ای، پارس کردن جواب و ذخیره‌سازی در دیتابیس را دارد.شما می‌توانید این مشکل را با سپردن مسئولیت‌ها به کلاس‌های کوچک برطرف نمائید.class Handler { 
    let apiHandler: APIHandler 
    let parseHandler: ParseHandler 
    let databaseHandler: DBHandler     

init(apiHandler: APIHandler, parseHandler: ParseHandler, dbHandler: DBHandler) { 
        self.apiHandler = apiHandler 
        self.parseHandler = parseHandler 
        self.dbHandler = dbHandler 
    }     func handle() { 
        let data = apiHandler.requestDataToAPI() 
        let array = parseHandler.parse(data: data)
        databaseHandler.saveToDatabase(array)
    } 
}
class NetworkHandler { 
    func requestDataToAPI() -&gt; Data { 
        // Network request and wait the response 
    } 
}
class ResponseHandler { 
    func parseResponse(data: Data) -&gt; [String] { 
        // Parse the network response into array 
    } 
}
class DatabaseHandler { 
    func saveToDatabase(array: [String]) { 
        // Save parsed response into database
    } 
}۲- اصل باز/بسته (Open/Closed Principle)اگر بخواهیم این اصل را به صورت ساده بیان کنیم، به این معناست که برای گسترش باز است اما برای تغییرات بسته‌ است.باز برای گسترش: شما باید بتوانید به راحتی یک کلاس را گسترش داده یا رفتارهایش را تغییر دهید.بسته برای تغییرات: شما باید یک کلاس را بدون تغییری در پیاده‌سازی آن گسترش دهید.یک کلاس به نام Logger داریم که آرایه Car‌ها را تکرار می‌کند و جزئیات هر Car را پرینت می‌کند.class Car {
    let name: String
    let color: String    init(name: String, color: String) {
        self.name = name
        self.color = color
    }   func printDetails() -&gt; String {
        return &amp;quotI have \(self.color) color \(self.name).&amp;quot
    }
}
class Logger {
    func printData() {
        let cars = [ Car(name: &amp;quotBMW&amp;quot, color: &amp;quotRed&amp;quot),
                     Car(name: &amp;quotAudi&amp;quot, color: &amp;quotBlack&amp;quot)]         cars.forEach { car in
             print(car.printDetails())
         }
     }
}اگر بخواهیم که امکان  پرینت جزئیات را به یک کلاس جدید نیز اضافه کنیم، هربار که بخواهیم کلاس جدیدی اضافه کنیم مجبور به تغییر پیاده‌سازی تابع PrintData هستیم که این کار موجب شکستن اصل باز/بسته می‌شود.class Bike {
    let name: String
    let color: String    init(name: String, color: String) {
        self.name = name
        self.color = color
    }    func printDetails() -&gt; String {
        return &amp;quotI have \(self.name) bike of color \(self.color).&amp;quot
    }
}
class Logger {
    func printData() {
        let cars = [ Car(name: &amp;quotBMW&amp;quot, color: &amp;quotRed&amp;quot),
                     Car(name: &amp;quotAudi&amp;quot, color: &amp;quotBlack&amp;quot)]         cars.forEach { car in
             print(car.printDetails())
         }        let bikes = [ Bike(name: &amp;quotHomda CBR&amp;quot, color: &amp;quotBlack&amp;quot),
                      Bike(name: &amp;quotTriumph&amp;quot, color: &amp;quotWhite&amp;quot)]        bikes.forEach { bike in
             print(bike.printDetails())
         }
     }
}ما می‌توانیم این مشکل را با ساخت یک پروتوکل به نام Printable که توسط کلاس‌ها پیاده‌سازی می‌شود، حل کنیم. در آخر تابع printData یک آرایه‌ای از Printableها را پرینت خواهد کرد.در این مسیر ما یک لایه انتزاعی بین printData و کلاس می‌سازیم، که به ما اجازه می‌دهد که کلاس‌های دیگر مانند Bike را بدون تغییری در ساختار printData پرینت کنیم.protocol Printable {
    func printDetails() -&gt; String
}
class Car: Printable { 
    let name: String 
    let color: String    init(name: String, color: String) {
        self.name = name
        self.color = color
    }    
func printDetails() -&gt; String {
        return &amp;quotI have \(self.color) color \(self.name).&amp;quot
    }
}

class Bike: Printable {
    let name: String
    let color: String
    init(name: String, color: String) {
        self.name = name
        self.color = color
    }   

 func printDetails() -&gt; String {
        return &amp;quotI have \(self.name) bike of color \(self.color).&amp;quot
    }
}

class Logger {
    func printData() {
        let vehicles: [Printable] = [Car(name: &amp;quotBMW&amp;quot, color: &amp;quotRed&amp;quot),
                                  Car(name: &amp;quotAudi&amp;quot, color: &amp;quotBlack&amp;quot),
                            Bike(name: &amp;quotHonda CBR&amp;quot, color: &amp;quotBlack&amp;quot),
                              Bike(name: &amp;quotTriumph&amp;quot, color: &amp;quotWhite&amp;quot)]        vehicles.forEach { vehicle in
            print(vehicle.printDetails())
        }
    }اصل جانشینی لیسکُو (Liskov Substitution Principle)توابعی که از نشان‌گرها و رفرنس‌ها به کلاس‌های پایه استفاده می‌‌کنند، باید قادر باشند آبجکت‌های کلاس‌های مشتق شده را بدون داشتن اطلاعاتی از آن استفاده کنند.این اصل به شما کمک می‌کند از ارث‌بری بدون بهم ریختن آن استفاده کنید.let requestKey: String = &amp;quotNSURLRequestKey&amp;quot

// NSError subclass provide additional functionality but don&#039;t mess with original class.
class RequestError: NSError {
    var request: NSURLRequest? {
        return self.userInfo[requestKey] as? NSURLRequest
    }
}

// I forcefully fail to fetch data and will return RequestError.
func fetchData(request: NSURLRequest) -&gt; (data: NSData?, error: RequestError?) {
    let userInfo: [String:Any] = [requestKey : request]
    return (nil, RequestError(domain:&amp;quotDOMAIN&amp;quot, code:0, userInfo: userInfo))
}

func willReturnObjectOrError() -&gt; (object: AnyObject?, error: NSError?) {

    let request = NSURLRequest()
    let result = fetchData(request: request)

    return (result.data, result.error)
}

let result = willReturnObjectOrError()

//RequestError
if let requestError = result.error as? RequestError {
    requestError.request
}تفکیک رابط (Interface Segregation) این اصل بیان می‌کند که کلاینت‌ها نباید مجبور به پیاده‌سازی ارتباطاتی شوند که استفاده نمی‌کنند. به جای یک رابط چاق بهتر است که از چند رابط کوچک بسته به گروه‌های متدها استفاده کرد، هر کدام به یک زیرماژول خدمت رسانی می‌کند.//We start with the protocol GestureProtocol with a method 
didTap: protocol GestureProtocol {    
    func didTap()
} 

//After some time, you have to add more gestures to the protocol
protocol GestureProtocol {    
    func didTap()    
    func didDoubleTap()
اگر SuperButton تمام متد‌های مورد نیازش را پیاده‌سازی کند.class SuperButton: GestureProtocol {     
func didTap() { 
        // Single tap operation 
    }    
func didDoubleTap() {
        // double tap operation
    }    
func didLongPress() {
        // long press operation
    }
}

//But if implement Double Tab Button it implement all the action 
class DoubleTapButton: GestureProtocol { 
    func didTap() { 
        // Single tap operation 
    }   
 func didDoubleTap() {
        // double tap operation
    }    
func didLongPress() {
        // long press operation
    }
}کلاس DoubleTapButton اکشن‌های didTap و didLongPress را نیاز ندارد. در اینجا قاعده تفکیک روابط رعایت نشده است. ما می‌توانی این مشکل را با استفاده از پروتکل‌های کوچک به جای پروتکل‌های بزرگ برطرف نمائیم.protocol TapProtocol { 
    func didTap()
}
protocol DoubleTapProtocol {
    func didDoubleTap()
}
protocol LongPressProtocol {
    func didLongPress()
}

class SuperButton: TapProtocol, DoubleTapProtocol, LongPressProtocol {
func didTap() { 
        // Single tap operation 
    }
func didDoubleTap() {
        // double tap operation
    }
func didLongPress() {
        // long press operation
    }
}

class DoubleTapButton: DoubleTapProtocol { 
    func didDoubleTap() {
        // double tap operation
    }
}وارونگی وابستگی (Dependency Inversion)ماژول‌‌های سطح بالا نباید به ماژول‌‌های سطح پایین وابسته باشند، همچنین هردو نباید به Abstractionها وابسته باشند. (Abstraction‌ها نباید به جزئیات و جزئیات نباید به Abstractionها وابستگی داشته باشند.)class FileSystemManager {
    func save(string: String) {
        // Open a file
        // Save the string in this file
        // Close the file
   }
}
class Handler {
    let fileManager = FilesystemManager()
    func handle(string: String) {
        fileManager.save(string: string)
    }
}ماژول FileSystemManager یک ماژول سطح پائین است و استفاده دوباره از آن در دیگر پروژه‌ها ساده است. مشکل اینجاست که ماژول سطح بالای Handler  قابل استفاده دوباره نیست چرا که با FileSystemManager جفت شده است. ما باید بتوانیم از ماژول‌های سطح بالا با انواع مختلف حافظه ذخیره‌سازی دوباره استفاده کنیم مانند دیتابیس، cloud و غیره.ما می‌توانیم با استفاده از پروتکل Storage این وابستگی را برطرف کنیم. در این راه Handler می‌تواند از این پروتکل abstract بدون نیاز به حمل نوع حافظه استفاده کند. با این رویکرد ما می‌توانیم به راحتی از یک filesystem به یک دیتابیس تغییر وضعیت دهیم.protocol Storage {
    func save(string: String)
}
class FileSystemManager: Storage {
    func save(string: String) {
        // Open a file in read-mode
        // Save the string in this file
        // Close the file
    }
}
class DatabaseManager: Storage {
    func save(string: String) {
        // Connect to the database
        // Execute the query to save the string in a table
        // Close the connection
    }
}
class Handler {
    let storage: Storage    // Storage types
    init(storage: Storage) {
        self.storage = storage
    }
    
    func handle(string: String) {
        storage.save(string: string)
    }
}اصل وارونگی وابستگی به اصل باز/بسته از نظر راه حل، هدف داشتن یک معماری تمیز و عدم جفت شدگی وابستگی‌ها، بسیار شباهت دارد.مقاله اصلی: https://medium.com/@nishant.kumbhare4/solid-principles-in-swift-73b505d3c63fمنابع مقاله اصلی: https://dzone.com/articles/solid-principles-applied-to-swift-1https://medium.com/@piyush.dez/solid-principles-in-swift-e9cc84ff5aa2</description>
                <category>محمد صادق پنادگو</category>
                <author>محمد صادق پنادگو</author>
                <pubDate>Mon, 28 Jun 2021 11:57:45 +0430</pubDate>
            </item>
                    <item>
                <title>همزمانی و Multithreading در iOS</title>
                <link>https://virgool.io/CodeLovers/%D9%87%D9%85%D8%B2%D9%85%D8%A7%D9%86%DB%8C-%D9%88-multithreading-%D8%AF%D8%B1-ios-dgxr0ulzxscs</link>
                <description>همزمانی و Multithreading، یک بخش اساسی در توسعه‌ iOS هستند. در این پست می‌خواهیم مستقیم به سراغ دلیل اهمیت این مبحث رفته و دریابیم که چگونه می‌توان از آن در کد‌ اپلیکیشن‌های cocoa touch خود استفاده کنیم.همزمانی به معنای رخ‌داد اتفاقات مختلف در زمانی یکسان است. این مقصود همچنین با استفاده از time-slicing یا قطعه‌بندی زمان یا حالت موازی (parallel) اگر CPUhd با چند هسته در اختیار داشته باشیم، دست یافتنی است. همه‌ی ما تجربه‌ای از نبود همزمانی داشته‌ایم، معمولاً این اتفاق زمانی می‌افتد که یک تسک سنگین موجب فریز شدن اپلیکیشن شود. البته این نکته قابل ذکر است که فریز شدن UI معمولاً با نبود همزمانی رخ نمی‌دهد، ممکن است این اتفاق به دلیل باگ‌‌های نرم‌افزاری نیز رخ دهد، اما اگر نرم‌افزاری از همه توان محاسباتی در دسترسش استفاده نکند ممکن است هنگام نیاز به انجام کار سنگین فریز شود. اگر شما همچین تجربه‌ای داشته باشید ممکن است گزارشی مانند تصویر زیر دریافت کرده‌باشید: هرآنچه که به I/O فایل، پردازش داده و یا ارتباط شبکه‌ای نیاز داشته باشد، حکم یک بکگراند تسک را دارد(مگر این که شما دلیل قانع کننده‌ای برای متوقف کردن کل برنامه داشته‌باشید). دلایل زیادی برای این‌که این تسک‌ها باید کاربر را از تعامل با بقیه برنامه‌تان محروم کنند، وجود ندارد. ببینید که  اپلیکیشن شما چقدر تجربه ‌کاربری بهتری خواهد داشت اگر همچین گزارشی از آنالیزور اپلیکیشن خود دریافت کنید:آنالیز کردن یک عکس، پردازش یک داکیومنت و یا یک فایل صوتی، یا نوشتن حجم داده‌ی قابل توجه روی دیسک، مثال‌هایی هستند که می‌توان برای انجام آن از مزیت threadهای بکگراند استفاده کرد. بیاید برویم سراغ اینکه ما چگونه می‌توانیم همچین رفتاری را در اپلیکیشن ‌iOS خود پیاده‌سازی کنیم.یک تاریخچه مختصردر زمان‌های قدیم، ماکزیمم مقدار کار به ازای هر سیکل پردازنده که یک کامپیوتر می‌توانست انجام دهد را با استفاده از سرعت ساعت اندازه گیری می‌کردند. هرچه طراحی‌های پردازنده‌ها کوچک‌تر شد، گرما و ابعاد فیزیکی یک فاکتور محدود کننده برای سرعت ساعت به حساب آمدند. در نتیجه، تولید کنندگان چیپ شروع به اضافه کردن هسته‌های پردازنده دیگری برروی چیپ‌هایشان کردند تا کارائی کلی را افزایش دهند. با افزایش تعداد هسته‌ها یک چیپ تنها بدون اینکه نیاز به افزایش سرعت، اندازه و یا خروجی گرما، قادر به اجرای دستورات CPU بیشتری در هر سیکل شد. اما یک مشکل وجود داشت...چگونه می‌توانیم از این هسته‌های اضافه بهره ببریم؟ پاسخ درست Multithreading است.مفهوم Multhithreading پیاده سازی شده. تا سیستم‌عامل بتواند استفاده و ساخت تعداد نامحدود thread را مدیریت کند. هدف اصلی آن شبیه‌سازی اجرای یک یا چند قسمت از برنامه‌است تا بتواند از تمام زمان دردسترس CPU بهره ببرد. Multhithreading یک تکنیک قدرتمند در جعبه ابزار برنامه‌نویس است که البته مسائل و مشکلات مختص خود را نیز دارد. یک تصور غلط این است که Multhithreading نیاز به یک پرازنده چند هسته دارد، اما این صحیح نیست و پرازنده‌های تک هسته نیز به خوبی قابلیت کار روی threadهای متعدد را دارند، اما ما یک نیم‌نگاهی خواهیم داشت به این که چرا threading در اولین قدم یک مشکل محسوب می‌شود. قبل از شروع به تفاوت‌های ظریفی که بین همزمانی و موازی‌کاری وجود دارد در تصویر زیر نگاهی بیاندازید:در اولین وضعیت نمایش داده شده در شکل بالا، ما میبینک که تسک‌های می‌توانند به صورت همزمان اما نه به صورت موازی اجرا شوند. این حالت بسیار شبیه وضعیت چت کردن در یک چت روم با چند نفر مختلف است، که شما هربار مجبور می‌شوید موضوع بحث را عوض کنید اما عملاً نمی‌توانید با دو نفر در یک زمان صحبت کنید. این چیزی است که ما به آن همزمانی می‌گوییم. شما را فریب می‌دهد که کار‌های مختلف دارد در یک زمان اتفاق می‌افتد اما حقیقت به این صورت است که خیلی سریع تعویض می‌شوند. همزمانی به معنای سر و کله زدن با تسک‌های زیاد در یک زمان است. کاملا با مدل موازی کاری در تضاد است به غیر ازین که هر دو تسک به صورت همزمان انجام می‌شوند. هر دو مدل اجرایی نمایانگر Multithreading هستند که درگیری threadهای مختلف را برای رسیدن به یک هدف مشترک نشان می‌دهند. Multithreading یک تکنیک تعمیم یافته به منظور استفاده از ترکیب همزمانی و موازی کاری در اپلیکیشن شما است.بار Threadهایک سیستم‌عامل مدرن مانند iOS صدها برنامه یا پردازش را در لحظه انجام می‌دهد. هرچند بیشتر این برنامه‌ها پرداز‌ش‌های سیستمی یا بکگراندی هستن که نیاز به حافظه کمی دارند، پس چیزی که واقعا نیاز  است راهیست که اپلیکیشن‌های مجزا بتوانند از هسته‌های اضافی موجود استفاده کنند. یک برنامه یا پردازش می‌تواند threadهای متفاوت داشته باشد که روی حافظه مشترک اجرا می‌شوند. هدف ما این است که بتوانیم این threadها را کنترل کرده و از آن‌ها به نفع خودمان استفاده کنیم.به منظور اضافه کردن همزمانی به یک اپلیکیشن نیازمند ساخت یک یا چند thread هستیم. Threadها ساختاری سطح پایین دارند که نیازمند مدیریت دستی هستند. با یک نگاه گذرا به راهنمای برنامه‌نویسی به کمک threadهای اپل به سادگی خواهید فهمید که کدنویسی با استفاده از threadهای چه میزان پیچیدگی به کد شما اضافه خواهد کرد. علاوه بر آن برای ساخت اپلیکیشن یک توسعه‌دهنده باید موارد زیر را نیز انجام دهد:هر thread جدید باید با پذیرش مسئولیت آن ساخته شود و هرمان که وضعیت سیستم تغییر کرد شماره آن به صورت داینامیک تنظیم شود.با دقت مدیریت شوند، هر زمان که اجرای آن‌ها به پایان رسید باید از حافظه آزاد شوند.از مکانیزم‌های همگام‌سازی مانند mutex، قفل و یا سمافور برای هماهنگ سازی منابع قابل دسترس بین threadهای مختلف استفاده شود که خود باعث اضافه شدن بار اضافی رو کد برنامه خواهد شد.برای کاهش ریسک در کدنویسی یک اپلیکیشن به نظر میرسد بیشترین هزینه مربوط به ساخت و نگهداری threadهایی است که استفاده می‌کند و نه سیستم‌عامل میزبان.باعث تاسف است که این مفهوم بدون اینکه تظمینی برای افزایش کارائی ارائه دهد باعث اضافه شدن سطوح مختلف پیچیدگی و ریسک می‌شود.مفهوم Grand Central Dispatch سیستم عامل iOS از رویکرد نامتقارن برای حل مسائل همزمانی در مدیریت threadها استفاده می‌کند. توابع نامتقارن در بیشتر محیط‌های برنامه نویسی مرسوم هستند و اغلب برای ساخت تسک‌هایی که ممکن است کمی زمان‌بر باشند استفاده می‌شود، مانند خواندن یک فایل از دیسک و یا دانلود یک فایل از وب. زمانی که این اتفاق رخ دهد یک تابع نامتقارن یک سری فعالیت در پشت صحنه برای راه اندازی تسک بکگراند انجام می‌دهد اما خیلی سریع بازمی‌گردد و کاری به این که تسک اصلی به چه مقدار زمان نیاز دارد تا کامل شود، ندارد. یک تکنولوژی بنیادی که iOS برای راه‌اندازی تسک‌های نامتقارن معرفی کرده Grand Central Dispatch یا GCD نام دارد. GCD کد مدیریت thread را خلاصه کرده و آن را به سطح پایین سیستم برده است و یک رابط کاربری سبک برای تعریف و اجرای تسک‌ها روی یک صف اعزام مناسب دارد. همچنین GCD از تمامی زمانبندی و مدیریت threadها پشتیبانی می‌کند و یک رویکرد جامع برای اجرا و مدیریت تسک دارد و همچنین بازدهی بهتری نسبت به threadهای سنتی دارد.بیایید نگاهی به اجزای GCD بیاندازیم:در این تصویر چه می‌بینیم؟ بیاید از سمت چپ شروع کنیم:یک- DispatchQueue.main: این thread اصلی یا همان UI thread است که پشتوانه آن تنها یک صف سریالی است. تمام تسک‌ها به صورت جانشینی اجرا می‌شوند پس می‌توان تضمین کرد که ترتیب اجرا رعایت خواهد شد. بسیار نکته مهمی است که شما اطمینان داشته باشید که تمامی بروزرسانی‌های UI در این صف وارد شوند و شما هرگز نباید تسکی که موجب بلاک شدگی شود را اجرا کنید. ما می‌خواهیم مطمئن شویم که حلقه اجرایی اپلکیشن (به نام CFRunLoop) هرگز بلاک نشده و همچنین بالاترین سرعت اجرا نیز فراهم شود. متعاقباً صف اصلی بالاترین اولویت را دارد و هر تسکی که به آن پوش شود به سرعت اجرا خواهد شد.دو- DispatchQueue.global: بسته مجموعه‌ای از صف‌های همزمانی عمومی که هرکدام به صورت مجزا استخر threadهای خود را مدیریت می‌کنند. بسته به اولویتی که تسک شما دارد، می‌توانید مشخص کنید که در کدام صف قرار بگیرد، اما معمولا در بیش‌تر اوقات بهتر است که از default استفاده کنید. چرا که تسک‌های درون این صف به صورت همزمان اجرا خواهند شد و تضمینی برای حفظ ترتیب تسک‌های درون صف ندارد.متوجه شدید که ما دیگر با یک thread تنها سروکار نداریم؟ ما با صفی سروکار داریم که استخری از thread‌های داخلی را مدیریت می‌کند و به زودی خواهید دید که چرا صف‌ها رویکرد پایدارتری برای Multithreading هستند.صف سریالی: Thread اصلیبه عنوان یک مثال نگاهی به تکه کد زیر بیاندازید که زمانی که کاربر دکمه‌ای درون برنامه را بفشارد اجرا خواهد شد. تابع پرهزینه compute می‌تواند هرچیزی باشد. بیاید فرض کنیم آن پس-پردازشی روی یک تصویر ذخیره شده درون دستگاه است.import UIKit 
class ViewController: UIViewController { 
   @IBAction func handleTap(_ sender: Any) { 
      compute() 
} 
   private func compute() -&gt; Void { 
      // Pretending to post-process a large image.
      var counter = 0 
      for _ in 0..&lt;9999999 {
         counter += 1 
      } 
   } 
}در نگاه اول شاید به نظر خیلی بی‌خطر باشد اما اگر شما این کد را درون یک اپلیکیشن واقعی اجرا کنید، UI فریز تا زمانی که حلقه به صورت کامل اجرا شود، فریز خواهد شد، که قطعا زمان قابل توجهی طول خواهد کشید. می‌توان این اتفاق را با استفاده از ابزاری اثبات کرد. شما می‌توانید ماژول Time Profiler را در ابزارهای در مسیر Xcode &gt; Open Developer Tool &gt; Instruments در منو Xcode را اجرا کنید. بیایید نگاهی به ماژول Threads در ابزار profile بیاندازیم و ببنید استفاده از CPU به بیشترین حد خود رسیده است.می‌توانیم ببینیم که thread اصلی به ۱۰۰ درصد ظرفیت خود برای ۵ ثانیه رسیده است. این مدت زمانیست که UI بلاک خواهد شد. به Call Tree زیر چارت نگاه کنید، می‌توان دید که Thread اصلی برای ۴.۴۳ ثانیه از ۹۹.۹ درصد ظرفیت خود استفاده می‌کند! نمایانگر این نکته است ک صف سریال رفتاری FIFO مانند دارد، تسک‌ها همیشه به ترتیبی که وارد می‌شوند، تکمیل خواهند شد. واضح است که متد compute در این مثال مقصر است. آیا می‌توانید تصور کنید که کلیک کردن یک دکمه می‌تواند UI شما را برای این مدت زمان فریز کند؟مفهوم Threadهای بکگراندچگونه می‌توان کار بهتری انجام دهیم؟ DispathcQueue.global برای نجات. این جایی است که threadهای بکگراند وارد کار می‌شوند. با ارجا به دیاگرام معماری GCD در بالا ما می‌توانیم ببینیم، هرچیزی که thread اصلی نیست یک thread بکگراند محسوب می‌شود. آن‌ها می‌توانند کنار thread اصلی اجرا شوند، می‌توان ‌آن را کاملا پر کرد و تمام اتفاقات UI مانند اسکرول کردن یا انیمیشن و پاسخ به تعامل کاربر و غیره را به راحتی مدیریت کرد. بیایید یک تغییر کوچک به تابع button clickمان در کد بالا بدهیم:class ViewController: UIViewController { 
   @IBAction func handleTap(_ sender: Any) {
      DispatchQueue.global(qos: .userInitiated).async { [unowned self] in 
         self.compute() 
      } 
   } 
   private func compute() -&gt; Void { 
      // Pretending to post-process a large image. 
      var counter = 0 
      for _ in 0..&lt;9999999 {
         counter += 1
      } 
   } 
}بدیهی است که یک تکه کد به صورت پیش فرض روی صف اصلی اجرا خواهد شد، پس ما باید آن را مجبور کنیم تا روی یک thread دیگر اجرا شود، ما compute را درون یک closure نامتقارن (async) قرار می‌دهیم و مشخص می‌کنیم که در صف DispatchQueue.global قرار بگیرد. به خاطر داشته باشید که ما در این‌جا قصد مدیریت threadها را نداریم. ما تسک‌ها را درون یک صف مناسب (در قالب closure یا بلاک) با فرض این که تضمین می‌کند تا در یک نقطه زمانی اجرا شود، قرار می‌دهیم. صف مورد تظر تصمیم می‌گیرد تا چه threadای به تسک تعلق بگیرید و تمام کار سخت ارزیابی نیازمندی‌های سیستم و مدیریت thread واقعی را انجام می‌دهد. این جادوی Grand Central Dispatch است. همانطور که در ضرب‌المثل‌های قدیمی آمده، شما نمی‌توانید چیزی که نمی‌توانید اندازه بگیرید را ارتقا دهید. خب ما توانستیم کلیک button فاجعه‌بارمان را ارزیابی کنیم و حالا آن را ارتقا دادیم، یک بار دیگر آن را ارزیابی می‌کنیم تا اطمینان پیداکنیم که چقد کارائی را بالا برده‌ایم.نگاه دیگری به profiler میاندازیم، کاملا مشخص که این پیشرفت بزرگی محسوب می‌شود. تسک مورد نظر زمان یکسانی با دفعه قبل برده تا انجام شود اما این بار، در بکگراند بدون این که UI را قفل کند اتفاق افتاده است. اگرچه اپلیکیشن ما کار یکسانی را انجام می‌دهد، اما عملکرد آن بهتر شده چرا که کاربر آزاد است تا کارهای دیگر را آزادانه انجام دهد.شما ممکن است متوجه این قضیه شده باشید که ما به صف عمومی با اولویت userInitiated دسترسی داده‌ایم. این attributeای است که ما می‌توانیم هنگام نیاز فوری به تسکمان بدهیم. اگر ما بتوانیم همان تسک را در صف عمومی انجام دهیدم و attribute کیفی بکگراند را به آن بدهیم، iOS فکر میکند که آن یک تسک utility است و منابع کمتری برای اجرا به آن اختصاص می‌دهد. پس ما کنترلی روی این موضوع که تسکمان چه زمانی انجام می‌شود نخواهیم داشت، ما روی اولویت آن کنترل داریم.نکته‌ای در رابطه با thread اصلی و صف اصلیشما ممکن است تعجب کنید که چرا Profiler دارد thread اصلی را نشان می‌دهد. و ما چرا به آن صف اصلی می‌گوییم. اگر نگاهی به معماری GCD بیاندازید در آنجا توضیح دادیم که تنها وظیفه صف اصلی مدیریت thread اصلی است. بخش صف اعزام در راهنمای برنامه نویسی با همزمانی می‌گوید که صف اعزام اصلی صف سریالی و عمومی همیشه دردسترس است که تسک‌ها را در thread اصلی برنامه اجرا می‌کند. از آن‌جایی که روی thread اصلی برنامه شما اجرا می‌شود، صف اصلی معمولاً به عنوان کلید نقطه هماهنگ‌سازی برای یک اپلیکیشن استفاده می‌شود.واژه اجرا روی thread اصلی و اجرا روی صف اصلی می‌توانند به جای یکدیگر استفاده شوند.صفوف همزمانیتا اینجا فهمیدیم که تسک‌های ما منحصراً با رفتار سریالی اجرا می‌شوند. DispatchQueue.main به صورت پیش‌فرض یک صف سریالی است و DispatchQueue.global به شما چهار صف اعزام همزمان بسته به پارامتر اولویتی که شما به آن پاس می‌دهید، ارائه می‌دهد.بیایید فرض کنیم ما پنج تصویر داریم و می‌خواهیم که اپلیکیشن‌مان آن‌ها را به صورت موازی در thread بکگراند پردازش کند. چگونه باید این کار را انجام دهیم؟ ما می‌توانیم یک صف همزمانی با مشخصه‌ای به انتخاب خودمان ایجاد کرده و تسک‌هایمان برای اجرا به آن اختصاص بدهیم. تمام چیزی که نیاز داریم attributeای با نام concurrent هنگام ساختن صف است.class ViewController: UIViewController {
   let queue = DispatchQueue(label: &amp;quotcom.app.concurrentQueue&amp;quot, attributes: .concurrent) 
   let images: [UIImage] = [UIImage].init(repeating: UIImage(), count: 5) 
   @IBAction func handleTap(_ sender: Any) { 
      for img in images {
            queue.async { [unowned self] in
                self.compute(img) 
            } 
      } 
   } 
   private func compute(_ img: UIImage) -&gt; Void {
      // Pretending to post-process a large image. 
      var counter = 0 
      for _ in 0..&lt;9999999 {            
         counter += 1 
      } 
   }
}وقتی این کد را با استفاده از ابزار Profiler بررسی کنیم، می‌بینیم که اپلیکیشن ما حالا ۵ thread مجزا را به صورت موازی برای حلقه مورد نظر اجرا می‌کند.موازی سازی N تسکتا به الان ما دیدم که چگونه می‌توان تسک یا تسک‌هایی که از نظره هزینه محاسباتی گران هستند را وارد thread بکگراند کنیم بدون اینکه‌ دچار گرفتگی در UI شویم. اما نظرتان در مودر اجرای تسک‌های موازی با مقداری محدودیت چیست؟ چگونه اپلیکیشن Spotify آهنگ‌های مختلف را به صورت موازی دانلود می‌کند یا چرا تعداد دانلود همزمان را به ۳ دانلود محدود کرده است؟ ما می‌توانیم برای فهم این مساله از راه‌های مختلفی شروع کنیم، اما مفید است تا یک ساختار مهم دیگر را در برنامه‌نویسی multithread بشناسیم به نام سمافور.سمافورها مکانسیم سیگنالی دارند. آن‌ها معمولاً برای کنترل دسترسی به منابع مشترک استفاده می‌شوند. سناریویی را فرض کنید که threadای می‌تواند دسترسی به یک تکه کدی که اجرا می‌کند را قفل کند و بعد از این‌که انجام شد آن را آنلاک کند و به بقیه thread‌ها اجازه بدهد تا آن را اجرا کند. این نوع رفتار را معمولاً در خواندن و نوشتن در دیتابیس مشاهده می‌کنید، برای مثال اگر بخواهید عملیات نوشتن در دیتابیس را فقط یک thread انجام دهد و دیگر threadها اجازه خواندن در آن زمان را نداشته باشند، چه کاری انجام می‌دهید؟  این یک مساله در امنیت thread است که قفل نویسنده و خواننده نام دارد. سمافور‌ها می‌توانند برای کنترل همزمانی در اپلیکیشن‌مان استفاده شوند که این‌کار را با قفل کردن N تعداد thread انجام ‌می‌دهند.let kMaxConcurrent = 3 // Or 1 if you want strictly ordered downloads! 
let semaphore = DispatchSemaphore(value: kMaxConcurrent) 
let downloadQueue = DispatchQueue(label: &amp;quotcom.app.downloadQueue&amp;quot, attributes: .concurrent) 
class ViewController: UIViewController { 
   @IBAction func handleTap(_ sender: Any) { 
      for i in 0..&lt;15 {            
         downloadQueue.async { [unowned self] in 
            // Lock shared resource access                
            semaphore.wait() 

            // Expensive task 
            self.download(i + 1)
 
            // Update the UI on the main thread, always! 
            DispatchQueue.main.async {                    
               tableView.reloadData() 

               // Release the lock                    
               semaphore.signal() 
            } 
         } 
      } 
   } 
   func download(_ songId: Int) -&gt; Void { 
      var counter = 0 

      // Simulate semi-random download times. 
      for _ in 0..&lt;Int.random(in: 999999...10000000) {            
         counter += songId         
      } 
   } 
}دیدیم که چگونه می‌توان به طور موثر سیستم دانلودمان، خود را به K تعداد دانلود محدود کند. زمانی که یک دانلود تمام شود (یا اجرای thread به اتمام برسد)، مقدار سمافور را کاهش داده و اجازه مدیریت صف را برای ایجاد یک thread دیگر صادر می‌کند تا یک آهنگ جدید شروع به دانلود شود. شما می‌توانید الگوی مشابهی را برای تراکنش‌های دیتابیس زمانی که با خواندن و نوشتن‌های همزمان سر و کار دارید، استفاده کنید.سمافور‌ها معمولاً برای کد‌هایی مانند مثالمان ضروری نیستند، اما آن‌ها زمانی که نیاز داشته باشند تا یک رفتار متقارن هنگام استفاده از یک API نامتقارن داشته باشید، خیلی قدرتمند ظاهر می‌شوند. مثال بالا نیز می‌تواند به خوبی با استفاده از NSOperationQueue به همراه یک maxConcurrentOperationCount انجام شود،‌ اما ارزشش را داشت که راه حل متفاوتی را نیز در نظر داشته باشیم.کنترل بهتر با OperationQueueمفهوم GCD زمانی که بخواهید یک تسک یکبار مصرف یا closureای را به یک صفی با مدل انجامش بده و فراموشش کن، اعزام کنید، عالی هستند و یک روش بسیار سبک برای این‌کار فراهم می‌کنند. اما چگونه می‌توان، یک تسک طولانی مدت ساختاریافته و قابل تکرار تولید کنیم که یک وضعیت یا دیتای مرتبط ایجاد میکند را بسازیم؟ و این که چطور می‌توان مدلی داشت که شامل زنجیره‌ای از عملیاتی باشد که بتوان آن‌ها را کنسل، معلق و ردیابی کرد و همچنان با یک API که closure دوست باشند کار کند؟ یک عملیات مانند تصویر زیر را تصور کنید:این پیاده‌سازی همچین چیزی با استفاده از GCD کمی به مشکل خواهیم خورد. ما یک راه ماژولارتری برای تعریف گروهی از تسک‌ها می‌خواهیم که همزمان بتوانیم از خوانا بودن آن‌ها اطمینان حاصل کنیم و همچنین کنترل بیش‌تری روی آن‌ها داشته باشیم. در این حالت، ما می‌توانیم از آبجکت‌های Operation استفاده کنیم و آن‌ها را در یک OperationQueue به صف کنیم، که یک wrapper سطح بالا دور DispatchQueue است. بیایید نگاهی به مزایای استفاده از این روش بیاندازیم و ببینیم که در مقایسه با API سطح پایین GCI چه پیشنهادی دارد:شاید شما بخواهید وابستگی‌هایی بین تسک‌ها ایجاد کنید و زمانی ‌که می‌خواهید این‌کار را با GCD انجام دهید بهتر است که آن‌ها را به عنوان آبجکت‌‌های Operation تعریف کنید یا واحد کار و آن‌ها را به صف خود انتقال دهید. این کا‌ر به شما ماکزیمم قابلیت استفاده مجدد را تا زمانی که بخواهید آن را در الگوی مشابهی هرجای دیگر اپلیکیشن استفاده ‌کنید، می‌دهد.کلاس‌های Operation و OperationQueue اجزای قابل مشاهده دارند که از KVO (مشاهده بار اساس key/value). این یک مزیت مهم دیگریست که می‌توانید وضعیت operation یا صف آن را مشاهده کنید.در این حالت Operationها می‌توانند pause، resume و کنسل شوند. زمانی که شما بخواهید توسط GCD یک تسک را اعزام کنید پس از آن هیچ کنترلی روی اجرای آن تسک نخواهید داشت. Operation API از این لحاظ بسیار انعطاف‌پذیرتر است و به توسعه‌دهنده کنترل روی سیکل زندگی operation را می‌دهد.صف Operation به شما اجازه می‌دهد تا ماکزیمم تعداد Operationهای داخل صف که می‌توانند همزمان اجرا شوند را مشخص کنید، و به شما قابلیت بیشتری برای کنترل روی جنبه‌های همزمانی خواهد داد.استفاده از Operation و OperationQueue می‌تواند یک پست خالی وبلاگ را کامل پر کند یعنی خیلی طولانی خواهد بود اما بیایید نگاهی به مثالی برای این‌که دریاببیم مدلسازی وابستگی‌ها در این حالت به چه صورت است بیاندازیم. (GCD هم وابستگی‌های ایجاد می‌کند، اما بهتر است که تسک‌های بزرگ را به یک سری زیرتسک‌های قابل ترکیب تقسیم‌بندی کنید.) برای ساخت زنجیره‌ای از operationهای که به یکدیگر وابسته‌اند، می‌توانیم به صورت زیر عمل کنیم:class ViewController: UIViewController { 
   var queue = OperationQueue() 
   var rawImage = UIImage? = nil 
   let imageUrl = URL(string: &amp;quothttps://example.com/portrait.jpg&amp;quot)! 
   @IBOutlet weak var imageView: UIImageView! 

   let downloadOperation = BlockOperation { 
      let image = Downloader.downloadImageWithURL(url: imageUrl) 
      OperationQueue.main.async { 
         self.rawImage = image         
      } 
   } 

   let filterOperation = BlockOperation { 
      let filteredImage = ImgProcessor.addGaussianBlur(self.rawImage) 
      OperationQueue.main.async { 
         self.imageView = filteredImage         
      } 
   }     

   filterOperation.addDependency(downloadOperation) 

   [downloadOperation, filterOperation].forEach {
   queue.addOperation($0) 
   } 
}خب پس چرا ما از یک انتزاع سطح بالاتر استفاده نکنیم و به کل از GCD دوری کنیم؟ چون GCD تنها برای پردازش‌های نامتقارنی خطی ایده‌آل هستند، Operation یک مدلی شی‌گرا و جامع‌تر برای محاسبات کپسوله‌سازی تمام دیتا‌ی ساختار‌یافته و تسک‌های قابل تکرار در یک اپلیکیشن ارائه می‌دهد. توسعه‌دهندگان باید از بالاترین سطح انتزاع ممکن برای مسائلی از قبیل کارهای تکرار شونده و سازگار با زمانبندی استفاده کنند که این انتزاع Operation نام دارد. در جای دیگر اگر نیاز به اجرای تسک‌های یکبارمصرف یا closure بود، استفاده از GCD بسیار منطقی به‌نظر می‌رسد. همچنین ما می‌توانیم هردوی GCD و OperationQueue را باهم ترکیب کنیم و از مزایای هر دو استفاده کنیم. برای مثال کاملاً امکان پذیر است نا ۱۰۰۰۰ تسک بسازید و آن‌ها را در یک صف قرار دهید، اما با این‌کار به طور غیر مسقیم فضای حافظه را میگیرید و باعث بوجود آمدن سربار به ازای هر تخصیص و گرفتن بلاک‌های Operation خواهید شد. این دقیقاً در تضاد با آنچه که به دنبالش هستید، می‌باشد! بهترین کار بررسی اپلیکیشن برای اطمنیان از این‌ است که آیا با این کار عملکرد اپلیکیشن را بهبود داده‌اید یا خیر.هزینه همزمانیصف اعزام و دوستانش به منظور راحت‌تر کردن اجرای کد به صورت همزمان برای توسعه‌دهندگان اپلیکیشن معرفی شده‌اند. البته این تکنولوژی‌ها هیچ تضمینی برای بهبودی در کارائی یا پاسخگويی در یک اپلیکیشن ارائه نمی‌دهند. این امر بستگی به شما دادر تا از یک صف با رفتاری استفاده کنید که هم عملکرد خوبی داشته باشد و هم تحمیل ناعادلانه‌ای روی دیگر منابع نداشته باشد. برای مثال این امر بسیار امکان‌پذیر است که شما ۱۰۰۰۰ تسک بسازید و آن‌ها را درون یک صف قرار دهید، اما با این‌کار شما مقدار قابل توجهی از حافظه را به آن‌ها اختصاص داده‌اید و سربار زیادی را برای تخصیص و آزادسازی بلاک‌های عملیات به سیستم تحمیل خواهید کرد. این دقیقاً در تضاد با خواسته‌ی شماست. بهترین راه‌کار این است که شما اپلیکیشن خود را آنالیز کنید تا از این‌که همزمانی عملکرد آن را بهبود داده یا تاثیر منفی داشته اطمینان حاصل کنید.ما در رابطه با این‌که که چطور همزمانی با خود هزینه‌هایی از قبیل پیچیدگی و تخصیص منابع سیستم را دربر دارد، صحبت کردیم، اما همزمانی ریسک‌های دیگری نیز همراه خود خواهد داشت:بن‌بست: وضعیتی اس که یک thread بخش مهمی از کد را قفل می‌کند و می‌توای حلقه‌ی اجرای برنامه را کاملاً از کار بیاندازد. هنگام استفاده از GCD باید زمان استفاده از صدازدن‌های dispatchQueue.sync بسیار مراقب بود چرا که به سادگی می‌توند شما را در وضعیتی قرار دهد که دو عملیات همزمان در انتظار یکدیگر گیر کنند.وارونگی اولویت: به وضعیتی گفته می‌شود که یک تسک با اولیت پایین جلوی اجرای تسکی با اولویت بالاتر را بگیرد. GCD به شما اجازه می‌دهد تا برای دو صف بکگراند اولویت‌های متفاوتی تعریف کنید پس برای حل این مشکل راه حل ساده‌ای وجود دارد.مشکل تولید‌کننده - مصرف‌کننده: یک وضعیت رقابتی بوجود آمده در زمانی که یک thread دیتای منبعی را می‌سازو و thread دیگری به آن دسترسی دارد. این یک مشکل هماهنگ‌سازی است و اگر شما از صف همزمانی در GCD استفاده کنید، می‌توانید با استفاده از لاک‌، سمافور، صف سریالی یا یک جلوگیری از اعزام برطرف شود.و شاید موارد دیگری از وضعیت‌های قفل‌شدگی و رقابت‌داده‌ای که دیباگ کردن آن‌ها مشکل باشد! thread امن بودن بزرگترین نگرانی‌ است که هنگام استفاده از همزمانی خواهید داشت.مطالعه بیش‌ترخوشبختانه این مقاله به شما پیش‌زمینه‌ای در رابطه با تکنیک‌های multithreading در iOS را می‌دهد و شما آموزش می‌دهد که چگونه از آن‌ها در اپلیکیشن خود استفاده کنید. ما قرار نیست که بیش‌تر ساختار سطح پایین مانند locks، mutex و این‌که چگونه آن‌ها برای دستیابی به هماهنگ‌سازی به ما کمک‌می‌کنند را شرح دهیم و همچنین نمی‌خواهیم مثال‌های پیچیده‌ای در رابطه‌ با این که چقدر همرمانی ‌می‌تواند به اپلیکیشن شما آسیب برساند،‌ بزنیم. این دسته موارد را برای بعد می‌گذاریم و اگر تمایل داشته باشید می‌توانید موارد زیر را مشاهده کنید.Building Concurrent User Interfaces on iOS (WWDC 2012)Concurrency and Parallelism: Understanding I/OApple&amp;amp;#x27;s Official Concurrency Programming GuideMutexes and Closure Capture in SwiftLocks, Thread Safety, and SwiftAdvanced NSOperations (WWDC 2015)NSHipster: NSOperationمنبع اصلی مقاله :  https://www.viget.com/articles/concurrency-multithreading-in-ios/</description>
                <category>محمد صادق پنادگو</category>
                <author>محمد صادق پنادگو</author>
                <pubDate>Sat, 26 Dec 2020 17:58:11 +0330</pubDate>
            </item>
                    <item>
                <title>۱۳ سوال اساسی در رابطه با Git که در مصاحبه از شما می‌پرسند.</title>
                <link>https://virgool.io/CodeLovers/%DB%B1%DB%B3-%D8%B3%D9%88%D8%A7%D9%84-%D8%A7%D8%B3%D8%A7%D8%B3%DB%8C-%D8%AF%D8%B1-%D8%B1%D8%A7%D8%A8%D8%B7%D9%87-%D8%A8%D8%A7-git-%DA%A9%D9%87-%D8%AF%D8%B1-%D9%85%D8%B5%D8%A7%D8%AD%D8%A8%D9%87-%D8%A7%D8%B2-%D8%B4%D9%85%D8%A7-%D9%85%DB%8C%D9%BE%D8%B1%D8%B3%D9%86%D8%AF-i91efqxembfv</link>
                <description>مقدمهممکن از خیلی از ما توسعه‌دهندگان گمان کنیم که همه چیز را در رابطه با Git می‌دانیم و Git چیزی نیست جز همان سه دستور ساده و اصلی commit، push، pull و مابقی دستورات خیلی اهمیتی ندارند و به کارمان نخواهند آمد. البته اگر از نرم‌افزارهای مدیریت کننده مخازن Git استفاده کرده باشید، بسیاری از این دستورات و کارائی‌های Git را به راحتی بدون دانستن دستور خاصی می‌توانید استفاده کنید. به هر حال ما برنامه‌نویسان عادت کرده‌ایم به هر روشی که شده مشکلاتی بر سر راهمان قرار می‌گیرند، یا حل کنیم یا دور بزنیم و همیشه گلیممان را از آب بیرون کشیده‌ایم. داشتن دانش صحیح در هر زمینه خالی از لطف نخواهد بود. در مسیر پیشرفت علمی و شغلی ممکن است خیلی‌ از ما به مصاحبه‌های شغلی متعددی رفته‌ باشیم، در مصاحبه‌های فنی بعید است که از Git سوالی از شما نپرسند. از شما خواهند پرسید که از چه دستورات Git استفاده کرده‌اید و در رابطه با آن‌ها توضیحی دهید. قطعا فرد مصاحبه کننده چیزی بیشتر از commit یا push و pull از شما انتظار خواهد داشت. از آن جا که برای شخص خودم نیز این مساله پیش آمده به دنبال مطالب پیش‌رفته‌تر در زمینه‌ Git رفتم و خواستم که در مصاحبه‌های شغلی آینده حرف بیشتری برای گفتن داشته باشم.در ادامه این مقاله با ۱۳ سوال اساسی که ممکن است در مصاحبه شغلی بدردتان بخورد روبرو خواهید شد و پاسخ هر سوال نیز بررسی خواهد شد. امیدوارم کمکی کرده باشم و با ترجمه و انتشار این مقاله بتوانم کمی هم به دانش خودم هم خوانندگان این متن اضافه کنم. البته باید این نکته را یادآوری کنم که ترجمه متون تخصصی کامپیوتر به فارسی کمی دشوار است، چرا که اکثر ما بسیاری از اصطلاحات و عبارات را به انگلیسی یاد گرفته و به کار می‌بریم و گاهی اوقات ترجمه این عبارات کار درستی نیست و موجب سردرگمی خواننده خواهد شد. به همین منظور پیشنهاد بنده به خوانندگان این متن این است که حتما متن اصلی انگلیسی منبع مقاله را در کنار متن فارسی ترجمه شده داشته باشند تا اگر اشتباهی از بنده رخ داده و نتوانستم حق مطلب را ادا کنم، با بررسی آن بتوانند مفهوم جملات را به درستی درک کنند.۱- چگونه یک commit که push و عمومی شده را برگشت دهیم؟یک یا چند commit می‌توانند توسط دستور git revert بازگشت داده شوند. این دستور، در ماهیت خود یک commit جدید با patch‌هایی می‌سازد که تفییرات معرفی شده در commit بخصوص را کنسل کند. زمانی که commitای که نیاز به بازگشت دارد، منتشر شده باشد و یا امکان تغییر تاریخچه مخزن وجود نداشته باشد، git revert می‌تواند commit را بازگشت دهد. اجرای دستور زیر امکان بازگشت دو commit را به ما می دهد. git revert HEAD~2..HEADروش دیگر این است که شما یک وضعیت مشخص از commitای که در گذشته انجام داده‌اید را checkout کنید و دوباره از نو commit کنید.۲- چگونه چند commit آخر خود را به یک commit تبدیل کنید؟تبدیل چندین commit به یک commit تاریخچه را بازنویسی خواهد کرد و باید با احتیاط انجام شود. هرچند این کار زمانی که شما روی branch ویژگی جدید کار می‌کنید بسیار بدرد بخور هست. برای ترکیب چند commit آخر branch فعلی خود، دستور زیر را اجرا نمائید: (N را با تعداد commitای که میخواهید ترکیب شوند عوض کنید) git rebase -i HEAD~{N}با اجرای این دستور یک ویرایشگر با لیستی از این N تعداد پیام commit هرکدام در یک خط مجزا باز خواهد شد. هرکدام از این خطوط با واژه pick شروع شده‌اند. با تغییر pick به squash یا s به Git می‌‌گویید تا commit مورد نظر را با commit قبلی ترکیب کند. برای ترکیب تمام N تعداد commit به یکی باید واژه pick تمام خطوط را به squash تغییر دهید به جز خط اول. با خروج از ویرایشگر، اگر هیچ conflictای رخ ندهد، دستور git rebase به شما این امکان را می‌دهد که یک پیام commit جدید برای commitهای ترکیب شده، بسازید.۳- چگونه لیستی از فایل‌هایی که در یک commit بخصوص تغییرات داشته‌اند را پیدا کنیم؟git diff-tree -r {hash}کد هش commit مورد نظر وارد می‌کنیم و با این کار لیستی از فایل‌های اضافه یا تغییر داده شده در آن commit نشان داده خواهد شد. flagای که در این دستور وجود دارد r- هست که موجب می‌شود، فایل‌ها به صورت مستقل نمایش داده شوند و از نمایش فولدر یا root directory که در آن قرار دارد جلوگیری می‌شود. خروجی دارای اطلاعات بیشتری هست که به راحتی می‌توان با اضافه نمودن چند flag می‌توان از نمایش آن‌ها جلوگیری کرد. git diff-tree --no-commit-id --name-only -r {hash}در اینجا no-commit-id هش‌های commit را از خروجی حذف می‌کند و name-only تنها نام فایل‌ها را به جای مسیرشان نمایش می‌دهد.۴- چگونه اسکریپتی بنویسیم تا هربار که به مخزن commit جدیدی push شد، اجرا شود؟به منظور نوشتن اسکریپتی که هر بار با push جدید به مخزن اجرا شود، ابتدا نیاز دارید که یک pre-receive یا یک update و یا یک post-receive هوک (hook) با توجه به آنچه که اسکریپت قرار است اجرا کند، داشته باشید.هوک pre-receive در مخزن مقصد زمانی اجرا می‌شود که commitها به آن push شوند. هر اسکریپتی که به این هوک متصل باشد، قبل از اینکه اتفاقی بیافتد، اجرا می‌شود. این یک هوک مفید برای اجرای اسکریپت‌هایی است که به اجرای مقررات توسعه کمک می‌کند.هوک update نیز بسیار رفتاری مشابه هوک pre-receive دارد و همواره قبل از اینکه هر بروزرسانی رخ دهد اجرا می‌شود. البته هوک update یک برا برای هر commitای که push شود، اجرا خواهد شد.درنهایت هوک post-receive که بعد از بروزرسانی مخزن انجام می‌شود. این هوک مناسب‌ترین حالت برای اجرای اسکریپت‌های ساده هستند که برای سیستم‌های یکپارچه مداوم و ارسال ایمیل اطلاع‌رسانی به نگه‌دارندگان مخزن کاربرد دارد.هوک‌ها برای مخازن Git به صورت محلی هستند و بروز نمی‌شوند. اسکریپت‌ها نیز هم می‌توانند درون دایرکتوری هوک در git. و یا هرجای دیگری ساخته شوند.۵- دستور git biscet چیست؟ چگونه می‌توانید از آن برای تشخیص یک باگ (regression - بازگشت) استفاده کنید؟مکانیزمی نسبتاً سریع توسط Git فراهم شده تا بتوان commitها بد و ناموفق را شناسایی کرد. به جای این‌که کاربر تک تک commitها را بررسی کند تا بتواند یکی را که باعث بوجود آمدن مشکلی در کد شده پیدا کند، دستور git biscet به کاربر این امکان را می‌دهد تا بتوانید یک جستجوی باینری روی کل تاریخچه‌ی مخزن انجام دهد.با استفاده از دستور git biscet start، مخزن به حالت دونیم (biscet) می‌رود. پس از آن، تنها کاری شما باید انجام دهید، مشخص کردن commitهای بد و خوب است. git bisect bad # marks the current version as bad 
git bisect good {hash or tag} # marks the given hash or tag as good, ideally of some earlier commitزمانی که این کار را انجام دهید، آنوقت Git مشخص می‌کند که شما commitهایی دارید که نیاز به اکتشاف دارند. در هر قدم، برای شما commitهایی را می‌آورد که تا روی آن‌ها علامت خوب یا بد بگذارید. زمانی که اولین commit بد پیدا شود یا حالت biscet نیاز به اتمام داشته باشد، دستور زیر می‌تواند برای خروج از این حالت استفاده شود.git bisect reset۶- راه‌های مختلف ارجاع دادن به یک commit چیست؟در Git هر commit یک هش کد منحصر به فرد دارد. این هش‌ها می‌توانند برای مشخص کردن commitهای متناظر در سناریوهای مختلف (مانند زمانی که سعی دارید یک حالت خاص از کد را با استفاده از دستور (git checkout (hash در پروژه checkout کنید.)علاوه بر این، نام‌های مستعار نیز برای هر commit توسط Git فراهم شده‌اند تا بتوان به آن‌ها ارجاع داد. همچنین هر تگی که شما در مخزن می‌سازید به راحتی می‌توانند به عنوان یک مرجع قرار گیرند (این دقیقاً کاری است که شماه می‌توانید به جای استفاده از هش‌ها در دستورات Git با تگ انجام دهید). همچنین Git نام‌های مستعاری فراهم کرده که بسته به وضعیت مخزن تغییر می‌کند، مانند HEAD، FETCH_HEAD، MERGE_HEAD و غیره.همچنین Git به commitها این امکان را می‌دهد تا به یکدیگر به عنوان وابسته ارجاع دهند. برای مثال HEAD~1 به یک commit والد HEAD ارجاع می‌دهد و HEAD~2 به پدربزرگ HEAD و به همین منوال ادامه پیدا می‌کند. زمانی که commit ادغام (merge) داشته باشیم که دارای دو والد باشد، ^ می‌تواند برای انتخاب بین دو والد استفاده شود. برای مثال HEAD^2 می‌تواند برای پیروی از دومین والد استفاده شود.و در آخر refspecها. آن‌ها برای اتصال branchهای محلی و ریموت با یکدیگر استفاده می‌شوند. همچنین می‌توان از آن‌ها برای ارجاع به ‌commitهایی که روی branchهای ریموت هستند، استفاده کرد و به شخص اجازه‌ی کنترل و دستکاری commitهایی که روی محیط Git محلی صورت گرفته را خواهد داد.۷- دستور git rebase چیست و چگونه می‌توان از آن برای رفع conflictهای در یک branch ویژگی قبل از merge استفاده کرد؟به عبارت ساده‌تر، دستور git rebase به شما این اجازه را می‌دهد تا اولین commitای که در branch انجام داده‌اید را به یک نقطه شروع جدید ببرید. برای مثال اگر یک branch ویژگی از master درست شده باشد و بعد از آن master چندین commit دریافت کرده باشد، دستور git rebase می‌تواند به منظور انتقال branch ویژگی به نوک شاخه master استفاده شود. این دستور کاملاً کارآمد تغییراتی که در branch ویژگی انجام شده را به نوک شاخه master اضافه می‌کند و امکان برطرف کردن conflictها را در این مسیر به شما خواهد داد. زمانی‌که این کار انجام شود در انتها به راحتی می‌توان branch ویژگی را با master ادغام کرد.۸- چگونه شما مخزنی می‌سازید که ابزارهای بررسی صحت کد (code sanity checking tools) را درست قبل از انجام commit اجرا کرده و اگر کد ایرادی داشت از آن جلوگیری کند؟برای انجام این کار به راحتی می‌توان از یک اسکریپت ساده در هوک pre-commit استفاده کرد. این هوک حتی قبل از این‌که شما بخواهید پیامی برای commit خود بنویسید اجرا خواهد شد. در این اسکریپت می‌توان ابزارهای دیگر را اجرا کرد، مانند linterها و صحت کد رای در تغییراتی که قرار است روی مخزن commit شود بررسی کرد. برای مثال به کد زیر نگاه بیاندازید: #!/bin/sh
 files=$(git diff --cached --name-only --diff-filter=ACM | grep &#039;.go$&#039;)
 if [ -z files ]; then 
    exit 0 
fi unfmtd=$(gofmt -l $files) 
if [ -z unfmtd ]; then
     exit 0 
fi 
echo “Some .go files are not fmt’d” 
exit 1این کد بررسی می‌کند که آیا فایل go.ای وجود دارد که قرار است commit شود. با خروج از این وضعیت، اسکریپت از انجام commit جلوگیری می‌کند.۹- یکی از هم‌تیمی‌های شما به اشتباه یک branch را حذف کرده و تغییراتش را نیز به مخزن اصلی push کرده. هیچ مخزن دیگری نیز وجود ندارد و هیچ‌یک از هم‌تیمی‌های شما کپی محلی از کد ندارد. چگونه شما این branch را بازیابی می‌کنید؟در reflog آخرین commit به آن branch را checkout کرده و آن را به عنوان یک branch جدید ثبت می‌کنیم.۱۰- چگونه یک commit انجام شده در branchای را در یک branch دیگر کپی می‌کنید؟ (برای مثال چگونه یک commitای که برای رفع باگ روی branch منتشر شده انجام گشته را روی یک branch درحال توسعه کپی کنیم؟)برای انجام این کار باید از دستوری cherry-pick استفاده کرد. این دستور این امکان را فراهم می‌کند تا شما یک commit موجود را به موقعیت یا branch فعلی خود اضافه کنید. پس برای این کار نیا به تغییر وضعیت به branch هدف دارید. دستورهای زیر مثالی از این روش را نشان خواهد داد: git checkout development git cherry-pick {hash of that commit}این‌کار علاوه بر این همان تغییرات را اضافه می‌کند، یک commit جدید با یک هش جدید بوجود می‌آورد چرا که تغییرات برروی یک مقصد متفاوت انجام شده است.۱۱- چگونه از cherry-pick برای یک commit ادغام استفاده می‌کنید؟دستور cherry-pick تفاوت بین branchها را پیدا می‌کند. زمانی که یک commit ادغام متعلق به یک branch دیگر باشد، دارای دو والد و دو لیست تغییرات است.برای مثال اگر شما یک commit ادغام با مرجع 63ad84c داشته باشید، باید به صورت زیر والد مورد نظر خود را انتخاب کنید، که در اینجا والد شماره ۱ مد نظر است.git checkout release_branch
git cherry-pick -m 1 63ad84c۱۲- تفاوت بین دو دستور git pull و git fetch چیست؟دستور git fetch تنها داده‌ی جدید را از مخزن ریموت دانلود می‌کند، اما هیچ یک از داده‌های دانلود شده را با فایل‌هایی که درحال کار روی آن هستید، ادغام نمی‌کند. تنها فایده آن این است که به شما دیدی از این داده‌ها خواهد داد.دستور git pull علاوه بر دانلود داده از مخزن ریموت آن‌ها را با فایل‌های محلی که در حال کار روی آن هستید merge می‌کند. این عمل حتی ممکن است موجب بوجود آمدن conflict روی فایل‌هایی که هنوز commit نکردید، شود. می‌توانید از دستور git stash برای مخفی کردن تغییرات محلی خود استفاده کنید.۱۳- مفهوم conflict در git چیست و چگونه برطرف می‌شود؟یک conflict زمانی رخ می‌دهد که بیش از یک commit در محلی یکسان یا خطی یکسان از کد رخ داده باشد. در این حالت Git قابلیت تشخیص این‌که اولویت با کدام تغییر است را ندارد. به این اتفاق conflict در git می‌گویند.به منظور حل conflict در git، باید فایل‌هایی که conflict در آن رخ داده را ویرایش کنیم و از دستور git add برای اضافه کردن آن‌ها به commit استفاده کنیم. بعد از این کار برای commit کردن از دستور git commit استفاده می‌کنیم. همواره git به شما یادآوری میکند که شما در وسط عملیات ادغام قرار دارید و همیشه والدین commit را به درستی مشخص می‌کند.مطلب آخراین سوالات بیش‌تر به درد مصاحبه شغلی می‌خورند و سوالات خیلی فنی و گمراه‌کننده‌ای نیستندو تنها به منظور راهنمایی شما فراهم شده‌اند. کاندیداهای درجه یک شغلی شاید قادر به پاسخگویی به همه این سوالات نباشند، همچنین اگر کسی همه این سوالات را بداند به این معنا نیست که درجه یک است.منبع: https://www.toptal.com/git/interview-questions#form</description>
                <category>محمد صادق پنادگو</category>
                <author>محمد صادق پنادگو</author>
                <pubDate>Sat, 28 Nov 2020 03:40:41 +0330</pubDate>
            </item>
                    <item>
                <title>مفهوم تزریق وابستگی (Dependency Injection) در Swift</title>
                <link>https://virgool.io/@mspanadgoo/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%AA%D8%B2%D8%B1%DB%8C%D9%82-%D9%88%D8%A7%D8%A8%D8%B3%D8%AA%DA%AF%DB%8C-dependency-injection-%D8%AF%D8%B1-swift-k7xrcvj8iml3</link>
                <description>بسیاری از توسعه‌دهندگان نرم‌افزار با شنیدن واژه تزریق وابستگی، عقب نشینی می‌کنند. این الگو به خاطر پیچیدگی‌‌هایی که دارد برای تازه‌کارها مناسب نیست. این چیزی است که خیلی از شماها باور دارید. اما حقیقت این است که تزریق وابستگی یک الگوی بنیادیست و به راحتی قابل استفاده است.نقل قول مورد علاقه من در رابطه با تزریق وابستگی جمله‌ای است از جیمز شار. در این جمله او به صورت خلاصه مفهوم تزریق وابستگی را شرح داده است.تزریق وابستگی یک واژه ۲۵ دلاریست برای یک مفهوم ۵ سنتی. جیمز شارزمانی که من نیز برای اولین بار واژه تزریق وابستگی را شنیدم، با خود فکر کردم که حتما روشی بسیار پیشرفته برای نیازهای حال حاضر من است. من امکان این‌که بدون تزریق وابستگی کارهایم را پیش ببرم داشتم، هرکاری که بود.اما تزریق وابستگی چیست؟بعدها دریافتم، اگر تنها به اصول بنیادی تزریق وابستگی نگاه کنیم، با یک مفهوم ساده روبرو هستیم. جیمز شار یک تعریف سر راست و خلاصه در رابطه با تزریق وابستگی‌ها ارائه داده‌است. تزریق وابستگی یعنی به یک شئ متغيرهای نمونه‌اش (Instance Variable)  را بدهیم. واقعا به همین سادگی. جیمز شاربرای برنامه‌نویسانی که با این مفهوم آشنا نیستند، یادگیری مفاهیم اولیه قبل از این که به سراغ framework یا کتابخانه برویم از اهمیت زیادی برخوردار است. احتمال این وجود دارد که شما در حال حاضر از مفهوم تزریق وابستگی‌ها استفاده می‌کنید، بدون آن که از این موضوع آگاه باشید.تزریق وابستگی چیزی بیش‌تر از تزریق وابستگی به یک شئ به جای آن که خود شئ را موظف به ساخت وابستگی‌هایش کنید، نیست. یا همانطور که جیمز شار گفته، شما به جای ساخت متغیرهای نمونه‌ی یک شئ در خودش، آن‌‌ها را به خودش پاس می‌دهید. بگذارید با یک مثال به شما نشان بدهم که منظور از این جمله چیست.یک مثالدر این مثال ما یک کلاس UIViewController را تعریف میکنیم که یک property با نام requestManager به صورت زیر دارد:import UIKit
class ViewController: UIViewController {
    var requestManager: RequestManager?
}ما برای مقدار دهی به requestManager با یکی از دو روش زیر می‌توانیم اقدام کنیم:۱- بدون تزریق وابستگیاولین گزینه این است که در همان جا که متغیر را تعریف کرده‌ایم آن را مقداردهی نیز کنیم. ما می‌توانیم متغیر را از نوع lazy تعریف کنیم یا اینکه در initializer خود ViewController آن را مقدار دهی نماییم. نکته مهمی اینجا نیست، نکته اصلی این است که ViewController وظیفه مقدار دهی به requestManager را به عهده دارد.import UIKit
class ViewController: UIViewController {
    lazy var requestManager: RequestManager? = RequestManager()
}این بدان معناست که کلاس ViewController نه تنها در رابطه با رفتار RequestManager آگاه است بلکه نمونه سازی (instantiation) آن را نیز می‌داند. نکته ظریفی است اما جزئیات مهمی را بیان می‌کند.۲- به وسیله تزریق وابستگیاما روش دیگری نیز وجود دارد. ما می‌توانیم با مقدار اولیه requestManager را به ViewController تزریق کنیم. البته چنین به نظر خواهد رسید که نتیجه نهایی یکسان است، اما در حقیقت اینگونه نیست. با تزریق requestManager کلاس ViewController از نمونه سازی آن آگاه نیست.// Initialize View Controller
let viewController = ViewController()

// Configure View Controller
viewController.requestManager = RequestManager()بسیاری از توسعه‌دهندگان به سرعت این گزینه را به دليل پیچیدگی غیرضروری و زحمت اضافی، رد می‌کنند. اما اگر مزایای این روش را در نظر داشته باشد، تزریق وابستگی جذاب‌تر خواهد شد.مثال دیگربه منظور تاکید بیش‌تر روی هدفی پیش‌تر بیان شد، مثال دیگری خواهیم زد. به کد زیر نگاهی بیاندازید:protocol Serializer {
    func serialize(data: AnyObject) -&gt; NSData?
}
class RequestSerializer: Serializer {
    func serialize(data: AnyObject) -&gt; NSData? {
        ...
    }
}
class DataManager {
    var serializer: Serializer? = RequestSerializer()
}کلاس DataManager یک property به نام serializer از نوع Serializer? دارد. در این مثال Serializer یک پروتکل است. کلاس DataManager مسئول مقداردهی به یک نمونه از نوع پروتکل Serializer است که در این مثال ًRequestSerializer نام دارد.آیا کلاس DataManager باید بداند که شئ‌ای از نوع Serializer? چگونه مقداردهی می‌شود؟ نگاهی به مثال زیر بیاندازید. این کد به شما قدرت پروتکل‌ها و تزریق وابستگی را نشان خواهد داد.// Initialize Data Manager
let dataManager = DataManager()
// Configure Data Manager
dataManager.serializer = RequestSerializer()کلاس DataManager دیگر مسئول مقداردهی به کلاس RequestSerializer نیست. دیگر به property از نوع serializer خورد مقداری را اطلاق نمی‌کند. در حقیقت ما می‌توانیم هر type دیگری را با RequestSerializer جابجا کنیم البته تا زمانی که از پروتکل Serializer پیروی کند. کلاس DataManager دیگر نه از این جزئیات خبر دارد نه این قضیه برایش اهمیتی دارد.شما چه چیزی به دست آوردیدامیدوارم که با این مثال‌ها نظر شما را جلب کرده باشم. بیایید لیستی از مزایای تزریق وابستگی‌ها را با هم نگاه‌ کنیم.۱- شفافیتبا تزریق وابستگی‌ها به یک شئ، نیازمندی‌ها و مسئولیت‌های یک کلاس یا ساختار تمیز‌تر و شفاف‌تر می‌شود. با تزریق requestManager به ViewController ما فهمیدیم که به آن نیاز دارد و ما می‌توانیم فرض کنیم که ViewController مسئول مدیریت و بررسی requestها است.۲- تست کردنتست unit به وسیله تزریق وابستگی بسیار ساده‌تر خواهد شد، چرا که به راحتی می‌توان وابستگی‌های یک شئ را با مقادير ساختگی جابجا کرد. همچنین پیاده‌سازی تست unit را به وسیله ایزوله کردن رفتار آسان‌تر خواهد نمود.در این مثال، ما یک کلاس به نام MockSerializer تعریف کرده‌ایم. از آن‌جا که این کلاس با پروتکل Serializer مطابقت دارد، ما می‌توانیم dataManager را به آن اطلاق کنیم.class MockSerializer: Serializer {
    func serialize(data: AnyObject) -&gt; NSData? {
        ...
    }
}// Initialize Data Manager
let dataManager = DataManager()
// Configure Data Manager
dataManager.serializer = MockSerializer()۳- جدایی نگرانی‌ها (Separation of Concerns)همانطور که پیش‌تر بیان شد، یکی دیگر از مزایای ظریف تزریق وابستگی‌ها جدایی نگرانی‌های دقیق‌تری است. کلاس DataManager در مثال قبلی دیگر مسئولیتی در قبال RequestSerializer ندارد.البته که کلاس DataManager باید نگران رفتار serializer خود باشد، اما نگرانی در باره مقداردهی آن ندارد. چه اتفاقی رخ‌ می‌داد اگر ReqeustManager در مثال اول تعدادی وابستگی نیز داشت؟ آیا نیاز بود که ViewController از این وابستگی‌ها آگاه باشد؟ این مساله به سرعت می‌تواند خیلی آشفته شود.۴- جفت شدگی (Coupling)مثالی که در آن کلاس DataManager وجود داشت نشان داد که استفاده از پروتکل‌ها و تزریق وابستگی می‌تواند باعث کاهش جفت‌شدگی در یک پروژه شود. پروتکل‌ها در Swift به طور اعجاب‌انگیری مفید و همه‌کاره هستند و این مثال یک سناریو بود که در آن پروتکل‌ها واقعا درخشیدند.انواع تزریق وابستگیبیشتر توسعه‌دهندگان سه نوع از تزریق وابستگی را در نظر می‌گیرند:تزریق آغازگر (initializer injection)تزریق دارائی‌ (property injection)تزریق متد‌ (method injection)به هیچ عنوان این موارد با هم یکسان نیستند. بیاید لیستی از مزايا و معایب هریک را بررسی کنیم.تزریق آغازگر (initializer injection)من به شخصه ترجیح می‌دهدم که وابستگی‌هارا در فاز مقداردهی یک شئ پاس بدهم چرا که این امر مزایای کلیدی متعددی دارد. مهم‌ترین مزیت این است که وابستگی‌ پاس داده شده هنگام مقداردهی می‌تواند، تغییرناپذیر (immutable) شود. این در Swift با contant تعریف کردن دارائی‌ها برای وابستگی‌ها به سادگی امکان پذیر است. نگاهی به مثال زیر بیاندازید:class DataManager {
    private let serializer: Serializer
    init(serializer: Serializer) {
        self.serializer = serializer
    }
}

// Initialize Request Serializer
let serializer = RequestSerializer()
// Initialize Data Manager
let dataManager = DataManager(serializer: serializer)تنها روش راه‌اندازی serializer پاس دادن متغیر هنگام مقداردهی آن است. متد init برای مقدار دهی طراحی شده و تضمین می‌کند که DataManager به درستی تنظیم شده است. مزیت دیگر این است که serializer نمی‌تواند تغییر کند.چون که ما نیاز داریم serializer را به عنوان ورودی هنگام مقداردهی پاس بدهیم، مقداردهنده‌ی طراحی شده به وضوح نشان می‌دهد که وابستگی‌های کلاس DataManager چیست.تزریق دارائی‌ (property injection)وابستگی‌ها همچنین می‌توانند هنگام تعریف یک دارائی داخلی یا عمومی به کلاس یا ساختاری که به آن نیاز دارد، تزریق شوند. این مساله شاید راحت به نظر برسد، اما این کار باعث بوجود آمدن این روزنه خواهد شد که وابستگی‌ها می‌توانند جایگزین یا ویرایش شوند. به عبارت دیگر، وابستگی‌ها تغییر ناپذیر نیستند.import UIKit
class ViewController: UIViewController {
    var requestManager: RequestManager?
}

// Initialize View Controller
let viewController = ViewController()

// Configure View Controller
viewController.requestManager = RequestManager()تزریق دارائی گاهی اوقات تنها گزینه پیش روی شماست. اگر شما از storyboard استفاده کنید برای مثال شما نمی‌توانید یک initializer دلخواه بسازید و باید از تزریق آغازگر استفاده کنید. تزریق دارائی گزینه دوم شما خواهد بود.تزریق متد‌ (method injection)وابستگی‌ها همچنین می‌توانند هرزمان که به آن‌ها نیاز است، تزریق شوند. راه ساده‌ایست که هنگام تعریف یک متد وابستگی‌هارا به عنوان پارامترهای ورودی قبول کنید. در این مثال serializer دارائی DataManager نیست و به جای آن serializer به عنوان یک متغیر به serializerRequest تزریق شده است.class DataManager {
    func serializeRequest(request: Request, with serializer: Serializer) -&gt; NSData? {
        ...
    }
}اگرچه کلاس DataManager مقداری از کنترل خود را روی وابستگی و serializer است دست می‌دهد، اما این نوع از تزریق وابستگی، انعطاف پذیری را به همراه دارد. بسته به مورد استفاده، شما می‌توانید تصمیم بگیرید که چه نوع serializerای را به serializeRequest پاس بدهید.از اهمیت زیادی برخوردار است که تاکید کنم، هر نوع از تزریق وابستگی مورد استفاده به خصوص خود را دارد. از آن جا که تزریق آغازگر در بیش‌تر سناریوها گزینه خوبی است، آن را به بهترین یا ارجحترین نوع تبدیل نمی‌کند. مورد استفاده را در نظر داشته باشید، آنگاه انتخاب کنید که کدام نوع تزریق وابستگی، مناسب کار شماست.یگانه‌ها (Singeltons)تزریق وابستگی‌ها الگوییست که می‌تواند به منظور حذف singeltonها از یک پروژه استفاده گردد. من طرفدار الگوی singelton نیستم و تا جایی که ممکن باشد از آن دوری می‌کنم. البته من الگوی singelton را یک ضد الگو نمی‌دانم، من بر این باورم که باید از آن به ندرت استفاده کرد. الگوی singelton باعث بوجود آمدن جفت شدگی می‌شود و تزریق وابستگی از این اتفاق جلوگیری می‌کند.در خیلی از مواقع، توسعه‌دهندگان به دلیل اینکه‌ singleton راه حل ساده‌ای برای مشکلات جزئی است از آن استفاده می‌کنند. اما تزریق وابستگی شفافیت را به پروژه اضافه می‌کند. با تزریق وابستگی‌ها هنگام مقدار دهی یک شئ، مشخص می‌شود که یک کلاس یا ساختار چه وابستگی‌هایی دارد و همچنین برخی مسئولیت‌های آن مشخص می‌شود.تزریق وابستگی‌های یکی از الگوی‌های مورد علاقه من است چرا که به من کمک می‌کند که از پس پروژه‌های پیچیده بر بیایم. این الگوی مزایای زیادی دارد و تنها عیبی که به ذهن من می‌رسد این است که باید چند خط بیش‌تر کد بنویسم.منبع: https://cocoacasts.com/dependency-injection-in-swift</description>
                <category>محمد صادق پنادگو</category>
                <author>محمد صادق پنادگو</author>
                <pubDate>Sun, 22 Nov 2020 03:15:54 +0330</pubDate>
            </item>
                    <item>
                <title>الگوی معماری MVVM در iOS Swift</title>
                <link>https://virgool.io/@mspanadgoo/%D8%A7%D9%84%DA%AF%D9%88%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-mvvm-%D8%AF%D8%B1-ios-swift-krtp85u5vbjy</link>
                <description>هر زمان که می‌خواهیم شروع به توسعه یک اپلیکیشن جدید نمائیم، این سوال به ذهنمان می‌رسد که بهتر است از کدام الگوی معماری‌ (architecture pattern) برای پروژه جدیدمان استفاده کنیم. بیشترین الگوی معماری استفاده شده در iOS معماری MVC است. بیشتر توسعه‌دهندگان از الگوی MVC برای پروژهای خود استفاده می‌کنند. پروژه‌های کوچک به خوبی با MVC کار می‌کنند، اما زمانی که اندازه پروژه شما به تدریج بزرگ شود، کدهای شما نیز کثیف و شلوغ خواهد شد.همیشه استفاده از الگوی معماری، مناسب به نظر می‌رسد، اما ما نباید همیشه اکیداً از یک الگوی معماری در پروژه خود استفاده کنیم. تمام الگوهای معماری به اندازه کافی برای این که به شما همه امکانات را بدهند، مناسب نیستند، هرکدام مزایا و معایبی دارند. اگر ما ماژول‌های زیادی در پروژه خود داشته باشیم، می‌توانیم بر اساس ماژول‌ها نیز الگوی معماری خود را انتخاب کنیم. برخی ماژول‌ها با MVVM سازگار هستند، اما ممکن است ماژول جدیدی که شما می‌خواهید به پروژه اضافه کنید، با MVVM سازگار نباشد، پس بهتر است که از الگوهای معماری دیگری مانند MVP و VIPER استفاده کنید. پس نتیجه میگیرم که ما الزاماً نباید به یک الگوی معماری تکیه کنیم و به جای آن می‌توانیم بر اساس ماژول‌ها تصمیم بگیریم.مقالات زیادی در اینترنت وجود دارد که مزایا و معایب الگوی MVVM را بررسی کرده و شرح داده‌اند. اما در این مقاله قصد داریم به اجرای عملیاتی این الگو بپردازیم و تنها به خواندن مفاهیمی از آن بسنده نکنیم.بیایید شروع کنیمدر این پروژه ما یک اپلیکیشن ساده با الگوی طراحی MVVM خواهیم ساخت. در بیشتر اپلیکیشن‌ها ما یک (ViewController UI) داریم که نیاز دارد دیتا را از server API واکشی کند و در UI نمایش دهد. ما این رفتار را با استفاده از الگوی MVVM پیاده سازی می‌کنیم.تصویر خروجی نهایی مورد نظر در انتهای مقاله به این صورت است.در اینجا ما از یک وب سرویس ساختگی (dummy) استفاده می‌کنیم که روی اینترنت در دسترس است.http://dummy.restapiexample.com/api/v1/employeesاین وب سرویس در پاسخ به ما یک لیست از دیتای کارمندان را برمی‌گرداند که ما این لیست را در table view نمایش می‌دهیم.بررسی کامپوننت ها و نقش هرکدامالف- View Controller : تنها مسائل مرتبط با UI و دریافت و نمایش اطلاعات در این قسمت انجام می‌شود. بخشی از لایه View است.ب- View Model : اطلاعات را از View Controller دریافت و تمام این اطلاعات را مدیریت می‌کند و به View Controller باز می‌گرداند.ج- Model : تنها مدل شما در این بخش است و چیز بیشتری وجود ندارد. دقیقاً همان مدلی که در MVC نیز وجود دارد. View Model از آن استفاده می‌کند و هر زمان که آپدیتی از طرف View Model برسد، به روزرسانی می‌شود.بیایید ساختار کد خود را پیاده سازی کنیم و فایل‌های مورد نیاز را در گروه‌های مرتبط با هر کدام بسازیم. ما سه فایل جدید در هر گروه ایجاد کرده‌ایم. (Models, ViewModels, API Service)مدلمدل نمایش دهنده دیتای ساده است. به سادگی دیتا را نگاه می‌دارد و کاری به هیچ یک از منطق‌های تجاری (business logics) ندارد. می‌توان گفت که مدل یک ساختار ساده از دیتایی است که ما از API انتظار دریافت آن را داریم.در اینجا می‌توان پاسخ دریافت شده از URL بالا را بررسی کرد و برای این پاسخ یک کلاس مدل ساخت. شما می‌توانید مدل را خودتان بسازید یا از هر وبسایت مدل جنراتوری استفاده نمایید.// MARK: - Employee
struct Employees: Decodable {
    let status: String
    let data: [EmployeeData]
}

// MARK: - EmployeeData
struct EmployeeData: Decodable {
    let id, employeeName, employeeSalary, employeeAge: String
    let profileImage: String

    enum CodingKeys: String, CodingKey {
        case id
        case employeeName = &amp;quotemployee_name&amp;quot
        case employeeSalary = &amp;quotemployee_salary&amp;quot
        case employeeAge = &amp;quotemployee_age&amp;quot
        case profileImage = &amp;quotprofile_image&amp;quot
    }
}جریان اپلیکیشن به این صورت خواهد بود:الف. View controller صدا زده می‌شود و view یک رفرنس به ViewModel خواهد داشت.ب. View تعدادی از اکشن‌های کاربرای را دریافت خواهد کرد و ViewModel را صدا خواهد زد.ج. ViewModel درخواست APIService را فراخوانی می‌کند و APIService پاسخ را به ViewModel باز می‌گرداند.د. زمانی که پاسخ دریافت شود، ViewModel از طریق binding با خبر می‌شود.ه. View با دیتا UI را به روزرسانی خواهد کرد.خب حالا نوبت کد نویسی به صورت مرحله‌ای است. در ابتدا ViewController باید صدا زده شود و از ViewController کلاس ViewModel را صدا می‌زنیم. در حال حاضر binding بین این دو را نمی‌نویسیم و بعداً به سراغ آن خواهیم رفت.کلاس ViewModelکلاس ViewModel جز اصلی این الگو معماری است. ViewModel هرگز متوجه این که view چیست و چه کاری انجام می‌دهد، نخواهد شد. این امر موجب می‌شود تا این معماری قابلیت تست بیشتری داشته باشد و پیچیدگی‌ها را از view حذف می‌کند.در ViewModel ما APIService را برای واکشی دیتا از سرور صدا خواهیم زد.زمانی که این کد را در کلاس ViewModel می نویسید، به شما خطا خواهد که کلاس APIService را نساخته اید. پس بیاید تا حالا کلاس APIService را پیاده سازی کنیم.کلاس APIServiceکلاس APIService یک کلاس ساده است که در آن داده‌ی کارمندان را با استفاده از کلاس URLSession واکشی می‌نماییم. شما می‌توانید از مدل شبکه‌ای برای واکشی داده از server خود استفاده کنید. از کلاس ViewModel که ساخته بودیم کلاس APIService را صدا می‌زنیم.iimport Foundation

class APIService :  NSObject {
    
    private let sourcesURL = URL(string: &amp;quothttp://dummy.restapiexample.com/api/v1/employees&amp;quot)!
    
    func apiToGetEmployeeData(completion : @escaping (Employees) -&gt; ()){
        URLSession.shared.dataTask(with: sourcesURL) { (data, urlResponse, error) in
            if let data = data {
                
                let jsonDecoder = JSONDecoder()
                
                let empData = try! jsonDecoder.decode(Employees.self, from: data)
                    completion(empData)
            }
        }.resume()
    }
}زمانی که بتوانیم پاسخ API را در کلاس ViewModelدریافت کنیم، وقت آن رسیده که اتصالات بین ViewController و ViewModel را انجام دهیم.MVVM اتصالاتاتصالات MVVM نقش اساسی در پروژه ما ایفا می‌کنند. این که چگونه ما بین ViewController و ViewModel ارتباط برقرار می‌کنیم از اهمیت زیادی برخوردار است. ما می‌توانیم اتصالات را به روش‌های مختلفی پیاده سازی کنیم.import Foundation

class EmployeesViewModel : NSObject {
    
    private var apiService : APIService!
    private(set) var empData : Employees! {
        didSet {
            self.bindEmployeeViewModelToController()
        }
    }
    
    var bindEmployeeViewModelToController : (() -&gt; ()) = {}
    
    override init() {
        super.init()
        self.apiService =  APIService()
        callFuncToGetEmpData()
    }
    
    func callFuncToGetEmpData() {
        self.apiService.apiToGetEmployeeData { (empData) in
            self.empData = empData
        }
    }
}ما می‌توایم یک متغیر در کلاس ViewModel به این صورت ایجاد کنیم:var bindEmployeeViewModelToController : (() -&gt; ()) = { }این متغییر باید از کلاس ViewController صدا زده شود.ما یک متغییر دیگر با نام empData از نوع مدل کارمندان می‌سازیم که نتیجه دریافتی APIService را گرفته و View را باخبر می‌کند که تغییری رخ داده است.private(set) var empData : Employees! {didSet{self.bindEmployeeViewModelToController()}}متغیر empData به گونه‌ای تنظیم شده تا دریافت از APIService را پاسخ دهد. با استفاده از observer می‌توان به محض دریافت مقدار در empData به عنوان پاسخ API، didSet متغیر empData صدا زده می‌شود و پس از آن bindEmployeeViewModelToController در داخل didSet صدا زده می‌شود.هر زمان که ما دیتا را از ViewModel به View رساندیم، نوبت به روزرسانی UI است.بخش Viewبرای دریافت دیتا از ViewModel ما باید ViewModel را در کلاس ViewController لینک کنیم.self.employeeViewModel.bindEmployeeViewModelToController = {self.updateDataSource()}import UIKit

class ViewController: UIViewController {
    
    
    @IBOutlet weak var employeeTableView: UITableView!
    
    private var employeeViewModel : EmployeesViewModel!
    
    private var dataSource : EmployeeTableViewDataSource&lt;EmployeeTableViewCell,EmployeeData&gt;!
    

    override func viewDidLoad() {
        super.viewDidLoad()
        callToViewModelForUIUpdate()
    }
    
    func callToViewModelForUIUpdate(){
        
        self.employeeViewModel =  EmployeesViewModel()
        self.employeeViewModel.bindEmployeeViewModelToController = {
            self.updateDataSource()
        }
    }
    
    func updateDataSource(){
        
        self.dataSource = EmployeeTableViewDataSource(cellIdentifier: &amp;quotEmployeeTableViewCell&amp;quot, items: self.employeeViewModel.empData.data, configureCell: { (cell, evm) in
            cell.employeeIdLabel.text = evm.id
            cell.employeeNameLabel.text = evm.employeeName
        })
        
        DispatchQueue.main.async {
            self.employeeTableView.dataSource = self.dataSource
            self.employeeTableView.reloadData()
        }
    }
    
}به منظور به روز رسانی UI شما می‌توانید کد یک TableView را در ViewController خود نیز بنویسید، اما برای این که ViewController را کمتر کثیف یا ماژولار کنیم در این قسمت یک کلاس مجزا به نام EmployeeTableViewDataSource ایجاد کردیم که از UITableViewDataSource اکستند شده است.import Foundation
import UIKit

class EmployeeTableViewDataSource&lt;CELL : UITableViewCell,T&gt; : NSObject, UITableViewDataSource {
    
    private var cellIdentifier : String!
    private var items : [T]!
    var configureCell : (CELL, T) -&gt; () = {_,_ in }
    
    
    init(cellIdentifier : String, items : [T], configureCell : @escaping (CELL, T) -&gt; ()) {
        self.cellIdentifier = cellIdentifier
        self.items =  items
        self.configureCell = configureCell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
        items.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
        
         let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CELL
        
        let item = self.items[indexPath.row]
        self.configureCell(cell, item)
        return cell
    }
}همانطور که قبلا نیز اشاره شده هر الگوی معماری مزایا و معایب خود را دارد، اگر با استفاده از الگوی MVVM فواید زیادی نصیب ما شود، معایبی نیز برای آن وجود دارند.معایب MVVMبرای تازه کارها پیاده سازی MVVM کار دشواری است.اپلیکیشن‌هایی که UI ساده‌ای دارند، استفاده از MVVM دردسر اضافه است.در اپلیکیشن‌های بزرگ data bindig پیچیده تر است و دیباگ را با مشکل مواجه می‌کند.نتیجه‌گیریدر انتهای این مقاله ما موفق به ساخت یک اپلیکیشن ساده با استفاده از الگوی MVVM شدیم. امیدوارم برایتان مفید واقع شود.با سپاسکد منبع (Source Code)برای دسترسی به Source Code این اپلیکیشن به این لینک مراجعه شود.منبع مقالهوبسایت Medium</description>
                <category>محمد صادق پنادگو</category>
                <author>محمد صادق پنادگو</author>
                <pubDate>Tue, 17 Nov 2020 04:03:15 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش React Native: اولین اپلیکیشن React Native خود را بسازید</title>
                <link>https://virgool.io/Only-js/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-react-native-%D8%A7%D9%88%D9%84%DB%8C%D9%86-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-react-native-%D8%AE%D9%88%D8%AF-%D8%B1%D8%A7-%D8%A8%D8%B3%D8%A7%D8%B2%DB%8C%D8%AF-rwpy1mmwkas1</link>
                <description>آیا به یادگیری React Native علاقه‌مندید؟ بیایید اولین اپلیکیشن React Native خود را بسازید، یک اپلیکیشن ساده موبایلی که لیستی از کشورها را از یک Rest API واکشی کرده و نمایش می‌دهد. هدف این آموزش React Native معرفی آن به شماست و به شما پایه و اساس آن چه که قبل از شروع ساخت یک اپلیکیشن واقعی با React Native را نیاز دارید، معرفی می‌کند. در انتهای این مقاله شما می‌توانید یک اپلیکیشن ًReact Native تولید کنید که بر روی دستگاه‌های با هر دو سیستم عامل اندروید یا iOS اجرا خواهد شد.کتابخانه React Native یک کتابخانه توسعه Cross Platform است که بر اساس React توسط Facebook برای توسعه برروی پلتفرم‌های موبایلی ساخته شده است. این کتابخانه در سال ۲۰۱۳ به صورت یک پروژه hackthon (پروژه هایی که به صورت جلسه ای با برنامه نویسان و متخصصين سخت افزار برگزار می‌شود و نتیجه آن می‌تواند یک نرم افزار یا یک سخت افزار جدید باشد.) درون Facebook ساخته شد و بعدها در سال ۲۰۱۵ برای استفاده عموم منتشر شد. به سرعت به محبوبیت بزرگی در بین جامعه توسعه‌دهندگان دست پیدا کرد و کمپانی‌های متعددی آن را به عنوان راه حل اصلی توسعه موبایل خود برگزیدند چرا که اپلیکیشن‌های ًReact Native دارای یک کد مشترک برای هر دو پلتفرم iOS و اندروید است و نتیجه نهایی به راحتی قابل تشخیص از اپلیکیشن‌هایی که به صورت Native نوشته شده‌اند، نیست. طبق وبسایت ًReact Native: در سال ۲۰۱۸ React Native در GitHub رتبه دوم را از نظر تعداد استفاده کنندگان به دست آورد. امروز React Native بسیاری از تولید کنندگان شخصی نرم افزار و کمپانی‌های در سرتاسر جهان مانند Microsoft و Expo پشتیبانی می‌شود.قبل از آن که شروع به تولید اولین اپلیکیشن React Native خود کنیم، بیایید مزایا و معایب آن و جزئیات بیشتری از این که واقعا React Native چیست را بررسی کنیم.الف- React Native چیست؟یک کتابخانه توسعه نرم افزاری موبایلی ترکیبی (Hybrid) است.اپلیکیشن‌های React Native با JSX و Javascript نوشته می‌شوند و به کد native کامپایل می‌شوند.اپلیکیشن‌های React Native شامل ۸۵ الی ۹۰ درصد کد مشترک JSX و Javascript هستند.ب- آنچه که React Native نیست؟الف- WebView نیست.ب- React نیست.ج- PWA نیست.ج- چرا باید React Native را انتخاب کنیم و یا یاد بگیریم؟پلتفرم‌های گوناگونی را با تلاش و کد یکسان پشتیبانی می‌کند. (iOS, Android, Web, TVOS)جامعه بزرگ و درحال رشدی دارد که پشتیبانی کافی برایتان فراهم می‌کند.قابلیت پشتیبانی از کد native را دارد که به این معنی است که شما می‌توانید کد native را برای use-case ها و ‌Business Logic برنامه تان تغییر دهید.نیاز به دانش Javascript دارد، Javascript زبانی پر طرفداریست که یادگیری آن همیشه به دردتان خواهد خورد.ویژگی جالبی دارد که به نام رفرش سریع یا ریلودینگ داغد- معایب React Nativeمعایبی نیز برای React Native وجود دارند، اما توسعه دهندگان این کتابخانه با توجه به رشد جامعه آن در تلاش برای بهبودش هستند:زمانی که کد برنامه بین پلتفرم‌های مختلف مشترک می‌شود، دانش توسعه native نیز گاهی نیاز است تا پروژه‌های پیشرفته React Native را انجام داد.علارغم این که بیشتر اپلیکیشن‌های React Native شبیه اپلیکیشن‌های native هستند، اما گاهی به نرمی و روانی آن‌ها اجرا نمی‌شوند.توسعه دهندگان React Native اتفاق نظر دارند که گزارش خطا به خوبی صورت نمی‌گیرد اما با گذر زمان با این مشکل کنار آمده‌اند.اولین اپلیکیشن React Native خود را بسازیدبیایید یک اپلیکیشن React Native بسازیم که لیستی از کشورها را نمایش دهد. اپلیکیشن شامل تنها یک صفحه است و ما میخواهیم عملکرد و طراحی ظاهر آن را به ساده‌ترین شکل ممکن انجام دهیم، چرا که هدف این آموزش، کمک به شما برای راه‌اندازی محیط React Native و لمس این زبان برنامه‌نویسی است. در آموزش React Native ما میخواهیم از Expo استفاده کنیم. Expo یک ابزار قوی برای تازه‌کاران در دنیای React Native است که امکان اجرا و مشاهده اپلیکیشن‌های React Native را برروی دستگاهتان به سادگی فراهم می‌کند.مرحله اول - نصب Expoمرجع رسمی how to install Expo مراحل نصب آن را توضیح داده است. هر زمان که Expo را نصب کردید، بازگردید و مراحل بعدی را دنبال کنید.مرحله دوم - ساخت اپلیکیشن React Nativeدستور زیر را در Terminal اجرا کنید:expo init firstappگزینه پروژه blank را انتخاب کنید.گزینه ِY را انتخاب کنید تا با yarn کار کنید.Yarn v1.19.2 found. Use Yarn to install dependencies? (Y/n)زمانی که Yes را کلیک کنید، Expo جادوی خود را انجام داده و تمام فایل‌های مورد نیازتان را می‌سازد. می‌توانید به ساختار فولدر ساخته شده نگاهی بیاندازید تا دریابید که پروژه قرار است چگونه کار کند.مرحله سوم - اولین اپلیکیشن React Native خود را اجرا کنیددستورات زیر را اجرا کنید تا پروژه تازه ساخته شده شروع به کار کند:cd firstappnpm run iosدر این لحظه اگر Expo به درستی در مراحل قبل تنظیم شده باشد، پروژه React Native شما باید با موفقیت اجرا شود و صفحه ای مانند اسکرین شات زیر به شما نمایش داده شود.این نمایی از اولین اپلیکیشن React Native شماست که با موفقیت ساخته‌اید. به صورت پیشفرض به شما می‌گوید که App.js را باز کنید تا بتوانید روی اپلیکیشن خود کار کنید. موافقم که بهترین خوش آمدگویی که Expo می‌توانسته پیشنهاد بدهد نیست اما ما به حرفش گوش می‌کنیم و آنچه که گفته را انجام می‌دهیم. بیایید فایل App.js را باز کنیم، که کد زیر در آن وجود دارد:قبل از ادامه توضیحی در رابطه با آنچه که در این فایل اتفاق می‌افتد، بدهیم:متغیرهای استایل شامل دیکشنری هایی ظاهری متفاوتی از کامپوننت‌ها هستند.تابع App یک کامپوننت عملگرای React Native است. به صورت پیشفرض آنچه که ما در حال حاضر می‌بینیم را روی صفحه رندر می‌کند. در بالا importها را می‌بینیم که کامپوننت‌های مورد نیاز را import می‌کنند.مرحله چهارم - اضافه کردن لیست کشورهامی‌خواهیم کمی پا را فراتر گذاشته و این صفحه بدردنخور را به صفحه دلخواهمان تغییر دهیم. همانطور که اشاره شد، قصد نمایش لیستی از کشورها را داریم، برای این منظور نیاز به پیاده سازی کد به شکل زیر است:همانطور که مشاهده می‌کنید ما App.js را به روز کردیم و موارد زیر را به آن اضافه نمودیم:یک Flatlist برای نمایش لیست کشورهایک dataset برای نمایش روی لیستهمچنین style را نیز به روز کردیم تا شامل style متن ما نیز شوداپلیکیشن‌تان را اگر هنوز اجرا نکرده‌اید، اجرا کنید و آنچه می‌بینید باید به شکل زیر باشد:حالا ما چیزی برای کار کردن داریم. اما داده‌هایمان استاتیک هستند و ما علاقه‌مندیم که یک لیست پویا و طولانی‌تر داشته‌باشیم، که قابلیت تغییر داشته و تغییرات را در صفحه برنامه خود ببینیم. پس ما نیاز داریم که UIای Reactive داشته باشیم تا بتوانیم مدل داده‌ایمان را تغییر دهیم. این اساس مفهوم React و ًReact Native است. پس ما نیاز به معرفی مفهوم state داریم.یک کامپوننت React Native داده را در درون state خود نگه می‌دارد و هر زمان که نیاز باشد آن را تغییر می‌دهد، UI نیز دوباره رندر شده و تغییرات را نشان می‌دهد. بیایید متغییر state اضافه کنیم و اسم آن را countriesData بگذاریم که لیست پویای کشورها را روی صفحه نمایش می‌دهد.const [countriesData, setCountriesData] = useState([])در React Native به این useState به اصطلاح hook گفته می شوند. بر اساس مقاله رسمی React در رابطه با hook: یک hook یک تابع مخصوص است که به شما اجازه‌ قلاب شدن به ویژگی‌های ‌React را می‌دهد.ما تازه یک state برای داده کشورها ساخته‌ایم که مقدار پیشفرض آرایه خالی دارد و یک تابع به نام setCountriesData برای بروزرسانی مقدار counriesData داریم. به عنوان مثال، صدا زدن تابع زیر مقدار countriesData را به مقدار ورودی تغییر خواهد داد.setCountriesData([1,2,3])یک hook مهم دیگه useEffect است که در تکه کد زیر پیاده‌سازی شده است. به آسانی هر کدی که داخل useEffect قرار بگیرد، زمانی که کامپوننت mount شود، اجرا خواهد شد.لطفاً از آنچه در حال رخ دادن است، نترسید. ما داریم از fetch API برای دریافت داده‌ی کشور‌ها از یک API استفاده می‌کنیم و لیستی از کشورهای آفریقایی را نمایش می‌دهیم. این Fetch API ابزاریست که به وسیله آن می‌توانید در React Native درخواست شبکه ایجاد کنید. همانطور که متوجه شدید، ما از یک آدرس API رایگان در وبسایت restcountries.eu استفاده کردیم که به ما یک آبجکت JSON برمی‌گرداند که در بند then متد واکشی پارس شده است. زمانی که عملیات پارس با موفقیت انجام شود، ما متغیر state کشورها را با لیست جدیدی از کشورها که به تازگی دریافت کرده‌ایم، بروزرسانی می‌کنیم. در نتیجه‌ی بروزرسانی آبجکت state، ظاهر UI نیز دوباره رندر شده و ما می‌توانیم کشورها را روی صفحه ببینیم.مرحله پنجم - به اولین اپلیکیشن React Native خود style بدهیدحالا می‌خواهیم روی طراحی اپلیکیشن تمرکز کنیم و تغییراتی را روی دیکشنری styles انجام دهیم.همانطور که مشاهده می‌کنید، رنگ پس‌زمینه را با تغییر صفت backgroundColor به کد hex #483d8b به بنفش تغییر دادیم.قواعد بسیار شبیه CSS است، با اندکی تفاوت. اما ایده اصلی یکسان است، پس اگر CSS را بلد باشید به راحتی می‌توانید به اپلیکیشن React Native خود را استایل دهید. همچنین اگر با flexbox layout آشنایی داشته باشید که چه بهتر.نتیجه‌گیریما بزرگترین نقاط قوت استفاده از React Native را برای فرایند توسعه نرم‌افزار موبایلی مشاهده کردیم و دیدیم که ساخت اپلیکیشن برای اندروید و iOS چه ساده است.ما Expo را که یک ابزار اعجاب‌انگیز برای تازه‌کارهای React Native است را راه اندازی کردیم و از آن برای ساخت اولین پروژه React Native خود استفاده کردیم.پس از آن در رابطه با کامپوننت‌های عملگرا، hook و state آموختیم. از Fetch API برای دریافت داده از طریق درخواست شبکه استفاده کردیم و کامپوننت‌های ظاهری را با تغییر در stylesheet طراحی کردیم.تبریک می‌گویم! شما ازین پس قادرید که اپلیکیشن React Native بسازید. حالا زمان آن است که کامپوننت‌های پیشرفته‌تر را نیز فرا بگیرید تا بتوانید یک اپلیکیشن با عملکردهای پیچیده‌تر بسازید که بتوانید آن را در App Store و Google Play Store منتشر کنید. لیستی از بهترین منابع React Native  را بررسی کنید تا درباره آن بیشتر بدانید و بتوانید به مراحل بعدی بروید.مراحل بعدیحالا که شما در رابطه با بهترین ابزارهای توسعه React Native دانش خوبی کسب کرده‌اید، بهتر است که به موضوعات زیر نیز نگاهی بیاندازید: Firebase —  Push notifications | Firebase storageHow To in React Native — WebView | Gradient| Camera| Adding GIF| Google Maps | Redux | Debugging |Hooks| Dark mode | Deep-link | GraphQL | AsyncStorage | Offline | Chart | Walkthrough  | Geolocation| Tinder swipe | App icon | REST API Payments — Apple Pay | StripeAuthentication — Google Login| Facebook login | Phone Auth |Best Resource – App idea | Podcasts | Newsletters | App templatesاگر شما به یک پایه برای شروع اپلیکیشن ‌React Native خود نیاز دارید، می‌توانید با کمک گرفتن از قالب‌های React Native آماده، کار خود را چندین ماه به جلو بیاندازید.منبع مقاله: https://www.instamobile.io</description>
                <category>محمد صادق پنادگو</category>
                <author>محمد صادق پنادگو</author>
                <pubDate>Tue, 10 Nov 2020 14:02:37 +0330</pubDate>
            </item>
                    <item>
                <title>الگوی طراحی VIPER برای توسعه اپلیکیشن iOS به زبان Swift</title>
                <link>https://virgool.io/@mspanadgoo/%D8%A7%D9%84%DA%AF%D9%88%DB%8C-%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-viper-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AA%D9%88%D8%B3%D8%B9%D9%87-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-ios-%D8%A8%D9%87-%D8%B2%D8%A8%D8%A7%D9%86-swift-wlwklgkdtmil</link>
                <description>الگوهای طراحی هدیه خداوند به توسعه دهندگان نرم افزار است که شامل تکنیک هایی است که تکرار کد را به حداقل می رساند از اتصالات زیاد جلوگیری میکند و یک روش استاندارد مشترک کد نویسی و یک راه حل عمومی برای وضعیت های تکرار شونده هنگام توسعه نرم افزار فراهم می کند. در این متن، ما با یک الگوی طراحی به نام VIPER برای توسعه نرم افزار iOS آشنا خواهیم شد. (View, Interactor, Presenter, Entity and Router)پیشنیازها: قبل از شروع VIPER، اطمینان پیداکنید که در رابطه با الگوهای طراحی معماری و الگوهای Delegation اطلاعات کافی داشته باشید.الگوی طراحی VIPER چیست؟الگوی VIPER یک الگوی طراحی است که براساس الگوی “تفکیک نگرانی” (separation of concern) را کار می‌کند. بسیار شبیه به MVP و MVC از رویکرد ماژولار پیروی می کند. هر ویژگی (feature) یک ماژول. هر ماژول VIPER پنج (گاهی اوقات ۴) کلاس مختلف با نقش‌های متمایز دارد. هیج کلاسی پایش را فراتر از هدف تعیین شده‌اش نخواهد گذاشت. این کلاسها از یکدیگر پیروی می‌کنند.‌یک - View : کلاسی که که تمام کدهای مرتبط با نمایش رابط اپلیکیشن به کاربر و گرفتن پاسخ‌های کاربر را در خود دارد. View با دریافت پاسخ، Presenter را آگاه می‌کند.دو - Presenter : هسته یک ماژول را تشکیل می‌دهد و پاسخ کاربر را از View می گیرد و براساس آن کار می‌کند. تنها کلاسی است که با تمام اجزای دیگر ارتباط دارد. مسیریاب (router) را برای سیم-کشی (wire-framing)، تعامل کننده (Interactor) را برای واکشی داده (درخواست های شبکه ای یا داده های محلی)، View را برای بروزرسانی UI فراخوانی می‌کند.سه - Router (مسیریاب): سیم کشی را انجام می‌دهد. به Presenter گوش می دهد تا بداند باید کدام صفحه را نمایش دهد و اجرا کند.چهار - Entity : شامل کلاس های ساده مدل هستند که توسط interactor استفاده می‌شوند.در زیر یک نمای ساده ای از VIPER را مشاهده می‌کنید.با مثال VIPER را یاد بگیریم...یک پروژه ساده آماده شده تا به کمک آن بتوان VIPER را شرح داد. این پروژه روی GitHub در دسترس است. یک اپلیکیشن خیلی ساده که سرخط اخبار را از یک API خارجی واکشی و نمایش می‌دهد.الگوی Viper یک معماری delegation محور است. به همین دلیل است که ارتباطات بین لایه‌های مختلف با استفاده از delegation انجام می‌شود. یک لایه، لایه دیگر را با استفاده از پروتکل فراخوانی می‌کند. فراخوانی لایه‌، یک تابع از پروتکل را فراخوانی می‌کند. لایه گوش دهنده با پروتکل مطابقت دارد و تابع را پیاده سازی می‌کند.در ادامه به توصیف نحوه پیاده سازی VIPER در یکی از پروژه های نمونه می‌پردازیم.پروتکل‌هابرای هر پروتکل نیاز به ساخت یک فایل مجزا است.برای نامگذاری پروتکل‌ها از یک قرارداد نام گذاری پیروی شده است، به عنوان مثال viewToPresenterProtocol. پس از این نام گذاری در می‌یابیم که این یک پروتکل است که توسط presenter برای گوش دادن به آنچه view می‌گوید، ساخته می‌شود.PresenterToViewProtocol:در این پروتکل presenter فراخوانی می‌کند، View گوش می‌دهد. Presenter یک مرجع از این پروتکل برای دسترسی به View دریافت می‌کند.ViewToPresenterProtocol:در این پروتکل View فراخوانی می‌کند، Presenter گوش می‌دهد.InteractorToPresenterProtocol:در این پروتکل Interactor فراخوانی می‌کند، Presenter گوش می‌دهد.PresenterToInteractorProtocol:در این پروتکل Presenter فراخوانی می‌کند، Interactor گوش می‌دهد.PresenterToRouterProtocol:در این پروتکل Presenter فراخوانی می‌کند، Router گوش می‌دهد.جریان اپلیکیشنباید بدانیم که View یک مرجعی از ViewToPresenterProtocol برای دسترسی به Presenter دارد که آن را با PresenterToViewProtocol مطابقت می‌دهد. در تابع viewDidLoad آن تابع updateView پروتوکل را فراخوانی می‌کند.//View
var presenter: ViewToPresenterProtocol?
override func viewDidLoad() {        
   super.viewDidLoad()        
   presenter?.updateView()           
}از طرف دیگر Presenter با ViewToPresenterProtocol مطابقت می‌دهد و تابع updateView را پیاده سازی می‌کند.//Presenter
var interactor: PresentorToInteractorProtocol?
func updateView() {
   interactor?.fetchLiveNews()    
}داخل updateView()، Presenter به Interactor می‌گوید تا تعدادی داده‌ی اخبار زنده را واکشی کند.خود Interactor با PresenterToInteractorProtocol تطابق دارد. پس تابع fetchLiveNews را پیاده سازی می‌کند. این تابع سعی می‌کند تا یک فراخوانی شبکه‌ای ایجاد کند و داده را واکشی کند که برای دسترسی به Presenter یک مرجع از InteractorToPresenterProtocol دارد.//Interactor
var presenter: InteractorToPresenterProtocol?اگر فراخوانی شبکه با موفقیت داده را واکشی کند، تابع زیر فراخوانی می‌شود.//Interactor
self.presenter?.liveNewsFetched(news: (arrayObject?[0])!)و اگر نه تابع زیر://Interactor
self.presenter?.liveNewsFetchedFailed()حالا presenter همچنین با InteractorToPresenterProtocol تطابق دارد و این توابع را پیاده سازی می‌کند.//presenter
func liveNewsFetched(news: LiveNewsModel) {
        view?.showNews(news: news)
}        
func liveNewsFetchedFailed(){
        view?.showError()    
}پس به View می‌گوید که یا اخبار را نشان دهد و یا خطا نشان دهد.حالا View با PresenterToViewProtocol تطابق دارد، بنابراین توابع showNews() و showError() را پیاده سازی می‌کند. در این دو تابع View با داده‌های واکشی شده یا خطا پر می‌شوند.لایه Entityدر بالا در بخش جربان اپلیکیشن در رابطه با entity بحث نشد. Entity به صورت مستقیم با جریان اپلیکیشن در ارتباط نیست. اما آن یک قسمت حیاتی برای Interactor است. لایه Entity یک مدل است که interact برای ساخت objectها از داده‌های واکشی شده از آن استفاده می‌کند.بخش Routerبخش Router از سیم کشی یک اپلیکیشن نگهداری می‌کند. تغییر صفحه در یک برنامه مساله بسیار پایه‌ای است. در VIPER، لایه‌ی Router مسئولیت اجرای آن را برعهده دارد.قبلا به این نتیجه رسیدیم که در معماری VIPER برای هر بخش عملیاتی به یک ماژول مجزا احتیاج داریم و هر ماژول دارای ۵ لایه است. یک Presenter، Router را برای ساخت یک ماژول جدید فراخوانی می‌کند. پس از آن Router تمام کلاس لایه را مقداردهی میکند و ماژول را برمیگرداند.در پروژه نمونه من هیچ تغییر ماژول درون برنامه‌ای وجود ندارد. اما مسیریابی زمانه که اپلیکیشن برای اولین بار اجرا شود، اتفاق می‌افتد. پس در AppDelegate در تابع didFinishLaunchigWithOptions تابع createModule درون Router فراخوانی میشود که یک ماژول برمیگرداند. کلاس UIWindow، View ماژول جدید را نمایش می‌دهد.چرا و چه زمانی از VIPER استفاده کنیم؟الگوی VIPER از یک معماری تمیز پیروی می‌کند که هر ماژول از دیگری متمایز و ایزوله است. پس به منظور انجام تغییرات یا رفع باگ به راحتی می‌توان تنها ماژول مورد نظر را به روز کرد. همچنین برای داشتن رویکرد ماژولار، VIPER محیط مناسبی به منظور انجام ‌َUnit Test فراهم می‌کند. از آنجایی که هر ماژول از دیگری مجزا است، از جفت شدگی‌های کم (low coupling) به خوبی جلوگیری می‌کند. پس تقسیم کار بین چند توسعه دهنده به راحتی امکان‌پذیر است.الگوی VIPER باید زمانی که نیازمندی‌های اپلیکیشن به خوبی شکل گرفته‌اند استفاده شود. کار با نیازمندی‌های همیشه درحال تغییر ممکن است سردرگمی و کد کثیف ایجاد کند. پس نباید از آن در پروژه‌های کوچکی که MVP و MVC نیازشان را برطرف می‌کند، استفاده کرد. همچنین VIPER باید زمانی استفاده شود که تمامی توسعه‌دهندگان درگیر پروژه الگوی آن را درک کرده باشند.ابزارهای VIPERاگر می‌خواهید از VIPER در یک پروژه استفاده کنید، هوشمندانه‌ترین کار این است که از یک تولید کننده ساختار ماژول خودکار استفاده کنید. در غیر اینصورت ساخت فایل برای ماژول ها بسیار طاقت فرساست. چند تولید کننده آنلاین برای این منظور وجود دارد:GenerambaVIPER CodeVIPER Genنتیجه گیریبه مانند تمام الگوهای طراحی دیگر، VIPER نیز خود بیانگر (self-explanatory) است. اگر شخصی بخواهد کل تصویر راه بفهمد باید دستش را آلوده کند. نصیحت من این است که ابتدا با یک اپلیکیشن ساده شروع کنید و کل مراحل را از منابع آنلاین بخوانید.موفق باشیدمنبع اصلی: Mediumمنابع:https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52https://medium.com/@ankoma22/the-good-the-bad-and-the-ugly-of-viper-architecture-for-ios-apps-7272001b5347https://github.com/MindorksOpenSource/iOS-Viper-Architecture/tree/master/iOS-Viper-Architecturehttps://sourcemaking.com/design_patterns</description>
                <category>محمد صادق پنادگو</category>
                <author>محمد صادق پنادگو</author>
                <pubDate>Mon, 09 Nov 2020 18:59:29 +0330</pubDate>
            </item>
            </channel>
</rss>