added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / sync / impl / apple / network_reachability_observer.cpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2016 Realm Inc.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 ////////////////////////////////////////////////////////////////////////////
18
19 #include "sync/impl/apple/network_reachability_observer.hpp"
20
21 #if NETWORK_REACHABILITY_AVAILABLE
22
23 using namespace realm;
24 using namespace realm::_impl;
25
26 namespace {
27
28 NetworkReachabilityStatus reachability_status_for_flags(SCNetworkReachabilityFlags flags)
29 {
30     if (!(flags & kSCNetworkReachabilityFlagsReachable))
31         return NotReachable;
32
33     if (flags & kSCNetworkReachabilityFlagsConnectionRequired) {
34         if (!(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ||
35             (flags & kSCNetworkReachabilityFlagsInterventionRequired))
36             return NotReachable;
37     }
38
39     NetworkReachabilityStatus status = ReachableViaWiFi;
40
41 #if TARGET_OS_IPHONE
42     if (flags & kSCNetworkReachabilityFlagsIsWWAN)
43         status = ReachableViaWWAN;
44 #endif
45
46     return status;
47 }
48
49 } // (anonymous namespace)
50
51 NetworkReachabilityObserver::NetworkReachabilityObserver(util::Optional<std::string> hostname,
52                                                          std::function<void (const NetworkReachabilityStatus)> handler)
53 : m_callback_queue(dispatch_queue_create("io.realm.sync.reachability", DISPATCH_QUEUE_SERIAL))
54 , m_change_handler(std::move(handler))
55 {
56     if (hostname) {
57         m_reachability_ref = util::adoptCF(SystemConfiguration::shared().network_reachability_create_with_name(nullptr,
58                                                                                                                hostname->c_str()));
59     } else {
60         struct sockaddr zeroAddress = {};
61         zeroAddress.sa_len = sizeof(zeroAddress);
62         zeroAddress.sa_family = AF_INET;
63
64         m_reachability_ref = util::adoptCF(SystemConfiguration::shared().network_reachability_create_with_address(nullptr,
65                                                                                                                   &zeroAddress));
66     }
67 }
68
69 NetworkReachabilityObserver::~NetworkReachabilityObserver()
70 {
71     stop_observing();
72     dispatch_release(m_callback_queue);
73 }
74
75 NetworkReachabilityStatus NetworkReachabilityObserver::reachability_status() const
76 {
77     SCNetworkReachabilityFlags flags;
78
79     if (SystemConfiguration::shared().network_reachability_get_flags(m_reachability_ref.get(), &flags))
80         return reachability_status_for_flags(flags);
81
82     return NotReachable;
83 }
84
85 bool NetworkReachabilityObserver::start_observing()
86 {
87     m_previous_status = reachability_status();
88
89     auto callback = [](SCNetworkReachabilityRef, SCNetworkReachabilityFlags, void* self) {
90         static_cast<NetworkReachabilityObserver*>(self)->reachability_changed();
91     };
92
93     SCNetworkReachabilityContext context = {0, this, nullptr, nullptr, nullptr};
94
95     if (!SystemConfiguration::shared().network_reachability_set_callback(m_reachability_ref.get(), callback, &context))
96         return false;
97
98     if (!SystemConfiguration::shared().network_reachability_set_dispatch_queue(m_reachability_ref.get(), m_callback_queue))
99         return false;
100
101     return true;
102 }
103
104 void NetworkReachabilityObserver::stop_observing()
105 {
106     SystemConfiguration::shared().network_reachability_set_dispatch_queue(m_reachability_ref.get(), nullptr);
107     SystemConfiguration::shared().network_reachability_set_callback(m_reachability_ref.get(), nullptr, nullptr);
108
109     // Wait for all previously-enqueued blocks to execute to guarantee that
110     // no callback will be called after returning from this method
111     dispatch_sync(m_callback_queue, ^{});
112 }
113
114 void NetworkReachabilityObserver::reachability_changed()
115 {
116     auto current_status = reachability_status();
117
118     // When observing reachability of the specific host the callback might be called
119     // several times (because of DNS queries) with the same reachability flags while
120     // the caller should be notified only when the reachability status is really changed.
121     if (current_status != m_previous_status) {
122         m_change_handler(current_status);
123         m_previous_status = current_status;
124     }
125 }
126
127 #endif