diff --git a/stone/backends/swift_client.py b/stone/backends/swift_client.py index 3b44e69a..58c914f6 100644 --- a/stone/backends/swift_client.py +++ b/stone/backends/swift_client.py @@ -212,6 +212,8 @@ def _generate_routes(self, namespace): template_globals['objc_init_args_to_swift'] = self._objc_init_args_to_swift template_globals['objc_result_from_swift'] = self._objc_result_from_swift template_globals['objc_no_defualts_func_args'] = self._objc_no_defualts_func_args + template_globals['objc_app_auth_route_wrapper_already_defined'] = \ + self._objc_app_auth_route_wrapper_already_defined ns_class = self._class_name(fmt_class(namespace.name)) @@ -220,6 +222,7 @@ def _generate_routes(self, namespace): template.globals = template_globals output_from_parsed_template = template.render(namespace=namespace) + self._write_output_in_target_folder(output_from_parsed_template, 'DBX{}Routes.swift'.format(ns_class), True, @@ -245,15 +248,23 @@ def _generate_request_boxes(self, api): template_globals['request_type_signature'] = self._request_type_signature template_globals['fmt_func'] = fmt_func template_globals['fmt_route_objc_class'] = self._fmt_route_objc_class + swift_class_name = '{}RequestBox'.format(self.args.class_name) if self.args.objc: template = self._jinja_template("ObjCRequestBox.jinja") template.globals = template_globals - class_name = 'DBX{}RequestBox'.format(self.args.class_name) - output = template.render(background_compatible_routes=background_compatible_routes, - background_objc_routes=background_objc_routes, - class_name=class_name) + # don't include the default case in the generated switch statement if it's unreachable + include_default_in_switch = \ + len(background_objc_routes) < len(background_compatible_routes) + + output = template.render( + background_compatible_routes=background_compatible_routes, + background_objc_routes=background_objc_routes, + class_name=swift_class_name, + include_default_in_switch=include_default_in_switch + ) + file_name = 'DBX{}RequestBox.swift'.format(self.args.class_name) self._write_output_in_target_folder(output, file_name, @@ -263,10 +274,9 @@ def _generate_request_boxes(self, api): template = self._jinja_template("SwiftRequestBox.jinja") template.globals = template_globals - class_name = '{}RequestBox'.format(self.args.class_name) output = template.render(background_compatible_routes=background_compatible_routes, background_objc_routes=background_objc_routes, - class_name=class_name) + class_name=swift_class_name) self._write_output_in_target_folder(output, '{}RequestBox.swift'.format(self.args.class_name)) @@ -276,15 +286,24 @@ def _generate_reconnection_helpers(self, api): if len(background_compatible_pairs) == 0: return + is_app_auth_client = self.args.auth_type == 'app' + class_name_prefix = 'AppAuth' if is_app_auth_client else '' + class_name = '{}ReconnectionHelpers'.format(class_name_prefix) + return_type = '{}RequestBox'.format(self.args.class_name) + template = self._jinja_template("SwiftReconnectionHelpers.jinja") template.globals['fmt_func'] = fmt_func template.globals['fmt_class'] = fmt_class + template.globals['class_name'] = class_name + template.globals['return_type'] = return_type output_from_parsed_template = template.render( background_compatible_namespace_route_pairs=background_compatible_pairs ) - self._write_output_in_target_folder(output_from_parsed_template, - 'ReconnectionHelpers.swift') + + self._write_output_in_target_folder( + output_from_parsed_template, '{}.swift'.format(class_name) + ) def _background_compatible_routes(self, api): background_compatible_pairs = self._background_compatible_namespace_route_pairs(api) @@ -323,14 +342,31 @@ def _valid_route_for_auth_type(self, route): # jlocke: this is a bit of a hack to match the route grouping style of the Objective-C SDK # in app auth situations without blowing up the current user and team auth names + # route_auth_type can be either a string or a list of strings route_auth_type = route.attrs.get('auth') client_auth_type = self.args.auth_type - is_app_auth_route = route_auth_type == 'app' + + # if building the app client, only include app auth routes + # if building the user or team client, include routes of all auth types except + # app auth exclusive routes + + is_app_auth_only_route = route_auth_type == 'app' + route_auth_types_include_app = 'app' in route_auth_type if client_auth_type == 'app': - return is_app_auth_route + return is_app_auth_only_route or route_auth_types_include_app else: - return not is_app_auth_route + return not is_app_auth_only_route + + # The objc compatibility wrapper generates a class to wrap each route providing properly + # typed completion handlers without generics. User and App clients are generated in separate + # passes, and if the wrapper is already defined for the user client, we must skip generating + # a second definition of it for the app client. + def _objc_app_auth_route_wrapper_already_defined(self, route): + client_auth_type = self.args.auth_type + is_app_auth_client = client_auth_type == 'app' + + return is_app_auth_client and route.attrs.get('auth') != 'app' def _namespace_contains_valid_routes_for_auth_type(self, namespace): valid_count = 0 @@ -539,7 +575,8 @@ def _background_compatible_routes_for_objc_requests(self, api): objc_class_to_route = {} for namespace in namespaces: for route in namespace.routes: - if self._background_session_route_style(route) is not None: + bg_route_style = self._background_session_route_style(route) + if bg_route_style is not None and self._valid_route_for_auth_type(route): args_data = self._route_client_args(route)[0] objc_class = self._fmt_route_objc_class(namespace, route, args_data) objc_class_to_route[objc_class] = [namespace, route, args_data] diff --git a/stone/backends/swift_rsrc/ObjCRequestBox.jinja b/stone/backends/swift_rsrc/ObjCRequestBox.jinja index 534fb04d..46e89f87 100644 --- a/stone/backends/swift_rsrc/ObjCRequestBox.jinja +++ b/stone/backends/swift_rsrc/ObjCRequestBox.jinja @@ -7,7 +7,7 @@ import Foundation import SwiftyDropbox -extension DropboxBaseRequestBox { +extension {{ class_name }} { var objc: DBXRequest { switch self { {% for route_args_data in background_objc_routes %} @@ -17,8 +17,10 @@ extension DropboxBaseRequestBox { case .{{ fmt_func(route.name, route.version) }}(let swift): return {{ fmt_route_objc_class(namespace, route, args_data) }}(swift: swift) {% endfor %} + {% if include_default_in_switch %} default: fatalError("For Obj-C compatibility, add this route to the Objective-C compatibility module allow-list") + {% endif %} } } } diff --git a/stone/backends/swift_rsrc/ObjCRoutes.jinja b/stone/backends/swift_rsrc/ObjCRoutes.jinja index b3e47f83..df741744 100644 --- a/stone/backends/swift_rsrc/ObjCRoutes.jinja +++ b/stone/backends/swift_rsrc/ObjCRoutes.jinja @@ -80,6 +80,9 @@ public class DBX{{ namespace_name }}Routes: NSObject { {% for route_args_data in routes_for_objc_requests(namespace) %} {% set route = route_args_data[0] %} {% if valid_route_for_auth_type(route) is true %} + +{# do not redefine DBXRequests defined by the user auth client #} +{% if not objc_app_auth_route_wrapper_already_defined(route) %} {% set args_data = route_args_data[1] %} {% set request_object_name = request_object_name(route, args_data) %} {% set result_serial_type = fmt_serial_type(route.result_data_type) %} @@ -179,5 +182,6 @@ public class {{ fmt_route_objc_class(namespace, route, args_data) }}: NSObject, } } +{% endif %} {% endif %} {% endfor %} diff --git a/stone/backends/swift_rsrc/SwiftReconnectionHelpers.jinja b/stone/backends/swift_rsrc/SwiftReconnectionHelpers.jinja index a76c7147..afae1da4 100644 --- a/stone/backends/swift_rsrc/SwiftReconnectionHelpers.jinja +++ b/stone/backends/swift_rsrc/SwiftReconnectionHelpers.jinja @@ -4,9 +4,9 @@ import Foundation -enum ReconnectionHelpers { +enum {{ class_name }} { - static func rebuildRequest(apiRequest: ApiRequest, client: DropboxTransportClientInternal) throws -> DropboxBaseRequestBox { + static func rebuildRequest(apiRequest: ApiRequest, client: DropboxTransportClientInternal) throws -> {{ return_type }} { let info = try persistedRequestInfo(from: apiRequest) switch info.routeName {