IOS extension app 日志输出问题

Demo

一、前言
extension app 和 宿主app 是在两个进程里~当我们运行宿主app时,在extension app中打印的log在Xcode控制台中是不打印的,反之,运行extension app时,Xcode控制台也不会打印宿主app的log。
在平时的开发阶段,我们可以通过运行extension app来分析extension app的问题。但在发包给测试时,我们需要收集宿主app的log以及extension app的log,以便更好的分析问题。

二、如何收集App log
有一个很简单的方法:将NSlog打印信息保存到Document目录下的文件中。

// 将log输入到文件

freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);

这个方法就可以将NSLog打印的信息存储到指定的文件下

但还存在一个问题,extension app 和 宿主app是两个进程,它们分别有自己的沙盒,也就是说,extension app中存储log的文件,在宿主app中获取不到~也就是说我们需要在宿主app中获取extension app中的日志。

针对上述问题,我们可以有以下三种方案
1.通过socket,将extension app中的日志,实时传输到宿主app中,再在宿主app中存储log
2.通过接口,将log上传到服务器。宿主app和extension app分别上传log
3.通过AppGroup实现数据共享,在extension app中,将log信息存储到共享文件中,在宿主app中获取共享文件中的log信息

经测试,方案1会比较耗性能,方案2太依赖网络,也不可行
方案3可行

三、通过AppGroup实现数据共享,存储日志
1.项目中配置App Group(注意:要用到数据共享的工程都要配置 extension app 和宿主app 都需要配置)
TARGETS->Signing & Capabilities ->Capability 选中 App Group


屏幕快照 2019-10-21 下午2.54.05.png

选择或者新增一个groups,比如我的是group.com.company.test。

2.代码实现

    //运行日志
- (void)redirectNSlogToDocumentFolder:(NSString *)fileName
{
    // 日志文件path
    NSURL *url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupId];
    NSURL *fileURL = [url URLByAppendingPathComponent:fileName];
    NSString *logFilePath = [fileURL path];
    
    // 先删除已经存在的文件
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    [defaultManager removeItemAtPath:logFilePath error:nil];
    
    // 将log输入到文件 记录当前文件流
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);

}

// 读取日志
- (NSString*)readLogFromFile:(NSString *)fileName{
    
    NSURL *url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupId];
    NSURL *fileURL = [url URLByAppendingPathComponent:fileName];
    BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]];
    if (isExist) {
        NSString *str = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:nil];
        return str;
    }else{
        return nil;
    }
    
}

四、优化日志存储
我们需要对日志存储进行优化,比如自动清除之前的日志,将日志排序,以及按一定的大小分割日志。

直接上代码
EDExtNSLOGManager 用来存储extension app中的日志

EDExtNSLOGManager.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface EDExtNSLOGManager : NSObject
// 初始化 manager
+ (instancetype)sharedInstance;

// 开始存储NSLOG
- (void)startSaveNSlog;

// 获取所有日志文件Path
+ (NSArray *)getAllLogFilePath;

// 删除所有日志文件
+ (void)deletAllLog;

@end

NS_ASSUME_NONNULL_END


EDExtNSLOGManager.m


#import "EDExtNSLOGManager.h"
#import "EDAppGroupManager.h"
#import <UIKit/UIKit.h>
#import "sys/utsname.h"
#include <sys/param.h>
#include <sys/mount.h>
#include <stdio.h>

static NSString * const groupId = @"group.com.company.test"; // 换成自己开发者账号对应的groups
static NSString * const EDNSLOGDocumentDirectory = @"RUNNINGLOG";
static float const EDFreeDiskLimit = 500 *1024 *1024;    //500M
static float const EDSingleFileLimit = 100 * 1024 *1024; //100M

@interface EDExtNSLOGManager ()
{
    FILE * _currentStdout;
    FILE * _currentStderr;
}
@property (nonatomic, strong) NSString *currentLogFilePath; // 当前日志文件Path

@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) NSString *timeFlag; // 标示
@property (nonatomic, strong) NSString *appLaunchTime; // App启动时间

@end

@implementation EDExtNSLOGManager

static EDExtNSLOGManager *manager = nil;
+ (instancetype)sharedInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[EDExtNSLOGManager alloc] init];
    });
    return manager;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        NSDateFormatter *format=[[NSDateFormatter alloc] init];
        format.timeZone=[NSTimeZone localTimeZone];
        format.dateFormat=@"HH.mm";
        self.timeFlag = [format stringFromDate:[NSDate date]];
        
        NSDateFormatter *format2=[[NSDateFormatter alloc] init];
        format2.timeZone=[NSTimeZone localTimeZone];
        format2.dateFormat=@"YYYY-MM-DD HH:mm";
        self.appLaunchTime = [format2 stringFromDate:[NSDate date]];
        
        NSFileManager *fmManager = [NSFileManager defaultManager];
        BOOL isExist = [fmManager fileExistsAtPath:[self getLogDirectory]];
        if (!isExist) {
            [fmManager createDirectoryAtPath:[self getLogDirectory] withIntermediateDirectories:YES attributes:nil error:nil];
        }

    }
    return self;
}

// 开启日志
- (void)startSaveNSlog {
    // 清除7天前的日志
    [self cleanBeforeLogWithDays:7];
    
    long long freeDiskSize = [self checkFreeDiskSpaceInBytes];
    if (freeDiskSize > EDFreeDiskLimit) {
        // 开启日志
        [self redirectNSlogToDocumentFolder];
    }else {
        // 关闭日志
        NSLog(@"存储空间不足,关闭日志存储");
        [self closeSaveNSLog];
    }
    
    // 打印app及系统信息
    [self printAppAndSystemInfo];
    

    __weak typeof(self) weakSelf = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:30*60 repeats:YES block:^(NSTimer * _Nonnull timer) {
        __strong typeof(self) self = weakSelf;
        // 如果当前文件大小 大于100M 另起文件
        long long fileSize = [self getFileSizeForPath:self.currentLogFilePath];
        
        if (fileSize > EDSingleFileLimit) {
            long long freeDiskSize = [self checkFreeDiskSpaceInBytes];
            if (freeDiskSize > EDFreeDiskLimit) {
                NSLog(@"当前文件大小达到上限 切换文件");
                [self closeSaveNSLog];
                [self redirectNSlogToDocumentFolder];
                NSLog(@"App启动时间:%@",self.appLaunchTime);
            }else{
                NSLog(@"存储空间不足,关闭日志存储");
                [self closeSaveNSLog];
            }
            
        }
    }];
    
}

//运行日志
- (void)redirectNSlogToDocumentFolder
{
    // 日志文件path
    NSString *documentDirectory = [self getLogDirectory];
    NSString *fileName = [self getLogFileName];
    
    NSString *logFilePath = [documentDirectory stringByAppendingPathComponent:fileName];
    
    // 先删除已经存在的文件
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    [defaultManager removeItemAtPath:logFilePath error:nil];
    
    // 将log输入到文件 记录当前文件流
    _currentStdout = freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
    _currentStderr = freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
    
    // 记录当前日志文件Path
    self.currentLogFilePath = logFilePath;
    
    [[EDAppGroupManager sharedInstance] setObject:logFilePath forKey:@"EDExtNSLOG_CurrentLogFilePath_Key"];
    
 
}

// 获取日志文件夹Path
- (NSString *)getLogDirectory {
    NSURL *appGroupUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupId];
    NSString *docmentPath= [[appGroupUrl URLByAppendingPathComponent:EDNSLOGDocumentDirectory] path];
    return docmentPath;
}

// 获取日志文件名
- (NSString *)getLogFileName {
    
    NSDateFormatter *format=[[NSDateFormatter alloc] init];
    format.timeZone=[NSTimeZone localTimeZone];
    format.dateFormat=@"yyyy.MM.dd.HH.mm.ss";
    NSString *time=[format stringFromDate:[NSDate date]];
    NSString *fileName = [NSString stringWithFormat:@"RUNLOG %@&%@&Ext.txt",time,self.timeFlag];
    
    return fileName;
}

// 关闭日志存储
- (void)closeSaveNSLog {
    if (_currentStdout) {
       fclose(_currentStdout);
    }
    
    if (_currentStderr) {
        fclose(_currentStderr);
    }
    
}

#pragma mark - - 日志管理 获取/删除
// 为什么不使用实例方法
// 避免EDExtNSLOGManager在两个进程里各初始化一遍
// EDExtNSLOGManager 在extension app内初始化了,如果在app内使用EDExtNSLOGManager的实例方法,
// 也会再次初始化EDExtNSLOGManager,这样会造成一定的资源浪费
// 获取所有日志Path
+ (NSArray *)getAllLogFilePath {
    NSURL *appGroupUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupId];
    NSString *documentsPath = [[appGroupUrl URLByAppendingPathComponent:EDNSLOGDocumentDirectory] path];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error = nil;
    NSArray *allLogNameArray = [[[fileManager contentsOfDirectoryAtPath:documentsPath error:&error] reverseObjectEnumerator] allObjects];
    
    NSMutableArray *array = [[NSMutableArray alloc] init];
    for (NSString *fileName in allLogNameArray) {
        if ([[fileName pathExtension] isEqualToString:@"txt"]){
            [array addObject:[documentsPath stringByAppendingPathComponent:fileName]];
        }
    }
    
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
    NSArray *fileArray = [NSMutableArray arrayWithArray:[array sortedArrayUsingDescriptors:@[sortDescriptor]]];
    return fileArray;
    
}
// 删除所有日志
+ (void)deletAllLog{
    NSURL *appGroupUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupId];
    NSString *documentsPath = [[appGroupUrl URLByAppendingPathComponent:EDNSLOGDocumentDirectory] path];
    
    NSFileManager *fm = [NSFileManager defaultManager];
    NSArray *allPath = [[[fm contentsOfDirectoryAtPath:documentsPath error:nil] reverseObjectEnumerator] allObjects];
    // 在App内执行delete方法时,重新初始化了manager,因而currentLogFilePath直接获取不到
    NSString *currentLog = [[EDAppGroupManager sharedInstance] objectForKey:@"EDExtNSLOG_CurrentLogFilePath_Key"];
    // 删除当前所有日志,除正在写入的日志外
    for (NSString *fileName in allPath) {
        if ([fileName containsString:@"RUNLOG"] && ![currentLog containsString:fileName]){
            NSString *logPath = [documentsPath stringByAppendingPathComponent:fileName];
            [fm removeItemAtPath:logPath error:nil];
        }

    }
    
}

#pragma mark - - 打印系统信息
// 打印系统信息
- (void)printAppAndSystemInfo {
    
    NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
    
    NSLog(@"App版本 VersionName:%@ , VersionCode:%@ , 包名:%@",[infoDictionary objectForKey:@"CFBundleShortVersionString"],[infoDictionary objectForKey:@"CFBundleVersion"],[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]);
    
    struct utsname systemInfo;
    uname(&systemInfo);
    NSString *deviceString = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
    
    NSLog(@"iOS系统版本:%@ , iPhone手机型号:%@",[[UIDevice currentDevice] systemVersion],deviceString);
}

#pragma mark - - 清除几天前的日志
// 清除几天前的日志
- (void)cleanBeforeLogWithDays:(int)days {
   
    NSString *documentsPath = [self getLogDirectory];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *allLogNameArray = [[[fileManager contentsOfDirectoryAtPath:documentsPath error:nil] reverseObjectEnumerator] allObjects];
    
    for (NSString *logName in allLogNameArray) {
        if ([logName containsString:@"RUNLOG"]){
            NSString *logPath = [documentsPath stringByAppendingPathComponent:logName];
            if ([fileManager fileExistsAtPath:logPath]){
                
                NSDate *logCreateDate = [[fileManager attributesOfItemAtPath:logPath error:nil] fileCreationDate];
                NSDate *currentDate = [NSDate date];
                NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
                NSDateComponents *delta = [calendar components:NSCalendarUnitDay fromDate:logCreateDate toDate:currentDate options:0];
                if (delta.day>days) {
                    [[NSFileManager defaultManager] removeItemAtPath:logPath error:nil];
                }
                
            }
        }
        
    }
    
    
}


#pragma mark - -  文件大小
// 获取文件的大小
- (long long)getFileSizeForPath:(NSString *)logPath {
    long long fileSize = 0;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:logPath]){
        fileSize = [[fileManager attributesOfItemAtPath:logPath error:nil] fileSize];
    }
    NSLog(@"当前文件大小:%lld",fileSize);
    return fileSize;
}

// 获取手机存储空间
- (long long)checkFreeDiskSpaceInBytes{
    struct statfs buf;
    long long freeSpace = -1;
    if (statfs("/var", &buf) >= 0) {
        freeSpace = (long long)(buf.f_bsize * buf.f_bavail);
    }
    NSLog(@"当前存储空间:%lld",freeSpace);
    return freeSpace;
}

@end

EDNSLogManager 用于存储宿主app日志
EDNSLogManager.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN

@interface EDNSLogManager : NSObject
// 初始化 manager
+ (instancetype)sharedInstance;

// 开始存储NSLOG
- (void)startSaveNSlog;

// 获取所有日志文件Path
- (NSArray *)getAllLogFilePath;

// 删除所有日志文件
- (void)deletAllLog;


@end

NS_ASSUME_NONNULL_END

EDNSLogManager.m

#import "EDNSLogManager.h"
#import "sys/utsname.h"
#include <sys/param.h>
#include <sys/mount.h>
#include <stdio.h>
static NSString * const EDNSLOGDocumentDirectory = @"RUNNINGLOG";
static float const EDFreeDiskLimit = 500 *1024 *1024;    //500M
static float const EDSingleFileLimit = 100 * 1024 *1024; //100M

@interface EDNSLogManager ()
{
    FILE * _currentStdout;
    FILE * _currentStderr;
}
@property (nonatomic, strong) NSString *currentLogFilePath; // 当前日志文件Path

@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) NSString *timeFlag; // 标示
@property (nonatomic, strong) NSString *appLaunchTime; // App启动时间

@end

@implementation EDNSLogManager
static EDNSLogManager *manager = nil;
+ (instancetype)sharedInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[EDNSLogManager alloc] init];
    });
    return manager;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        NSDateFormatter *format=[[NSDateFormatter alloc] init];
        format.timeZone=[NSTimeZone localTimeZone];
        format.dateFormat=@"HH.mm";
        self.timeFlag = [format stringFromDate:[NSDate date]];
        
        NSDateFormatter *format2=[[NSDateFormatter alloc] init];
        format2.timeZone=[NSTimeZone localTimeZone];
        format2.dateFormat=@"YYYY-MM-DD HH:mm";
        self.appLaunchTime = [format2 stringFromDate:[NSDate date]];
        
        NSFileManager *fmManager = [NSFileManager defaultManager];
        BOOL isExist = [fmManager fileExistsAtPath:[self getLogDirectory]];
        if (!isExist) {
            [fmManager createDirectoryAtPath:[self getLogDirectory] withIntermediateDirectories:YES attributes:nil error:nil];
        }

    }
    return self;
}

- (void)startSaveNSlog {
    // 清除7天前的日志
    [self cleanBeforeLogWithDays:7];
    
    long long freeDiskSize = [self checkFreeDiskSpaceInBytes];
    if (freeDiskSize > EDFreeDiskLimit) {
        // 开启日志
        [self redirectNSlogToDocumentFolder];
    }else {
        // 关闭日志
        NSLog(@"存储空间不足,关闭日志存储");
        [self closeSaveNSLog];
    }
    
    // 打印app及系统信息
    [self printAppAndSystemInfo];
    
    
    __weak typeof(self) weakSelf = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:30*60 repeats:YES block:^(NSTimer * _Nonnull timer) {
        __strong typeof(self) self = weakSelf;
        // 如果当前文件大小 大于100M 另起文件
        long long fileSize = [self getFileSizeForPath:self.currentLogFilePath];
        if (fileSize > EDSingleFileLimit) {
            long long freeDiskSize = [self checkFreeDiskSpaceInBytes];
            if (freeDiskSize > EDFreeDiskLimit) {
                NSLog(@"当前文件大小达到上限 切换文件");
                [self closeSaveNSLog];
                [self redirectNSlogToDocumentFolder];
                NSLog(@"App启动时间:%@",self.appLaunchTime);
            }else{
                NSLog(@"存储空间不足,关闭日志存储");
                [self closeSaveNSLog];
            }
            
        }
    }];
    
}

//运行日志
- (void)redirectNSlogToDocumentFolder
{
    // 日志文件path
    NSString *documentDirectory = [self getLogDirectory];
    NSString *fileName = [self getLogFileName];
    
    NSString *logFilePath = [documentDirectory stringByAppendingPathComponent:fileName];
    
    // 先删除已经存在的文件
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    [defaultManager removeItemAtPath:logFilePath error:nil];
    
    // 将log输入到文件 记录当前文件流
    _currentStdout = freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
    _currentStderr = freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
    
    // 记录当前日志文件Path
    self.currentLogFilePath = logFilePath;
 
}

// 获取日志文件夹Path
- (NSString *)getLogDirectory {
//    NSString *docmentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) safeObjectAtIndex:0];
    
     NSString *docmentPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:EDNSLOGDocumentDirectory];
    return docmentPath;
}

// 获取日志文件名
- (NSString *)getLogFileName {
    
    NSDateFormatter *format=[[NSDateFormatter alloc] init];
    format.timeZone=[NSTimeZone localTimeZone];
    format.dateFormat=@"yyyy.MM.dd.HH.mm.ss";
    NSString *time=[format stringFromDate:[NSDate date]];
    NSString *fileName = [NSString stringWithFormat:@"RUNLOG %@&%@.txt",time,self.timeFlag];
    
    return fileName;
}




// 关闭日志存储
- (void)closeSaveNSLog {
    if (_currentStdout) {
       fclose(_currentStdout);
    }
    
    if (_currentStderr) {
        fclose(_currentStderr);
    }
    
}

#pragma mark - - 日志管理 获取/删除
// 获取所有日志Path
- (NSArray *)getAllLogFilePath {
    
    NSString *documentsPath = [self getLogDirectory];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error = nil;
    NSArray *allLogNameArray = [[[fileManager contentsOfDirectoryAtPath:documentsPath error:&error] reverseObjectEnumerator] allObjects];
    
    NSMutableArray *array = [[NSMutableArray alloc] init];
    for (NSString *fileName in allLogNameArray) {
        if ([[fileName pathExtension] isEqualToString:@"txt"]){
            [array addObject:[documentsPath stringByAppendingPathComponent:fileName]];
        }
    }
    
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
    NSArray *fileArray = [NSMutableArray arrayWithArray:[array sortedArrayUsingDescriptors:@[sortDescriptor]]];
    return fileArray;
    
}
// 删除所有日志
- (void)deletAllLog{
    NSString *documentsPath = [self getLogDirectory];
    
    NSFileManager *fm = [NSFileManager defaultManager];
    NSArray *allPath = [[[fm contentsOfDirectoryAtPath:documentsPath error:nil] reverseObjectEnumerator] allObjects];
    // 删除当前所有日志,除正在写入的日志外
    for (NSString *fileName in allPath) {
        if ([fileName containsString:@"RUNLOG"] && ![self.currentLogFilePath containsString:fileName]){
            NSString *logPath = [documentsPath stringByAppendingPathComponent:fileName];
            [fm removeItemAtPath:logPath error:nil];
        }
    }
    
}


#pragma mark - - 清除几天前的日志
// 清除几天前的日志
- (void)cleanBeforeLogWithDays:(int)days {
   
    NSString *documentsPath = [self getLogDirectory];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *allLogNameArray = [[[fileManager contentsOfDirectoryAtPath:documentsPath error:nil] reverseObjectEnumerator] allObjects];
    
    for (NSString *logName in allLogNameArray) {
        if ([logName containsString:@"RUNLOG"]){
            NSString *logPath = [documentsPath stringByAppendingPathComponent:logName];
            if ([fileManager fileExistsAtPath:logPath]){
                
                NSDate *logCreateDate = [[fileManager attributesOfItemAtPath:logPath error:nil] fileCreationDate];
                NSDate *currentDate = [NSDate date];
                NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
                NSDateComponents *delta = [calendar components:NSCalendarUnitDay fromDate:logCreateDate toDate:currentDate options:0];
                if (delta.day>days) {
                    [[NSFileManager defaultManager] removeItemAtPath:logPath error:nil];
                }
                
            }
        }
        
    }
    
    
}

#pragma mark - -打印系统信息
// 打印系统信息
- (void)printAppAndSystemInfo {
    
    NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
    
    NSLog(@"App版本 VersionName:%@ , VersionCode:%@ , 包名:%@",[infoDictionary objectForKey:@"CFBundleShortVersionString"],[infoDictionary objectForKey:@"CFBundleVersion"],[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]);
    
    struct utsname systemInfo;
    uname(&systemInfo);
    NSString *deviceString = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
    
    NSLog(@"iOS系统版本:%@ , iPhone手机型号:%@",[[UIDevice currentDevice] systemVersion],deviceString);
}

#pragma mark - - 文件大小
// 获取文件的大小
- (long long)getFileSizeForPath:(NSString *)logPath {
    long long fileSize = 0;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:logPath]){
        fileSize = [[fileManager attributesOfItemAtPath:logPath error:nil] fileSize];
    }
    NSLog(@"当前文件大小:%lld",fileSize);
    return fileSize;
}

// 获取手机存储空间
- (long long)checkFreeDiskSpaceInBytes{
    struct statfs buf;
    long long freeSpace = -1;
    if (statfs("/var", &buf) >= 0) {
        freeSpace = (long long)(buf.f_bsize * buf.f_bavail);
    }
    NSLog(@"当前存储空间:%lld",freeSpace);
    return freeSpace;
}

@end

EDAppGroupManager 用于宿主app和extension app简单的数据共享
EDAppGroupManager.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

static NSString *const AppGroupUserDefault_SaveLog_Key = @"AppGroupUserDefault_SaveLog_Key";

@interface EDAppGroupManager : NSObject
// 初始化 manager
+ (instancetype)sharedInstance;

#pragma mark - - UserDefaults
- (BOOL)setObject:(nullable id)value forKey:(NSString *_Nullable)defaultName;

- (nullable id)objectForKey:(NSString *_Nullable)defaultName;

@end

NS_ASSUME_NONNULL_END



EDAppGroupManager.m

#import "EDAppGroupManager.h"
static NSString * const groupId = @"group.com.company.test"; // 换成自己开发者账号对应的groups

@interface EDAppGroupManager ()
@property (nonatomic, strong) NSUserDefaults * appGroupUserDefaults;
@end

@implementation EDAppGroupManager

// 初始化 manager
static EDAppGroupManager *manager = nil;
+ (instancetype)sharedInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[EDAppGroupManager alloc] init];
    });
    return manager;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        self.appGroupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:groupId];
    }
    return self;
}

- (BOOL)setObject:(nullable id)value forKey:(NSString *_Nullable)defaultName{
    if (!value || !defaultName) {
        return NO;
    }
    [self.appGroupUserDefaults setObject:value forKey:defaultName];
    return YES;
}

- (nullable id)objectForKey:(NSString *_Nullable)defaultName{
    if (!defaultName) {
        return nil;
    }
    return [self.appGroupUserDefaults objectForKey:defaultName];
}

@end


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342