Home telegram-mtNetworkAvailability
Post
Cancel

telegram-mtNetworkAvailability

MTNetworkAvailability

protocol MTNetworkAvailabilityDelegate

- (void)networkAvailabilityChanged:(MTNetworkAvailability *)networkAvailability networkIsAvailable:(bool)networkIsAvailable;

class MTNetworkAvailability

@property (nonatomic, weak) id<MTNetworkAvailabilityDelegate> delegate;

SCNetworkReachabilityRef _reachability;
MTTimer *_timer;
NSString *_lastReachabilityState;


// the queue
+ (MTQueue *)networkAvailabilityQueue
{
    static MTQueue *queue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
                  {
                      queue = [[MTQueue alloc] initWithName:"org.mtproto.MTNetwotkAvailability"];
                  });
    return queue;
}

- (instancetype)initWithDelegate:(id<MTNetworkAvailabilityDelegate>)delegate
{
    self = [super init];
    if (self != nil)
    {
        _delegate = delegate;
        
        [[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^
         {
             struct sockaddr_in zeroAddress;
             bzero(&zeroAddress, sizeof(zeroAddress));
             zeroAddress.sin_len = sizeof(zeroAddress);
             zeroAddress.sin_family = AF_INET;
             
             _reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress);
             if (_reachability != NULL)
             {
               // prepare availability context
                 MTNetworkAvailabilityContext *availabilityContext = [[MTNetworkAvailabilityContext alloc] init];
                 availabilityContext.context = self;
                 SCNetworkReachabilityContext context = { 0, (__bridge void *)availabilityContext, &MTNetworkAvailabilityContextRetain, &MTNetworkAvailabilityContextRelease, NULL };
                 
                 [self updateReachability:kSCNetworkReachabilityFlagsReachable notify:false];
                 [self updateFlags:true];
                 
                 __weak MTNetworkAvailability *weakSelf = self;
                 _timer = [[MTTimer alloc] initWithTimeout:5.0 repeat:true completion:^
                           {
                               __strong MTNetworkAvailability *strongSelf = weakSelf;
                               if (strongSelf != nil) {
                                   [strongSelf updateFlags:true];
                               }
                           } queue:[MTNetworkAvailability networkAvailabilityQueue].nativeQueue];
                 [_timer start];
                 
                 if (SCNetworkReachabilitySetCallback(_reachability, &MTAvailabilityCallback, &context))
                     SCNetworkReachabilitySetDispatchQueue(_reachability, [MTNetworkAvailability networkAvailabilityQueue].nativeQueue);
             }
         }];
    }
    return self;
}

Update Flags

- (void)updateFlags:(bool)notify
{
    [[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^
     {
         
         if (_reachability != nil)
         {
             SCNetworkReachabilityFlags currentFlags = 0;
             if (SCNetworkReachabilityGetFlags(_reachability, &currentFlags))
                 [self updateReachability:currentFlags notify:notify];
         }
     }];
}

Update Availiability

- (void)updateReachability:(SCNetworkReachabilityFlags)flags notify:(bool)notify
{
    [[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^
     {
         BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
         BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
         BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
                                            ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
         BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically &&
                                                  (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
         BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
         
         bool isWWAN = false;
#if TARGET_OS_IPHONE
         isWWAN = (flags & kSCNetworkReachabilityFlagsIsWWAN);
#endif
         NSString *currentReachabilityState = [[NSString alloc] initWithFormat:@"%s_%s_%s", isWWAN ? "M" : "L", canConnectWithoutUserInteraction ? "+U" : "-U", isNetworkReachable ? "+" : "-"];
         if (![currentReachabilityState isEqualToString:_lastReachabilityState])
         {
             _lastReachabilityState = currentReachabilityState;
             if (MTLogEnabled()) {
                 MTLog(@"[MTNetworkAvailability#%p state: %@]", self, _lastReachabilityState);
             }
             
             if (notify)
             {
                 id<MTNetworkAvailabilityDelegate> delegate = _delegate;
                 if ([delegate respondsToSelector:@selector(networkAvailabilityChanged:networkIsAvailable:)])
                     [delegate networkAvailabilityChanged:self networkIsAvailable:isNetworkReachable];
             }
         }
     }];
}

Availability Callback

static void MTAvailabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
{
    MTNetworkAvailabilityContext *availabilityContext = ((__bridge MTNetworkAvailabilityContext *)info);
    MTNetworkAvailability *availability = availabilityContext.context;
    [availability updateReachability:flags notify:true];
}
This post is licensed under CC BY 4.0 by the author.