Shaka Player Embedded
objc_utils.h
Go to the documentation of this file.
1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SHAKA_EMBEDDED_UTIL_OBJC_UTILS_H_
16 #define SHAKA_EMBEDDED_UTIL_OBJC_UTILS_H_
17 
18 #if !defined(__OBJC__) || !defined(__cplusplus)
19 # error "Can only be included from Objective-C++"
20 #endif
21 
22 #include <Foundation/Foundation.h>
23 
24 #include <string>
25 #include <type_traits>
26 #include <unordered_map>
27 #include <utility>
28 #include <vector>
29 
30 #include "shaka/async_results.h"
31 #include "shaka/error_objc.h"
32 #include "shaka/optional.h"
33 #include "shaka/variant.h"
35 
36 namespace shaka {
37 namespace util {
38 
39 template <typename T>
41 
42 template <>
43 struct ObjcConverter<std::string> {
44  static NSString *ToObjc(const std::string &value) {
45  return [[NSString alloc] initWithBytes:value.c_str()
46  length:value.size()
47  encoding:NSUTF8StringEncoding];
48  }
49 
50  static std::string FromObjc(NSString *value) {
51  return [value UTF8String];
52  }
53 };
54 
55 template <>
56 struct ObjcConverter<bool> {
57  static BOOL ToObjc(bool value) {
58  return value ? YES : NO;
59  }
60 };
61 
62 template <>
63 struct ObjcConverter<double> {
64  static double ToObjc(double value) {
65  return value;
66  }
67 };
68 
69 template <>
70 struct ObjcConverter<optional<double>> {
71  static NSNumber *ToObjc(optional<double> value) {
72  return value.has_value() ? [[NSNumber alloc] initWithDouble:value.value()]
73  : nil;
74  }
75 };
76 
77 template <>
78 struct ObjcConverter<optional<std::string>> {
79  static NSString *ToObjc(optional<std::string> value) {
80  return value.has_value() ? ObjcConverter<std::string>::ToObjc(*value) : nil;
81  }
82 };
83 
84 template <typename T>
85 struct ObjcConverter<std::vector<T>> {
86  using dest_type = decltype(ObjcConverter<T>::ToObjc(std::declval<T>()));
87 
88  static NSMutableArray<dest_type> *ToObjc(const std::vector<T>& value) {
89  NSMutableArray<dest_type>* ret =
90  [[NSMutableArray<dest_type> alloc] initWithCapacity:value.size()];
91  for (size_t i = 0; i < value.size(); i++)
92  [ret addObject:ObjcConverter<T>::ToObjc(value[i])];
93  return ret;
94  }
95 
96  static std::vector<T> FromObjc(NSArray<dest_type> *value) {
97  std::vector<T> ret;
98  ret.reserve([value count]);
99  for (size_t i = 0; i < [value count]; i++)
100  ret.emplace_back(ObjcConverter<T>::FromObjc(value[i]));
101  return ret;
102  }
103 };
104 
105 template <typename T>
106 struct ObjcConverter<std::unordered_map<std::string, T>> {
107  using dest_type = decltype(ObjcConverter<T>::ToObjc(std::declval<T>()));
108 
109  static NSMutableDictionary<NSString *, dest_type> *ToObjc(
110  const std::unordered_map<std::string, T> &map) {
111  std::vector<id> keys;
112  keys.reserve(map.size());
113  std::vector<id> values;
114  values.reserve(map.size());
115 
116  for (const auto& pair : map) {
117  keys.emplace_back(ObjcConverter<std::string>::ToObjc(pair.first));
118  values.emplace_back(ObjcConverter<T>::ToObjc(pair.second));
119  }
120 
121  return [NSMutableDictionary dictionaryWithObjects:values.data()
122  forKeys:keys.data()
123  count:map.size()];
124  }
125 
126  static std::unordered_map<std::string, T> FromObjc(
127  NSDictionary<NSString *, dest_type> *value) {
128  std::unordered_map<std::string, T> ret;
129  ret.reserve([value count]);
130  for (NSString *key in value) {
131  ret.emplace(ObjcConverter<std::string>::FromObjc(key),
132  ObjcConverter<T>::FromObjc(value[key]));
133  }
134  return ret;
135  }
136 };
137 
138 
139 template <typename... Args>
140 void DispatchObjcEvent(__weak id weak_client, SEL selector, Args... args) {
141  // See https://stackoverflow.com/a/20058585
142  dispatch_async(dispatch_get_main_queue(), ^{
143  NSObject *client = weak_client;
144  if (client && [client respondsToSelector:selector]) {
145  IMP imp = [client methodForSelector:selector];
146  auto func = reinterpret_cast<void (*)(id, SEL, Args...)>(imp);
147  func(client, selector, args...);
148  }
149  });
150 }
151 
152 namespace impl {
153 
154 template <typename Ret>
155 struct BlockInvoker {
156  template <typename Func>
157  static void Invoke(const AsyncResults<Ret>& future, Func block) {
158  using T = decltype(ObjcConverter<Ret>::ToObjc(std::declval<Ret>()));
159  if (future.has_error())
160  block(T(), [[ShakaPlayerError alloc] initWithError:future.error()]);
161  else
162  block(ObjcConverter<Ret>::ToObjc(future.results()), nil);
163  }
164 };
165 template <>
166 struct BlockInvoker<void> {
167  template <typename Func>
168  static void Invoke(const AsyncResults<void>& future, Func block) {
169  if (future.has_error())
170  block([[ShakaPlayerError alloc] initWithError:future.error()]);
171  else
172  block(nil);
173  }
174 };
175 
176 } // namespace impl
177 
178 template <typename This, typename Ret, typename Func>
179 void CallBlockForFuture(This that, AsyncResults<Ret> future, Func block) {
180  __block AsyncResults<Ret> local_future = std::move(future);
181  dispatch_async(
182  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
183  ^{
184  // Wait on a background thread; then, once we have a value, call the
185  // block on the main thread.
186  local_future.wait();
187  dispatch_async(dispatch_get_main_queue(), ^{
188  // Keep a reference to "this" so the Objective-C object remains alive
189  // until the callback is done.
190  This other = that;
191  (void)other;
192 
193  impl::BlockInvoker<Ret>::Invoke(local_future, block);
194  });
195  });
196 }
197 
198 } // namespace util
199 } // namespace shaka
200 
201 #endif // SHAKA_EMBEDDED_UTIL_OBJC_UTILS_H_
static void Invoke(const AsyncResults< void > &future, Func block)
Definition: objc_utils.h:168
static NSNumber * ToObjc(optional< double > value)
Definition: objc_utils.h:71
const Error & error() const
static double ToObjc(double value)
Definition: objc_utils.h:64
static NSString * ToObjc(const std::string &value)
Definition: objc_utils.h:44
static std::unordered_map< std::string, T > FromObjc(NSDictionary< NSString *, dest_type > *value)
Definition: objc_utils.h:126
static NSString * ToObjc(optional< std::string > value)
Definition: objc_utils.h:79
static NSMutableArray< dest_type > * ToObjc(const std::vector< T > &value)
Definition: objc_utils.h:88
const T & value() const &
Definition: optional.h:147
static BOOL ToObjc(bool value)
Definition: objc_utils.h:57
void DispatchObjcEvent(__weak id weak_client, SEL selector, Args... args)
Definition: objc_utils.h:140
const U & results() const
static std::string FromObjc(NSString *value)
Definition: objc_utils.h:50
void CallBlockForFuture(This that, AsyncResults< Ret > future, Func block)
Definition: objc_utils.h:179
decltype(ObjcConverter< T >::ToObjc(std::declval< T >())) dest_type
Definition: objc_utils.h:107
decltype(ObjcConverter< T >::ToObjc(std::declval< T >())) dest_type
Definition: objc_utils.h:86
static NSMutableDictionary< NSString *, dest_type > * ToObjc(const std::unordered_map< std::string, T > &map)
Definition: objc_utils.h:109
static std::vector< T > FromObjc(NSArray< dest_type > *value)
Definition: objc_utils.h:96
static void Invoke(const AsyncResults< Ret > &future, Func block)
Definition: objc_utils.h:157
bool has_value() const
Definition: optional.h:143
bool has_error() const