From 07957064abeb8f08297d21e105667fc4de417ca3 Mon Sep 17 00:00:00 2001 From: olivierbrand Date: Mon, 20 Jul 2020 19:48:21 -0600 Subject: [PATCH 1/4] Fixed plugin on iOS. Entire lifecycle was not taken care of --- .../ios/Runner.xcodeproj/project.pbxproj | 17 +-- packages/quick_actions/example/lib/main.dart | 45 +++++-- .../ios/Classes/FLTQuickActionsPlugin.m | 115 +++++++++++------- 3 files changed, 107 insertions(+), 70 deletions(-) diff --git a/packages/quick_actions/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/example/ios/Runner.xcodeproj/project.pbxproj index fdd275fcede5..338ff6e8f744 100644 --- a/packages/quick_actions/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/quick_actions/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,11 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83C36CAF23D629E5ABE75B2A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC799F2B0AB50A9C34344F0 /* libPods-Runner.a */; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -28,8 +24,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -40,14 +34,12 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 5278439583922091276A37C9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -63,8 +55,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 83C36CAF23D629E5ABE75B2A /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -75,9 +65,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -229,7 +217,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -269,9 +257,12 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/packages/quick_actions/example/lib/main.dart b/packages/quick_actions/example/lib/main.dart index fc289810ea24..eee849714ed5 100644 --- a/packages/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/example/lib/main.dart @@ -32,17 +32,17 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - String shortcut = "no action set"; + final QuickActions quickActions = QuickActions(); @override - void initState() { - super.initState(); + void didChangeDependencies() { + super.didChangeDependencies(); - final QuickActions quickActions = QuickActions(); + print('DidChangeDependencies'); quickActions.initialize((String shortcutType) { - setState(() { - if (shortcutType != null) shortcut = shortcutType; - }); + print('ShortcutType: $shortcutType'); + + _shortcutDialog(context, shortcutType); }); quickActions.setShortcutItems([ @@ -62,16 +62,39 @@ class _MyHomePageState extends State { ]); } + Future _shortcutDialog(BuildContext context, String shortcutType) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Opened via Actions'), + content: Text('Opened via shortcut: $shortcutType'), + actions: [ + FlatButton( + child: const Text('Ok'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('$shortcut'), + title: const Text('Flutter Quick Actions Demo'), ), body: const Center( - child: Text('On home screen, long press the app icon to ' - 'get Action one or Action two options. Tapping on that action should ' - 'set the toolbar title.'), + child: Padding( + padding: EdgeInsets.all(32.0), + child: Text('On home screen, long press the app icon to ' + 'get Action one or Action two options. Tapping on that action should ' + 'set the toolbar title.'), + ), ), ); } diff --git a/packages/quick_actions/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/ios/Classes/FLTQuickActionsPlugin.m index 88ff7397af8a..7ce1c3d21fe1 100644 --- a/packages/quick_actions/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/ios/Classes/FLTQuickActionsPlugin.m @@ -8,76 +8,99 @@ @interface FLTQuickActionsPlugin () @property(nonatomic, retain) FlutterMethodChannel *channel; +@property(nonatomic, strong) NSString *shortcutType; @end @implementation FLTQuickActionsPlugin + (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:CHANNEL_NAME - binaryMessenger:[registrar messenger]]; - FLTQuickActionsPlugin *instance = [[FLTQuickActionsPlugin alloc] init]; - instance.channel = channel; - [registrar addMethodCallDelegate:instance channel:channel]; + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:CHANNEL_NAME + binaryMessenger:[registrar messenger]]; + FLTQuickActionsPlugin *instance = [[FLTQuickActionsPlugin alloc] init]; + instance.channel = channel; + [registrar addMethodCallDelegate:instance channel:channel]; + [registrar addApplicationDelegate:instance]; } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if (@available(iOS 9.0, *)) { - if ([call.method isEqualToString:@"setShortcutItems"]) { - _setShortcutItems(call.arguments); - result(nil); - } else if ([call.method isEqualToString:@"clearShortcutItems"]) { - [UIApplication sharedApplication].shortcutItems = @[]; - result(nil); - } else if ([call.method isEqualToString:@"getLaunchAction"]) { - result(nil); + if (@available(iOS 9.0, *)) { + if ([call.method isEqualToString:@"setShortcutItems"]) { + _setShortcutItems(call.arguments); + result(nil); + } else if ([call.method isEqualToString:@"clearShortcutItems"]) { + [UIApplication sharedApplication].shortcutItems = @[]; + result(nil); + } else if ([call.method isEqualToString:@"getLaunchAction"]) { + result(self.shortcutType); // This is used when the app is killed and open the first time via + // quick actions + self.shortcutType = nil; + } else { + result(FlutterMethodNotImplemented); + } } else { - result(FlutterMethodNotImplemented); + NSLog(@"Shortcuts are not supported prior to iOS 9."); + result(nil); } - } else { - NSLog(@"Shortcuts are not supported prior to iOS 9."); - result(nil); - } } - (void)dealloc { - [_channel setMethodCallHandler:nil]; - _channel = nil; + [_channel setMethodCallHandler:nil]; + _channel = nil; } - (BOOL)application:(UIApplication *)application - performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem - completionHandler:(void (^)(BOOL succeeded))completionHandler - API_AVAILABLE(ios(9.0)) { - [self.channel invokeMethod:@"launch" arguments:shortcutItem.type]; - return YES; +WillFinishLaunchingWithOptions: +(NSDictionary *)launchOptions { + if (@available(iOS 9.0, *)) { + UIApplicationShortcutItem *shortcutItem = + launchOptions[UIApplicationLaunchOptionsShortcutItemKey]; + if (shortcutItem != NULL) { + self.shortcutType = shortcutItem.type; + [self.channel invokeMethod:@"launch" arguments:shortcutItem.type]; + return NO; + } else { + [self.channel invokeMethod:@"launch" arguments:nil]; + self.shortcutType = nil; + } + } + return YES; +} + +- (BOOL)application:(UIApplication *)application +performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem + completionHandler:(void (^)(BOOL succeeded))completionHandler +API_AVAILABLE(ios(9.0)) { + self.shortcutType = shortcutItem.type; + [self.channel invokeMethod:@"launch" arguments:shortcutItem.type]; + return YES; } #pragma mark Private functions NS_INLINE void _setShortcutItems(NSArray *items) API_AVAILABLE(ios(9.0)) { - NSMutableArray *newShortcuts = [[NSMutableArray alloc] init]; - - for (id item in items) { - UIApplicationShortcutItem *shortcut = _deserializeShortcutItem(item); - [newShortcuts addObject:shortcut]; - } - - [UIApplication sharedApplication].shortcutItems = newShortcuts; + NSMutableArray *newShortcuts = [[NSMutableArray alloc] init]; + + for (id item in items) { + UIApplicationShortcutItem *shortcut = _deserializeShortcutItem(item); + [newShortcuts addObject:shortcut]; + } + + [UIApplication sharedApplication].shortcutItems = newShortcuts; } NS_INLINE UIApplicationShortcutItem *_deserializeShortcutItem(NSDictionary *serialized) - API_AVAILABLE(ios(9.0)) { - UIApplicationShortcutIcon *icon = - [serialized[@"icon"] isKindOfClass:[NSNull class]] - ? nil - : [UIApplicationShortcutIcon iconWithTemplateImageName:serialized[@"icon"]]; - - return [[UIApplicationShortcutItem alloc] initWithType:serialized[@"type"] - localizedTitle:serialized[@"localizedTitle"] - localizedSubtitle:nil - icon:icon - userInfo:nil]; +API_AVAILABLE(ios(9.0)) { + UIApplicationShortcutIcon *icon = + [serialized[@"icon"] isKindOfClass:[NSNull class]] + ? nil + : [UIApplicationShortcutIcon iconWithTemplateImageName:serialized[@"icon"]]; + + return [[UIApplicationShortcutItem alloc] initWithType:serialized[@"type"] + localizedTitle:serialized[@"localizedTitle"] + localizedSubtitle:nil + icon:icon + userInfo:nil]; } @end From e2b0de207d375e415f434f1a14b51e9314b209be Mon Sep 17 00:00:00 2001 From: olivierbrand Date: Mon, 20 Jul 2020 20:32:01 -0600 Subject: [PATCH 2/4] Added change and bumped version --- packages/quick_actions/CHANGELOG.md | 5 +++++ packages/quick_actions/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/quick_actions/CHANGELOG.md b/packages/quick_actions/CHANGELOG.md index 1afc92af7c06..32e7f860df1f 100644 --- a/packages/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0+7 + +* Fixed iOS lifecycle. Quick actions work when the app is running in + the background as well as not running at all + ## 0.4.0+6 * Post-v2 Android embedding cleanup. diff --git a/packages/quick_actions/pubspec.yaml b/packages/quick_actions/pubspec.yaml index 570c864ffe38..fbfdd4496fd3 100644 --- a/packages/quick_actions/pubspec.yaml +++ b/packages/quick_actions/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. homepage: https://github.com/flutter/plugins/tree/master/packages/quick_actions -version: 0.4.0+6 +version: 0.4.0+7 flutter: plugin: From e520e81bdbefc5d1be07d6781d13ea7c9afef10d Mon Sep 17 00:00:00 2001 From: olivierbrand Date: Mon, 20 Jul 2020 22:58:49 -0600 Subject: [PATCH 3/4] Added async import --- packages/quick_actions/example/lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/quick_actions/example/lib/main.dart b/packages/quick_actions/example/lib/main.dart index eee849714ed5..cc09646daf66 100644 --- a/packages/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/example/lib/main.dart @@ -4,6 +4,7 @@ // ignore_for_file: public_member_api_docs +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:quick_actions/quick_actions.dart'; From 105c51b626e00a608905f6568b2fb2e76271b0cc Mon Sep 17 00:00:00 2001 From: olivierbrand Date: Mon, 20 Jul 2020 23:25:58 -0600 Subject: [PATCH 4/4] reformat obj-c --- .../ios/Classes/FLTQuickActionsPlugin.m | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/packages/quick_actions/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/ios/Classes/FLTQuickActionsPlugin.m index 7ce1c3d21fe1..ab2c5ece9908 100644 --- a/packages/quick_actions/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/ios/Classes/FLTQuickActionsPlugin.m @@ -14,93 +14,93 @@ @interface FLTQuickActionsPlugin () @implementation FLTQuickActionsPlugin + (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:CHANNEL_NAME - binaryMessenger:[registrar messenger]]; - FLTQuickActionsPlugin *instance = [[FLTQuickActionsPlugin alloc] init]; - instance.channel = channel; - [registrar addMethodCallDelegate:instance channel:channel]; - [registrar addApplicationDelegate:instance]; + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:CHANNEL_NAME + binaryMessenger:[registrar messenger]]; + FLTQuickActionsPlugin *instance = [[FLTQuickActionsPlugin alloc] init]; + instance.channel = channel; + [registrar addMethodCallDelegate:instance channel:channel]; + [registrar addApplicationDelegate:instance]; } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if (@available(iOS 9.0, *)) { - if ([call.method isEqualToString:@"setShortcutItems"]) { - _setShortcutItems(call.arguments); - result(nil); - } else if ([call.method isEqualToString:@"clearShortcutItems"]) { - [UIApplication sharedApplication].shortcutItems = @[]; - result(nil); - } else if ([call.method isEqualToString:@"getLaunchAction"]) { - result(self.shortcutType); // This is used when the app is killed and open the first time via - // quick actions - self.shortcutType = nil; - } else { - result(FlutterMethodNotImplemented); - } + if (@available(iOS 9.0, *)) { + if ([call.method isEqualToString:@"setShortcutItems"]) { + _setShortcutItems(call.arguments); + result(nil); + } else if ([call.method isEqualToString:@"clearShortcutItems"]) { + [UIApplication sharedApplication].shortcutItems = @[]; + result(nil); + } else if ([call.method isEqualToString:@"getLaunchAction"]) { + result(self.shortcutType); // This is used when the app is killed and open the first time via + // quick actions + self.shortcutType = nil; } else { - NSLog(@"Shortcuts are not supported prior to iOS 9."); - result(nil); + result(FlutterMethodNotImplemented); } + } else { + NSLog(@"Shortcuts are not supported prior to iOS 9."); + result(nil); + } } - (void)dealloc { - [_channel setMethodCallHandler:nil]; - _channel = nil; + [_channel setMethodCallHandler:nil]; + _channel = nil; } - (BOOL)application:(UIApplication *)application WillFinishLaunchingWithOptions: (NSDictionary *)launchOptions { - if (@available(iOS 9.0, *)) { - UIApplicationShortcutItem *shortcutItem = - launchOptions[UIApplicationLaunchOptionsShortcutItemKey]; - if (shortcutItem != NULL) { - self.shortcutType = shortcutItem.type; - [self.channel invokeMethod:@"launch" arguments:shortcutItem.type]; - return NO; - } else { - [self.channel invokeMethod:@"launch" arguments:nil]; - self.shortcutType = nil; - } + if (@available(iOS 9.0, *)) { + UIApplicationShortcutItem *shortcutItem = + launchOptions[UIApplicationLaunchOptionsShortcutItemKey]; + if (shortcutItem != NULL) { + self.shortcutType = shortcutItem.type; + [self.channel invokeMethod:@"launch" arguments:shortcutItem.type]; + return NO; + } else { + [self.channel invokeMethod:@"launch" arguments:nil]; + self.shortcutType = nil; } - return YES; + } + return YES; } - (BOOL)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler API_AVAILABLE(ios(9.0)) { - self.shortcutType = shortcutItem.type; - [self.channel invokeMethod:@"launch" arguments:shortcutItem.type]; - return YES; + self.shortcutType = shortcutItem.type; + [self.channel invokeMethod:@"launch" arguments:shortcutItem.type]; + return YES; } #pragma mark Private functions NS_INLINE void _setShortcutItems(NSArray *items) API_AVAILABLE(ios(9.0)) { - NSMutableArray *newShortcuts = [[NSMutableArray alloc] init]; - - for (id item in items) { - UIApplicationShortcutItem *shortcut = _deserializeShortcutItem(item); - [newShortcuts addObject:shortcut]; - } - - [UIApplication sharedApplication].shortcutItems = newShortcuts; + NSMutableArray *newShortcuts = [[NSMutableArray alloc] init]; + + for (id item in items) { + UIApplicationShortcutItem *shortcut = _deserializeShortcutItem(item); + [newShortcuts addObject:shortcut]; + } + + [UIApplication sharedApplication].shortcutItems = newShortcuts; } NS_INLINE UIApplicationShortcutItem *_deserializeShortcutItem(NSDictionary *serialized) API_AVAILABLE(ios(9.0)) { - UIApplicationShortcutIcon *icon = - [serialized[@"icon"] isKindOfClass:[NSNull class]] - ? nil - : [UIApplicationShortcutIcon iconWithTemplateImageName:serialized[@"icon"]]; - - return [[UIApplicationShortcutItem alloc] initWithType:serialized[@"type"] - localizedTitle:serialized[@"localizedTitle"] - localizedSubtitle:nil - icon:icon - userInfo:nil]; + UIApplicationShortcutIcon *icon = + [serialized[@"icon"] isKindOfClass:[NSNull class]] + ? nil + : [UIApplicationShortcutIcon iconWithTemplateImageName:serialized[@"icon"]]; + + return [[UIApplicationShortcutItem alloc] initWithType:serialized[@"type"] + localizedTitle:serialized[@"localizedTitle"] + localizedSubtitle:nil + icon:icon + userInfo:nil]; } @end