网络请求模块封装实战(Objective-C)

设计思路
  1. 高复用性:通过单例模式+回调机制实现
  2. 功能完整:支持GET/POST/上传/下载/缓存
  3. 错误处理:统一错误码和回调格式
  4. 扩展性:使用协议解耦具体实现
核心实现(NetworkManager类)
// NetworkManager.h
#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, HTTPMethod) {
    GET,
    POST,
    PUT,
    DELETE
};

typedef void (^NetworkSuccessBlock)(id _Nullable responseObject);
typedef void (^NetworkFailureBlock)(NSError * _Nonnull error);

@interface NetworkManager : NSObject

+ (instancetype)shared;

- (void)request:(HTTPMethod)method
             url:(NSString *)url
      parameters:(nullable NSDictionary *)params
         headers:(nullable NSDictionary *)headers
         success:(NetworkSuccessBlock)success
         failure:(NetworkFailureBlock)failure;

- (void)uploadFile:(NSData *)fileData
                url:(NSString *)url
         parameters:(nullable NSDictionary *)params
           fileName:(NSString *)fileName
           mimeType:(NSString *)mimeType
            success:(NetworkSuccessBlock)success
            failure:(NetworkFailureBlock)failure;

@end

// NetworkManager.m
#import "NetworkManager.h"

@implementation NetworkManager {
    NSURLSession *_session;
}

+ (instancetype)shared {
    static NetworkManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (instancetype)init {
    if (self = [super init]) {
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        config.timeoutIntervalForRequest = 30.0;
        _session = [NSURLSession sessionWithConfiguration:config];
    }
    return self;
}

#pragma mark - 核心请求方法
- (void)request:(HTTPMethod)method
             url:(NSString *)url
      parameters:(NSDictionary *)params
         headers:(NSDictionary *)headers
         success:(NetworkSuccessBlock)success
         failure:(NetworkFailureBlock)failure {
    
    // 1. 构建URLRequest
    NSURL *requestURL = [NSURL URLWithString:url];
    if (!requestURL) {
        failure([self errorWithCode:1001 message:@"URL格式错误"]);
        return;
    }
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
    request.HTTPMethod = [self methodString:method];
    
    // 2. 添加请求头
    [headers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        [request addValue:obj forHTTPHeaderField:key];
    }];
    
    // 3. 处理请求体 (POST/PUT)
    if (params && (method == POST || method == PUT)) {
        NSError *error;
        NSData *bodyData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
        if (!error) {
            request.HTTPBody = bodyData;
            [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        }
    }
    
    // 4. 创建任务并执行
    NSURLSessionDataTask *task = [_session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                failure(error);
            } else {
                NSError *jsonError;
                id responseObj = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
                if (jsonError) {
                    failure([self errorWithCode:1002 message:@"JSON解析失败"]);
                } else {
                    success(responseObj);
                }
            }
        });
    }];
    
    [task resume];
}

#pragma mark - 文件上传
- (void)uploadFile:(NSData *)fileData
                url:(NSString *)url
         parameters:(NSDictionary *)params
           fileName:(NSString *)fileName
           mimeType:(NSString *)mimeType
            success:(NetworkSuccessBlock)success
            failure:(NetworkFailureBlock)failure {
    
    // 1. 构建多部分请求体
    NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", NSUUID.UUID.UUIDString];
    NSMutableData *body = [NSMutableData data];
    
    // 添加参数
    [params enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) {
        [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"%@\r\n", value] dataUsingEncoding:NSUTF8StringEncoding]];
    }];
    
    // 添加文件
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", fileName] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:fileData];
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    
    // 2. 配置请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
    request.HTTPMethod = @"POST";
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)body.length] forHTTPHeaderField:@"Content-Length"];
    request.HTTPBody = body;
    
    // 3. 执行上传任务
    NSURLSessionUploadTask *task = [_session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                failure(error);
            } else {
                success(data);
            }
        });
    }];
    
    [task resume];
}

#pragma mark - 工具方法
- (NSString *)methodString:(HTTPMethod)method {
    switch (method) {
        case GET: return @"GET";
        case POST: return @"POST";
        case PUT: return @"PUT";
        case DELETE: return @"DELETE";
    }
}

- (NSError *)errorWithCode:(NSInteger)code message:(NSString *)message {
    return [NSError errorWithDomain:@"NetworkError" 
                               code:code 
                           userInfo:@{NSLocalizedDescriptionKey: message}];
}

@end

使用示例
// 发起GET请求
[[NetworkManager shared] request:GET
                            url:@"https://api.example.com/data"
                     parameters:@{@"page": @1}
                        headers:@{@"Authorization": @"Bearer token123"}
                        success:^(id responseObject) {
    NSLog(@"请求成功: %@", responseObject);
} failure:^(NSError *error) {
    NSLog(@"请求失败: %@", error.localizedDescription);
}];

// 上传图片
UIImage *image = [UIImage imageNamed:@"avatar"];
NSData *imageData = UIImageJPEGRepresentation(image, 0.8);

[[NetworkManager shared] uploadFile:imageData
                                url:@"https://api.example.com/upload"
                         parameters:@{@"userId": @"123"}
                           fileName:@"avatar.jpg"
                           mimeType:@"image/jpeg"
                            success:^(id response) {
    NSLog(@"上传成功");
} failure:^(NSError *error) {
    NSLog(@"上传失败: %@", error);
}];

设计亮点
  1. 单例全局访问:通过shared方法获取唯一实例
  2. 统一错误处理:自定义错误域和错误码
  3. 线程安全:使用GCD确保回调在主线程执行
  4. 类型安全:枚举定义HTTP方法
  5. 扩展性强:可轻松添加缓存、日志等模块
扩展建议
  1. 添加缓存层:集成NSCache实现内存缓存
  2. 网络状态监测:使用Reachability检测网络变化
  3. 请求重试机制:对特定错误自动重试
  4. 安全加固:增加HTTPS证书校验
  5. 性能监控:记录请求耗时和成功率

此实现满足高复用性需求,通过统一接口封装了网络请求的核心功能,可应用于各类iOS项目,大幅减少重复代码量。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐