You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS中如何确保页面在获取RESTful API Token后再加载?

老哥,我刚好踩过这个坑,给你几个落地性强的方案,帮你把Token获取和页面加载的逻辑理顺,彻底告别每个API都要查Token的麻烦:

方案一:用启动过渡页+异步回调控制根页面加载

这是最常用也最友好的方式,用户体验不会打折扣:

  1. 先在Storyboard里设置一个过渡加载页(比如叫LaunchLoadingVC)作为初始ViewController,页面上放个加载动画(UIActivityIndicatorView或者自定义的加载图),没有其他可交互元素。
  2. 在AppDelegate.m的didFinishLaunchingWithOptions里,先正常初始化这个加载页,然后启动你的NSURLSessionDataTask去拿Token:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 初始化加载页
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *loadingVC = [storyboard instantiateViewControllerWithIdentifier:@"LaunchLoadingVC"];
    self.window.rootViewController = loadingVC;
    [self.window makeKeyAndVisible];

    // 异步获取Token
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"你的Token接口地址"]];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (!error && data) {
                // 解析Token并保存(比如存在UserDefaults或者Keychain)
                NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                NSString *token = result[@"token"];
                [[NSUserDefaults standardUserDefaults] setObject:token forKey:@"APIToken"];
                
                // Token获取成功,切换到主页面(比如MainTabBarVC)
                UIViewController *mainVC = [storyboard instantiateViewControllerWithIdentifier:@"MainTabBarVC"];
                self.window.rootViewController = mainVC;
            } else {
                // 处理Token获取失败的情况,比如显示重试按钮
                // 可以给loadingVC加个方法,显示错误提示
                [(LaunchLoadingVC *)loadingVC showTokenError];
            }
        });
    }];
    [task resume];

    return YES;
}
  1. 这样用户打开App看到的是加载页,直到Token拿到手才会进入主页面,从根源上保证所有后续页面加载时Token已经存在。
方案二:全局Token状态管理+页面路由拦截

如果不想用过渡页,也可以搞个全局的Token管理器,统一控制页面访问:

  1. 创建一个单例类APIManager,用来管理Token状态和所有API请求:
@interface APIManager : NSObject
@property (nonatomic, assign, readonly) BOOL isTokenReady;
@property (nonatomic, copy, readonly) NSString *apiToken;
+ (instancetype)sharedManager;
- (void)fetchTokenWithCompletion:(void(^)(BOOL success))completion;
@end

@implementation APIManager
static APIManager *_sharedInstance;
@property (nonatomic, assign) BOOL isTokenReady;
@property (nonatomic, copy) NSString *apiToken;

+ (instancetype)sharedManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

- (void)fetchTokenWithCompletion:(void(^)(BOOL success))completion {
    // 这里写获取Token的异步逻辑,和之前一样
    NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:... completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (!error) {
                self.apiToken = @"解析后的Token";
                self.isTokenReady = YES;
                completion(YES);
            } else {
                self.isTokenReady = NO;
                completion(NO);
            }
        });
    }];
    [task resume];
}
@end
  1. 在AppDelegate启动时先调用fetchToken,然后在Storyboard的初始页面(比如一个空白的入口页)的viewDidAppear里检查Token状态:
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    APIManager *manager = [APIManager sharedManager];
    if (manager.isTokenReady) {
        // 跳转到主页面
        UIViewController *mainVC = [self.storyboard instantiateViewControllerWithIdentifier:@"MainTabBarVC"];
        [self presentViewController:mainVC animated:YES completion:nil];
    } else {
        // 显示加载动画,等待Token
        [manager fetchTokenWithCompletion:^(BOOL success) {
            if (success) {
                UIViewController *mainVC = [self.storyboard instantiateViewControllerWithIdentifier:@"MainTabBarVC"];
                [self presentViewController:mainVC animated:YES completion:nil];
            } else {
                // 处理错误,比如显示重试按钮
            }
        }];
    }
}
  1. 这种方式的好处是可以统一管理Token的刷新、过期重取等逻辑,后续所有API请求都通过APIManager发起,不用再单独检查Token。
方案三:同步等待Token(不推荐,仅应急用)

如果实在要在启动时同步拿到Token,也可以用信号量,但强烈不推荐,因为会阻塞主线程,可能导致App启动超时被系统杀掉:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block NSString *token = nil;

    NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:... completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (!error) {
            token = @"解析后的Token";
        }
        dispatch_semaphore_signal(semaphore);
    }];
    [task resume];

    // 等待最多5秒,避免无限阻塞
    dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));

    if (token) {
        // 正常加载主页面
        UIViewController *mainVC = [self.storyboard instantiateViewControllerWithIdentifier:@"MainTabBarVC"];
        self.window.rootViewController = mainVC;
    } else {
        // 处理Token获取失败,比如显示错误页
    }

    return YES;
}

这种方式会让App启动时卡住,用户体验很差,除非万不得已不要用。

总结一下,方案一和方案二是最优解,核心思路就是把Token获取作为页面加载的前置条件,从App启动的入口就把这个逻辑堵死,这样后续所有页面都不用再检查Token是否存在了。

内容的提问来源于stack exchange,提问作者foresightyj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:10:48