1. CocoaPods principle

CocoaPods is a dependency management tool for iOS projects, whose source code is managed on Github.

The way CocoaPods works is to put all the dependent libraries into another project called Pods, and then make the main project depend on the Pods project, so that the source management is moved from the main project to the Pods project. The Pods project eventually compiles into a file called libPods. A, and the main project only needs to rely on this.a file.

  • Run the pre – install hooks

  • Generate the Pod Project

  • Add the POD file to the project

  • Add the corresponding framework,. A library, bundle, etc

  • Link header file to generate Target

  • Run the post – install hooks

  • Generate podfile.lock, then generate a copy of the file mainfest.lock and place it in the Pod folder. If The sandbox is not sync with The podfile.lock, it indicates that The manifest.lock and podfile.lock files are inconsistent.

  • Configure the original project file (Add Build Phase)

  • Added Embed Pods Frameworks

  • Add Copy Pod Resources where pre-install hooks and post-install hooks can be understood as callbacks, which are logic that can be executed in a podfile before or after install (generating the project but not yet written to disk). Logic is:

    Pre_install do | installer | # do some hook end prior to installation

    Post_install do | installer | # do some hook end after installation

Note: Pod Install follows the version information specified in the Podfile first, and follows the version information specified in the Podfile.lock to install the corresponding dependencies.

  • Compare the local version of Pod with that in the podfile.lock file. If the version is inconsistent, there is a risk.

  • To see if the Podfile has changed, the Add/Remove Pod dependency, if present, generates two lists, one for pods to add and one for Pods to remove.

  • Remove removed Pods if they exist (version dependencies in Podfile.lock will be removed).

  • In this case, if you are using a regular Git based CocoaPods library, you will first go to: Spec to find the pods folder, find the tag, find the PodSpec file below the tag, git Clone the code and copy it to the Pod directory

  • Directory structure. PNG ·

  • Run the pre – install hooks

  • Generate the Pod Project

  • Add the POD file to the project

  • Add the corresponding framework,. A library, bundle, etc

  • Link header file to generate Target

  • Run the post – install hooks

  • Generate podfile.lock, then generate a copy of the file mainfest.lock and place it in the Pod folder. If The sandbox is not sync with The podfile.lock, it indicates that The manifest.lock and podfile.lock files are inconsistent.

  • Configure the original project file (Add Build Phase)

  • Added Embed Pods Frameworks

  • Add Copy Pod Resources where pre-install hooks and post-install hooks can be understood as callbacks, which are logic that can be executed in a podfile before or after install (generating the project but not yet written to disk). Logic is:

    Pre_install do | installer | # do some hook end prior to installation

    Post_install do | installer | # do some hook end after installation

Note: Pod Install follows the version information specified in the Podfile first, and follows the version information specified in the Podfile.lock to install the corresponding dependencies.

3. Make your framework support Cocoapods

So let’s start with the first step, step by step, to make your own framework to support cocoapods installation (for how to install cocoapods on your own computer, please search, there are many tutorials).

First, outline the steps

  • The code is uploaded to Github
  • Create a Podspec file
  • Create a release on Github
  • Sign up for cocoapods
  • Upload the code to Cocoapods
  • Check whether the upload is successful
  • Update the framework version

Focus on

Solve cocoapods download framework

  • Xib initialization crash problem

  • The picture cannot be displayed properly

    Fix xiB initialization crash issue in cocoapods download framework

    Because UITableView and UICollectionView are used in the framework, there is a XIB file for the corresponding cell. We downloaded our framework through Cocoapods, and if the XIB existed, our usual code to initialize the XIB (below) would not work properly.

    [[[NSBundle mainBundle] loadNibNamed:@"xibName" owner:self options:nil] lastObject];
    [self.collectionView registerNib:[UINib nibWithNibName:@"xibName" bundle:nil] forCellWithReuseIdentifier:@"ZLCollectionCell"];
    Copy the code

    Because our XIB cannot be obtained through mainBundle, the solution is as follows

    #define kZLPhotoBrowserBundle [NSBundle bundleForClass:[self class]]
    
    [[kZLPhotoBrowserBundle loadNibNamed:@"ZLPhotoActionSheet" owner:self options:nil] lastObject];
    [self.collectionView registerNib:[UINib nibWithNibName:@"ZLCollectionCell" bundle:kZLPhotoBrowserBundle] forCellWithReuseIdentifier:@"ZLCollectionCell"];
    Copy the code

    After this, whether through the copy folder or the way to download and install cocoapods, can normally use xiB for initialization

    Solve cocoapods download frame pictures can not display the problem

    Xib initialization problem solved, image resources do not show is a headache. During my test, I got the following results:

    • Through the cocoapods download installation, if the xiB directly filled in the good picture, then the image resources can be directly displayed, if the code “[UIImage imageNamed:@””]” to set, then the image resources are not displayed at all

    Final solution:

    • Create the Bundle resource directory

      command+N -> Resource -> Settings Bundle

    Delete useless files carried by the bundle, add image resources to the bundle resource,

    • Change the code image path

      / / image path # define kZLPhotoBrowserSrcName (file) [@ “ZLPhotoBrowser. Bundle” stringByAppendingPathComponent: file] # define kZLPhotoBrowserFrameworkSrcName(file) [@”Frameworks/ZLPhotoBrowser.framework/ZLPhotoBrowser.bundle” stringByAppendingPathComponent:file]

    • KZLPhotoBrowserSrcName (File) is a macro that obtains the path of the picture by means of copy folder

    • KZLPhotoBrowserFrameworkSrcName (file) by cocoapods download and install the image path of macros

    Then modify the code to set the image as follows

    UIImage *img = [UIImage imageNamed:kZLPhotoBrowserSrcName(@"img.png")]? :[UIImage imageNamed:kZLPhotoBrowserFrameworkSrcName(@"img.png")];Copy the code

    Ok, good, problem solved.

    -. .

4. Componentize using CocoaPods

Some time ago with the help of CocoaPods componentization development, now summarize the steps and problems encountered, for later use, with the help of Carthage componentization of the actual principle and steps are the same.

Create a private Pod repository

  1. Create a private repository on your Git server

  2. Run the pod repo add command to add a private repository (HTSpecs is the private repository name).

    $ pod repo add HTSpecs [email protected]:IOS/HTSpecs.git

  3. Run the pod repo list command to check whether the pod is added successfully

    $ pod repo list

Create a private Pod component library

  1. Create a template project

    Pod Lib Create Project nameCopy the code

    The first issue is the development language we need to choose, in this case ObjC;

    The second question is to ask whether to include a Demo project, and generally Yes will be selected. Other questions will be selected according to the actual situation.

  2. Pod automatically generates a project in the current directory and configures the PodSpec file in the project

    S. sion indicates the version number of the current class library. // Match the tag value

    S.ource indicates the remote address of the current class library.

    S.ources_files indicates the directory where the source files of the class library are stored.

    S.resource_bundles indicates the directory for storing resource files.

    S. frameworks represents the framework that the class library depends on;

    S.static_framework = true compiles the framework in the current project to a static library, if the project contains other closed source static libraries.

    S. dependency indicates the dependent third-party class library.

    S.requires_arc = true, must be added if the project depends on a third-party library;

The other fields refer to guides.cocoapods.org/syntax/pods…

  1. Import the files you want to import into your project at the location specified in your podSpec

    After the import is complete, run git

    Git remote add origin repository address // The repository address corresponds to s.ource in the. Podspec file. Git commit -a -m 'Modify content' git tag -a tag version -m 'Modify content' // The tag version number should be the same as s. sion in the podSpec configuration file 3. Git push -u origin master git push --tagsCopy the code
  2. Use –allow-warnings to ignore warnings. It is recommended to run this command before taging to check if your own pod has been incorrectly written. When pushed to remote Specs (Step 5), This step is automatically run.

If the project depends on a private library, add –sources to specify the library’s owning specs address, as follows:

Pod Lib Lint project name. Podspec --allow-warnings = '[email protected] - sources: IOS/HTSpecs git, https://github.com/CocoaPods/Specs'Copy the code

  1. Push the current project’s Podspec file to a private source

    pod repo push HTSpecs YourPod.podspec –allow-warnings — Sources = ‘[email protected]: IOS/HTSpecs git, github.com/CocoaPods/S… ‘

After a successful submission, the private repository in GitLab can see the uploaded project configuration file

Follow these steps to submit a new version:

Check if it compiled successfully (pod lib Lint) -> submit and tag -> push to the appropriate spec(Pod repo push)Copy the code

Using private libraries

Add a reference to the private source address in the main project Podfile file:

The source '[email protected]: IOS/HTSpecs. Git' source 'https://github.com/CocoaPods/Specs.git'Copy the code

Use the following command to specify the address of the third-party remote repository to import:

Pod 'HTNetwork' : git = > '[email protected]: IOS/HTNetwork git'Copy the code

Coding note

To avoid an introduction error when provided outside Pod, references to and inheritance from third-party libraries in Pod need to be imported as follows:

#if __has_include(<AFNetworking/AFNetworking.h>)
#import <AFNetworking/AFNetworking.h>
#else
#import "AFNetworking.h"
#endif
Copy the code

First, use macros to check whether there are third-party libraries to import, if yes, this way to import, if not to directly import the source code.

.

4. Componentized middleware communication through Cocoapod

There are two main ideas for app routing design: target-action and URl-scheme

1. Target-action scheme (recommended)

With the help of THE runtime feature of OC, CTMediator solution realizes the automatic discovery of services and can be invoked between components without registration. Over url-scheme schemes, so focus on CTMediator and its use.

Before componentization, each component page intrudes into each other through import invocation and is seriously coupled

PNG before componentization

After componentization, we write the component-based methods in a Category, calling them in the form performTarget: Action: params: ShouldCacheTarget: method, which finally eliminates the dependency of middleware Mediator on components. Components no longer depend on each other. Calls between components only rely on Mediator Mediator, and Mediator does not depend on any other components.

PNG after componentization

The implementation process of CTMediator is shown in the figure below

Componentized architecture solution. PNG

Url-scheme scheme

MGJRouter

#import typedef void (^componentBlock) (NSDictionary *param); @interface URL_Roueter : NSObject + (instancetype)sharedInstance; - (void)registerURLPattern:(NSString *)urlPattern toHandler:(componentBlock)blk; - (void)openURL:(NSString *)url withParam:(id)param; @end ==================== #import "URL_Roueter.h" @interface URL_Roueter() @property (nonatomic, strong) NSMutableDictionary *cache; @end @implementation URL_Roueter + (instancetype)sharedInstance { static URL_Roueter *router; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ router = [[URL_Roueter alloc] init]; }); return router; } -(NSMutableDictionary *)cache{ if (! _cache) { _cache = [NSMutableDictionary new]; } return _cache; } - (void)registerURLPattern:(NSString *)urlPattern toHandler:(componentBlock)blk { [self.cache setObject:blk forKey:urlPattern]; } - (void)openURL:(NSString *)url withParam:(id)param { componentBlock blk = [self.cache objectForKey:url]; if (blk) blk(param); } @end #import "a_vc. h" #import" url_roue. h" @implementation A_VC Register with the RM component +(void)load{[[URL_Roueter sharedInstance]registerURLPattern:@ ' '"test://A_Action"' `toHandler:^(NSDictionary* para) {` NSString *para1 = para[@"para1"]; [[self new] action_A:para1]; }]; } -(void)viewDidLoad{ [super viewDidLoad]; UIButton *btn = [UIButton new]; [BTN setTitle: @ "calling component B" forState: UIControlStateNormal]; btn.frame = CGRectMake(100, 100, 100, 50); [btn addTarget:self action:@selector(btn_click) forControlEvents:UIControlEventTouchUpInside]; [btn setBackgroundColor:[UIColor redColor]]; self.view.backgroundColor = [UIColor blueColor]; [self.view addSubview:btn]; [[URL_Roueter sharedInstance]openURL:@"test://B_Action" withParam:@{@"para1":@" para1", @"para2":@(222),@"para3":@(333),@"para4":@(444)}]; } -(void)action_A:(NSString*)para1 { NSLog(@"call action_A: %@",para1); } @endCopy the code

Component B implements similar code.

The implementation principle is simple: each component in its own load, its external service (callback block) marked through the URl-scheme, and then registered with urL-router.

The url-router accepts the registration of each component and uses a dictionary to store the registered URL of each component and the corresponding service. Whenever another component calls the openURL method, it will go to the dictionary to find the corresponding block to execute the service provided by another component.

4. How __block works

Block does not allow you to change the value of an external variable. In this case, the value of an external variable is the memory address that a pointer points to on the stack. The function of __block is to move the address pointer of the external variable from the stack to the heap as soon as the variable is observed to be held by the block. (The stack is the red light district, the heap is the green light district). This allows you to modify the value of an external variable within a block.