Moya !

عند تصميم أي تطبيق في غالب التطبيقات أو ربما تكاد تكون جميعها تحتاج إلى الاتصال مع الـ API وربما يواجه الأغلب مشاكل في عملية تنظيم كود التطبيق الذي يعمل عليه ويكون مبعثر هنا وهناك (أقصد بذلك الاتصال بالسيرفر لجلب البيانات) .. فالبعض ربما يقوم باستخدم URLSession او NSURLSession (بالمناسبة جميعهم نفس الشيئ ولكن حسب التسمية من Swift or ObjC !) .. فربما يلجأ البعض لجمع جميع الـ funcation في class واحد ويسميه بـ APIManager او غيره من التسميات ..

الجميل في الموضوع ان Moya اتت بشكل بمختلف كما في هذا الشكل ..

الصورة من مصدر المكتبة

وبالمناسبة هي معتمدة على Alamofire بحكم أن الأغلبية يقوم باستخدام هذه المكتبة في أغلب التطبيقات ..

من ناحية التركيب فهي سهلة جداً .. متوفرة على Swift Package Manager كذلك على مستوى cocoapods و Carthage !

Swift Package Manager

import PackageDescription

let package = Package(  
  name: "MyApp",
  dependencies: [
    .Package(url: "https://github.com/Moya/Moya", majorVersion: 8)
  ]
)

Cococpods

pod 'Moya'  

carthage

github "Moya/Moya"  

الفكرة في استخدامها هو توفير كل ما تحتاجه في التطبيق من عملية الاتصال بالسيرفر وكل مراحل تجهيز الـ Request قبل ان يتم ارساله للسيرفر ..

ما يهمنا في هذه المرحلة هو التعامل مع الـ TargetType ؟ طيب ما هو ؟

هو عبارة عن Protocol تقوم عن طريقة بـ implement كل ما تحتاجه لك APi تريد الاتصال فيها .. الكلام ربما يكون مبهم قليلا لكن بالمثال يتضح المقال ..

لنفرض أنه يوجد لدينا تطبيق ونريد من خلاله الاتصال بـ Github "المثال من الموقع نفسه" . والتطبيق الذي سنقوم بعمله سيقوم بالاتصال بالسيرفر ويدعم التالي : ١- البحث عن مستخدم معين . ٢- مشاهدة جميع مخازن الاكواد التي لديه . ٣- مشاهدة كل الفروع او branches المتواجده في مخزن معين.

سنقوم بانشاء enum كما بالأسفل .

public enum GitHub {  
    case userProfile(String)
    case userRepositories(String)
    case branches(String, Bool)
}

كذلك سنقوم بتحديد السيرفر الذي سنقوم بالاتصال به .

extension GitHub: TargetType {  
    public var baseURL: URL { return URL(string: "https://api.github.com")! }
}

كذلك سنقوم بتحديد المسار لكل API service نريد الاتصال بها ..

public var path: String {  
    switch self {
    case .userProfile(let name):
        return "/users/\(name.urlEscaped)"
    case .userRepositories(let name):
        return "/users/\(name.urlEscaped)/repos"
    case .branches(let repo, _)
        return "/repos/\(repo.urlEscaped)/branches"
    }
}

كذلك كما نعلم ان كل service تريد الاتصال بها يجب تحديد HTTP Method التي يقوم السيرفر بدعمها ! Moya وفرت ذلك وبامكانك تحديدها لك خدمة من الخدمات التي ستقوم بدعمها بتطبيقك .

public var method: Moya.Method {  
    return .get
}

كذلك كما نعلم انه في كل خدمة من الخدمات التي تود الاتصال او التواصل معها يجب عليك ارسال بعض البيانات او الـ parameters التي يحتاجاها السيرفر لخدمة التطبيق حسب ما تم طلبه من المستخدم .

public var parameters: [String: Any]? {  
    switch self {
    case .userRepositories(_):
        return ["sort": "pushed"]
    case .branches(_, let protected):
        return ["protected": "\(protected)"]
    default:
        return nil
    }
}

كذلك تحديد نوع البيانات او شكل ارسالها لك خدمة يتم توفيرها من السيرفر اما كـ Json أو UrlEncoded او غيرها من الصيغ التي تود ارسال البيانات التي لديك للسيرفر ..

public var parameterEncoding: ParameterEncoding {  
    switch self {
    case .zen:
        return JSONEncoding.default
    default:
        return URLEncoding.default
    }
}

كذلك بامكانك تحديد نوع الـ request الذي ستقوم بارساله للسيرفر اما رفع صورة او تحميل ملف للتعامل معه بما يتناسب بالسشكل التالي . وبطبيعة ان التطبيق الذي لدينا لا يتوافر فيه رفع صور او ما شبابه فسنقوم بكتابتها بالشكل التالي .

public var task: Task {  
    switch self {
    case .userProfile, .userRepositories, .branches:
        return .request
    }
}

وأخيراً يجب عليك تحديد Sample Data ويتم استخدامها في التطبيق لـ testing والتأكد من ان الكود الذي قمت بكتابته بشكل سليم .. "لن أتطرق لهذا الجزء"

public var sampleData: Data {  
    return Data() // empty Data
}

وبعد كتابة كل هذا ما هو الشيء الذي أضافتها Moya ! عند عملية استخدامها ستكون سهله كما في الأسفل .

provider = MoyaProvider<GitHub>()  
provider.request(.userProfile("SalehAlDhobaie")) { result in  
    switch result {
    case let .success(moyaResponse):
        let data = moyaResponse.data
        let statusCode = moyaResponse.statusCode
        // do something with the response data or statusCode
    case let .failure(error):
        // this means there was a network failure - either the request
        // wasn't sent (connectivity), or no response was received (server
        // timed out).  If the server responds with a 4xx or 5xx error, that
        // will be sent as a ".success"-ful response.
    }
}

ربما تشاهد الان انه الخدمات التي سيقوم التطبيق بالاتصال بها واضحه ومن الصعب الخطأ بها وأصبحت سهلة الاستخدام وكذلك من السهل اضافة خدمات اخرى كما تشاء .

بإمكانك الوصول لـ Moya عن طريق هذا الرابط : https://github.com/Moya/Moya
وهذا مثال آخر قمت بانشاءه لتسهيل الفهم .. https://github.com/SalehAlDhobaie/Moya-Example

وبس !