The introduction

Recently, we are working on SDK. We need to integrate many third-party libraries into our SDK, such as Twitter and Facebook, etc. We need to use the login, sharing and other functions of these third-party SDK in our SDK. We will not package these third-party libraries into our OWN SDK. Instead, access parties import into their projects via Pod or other means. People who use our SDK have different needs. Some use Facebook only, some use Twitter only. If someone accesses our SDK and only uses Facebook, and we ask them to import not only Facebook’s SDK, but Also Twitter’s SDK (because the SDK integrates Twitter’s stuff, and if they don’t import it, they’ll get an error), wouldn’t that be a bad idea, and they might say, We don’t even use this third party, why import their SDK?

Then the question arises: how can we integrate all the third-party SDKS in our SDK and meet the needs of the access party to import the corresponding third-party SDK?

The solution

After a period of thinking, came up with several plans:

Solution 1: Determine whether a third-party library has been imported from within the SDK, and then whether to compile some code by precompiling (this solution does not work)

Solution 2: The SDK internally calls the third-party SDK through the Way of Runtime, using classes, methods and attributes in a dynamic way (this method can)

Scheme 3: SDK is divided into two parts, one part is Framework part, the other part is source part, combining scheme 1 and Scheme 2 (the final scheme).

The following is a detailed description of these three schemes, including the difficulties encountered in the use of these three schemes as well as their advantages and disadvantages

Note: The code in this article is intended to be a simple test, and it’s best to use some discretion during normal development to avoid unnecessary complications

Plan a

So let’s go to the code. This is all the sample code for the.m file

#import "Test.h"// Check whether TwitterKit/ twtrKit. h exists, if so, it indicates that the SDK of Twitter has been imported#define TwitterSDKModule __has_include(<TwitterKit/TWTRKit.h>)// Only when the SDK of Twitter is imported#if TwitterSDKModule
@import TwitterKit;
#endif// Follow one of Twitter's protocols, and use one of Twitter's classes for object properties#if TwitterSDKModule@interface Test()< here is a protocol for Twitter > @Property (nonatomic, strong) TWTRTwitter * Twitter; @end#endif@interface Test() @property (nonatomic, copy) NSString *message; @end @implementation Test#if TwitterSDKModuleVoid loginByTwitter {[[Twitter sharedInstance]logInWithCompletion:^(TWTRSession * _Nullable session, NSError * _Nullable error) {
        
    }];
}

#endif

@end
Copy the code

Originally full of confidence, but miserably miserably move hit the face, why not this way? The reason is that when we package the Framework, the code inside our SDK is compiled, and the presence of <TwitterKit/ TwtrKit.h > is determined at the time of packaging the Framework, not whether the access project contains <TwitterKit/ TwtrKit.h >. So this approach failed

Scheme 2

Use runtime mode. We can use the Runtime method to call a class without introducing a header file.

PersonClass = NSClassFromString(@"Person"); SEL shareSEL = NSSelectorFromString(@"shared"); // Call the Person class method [personClass performSelector:shareSEL withObject:nil]; Person = [[personClass alloc] init]; NSString *name = [Person valueForKey:@"name"]; // Assign the attribute to [person]setValue:@"xiao hai" forKey:@"name"]; SEL runSEL = NSSelectorFromString(@"run"); // Instance method call [Person performSelector:runSEL withObject:nil];Copy the code

Here get the class, call the class method, get the attribute value, assign the attribute value, call the instance method, then the problem comes, how to follow the protocol, implement the protocol method, the general writing is as follows:

@interface Test ()<PersonDelegate>
@end

@implementation ViewController
- (void)eatWithPerson:(Person *)person {
    
}
@end
Copy the code

PersonDelegate (@interface Test ()); PersonDelegate (@interface Test ()); PersonDelegate (@interface Test ()); PersonDelegate (@interface Test ()); PersonDelegate (@interface Test ()); Have to write my personal understanding is convenient to write the code, reduce the error on writing code Follow agent solved, so the realization of the proxy method how to solve, the Person is unknown, write directly go up would be an error, we only need to use the Person id instead of went, why so to write, because these two kinds of writing, The method name is the same, “eatWithPerson:”, which solves the proxy-related problem perfectly. Example code is as follows:

@interface Test ()
@end

@implementation Test
- (void)eatWithPerson:(id)person {
    
}
@end
Copy the code

MXRuntimeUtils solved this problem for me, thanks to the author of MXRuntimeUtils. MXRuntimeUtils does not know the parameters of this block type, so I improved MXRuntimeUtils and added support for block type parameters. If you need to contact me, please contact me

This is the end of it, and I’m going to talk about one of the big downsides of this approach. Every place that uses a third-party SDK has to write in this way, and I can only say that it’s too tiring and cumbersome

Plan 3 (Final Plan)

Combine plan 1 with Plan 2 and improve it. I take the INTEGRATION of SDK with Facebook and Twitter as an example, and I will directly present the schematic diagram of our SDK in this area below

Let me explain this figure. The SDK is divided into two parts, Framework part and source file part. Framework part is the form in which we usually make packages into Framework, and source file part is mainly for secondary encapsulation of the interface of third-party SDK. Direct exposure of source code to SDK access (completely unrelated to our SDK internal business, code exposure to access is irrelevant). The Adapter in the source file (hereinafter referred to as A) is to integrate the third-party interfaces needed. For example, our SDK needs to integrate the login functions of Twitter and Facebook. A provides A login interface (A parameter is used to determine whether to call Twitter login or Facebook login). The secondary encapsulation of The Twitter and Facebook interfaces in the source file is encapsulated by the method used in Scheme 1. Adapter in the Framework (hereinafter referred to as B) is A translation of A, which can be called directly from other parts of the Framework. B is an internal framework file. A is an external framework file. How can B call A?

Analysis: this scheme also uses almost the same scheme as scheme one, why scheme one can not achieve the effect, and this scheme can, the reason is that in this scheme, the scheme one used in the source code. The difference between the two schemes is that we write about the compilation timing of the pre-compiled parts. In scheme one, the pre-compiled part of the code is compiled when the Framework is packaged, whereas in this scheme, the pre-compiled part of the code is compiled when the access side is compiled, which achieves the desired effect.

conclusion

If there is something wrong, you are welcome to correct it. If there is something you don’t understand, you are also welcome to discuss it together

The author’s contact information: QQ 782304472 (please specify what you see in this article, thank you)