10 |
11 | ## Example
12 |
13 | ```objective-c
14 | [self.wormhole passMessageObject:@{@"buttonNumber" : @(1)} identifier:@"button"];
15 |
16 | [self.wormhole listenForMessageWithIdentifier:@"button"
17 | listener:^(id messageObject) {
18 | self.numberLabel.text = [messageObject[@"buttonNumber"] stringValue];
19 | }];
20 | ```
21 |
22 | ## Getting Started
23 |
24 | - Install MMWormhole via CocoaPods or by downloading the Source files
25 | - [Configure your App and Extension to support App Groups](https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html)
26 | - Begin using MMWormhole to pass messages between your App and Extension
27 |
28 | ### Note
29 |
30 | The MMWormhole Example app will only work with your shared App Group identifiers and Entitlements and is meant purely for reference
31 |
32 | ---
33 | ## Installing MMWormhole
34 |
35 | You can install Wormhole in your project by using [CocoaPods](https://github.com/cocoapods/cocoapods):
36 |
37 | ```Ruby
38 | pod 'MMWormhole', '~> 2.0.0'
39 | ```
40 |
41 | [](https://github.com/Carthage/Carthage)
42 | MMWormhole also supports Carthage.
43 |
44 | ## Overview
45 |
46 | MMWormhole is designed to make it easy to share very basic information and commands between an extension and it's containing application. The wormhole should remain stable whether the containing app is running or not, but notifications will only be triggered in the containing app if the app is awake in the background. This makes MMWormhole ideal for cases where the containing app is already running via some form of background modes.
47 |
48 | A good way to think of the wormhole is a collection of shared mailboxes. An identifier is essentially a unique mailbox you can send messages to. You know where a message will be delivered to because of the identifier you associate with it, but not necessarily when the message will be picked up by the recipient. If the app or extension are in the background, they may not receive the message immediately. By convention, sending messages should be done from one side to another, not necessarily from yourself to yourself. It's also a good practice to check the contents of your mailbox when your app or extension wakes up, in case any messages have been left there while you were away.
49 |
50 | MMWormhole uses NSKeyedArchiver as a serialization medium, so any object that is NSCoding compliant can work as a message. For many apps, sharing simple strings, numbers, or JSON objects is sufficient to drive the UI of a Widget or Apple Watch app. Messages can be sent and persisted easily as archive files and read later when the app or extension is woken up later.
51 |
52 | Using MMWormhole is extremely straightforward. The only real catch is that your app and it's extensions must support shared app groups. The group will be used for writing the archive files that represent each message. While larger files and structures, including a whole Core Data database, can be shared using App Groups, MMWormhole is designed to use it's own directory simply to pass messages. Because of that, a best practice is to initialize MMWormhole with a directory name that it will use within your app's shared App Group.
53 |
54 | ### Initialization
55 |
56 | Initialize MMWormhole with your App Group identifier and an optional directory name
57 |
58 | Objective-C:
59 | ```objective-c
60 | self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:@"group.com.mutualmobile.wormhole"
61 | optionalDirectory:@"wormhole"];
62 | ```
63 | Swift:
64 | ```swift
65 | let wormhole = MMWormhole(applicationGroupIdentifier: "group.com.mutualmobile.wormhole", optionalDirectory: "wormhole")
66 | ```
67 |
68 | ### Passing a Message
69 |
70 | Pass a message with an identifier for the message and a NSCoding compliant object as the message itself
71 |
72 | Objective-C:
73 | ```objective-c
74 | [self.wormhole passMessageObject:@{@"titleString" : title}
75 | identifier:@"messageIdentifier"];
76 | ```
77 |
78 | Swift:
79 | ```swift
80 | wormhole.passMessageObject("titleString", identifier: "messageIdentifier")
81 | ```
82 |
83 | ### Reading a Message
84 |
85 | You have two options for reading a message. You can obtain the message for an identifier at any time by asking the wormhole for the message.
86 |
87 | Objective-C:
88 | ```objective-c
89 | id messageObject = [self.wormhole messageWithIdentifier:@"messageIdentifier"];
90 | ```
91 |
92 | You can also listen for changes to that message and be notified when that message is updated.
93 |
94 | Objective-C:
95 | ```objective-c
96 | [self.wormhole listenForMessageWithIdentifier:@"messageIdentifier"
97 | listener:^(id messageObject) {
98 | // Do Something
99 | }];
100 |
101 | ```
102 | Swift:
103 | ```swift
104 | wormhole.listenForMessageWithIdentifier("messageIdentifier", listener: { (messageObject) -> Void in
105 | if let message: AnyObject = messageObject {
106 | // Do something
107 | }
108 | })
109 | ```
110 |
111 | ### Designing Your Communication Scheme
112 |
113 | You can think of message passing between apps and extensions sort of like a web service. The web service has endpoints that you can read and write. The message identifiers for your MMWormhole messages can be thought of in much the same way. A great practice is to design very clear message identifiers so that you immediately know when reading your code who sent the message and why, and what the possible contents of the message might be. Just like you would design a web service with clear semantics, you should do the same with your wormhole messaging scheme.
114 |
115 | ### Communication with WatchConnectivity
116 |
117 | The design of your communication scheme is even more important when you need to support watchOS 2. MMWormhole supports the [WatchConnectivity](https://developer.apple.com/library/prerelease/watchos/documentation/WatchConnectivity/Reference/WatchConnectivity_framework/index.html#//apple_ref/doc/uid/TP40015269) framework provided by Apple as an easy way to get up and running quickly with a basic implementation of WatchConnectivity. This support is not intended to replace WatchConnectivity entirely, and it's important to carefully consider your watch app's communication system to see where MMWormhole will fit best.
118 |
119 | Here are two things you need to know if you want to use WatchConnectivity support in your app:
120 |
121 | - [MMWormholeSession](http://cocoadocs.org/docsets/MMWormhole/2.0.0/Classes/MMWormholeSession.html) is a singleton subclass of MMWormhole that supports listening for WatchConnectivity messages. It should be used as the listener for all MMWormhole messages you expect to receive from the WatchConnectivity framework. Be sure to activate the session once your listeners are set so that you can begin receiving message notifications.
122 |
123 | - Use the MMWormholeSessionTransiting types described below when creating your wormholes, but be careful not to send too many messages at once. You can easily overload the pipeline by sending too many messages at once.
124 |
125 | ### Message Transiting Options
126 |
127 | The mechanism by which data flows through MMWormhole is defined by the [MMWormholeTransiting](http://cocoadocs.org/docsets/MMWormhole/2.0.0/Classes/MMWormholeTransiting.html) protocol. The default implementation of the protocol is called [MMWormholeFileTransiting](http://cocoadocs.org/docsets/MMWormhole/2.0.0/Classes/MMWormholeFileTransiting.html), which reads and writes messages as archived data files in the app groups shared container. Users of MMWormhole can implement their own version of this protocol to change the message passing behavior.
128 |
129 | There are three new implementations of the MMWormholeTransiting protocol that support the WCSession application context, message, and file transfer systems. You may only use one form of transiting with a wormhole at a time, so you need to consider which type of messaging system best fits a given part of your application.
130 |
131 | Most apps will find the application context system to be a good balance between real time messaging and simple persistence, so we recommend [MMWormholeSessionContextTransiting](http://cocoadocs.org/docsets/MMWormhole/2.0.0/Classes/MMWormholeSessionContextTransiting.html) as the best place to start. Check out the [documentation](https://developer.apple.com/library/prerelease/watchos/documentation/WatchConnectivity/Reference/WatchConnectivity_framework/index.html#//apple_ref/doc/uid/TP40015269) and header comments for descriptions about the other messaging types.
132 |
133 | You can get started quickly with a wormhole using one of the built in transiting types by calling the optional initializer to set up an instance with the right transiting type for your use case.
134 |
135 | Objective-C:
136 | ```objective-c
137 | self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:@"group.com.mutualmobile.wormhole"
138 | optionalDirectory:@"wormhole"
139 | transitingType:MMWormholeTransitingTypeSessionContext];
140 | ```
141 |
142 | Swift:
143 | ```swift
144 | let wormhole = MMWormhole(applicationGroupIdentifier: "group.com.mutualmobile.wormhole",
145 | optionalDirectory: "wormhole",
146 | transitingType: .SessionContext)
147 | ```
148 |
149 | ## Requirements
150 |
151 | MMWormhole requires iOS 7.0 or higher or OS X 10.10 or higher.
152 | MMWormholeSession requires iOS 9.0 or higher.
153 |
154 | ## Troubleshooting
155 |
156 | If messages are not received on the other end, check Project->Capabilities->App Groups.
157 | Three checkmarks should be displayed in the steps section.
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 | ## Credits
168 |
169 | MMWormhole was created by [Conrad Stoll](http://conradstoll.com) at [Mutual Mobile](http://www.mutualmobile.com).
170 |
171 | Credit also to [Wade Spires](https://devforums.apple.com/people/mindsaspire), [Tom Harrington](https://twitter.com/atomicbird), and [Rene Cacheaux](https://twitter.com/rcachatx) for work and inspiration surrounding notifications between the containing app and it's extensions.
172 |
173 | ## License
174 |
175 | MMWormhole is available under the MIT license. See the LICENSE file for more info.
176 |
--------------------------------------------------------------------------------
/Source/MMWormhole.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormhole.h
3 | //
4 | // Copyright (c) 2014 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import
25 |
26 | #import "MMWormholeCoordinatedFileTransiting.h"
27 | #import "MMWormholeFileTransiting.h"
28 |
29 | #if ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 )
30 | #import "MMWormholeSessionContextTransiting.h"
31 | #import "MMWormholeSessionFileTransiting.h"
32 | #import "MMWormholeSessionMessageTransiting.h"
33 | #endif
34 |
35 | #import "MMWormholeTransiting.h"
36 |
37 | typedef NS_ENUM(NSInteger, MMWormholeTransitingType) {
38 | MMWormholeTransitingTypeFile = 0,
39 | MMWormholeTransitingTypeCoordinatedFile,
40 | MMWormholeTransitingTypeSessionContext,
41 | MMWormholeTransitingTypeSessionMessage,
42 | MMWormholeTransitingTypeSessionFile
43 | };
44 |
45 |
46 | NS_ASSUME_NONNULL_BEGIN
47 |
48 | /**
49 | This class creates a wormhole between a containing iOS application and an extension. The wormhole
50 | is meant to be used to pass data or commands back and forth between the two locations. The effect
51 | closely resembles interprocess communication between the app and the extension, though this is not
52 | really the case. The wormhole does have some disadvantages, including the fact that a contract must
53 | be determined in advance between the app and the extension that defines the interchange format.
54 |
55 | A good way to think of the wormhole is a collection of shared mailboxes. An identifier is
56 | essentially a unique mailbox you can send messages to. You know where a message will be delivered
57 | to because of the identifier you associate with it, but not necessarily when the message will be
58 | picked up by the recipient. If the app or extension are in the background, they may not receive the
59 | message immediately. By convention, sending messages should be done from one side to another, not
60 | necessarily from yourself to yourself. It's also a good practice to check the contents of your
61 | mailbox when your app or extension wakes up, in case any messages have been left there while you
62 | were away.
63 |
64 | Passing a message to the wormhole can be inferred as a data transfer package or as a command. In
65 | both cases, the passed message is archived using NSKeyedArchiver to a .archive file named with the
66 | included identifier. Once passed, the contents of the written .archive file can be queried using
67 | the messageWithIdentifier: method. As a command, the simple existence of the message in the shared
68 | app group should be taken as proof of the command's invocation. The contents of the message then
69 | become parameters to be evaluated along with the command. Of course, to avoid confusion later, it
70 | may be best to clear the contents of the message after recognizing the command. The
71 | -clearMessageContentsForIdentifier: method is provided for this purpose.
72 |
73 | A good wormhole includes wormhole aliens who listen for message changes. This class supports
74 | CFNotificationCenter Darwin Notifications, which act as a bridge between the containing app and the
75 | extension. When a message is passed with an identifier, a notification is fired to the Darwin
76 | Notification Center with the given identifier. If you have indicated your interest in the message
77 | by using the -listenForMessageWithIdentifier:completion: method then your completion block will be
78 | called when this notification is received, and the contents of the message will be unarchived and
79 | passed as an object to the completion block.
80 |
81 | It's worth noting that as a best practice to avoid confusing issues or deadlock that messages
82 | should be passed one way only for a given identifier. The containing app should pass messages to
83 | one set of identifiers, which are only ever read or listened for by the extension, and vic versa.
84 | The extension should not then write messages back to the same identifier. Instead, the extension
85 | should use it's own set of identifiers to associate with it's messages back to the application.
86 | Passing messages to the same identifier from two locations should be done only at your own risk.
87 | */
88 | @interface MMWormhole : NSObject
89 |
90 | /**
91 | The wormhole messenger is an object that conforms to the MMWormholeTransiting protocol. By default
92 | this object will be set to a default implementation of this protocol which handles archiving and
93 | unarchiving the message to the shared app group in a file named after the identifier of the
94 | message.
95 |
96 | Users of this class may create their own implementation of the MMWormholeTransiting protocol to use
97 | for the purpose of defining the means by which messages transit the wormhole. You could use this to
98 | change the way that MMWormhole stores messages as files, to read and write messages to a database,
99 | or otherwise be notified in other ways when messages are changed.
100 |
101 | @warning While changing this property is optional, the value of the wormhole messenger should
102 | not be nil and is required for the class to work.
103 | */
104 | @property (nonatomic, strong) id wormholeMessenger;
105 |
106 | /**
107 | Designated Initializer. This method must be called with an application group identifier that will
108 | be used to contain passed messages. It is also recommended that you include a directory name for
109 | messages to be read and written, but this parameter is optional.
110 |
111 | @param identifier An application group identifier
112 | @param directory An optional directory to read/write messages
113 | */
114 |
115 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
116 | optionalDirectory:(nullable NSString *)directory NS_DESIGNATED_INITIALIZER;
117 |
118 | /**
119 | Optional Initializer. This method is provided for convenience while creating MMWormhole instances
120 | with custom message transiting options. By default MMWormhole will use the
121 | MMWormholeTransitingTypeFile option when creating a Wormhole, however, this method can be used to
122 | easily choose a different transiting class at initialization time. You can always initialize a
123 | different class that implements the MMWormholeTransiting class later and replace the Wormhole's
124 | 'wormholeMessenger' property to change the transiting type at a later time.
125 |
126 | @param identifier An application group identifier
127 | @param directory An optional directory to read/write messages
128 | @param transitingType A type of wormhole message transiting that will be used for message passing.
129 | */
130 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
131 | optionalDirectory:(nullable NSString *)directory
132 | transitingType:(MMWormholeTransitingType)transitingType;
133 |
134 | /**
135 | This method passes a message object associated with a given identifier. This is the primary means
136 | of passing information through the wormhole.
137 |
138 | @warning You should avoid situations where you need to pass messages to the same identifier in
139 | rapid succession. If a message's contents will be changing rapidly then consider modifying your
140 | workflow to write bulk changes without listening on the other side of the wormhole, and then add a
141 | listener for a "finished changing" message to let the other side know it's safe to read the
142 | contents of your message.
143 |
144 | @param messageObject The message object to be passed.
145 | This object may be nil. In this case only a notification is posted.
146 | @param identifier The identifier for the message
147 | */
148 | - (void)passMessageObject:(nullable id )messageObject
149 | identifier:(nullable NSString *)identifier;
150 |
151 | /**
152 | This method returns the value of a message with a specific identifier as an object.
153 |
154 | @param identifier The identifier for the message
155 | */
156 | - (nullable id)messageWithIdentifier:(nullable NSString *)identifier;
157 |
158 | /**
159 | This method clears the contents of a specific message with a given identifier.
160 |
161 | @param identifier The identifier for the message
162 | */
163 | - (void)clearMessageContentsForIdentifier:(nullable NSString *)identifier;
164 |
165 | /**
166 | This method clears the contents of your optional message directory to give you a clean state.
167 |
168 | @warning This method will delete all messages passed to your message directory. Use with care.
169 | */
170 | - (void)clearAllMessageContents;
171 |
172 | /**
173 | This method begins listening for notifications of changes to a message with a specific identifier.
174 | If notifications are observed then the given listener block will be called along with the actual
175 | message object.
176 |
177 | @discussion This class only supports one listener per message identifier, so calling this method
178 | repeatedly for the same identifier will update the listener block that will be called when a
179 | message is heard.
180 |
181 | @param identifier The identifier for the message
182 | @param listener A listener block called with the messageObject parameter when a notification
183 | is observed.
184 | */
185 | - (void)listenForMessageWithIdentifier:(nullable NSString *)identifier
186 | listener:(nullable void (^)(__nullable id messageObject))listener;
187 |
188 | /**
189 | This method stops listening for change notifications for a given message identifier.
190 |
191 | NOTE: This method is NOT required to be called. If the wormhole is deallocated then all listeners
192 | will go away as well.
193 |
194 | @param identifier The identifier for the message
195 | */
196 | - (void)stopListeningForMessageWithIdentifier:(nullable NSString *)identifier;
197 |
198 | @end
199 |
200 | NS_ASSUME_NONNULL_END
201 |
--------------------------------------------------------------------------------
/Source/MMWormhole.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormhole.m
3 | //
4 | // Copyright (c) 2014 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormhole.h"
25 |
26 | #if !__has_feature(objc_arc)
27 | #error This class requires automatic reference counting
28 | #endif
29 |
30 | #include
31 |
32 | NS_ASSUME_NONNULL_BEGIN
33 |
34 | static NSString * const MMWormholeNotificationName = @"MMWormholeNotificationName";
35 |
36 | void wormholeNotificationCallback(CFNotificationCenterRef center,
37 | void * observer,
38 | CFStringRef name,
39 | void const * object,
40 | CFDictionaryRef userInfo);
41 |
42 | @interface MMWormhole ()
43 |
44 | @property (nonatomic, strong) NSMutableDictionary *listenerBlocks;
45 |
46 | @end
47 |
48 | @implementation MMWormhole
49 |
50 | #pragma clang diagnostic push
51 | #pragma clang diagnostic ignored "-Wobjc-designated-initializers"
52 |
53 | - (id)init {
54 | return nil;
55 | }
56 |
57 | #pragma clang diagnostic pop
58 |
59 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
60 | optionalDirectory:(nullable NSString *)directory {
61 | if ((self = [super init])) {
62 |
63 | if (NO == [[NSFileManager defaultManager] respondsToSelector:@selector(containerURLForSecurityApplicationGroupIdentifier:)]) {
64 | //Protect the user of a crash because of iOSVersion < iOS7
65 | return nil;
66 | }
67 |
68 | self.wormholeMessenger = [[MMWormholeFileTransiting alloc] initWithApplicationGroupIdentifier:[identifier copy]
69 | optionalDirectory:[directory copy]];
70 |
71 | _listenerBlocks = [NSMutableDictionary dictionary];
72 |
73 | // Only respects notification coming from self.
74 | [[NSNotificationCenter defaultCenter] addObserver:self
75 | selector:@selector(didReceiveMessageNotification:)
76 | name:MMWormholeNotificationName
77 | object:self];
78 | }
79 |
80 | return self;
81 | }
82 |
83 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
84 | optionalDirectory:(nullable NSString *)directory
85 | transitingType:(MMWormholeTransitingType)transitingType {
86 | if ((self = [self initWithApplicationGroupIdentifier:identifier optionalDirectory:directory])) {
87 | switch (transitingType) {
88 | case MMWormholeTransitingTypeFile:
89 | // Default
90 | break;
91 | case MMWormholeTransitingTypeCoordinatedFile:
92 | self.wormholeMessenger = [[MMWormholeCoordinatedFileTransiting alloc] initWithApplicationGroupIdentifier:identifier
93 | optionalDirectory:directory];
94 | break;
95 | case MMWormholeTransitingTypeSessionContext:
96 | #if ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 )
97 | self.wormholeMessenger = [[MMWormholeSessionContextTransiting alloc] initWithApplicationGroupIdentifier:identifier
98 | optionalDirectory:directory];
99 | #endif
100 | break;
101 | case MMWormholeTransitingTypeSessionFile:
102 | #if ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 )
103 | self.wormholeMessenger = [[MMWormholeSessionFileTransiting alloc] initWithApplicationGroupIdentifier:identifier
104 | optionalDirectory:directory];
105 | #endif
106 | break;
107 | case MMWormholeTransitingTypeSessionMessage:
108 | #if ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 )
109 | self.wormholeMessenger = [[MMWormholeSessionMessageTransiting alloc] initWithApplicationGroupIdentifier:identifier
110 | optionalDirectory:directory];
111 | #endif
112 | break;
113 | default:
114 | break;
115 | }
116 | }
117 |
118 | return self;
119 | }
120 |
121 | - (void)dealloc {
122 | [[NSNotificationCenter defaultCenter] removeObserver:self];
123 |
124 | CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();
125 | CFNotificationCenterRemoveEveryObserver(center, (__bridge const void *)(self));
126 | }
127 |
128 |
129 | #pragma mark - Private Notification Methods
130 |
131 | - (void)sendNotificationForMessageWithIdentifier:(nullable NSString *)identifier {
132 | CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();
133 | CFDictionaryRef const userInfo = NULL;
134 | BOOL const deliverImmediately = YES;
135 | CFStringRef str = (__bridge CFStringRef)identifier;
136 | CFNotificationCenterPostNotification(center, str, NULL, userInfo, deliverImmediately);
137 | }
138 |
139 | - (void)registerForNotificationsWithIdentifier:(nullable NSString *)identifier {
140 | [self unregisterForNotificationsWithIdentifier:identifier];
141 |
142 | CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();
143 | CFStringRef str = (__bridge CFStringRef)identifier;
144 | CFNotificationCenterAddObserver(center,
145 | (__bridge const void *)(self),
146 | wormholeNotificationCallback,
147 | str,
148 | NULL,
149 | CFNotificationSuspensionBehaviorDeliverImmediately);
150 | }
151 |
152 | - (void)unregisterForNotificationsWithIdentifier:(nullable NSString *)identifier {
153 | CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();
154 | CFStringRef str = (__bridge CFStringRef)identifier;
155 | CFNotificationCenterRemoveObserver(center,
156 | (__bridge const void *)(self),
157 | str,
158 | NULL);
159 | }
160 |
161 | void wormholeNotificationCallback(CFNotificationCenterRef center,
162 | void * observer,
163 | CFStringRef name,
164 | void const * object,
165 | CFDictionaryRef userInfo) {
166 | NSString *identifier = (__bridge NSString *)name;
167 | NSObject *sender = (__bridge NSObject *)(observer);
168 | [[NSNotificationCenter defaultCenter] postNotificationName:MMWormholeNotificationName
169 | object:sender
170 | userInfo:@{@"identifier" : identifier}];
171 | }
172 |
173 | - (void)didReceiveMessageNotification:(NSNotification *)notification {
174 | NSDictionary *userInfo = notification.userInfo;
175 | NSString *identifier = [userInfo valueForKey:@"identifier"];
176 |
177 | if (identifier != nil) {
178 | id messageObject = [self.wormholeMessenger messageObjectForIdentifier:identifier];
179 |
180 | [self notifyListenerForMessageWithIdentifier:identifier message:messageObject];
181 | }
182 | }
183 |
184 | - (id)listenerBlockForIdentifier:(NSString *)identifier {
185 | return [self.listenerBlocks valueForKey:identifier];
186 | }
187 |
188 | - (void)notifyListenerForMessageWithIdentifier:(nullable NSString *)identifier message:(nullable id)message {
189 | typedef void (^MessageListenerBlock)(id messageObject);
190 |
191 | MessageListenerBlock listenerBlock = [self listenerBlockForIdentifier:identifier];
192 |
193 | if (listenerBlock) {
194 | dispatch_async(dispatch_get_main_queue(), ^{
195 | listenerBlock(message);
196 | });
197 | }
198 | }
199 |
200 |
201 | #pragma mark - Public Interface Methods
202 |
203 | - (void)passMessageObject:(nullable id )messageObject identifier:(nullable NSString *)identifier {
204 | if ([self.wormholeMessenger writeMessageObject:messageObject forIdentifier:identifier]) {
205 | [self sendNotificationForMessageWithIdentifier:identifier];
206 | }
207 | }
208 |
209 |
210 | - (nullable id)messageWithIdentifier:(nullable NSString *)identifier {
211 | id messageObject = [self.wormholeMessenger messageObjectForIdentifier:identifier];
212 |
213 | return messageObject;
214 | }
215 |
216 | - (void)clearMessageContentsForIdentifier:(nullable NSString *)identifier {
217 | [self.wormholeMessenger deleteContentForIdentifier:identifier];
218 | }
219 |
220 | - (void)clearAllMessageContents {
221 | [self.wormholeMessenger deleteContentForAllMessages];
222 | }
223 |
224 | - (void)listenForMessageWithIdentifier:(nullable NSString *)identifier
225 | listener:(nullable void (^)(__nullable id messageObject))listener {
226 | if (identifier != nil) {
227 | [self.listenerBlocks setValue:listener forKey:identifier];
228 | [self registerForNotificationsWithIdentifier:identifier];
229 | }
230 | }
231 |
232 | - (void)stopListeningForMessageWithIdentifier:(nullable NSString *)identifier {
233 | if (identifier != nil) {
234 | [self.listenerBlocks setValue:nil forKey:identifier];
235 | [self unregisterForNotificationsWithIdentifier:identifier];
236 | }
237 | }
238 |
239 | @end
240 |
241 | NS_ASSUME_NONNULL_END
242 |
--------------------------------------------------------------------------------
/Source/MMWormholeCoordinatedFileTransiting.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeCoordinatedFileTransiting.h
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeFileTransiting.h"
25 |
26 | /**
27 | This class inherits from the default implementation of the MMWormholeTransiting protocol
28 | and implements message transiting in a similar way but using NSFileCoordinator for its file
29 | reading and writing.
30 | */
31 | @interface MMWormholeCoordinatedFileTransiting : MMWormholeFileTransiting
32 |
33 | /**
34 | The default file writing option is NSDataWritingAtomic. It may be important for your app to use
35 | additional file writing options to control the specific data protection class for message files
36 | being written by your application. When you create your file transiting object, set this property
37 | to the additional writing options you want to use.
38 | */
39 | @property (nonatomic, assign) NSDataWritingOptions additionalFileWritingOptions;
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/Source/MMWormholeCoordinatedFileTransiting.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeCoordinatedFileTransiting.h
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeCoordinatedFileTransiting.h"
25 |
26 | @implementation MMWormholeCoordinatedFileTransiting
27 |
28 | #pragma mark - MMWormholeTransiting Methods
29 |
30 | - (BOOL)writeMessageObject:(id)messageObject forIdentifier:(NSString *)identifier {
31 | if (identifier == nil) {
32 | return NO;
33 | }
34 |
35 | if (messageObject) {
36 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:messageObject];
37 | NSString *filePath = [self filePathForIdentifier:identifier];
38 | NSURL *fileURL = [NSURL fileURLWithPath:filePath];
39 |
40 | if (data == nil || filePath == nil || fileURL == nil) {
41 | return NO;
42 | }
43 |
44 | NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
45 | NSError *error = nil;
46 | __block BOOL success = NO;
47 |
48 | [fileCoordinator
49 | coordinateWritingItemAtURL:fileURL
50 | options:0
51 | error:&error
52 | byAccessor:^(NSURL *newURL) {
53 | NSError *writeError = nil;
54 |
55 | success = [data writeToURL:newURL
56 | options:NSDataWritingAtomic | self.additionalFileWritingOptions
57 | error:&writeError];
58 | }];
59 |
60 | if (!success) {
61 | return NO;
62 | }
63 | }
64 |
65 | return YES;
66 | }
67 |
68 | - (id)messageObjectForIdentifier:(NSString *)identifier {
69 | if (identifier == nil) {
70 | return nil;
71 | }
72 |
73 | NSString *filePath = [self filePathForIdentifier:identifier];
74 | NSURL *fileURL = [NSURL fileURLWithPath:filePath];
75 |
76 | if (filePath == nil || fileURL == nil) {
77 | return nil;
78 | }
79 |
80 | NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
81 | NSError *error = nil;
82 | __block NSData *data = nil;
83 |
84 | [fileCoordinator
85 | coordinateReadingItemAtURL:fileURL
86 | options:0
87 | error:&error
88 | byAccessor:^(NSURL *newURL) {
89 | data = [NSData dataWithContentsOfURL:newURL];
90 | }];
91 |
92 | if (data == nil) {
93 | return nil;
94 | }
95 |
96 | id messageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
97 |
98 | return messageObject;
99 | }
100 |
101 | @end
102 |
--------------------------------------------------------------------------------
/Source/MMWormholeFileTransiting.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeFileTransiting.h
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeTransiting.h"
25 |
26 | NS_ASSUME_NONNULL_BEGIN
27 |
28 | /**
29 | This class is a default implementation of the MMWormholeTransiting protocol that implements
30 | message transiting by archiving and unarchiving messages that are written and read to files on
31 | disk in an optional directory in the given app group. This default implementation has a relatively
32 | naive implementation of file writing, and simply uses the built in NSData file operations.
33 |
34 | This class is able to be subclassed to provide slightly different file reading and writing behavior
35 | while still maintaining the logic for naming a file within the given directory and app group.
36 | */
37 | @interface MMWormholeFileTransiting : NSObject
38 |
39 | /**
40 | Designated Initializer. This method must be called with an application group identifier that will
41 | be used to contain passed messages. It is also recommended that you include a directory name for
42 | messages to be read and written, but this parameter is optional.
43 |
44 | @param identifier An application group identifier
45 | @param directory An optional directory to read/write messages
46 | */
47 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
48 | optionalDirectory:(nullable NSString *)directory NS_DESIGNATED_INITIALIZER;
49 |
50 | /**
51 | The File Manager associated with this transiting implementation. You can use this property for
52 | implementing your own variant of file transiting that needs to customize where and how files are
53 | stored.
54 | */
55 | @property (nonatomic, strong, readonly) NSFileManager *fileManager;
56 |
57 | /**
58 | This method returns the full file path for the message passing directory, including the optional
59 | directory passed in the designated initializer. Subclasses can use this method to provide custom
60 | implementations.
61 |
62 | @return The full path to the message passing directory.
63 | */
64 | - (nullable NSString *)messagePassingDirectoryPath;
65 |
66 | /**
67 | This method returns the full file path for the file associated with the given message identifier.
68 | It includes the optional directory passed in the designated initializer if there is one. Subclasses
69 | can use this method to provide custom implementations.
70 |
71 | @return The full path to the file associated with the given message identifier.
72 | */
73 | - (nullable NSString *)filePathForIdentifier:(nullable NSString *)identifier;
74 |
75 | @end
76 |
77 | NS_ASSUME_NONNULL_END
78 |
--------------------------------------------------------------------------------
/Source/MMWormholeFileTransiting.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeFileTransiting.m
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import
25 |
26 | #import "MMWormholeFileTransiting.h"
27 |
28 | @interface MMWormholeFileTransiting ()
29 |
30 | @property (nonatomic, copy) NSString *applicationGroupIdentifier;
31 | @property (nonatomic, copy) NSString *directory;
32 | @property (nonatomic, strong, readwrite) NSFileManager *fileManager;
33 |
34 | @end
35 |
36 | @implementation MMWormholeFileTransiting
37 |
38 | - (instancetype)init {
39 | return [self initWithApplicationGroupIdentifier:@"dev.assertion.nonDesignatedInitializer"
40 | optionalDirectory:nil];
41 | }
42 |
43 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
44 | optionalDirectory:(nullable NSString *)directory {
45 | if ((self = [super init])) {
46 | _applicationGroupIdentifier = [identifier copy];
47 | _directory = [directory copy];
48 | _fileManager = [[NSFileManager alloc] init];
49 |
50 | if (_applicationGroupIdentifier) {
51 | [self checkAppGroupCapabilities];
52 | }
53 | }
54 |
55 | return self;
56 | }
57 |
58 |
59 | #pragma mark - Private Check App Group Capabilities
60 |
61 | - (void)checkAppGroupCapabilities {
62 | NSAssert([self.fileManager containerURLForSecurityApplicationGroupIdentifier:self.applicationGroupIdentifier] != nil, @"App Group Capabilities may not be correctly configured for your project, or your appGroupIdentifier may not match your project settings. Check Project->Capabilities->App Groups. Three checkmarks should be displayed in the steps section, and the value passed in for your appGroupIdentifier should match the setting in your project file.");
63 | }
64 |
65 |
66 | #pragma mark - Private File Operation Methods
67 |
68 | - (nullable NSString *)messagePassingDirectoryPath {
69 | NSURL *appGroupContainer = [self.fileManager containerURLForSecurityApplicationGroupIdentifier:self.applicationGroupIdentifier];
70 | NSString *appGroupContainerPath = [appGroupContainer path];
71 | NSString *directoryPath = appGroupContainerPath;
72 |
73 | if (self.directory != nil) {
74 | directoryPath = [appGroupContainerPath stringByAppendingPathComponent:self.directory];
75 | }
76 |
77 | [self.fileManager createDirectoryAtPath:directoryPath
78 | withIntermediateDirectories:YES
79 | attributes:nil
80 | error:NULL];
81 |
82 | return directoryPath;
83 | }
84 |
85 | - (nullable NSString *)filePathForIdentifier:(nullable NSString *)identifier {
86 | if (identifier == nil || identifier.length == 0) {
87 | return nil;
88 | }
89 |
90 | NSString *directoryPath = [self messagePassingDirectoryPath];
91 | NSString *fileName = [NSString stringWithFormat:@"%@.archive", identifier];
92 | NSString *filePath = [directoryPath stringByAppendingPathComponent:fileName];
93 |
94 | return filePath;
95 | }
96 |
97 |
98 | #pragma mark - Public Protocol Methods
99 |
100 | - (BOOL)writeMessageObject:(id)messageObject forIdentifier:(NSString *)identifier {
101 | if (identifier == nil) {
102 | return NO;
103 | }
104 |
105 | if (messageObject) {
106 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:messageObject];
107 | NSString *filePath = [self filePathForIdentifier:identifier];
108 |
109 | if (data == nil || filePath == nil) {
110 | return NO;
111 | }
112 |
113 | BOOL success = [data writeToFile:filePath atomically:YES];
114 |
115 | if (!success) {
116 | return NO;
117 | }
118 | }
119 |
120 | return YES;
121 | }
122 |
123 | - (id)messageObjectForIdentifier:(NSString *)identifier {
124 | if (identifier == nil) {
125 | return nil;
126 | }
127 |
128 | NSString *filePath = [self filePathForIdentifier:identifier];
129 |
130 | if (filePath == nil) {
131 | return nil;
132 | }
133 |
134 | NSData *data = [NSData dataWithContentsOfFile:filePath];
135 |
136 | if (data == nil) {
137 | return nil;
138 | }
139 |
140 | id messageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
141 |
142 | return messageObject;
143 | }
144 |
145 | - (void)deleteContentForIdentifier:(NSString *)identifier {
146 | [self.fileManager removeItemAtPath:[self filePathForIdentifier:identifier] error:NULL];
147 | }
148 |
149 | - (void)deleteContentForAllMessages {
150 | if (self.directory != nil) {
151 | NSArray *messageFiles = [self.fileManager contentsOfDirectoryAtPath:[self messagePassingDirectoryPath] error:NULL];
152 |
153 | NSString *directoryPath = [self messagePassingDirectoryPath];
154 |
155 | for (NSString *path in messageFiles) {
156 | NSString *filePath = [directoryPath stringByAppendingPathComponent:path];
157 |
158 | [self.fileManager removeItemAtPath:filePath error:NULL];
159 | }
160 | }
161 | }
162 |
163 | @end
164 |
--------------------------------------------------------------------------------
/Source/MMWormholeSession.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeSession.h
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormhole.h"
25 |
26 | #import
27 |
28 | NS_ASSUME_NONNULL_BEGIN
29 |
30 | @interface MMWormholeSession : MMWormhole
31 |
32 | /**
33 | This method returns a specific instance of MMWormholeSession that should be used for listening. You
34 | may create your own instances of MMWormholeSession for sending messages, but this is the only object
35 | that will be able to receive messages.
36 |
37 | The reason for this is that MMWormholeSession is based on the WCSession class that is part of the
38 | WatchConnectivity framework provided by Apple, and WCSession is itself a singleton with a single
39 | delegate. Therefore, to receive callbacks, only one MMWormholeSession object may register itself
40 | as a listener.
41 | */
42 | + (instancetype)sharedListeningSession;
43 |
44 | /**
45 | This method should be called after all of your initial listeners have been set and you are ready to
46 | begin listening for messages. There are likely some listeners that your application requires to be
47 | active so that it won't miss critical messages. You should set up these listeners before calling
48 | this method so that any already queued messages will be delivered immediately when you activate the
49 | session. Any listeners you set up after calling this method may miss messages that were already
50 | queued and waiting to be delivered.
51 | */
52 | - (void)activateSessionListening;
53 |
54 | @end
55 |
56 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/Source/MMWormholeSession.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeSession.m
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeSession.h"
25 |
26 | @interface MMWormholeSession ()
27 | @property (nonatomic, strong) WCSession *session;
28 | @end
29 |
30 | @implementation MMWormholeSession
31 |
32 | + (instancetype)sharedListeningSession {
33 | static MMWormholeSession *sharedSession = nil;
34 |
35 | static dispatch_once_t onceToken;
36 | dispatch_once(&onceToken, ^{
37 | sharedSession = [[self alloc] initWithApplicationGroupIdentifier:nil
38 | optionalDirectory:nil];
39 |
40 | sharedSession.session = [WCSession defaultSession];
41 | sharedSession.session.delegate = sharedSession;
42 | });
43 |
44 | return sharedSession;
45 | }
46 |
47 |
48 | #pragma mark - Public Interface Methods
49 |
50 | - (void)activateSessionListening {
51 | [self.session activateSession];
52 | }
53 |
54 |
55 | #pragma mark - Subclass Methods
56 |
57 | - (void)passMessageObject:(nullable id )messageObject identifier:(nullable NSString *)identifier {
58 | NSAssert(NO, @"Message passing is not supported in MMWormholeSession. Please use MMWormhole with an MMWormholeSessionTransiting type to pass messages using WatchConnectivity.");
59 | }
60 |
61 |
62 | - (nullable id)messageWithIdentifier:(nullable NSString *)identifier {
63 | NSAssert(NO, @"Message passing is not supported in MMWormholeSession. Please use MMWormhole with an MMWormholeSessionTransiting type to pass messages using WatchConnectivity.");
64 | return nil;
65 | }
66 |
67 | - (void)clearMessageContentsForIdentifier:(nullable NSString *)identifier {
68 | NSAssert(NO, @"Message passing is not supported in MMWormholeSession. Please use MMWormhole with an MMWormholeSessionTransiting type to pass messages using WatchConnectivity.");
69 | }
70 |
71 | - (void)clearAllMessageContents {
72 | NSAssert(NO, @"Message passing is not supported in MMWormholeSession. Please use MMWormhole with an MMWormholeSessionTransiting type to pass messages using WatchConnectivity.");
73 | }
74 |
75 |
76 | #pragma mark - Private Subclass Methods
77 |
78 | - (void)registerForNotificationsWithIdentifier:(nullable NSString *)identifier {
79 | // MMWormholeSession uses WatchConnectivity delegate callbacks and does not support Darwin Notification Center notifications.
80 | }
81 |
82 | - (void)unregisterForNotificationsWithIdentifier:(nullable NSString *)identifier {
83 | // MMWormholeSession uses WatchConnectivity delegate callbacks and does not support Darwin Notification Center notifications.
84 | }
85 |
86 |
87 | #pragma mark - WCSessionDelegate Methods
88 |
89 | - (void)session:(nonnull WCSession *)session didReceiveMessage:(nonnull NSDictionary *)message {
90 | for (NSString *identifier in message.allKeys) {
91 | NSData *data = message[identifier];
92 | id messageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
93 |
94 | [self notifyListenerForMessageWithIdentifier:identifier message:messageObject];
95 | }
96 | }
97 |
98 | - (void)session:(WCSession *)session didReceiveApplicationContext:(NSDictionary *)applicationContext {
99 | for (NSString *identifier in applicationContext.allKeys) {
100 | NSData *data = applicationContext[identifier];
101 | id messageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
102 |
103 | [self notifyListenerForMessageWithIdentifier:identifier message:messageObject];
104 | }
105 | }
106 |
107 | - (void)session:(nonnull WCSession *)session didReceiveFile:(nonnull WCSessionFile *)file {
108 | NSString *identifier = file.metadata[@"identifier"];
109 |
110 | NSData *data = [NSData dataWithContentsOfURL:file.fileURL];
111 | id messageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
112 |
113 | [self notifyListenerForMessageWithIdentifier:identifier message:messageObject];
114 |
115 | MMWormholeFileTransiting *wormholeMessenger = self.wormholeMessenger;
116 |
117 | if ([wormholeMessenger respondsToSelector:@selector(filePathForIdentifier:)] == NO) {
118 | return;
119 | }
120 |
121 | if (messageObject) {
122 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:messageObject];
123 |
124 | NSString *filePath = [wormholeMessenger filePathForIdentifier:identifier];
125 |
126 | if (data == nil || filePath == nil) {
127 | return;
128 | }
129 |
130 | [data writeToFile:filePath atomically:YES];
131 | }
132 | }
133 |
134 | @end
135 |
136 |
--------------------------------------------------------------------------------
/Source/MMWormholeSessionContextTransiting.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeSessionContextTransiting.h
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeFileTransiting.h"
25 |
26 | /**
27 | This class provides support for the WatchConnectivity framework's Application Context message
28 | reading and writing ability. This class will pass it's messages directly via the
29 | -updateApplicationContext method, and read message values from application context.
30 |
31 | This class also uses a local mutable dictionary for maintaining a more consistent version of your
32 | wormhole-based application context. The contents of the local dictionary are merged with the
33 | application context for passing messages. Clearing message contents on a wormhole using this
34 | transiting implementation will clear both the applicationContext as well as the local mutable
35 | dictionary.
36 |
37 | @discussion This class should be treated as the default MMWormholeTransiting implementation for
38 | applications wanting to leverage the WatchConnectivity framework within MMWormhole. The application
39 | context provides the best of both real time message passing and baked in state persistence for
40 | setting up your UI.
41 | */
42 | @interface MMWormholeSessionContextTransiting : MMWormholeFileTransiting
43 |
44 | @end
45 |
--------------------------------------------------------------------------------
/Source/MMWormholeSessionContextTransiting.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeSessionContextTransiting.m
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeSessionContextTransiting.h"
25 |
26 | #import
27 |
28 | @interface MMWormholeSessionContextTransiting ()
29 | @property (nonatomic, strong) WCSession *session;
30 | @property (nonatomic, strong) NSMutableDictionary *lastContext;
31 | @end
32 |
33 | @implementation MMWormholeSessionContextTransiting
34 |
35 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
36 | optionalDirectory:(nullable NSString *)directory {
37 | if ((self = [super initWithApplicationGroupIdentifier:identifier optionalDirectory:directory])) {
38 | // Setup transiting with the default session
39 | _session = [WCSession defaultSession];
40 |
41 | // Ensure that the MMWormholeSession's delegate is set to enable message sending
42 | NSAssert(_session.delegate != nil, @"WCSession's delegate is required to be set before you can send messages. Please initialize the MMWormholeSession sharedListeningSession object prior to creating a separate wormhole using the MMWormholeSessionTransiting classes.");
43 | }
44 |
45 | return self;
46 | }
47 |
48 | - (BOOL)writeMessageObject:(id)messageObject forIdentifier:(NSString *)identifier {
49 | if (identifier == nil) {
50 | return NO;
51 | }
52 |
53 | if ([WCSession isSupported] == false) {
54 | return NO;
55 | }
56 |
57 | if (messageObject) {
58 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:messageObject];
59 |
60 | if (data == nil) {
61 | return NO;
62 | }
63 |
64 | NSMutableDictionary *applicationContext = [self.session.applicationContext mutableCopy];
65 |
66 | if (applicationContext == nil) {
67 | applicationContext = [NSMutableDictionary new];
68 | }
69 |
70 | if (self.lastContext == nil) {
71 | self.lastContext = applicationContext;
72 | }
73 |
74 | NSMutableDictionary *currentContext = applicationContext;
75 | [currentContext addEntriesFromDictionary:self.lastContext];
76 | currentContext[identifier] = data;
77 |
78 | self.lastContext = currentContext;
79 |
80 | [self.session updateApplicationContext:currentContext error:nil];
81 | }
82 |
83 | return NO;
84 | }
85 |
86 | - (nullable id)messageObjectForIdentifier:(nullable NSString *)identifier {
87 | NSDictionary *receivedContext = self.session.receivedApplicationContext;
88 | NSData *data = receivedContext[identifier];
89 |
90 | if (data == nil) {
91 | NSDictionary *currentContext = self.session.applicationContext;
92 | data = currentContext[identifier];
93 |
94 | if (data == nil) {
95 | return nil;
96 | }
97 | }
98 |
99 | id messageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
100 |
101 | return messageObject;
102 | }
103 |
104 | - (void)deleteContentForIdentifier:(nullable NSString *)identifier {
105 | [self.lastContext removeObjectForKey:identifier];
106 |
107 | NSMutableDictionary *currentContext = [self.session.applicationContext mutableCopy];
108 | [currentContext removeObjectForKey:identifier];
109 |
110 | [self.session updateApplicationContext:currentContext error:nil];
111 | }
112 |
113 | - (void)deleteContentForAllMessages {
114 | [self.lastContext removeAllObjects];
115 | [self.session updateApplicationContext:@{} error:nil];
116 | }
117 |
118 | @end
119 |
--------------------------------------------------------------------------------
/Source/MMWormholeSessionFileTransiting.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeSessionFileTransiting.h
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeFileTransiting.h"
25 |
26 | /**
27 | This class provides support for the WatchConnectivity framework's file transfer ability. This class
28 | will behave very similar to the MMWormholeFileTransiting implementation, meaning it will archive
29 | messages to disk as files and send them via the WatchConnectivity framework's -transferFile API.
30 |
31 | @warning In order for your Wormhole to support reading the contents of transferred file messages,
32 | you will need to set this object as the 'wormholeMessenger' property on the
33 | [MMWormholeSession sharedListeningSession]. The reason for this is that the sharedListeningSession
34 | requires a configured application group and optional file directory in order to know where to save
35 | received files. If you don't set this object as the wormholeMessenger you will still be notified
36 | when your receive files, but you will be responsible for storing the contents yourself and they
37 | won't be persisted for you.
38 |
39 | @discussion This class should only be used in very specific circumstances. Typically speaking, if
40 | you find yourself needing to use the WatchConnectivity framework's file transfer APIs you will best
41 | be served by using the WatchConnectivity framework directly and bypassing MMWormhole. This class
42 | is provided as a basic implementation for simple use cases and isn't intended to be the core of
43 | your file based message transfer system.
44 | */
45 | @interface MMWormholeSessionFileTransiting : MMWormholeFileTransiting
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/Source/MMWormholeSessionFileTransiting.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeSessionFileTransiting.m
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeSessionFileTransiting.h"
25 |
26 | #import
27 |
28 | @interface MMWormholeSessionFileTransiting ()
29 | @property (nonatomic, strong) WCSession *session;
30 | @end
31 |
32 | @implementation MMWormholeSessionFileTransiting
33 |
34 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
35 | optionalDirectory:(nullable NSString *)directory {
36 | if ((self = [super initWithApplicationGroupIdentifier:identifier optionalDirectory:directory])) {
37 | // Setup transiting with the default session
38 | _session = [WCSession defaultSession];
39 |
40 | // Ensure that the MMWormholeSession's delegate is set to enable message sending
41 | NSAssert(_session.delegate != nil, @"WCSession's delegate is required to be set before you can send messages. Please initialize the MMWormholeSession sharedListeningSession object prior to creating a separate wormhole using the MMWormholeSessionTransiting classes.");
42 | }
43 |
44 | return self;
45 | }
46 |
47 | - (BOOL)writeMessageObject:(id)messageObject forIdentifier:(NSString *)identifier {
48 | if (identifier == nil) {
49 | return NO;
50 | }
51 |
52 | if ([WCSession isSupported] == false) {
53 | return NO;
54 | }
55 |
56 | if (messageObject) {
57 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:messageObject];
58 |
59 | if (data == nil) {
60 | return NO;
61 | }
62 |
63 | NSString *tempDir = [self messagePassingDirectoryPath];
64 |
65 | if (tempDir == nil) {
66 | tempDir = NSTemporaryDirectory();
67 | }
68 |
69 | NSString *tempPath = [tempDir stringByAppendingPathComponent:identifier];
70 | NSURL *tempURL = [NSURL fileURLWithPath:tempPath];
71 |
72 | NSError *fileError = nil;
73 |
74 | [data writeToURL:tempURL options:NSDataWritingAtomic error:&fileError];
75 |
76 | [self.session transferFile:tempURL metadata:@{@"identifier" : identifier}];
77 | }
78 |
79 | return NO;
80 | }
81 |
82 | @end
83 |
--------------------------------------------------------------------------------
/Source/MMWormholeSessionMessageTransiting.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeSessionMessageTransiting.h
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 |
25 | #import "MMWormholeFileTransiting.h"
26 |
27 | /**
28 | This class provides support for the WatchConnectivity framework's real time message passing ability.
29 | This is the only version of the MMWormholeSessionTransiting system that will be able to wake up
30 | your iPhone app in the background. As such, this class has a very specific purpose. It also means
31 | that it will most likely be used only by your Apple Watch app, or in very very specific instances
32 | by your iPhone app. Typically, your iPhone app will want to use the
33 | MMWormholeSessionContextTransiting option instead.
34 |
35 | @warning Waking up the iPhone app from a Watch app is an expensive operation. You should only use
36 | this transiting implementation when this action is required for your application. Otherwise you are
37 | better served by using the MMWormholeSessionContextTransiting implementation.
38 |
39 | @warning This transiting implementation does not support reading message contents because real time
40 | messages are delivered once and not persisted.
41 |
42 | @discussion This class should be used in cases where your Apple Watch app needs to ensure your
43 | iPhone app is running to receive a message and take some action on it. One example of this would be
44 | to start background location tracking or audio.
45 | */
46 | @interface MMWormholeSessionMessageTransiting : MMWormholeFileTransiting
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/Source/MMWormholeSessionMessageTransiting.m:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeSessionMessageTransiting.m
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "MMWormholeSessionMessageTransiting.h"
25 |
26 | #import
27 |
28 | @interface MMWormholeSessionMessageTransiting ()
29 | @property (nonatomic, strong) WCSession *session;
30 | @end
31 |
32 | @implementation MMWormholeSessionMessageTransiting
33 |
34 | - (instancetype)initWithApplicationGroupIdentifier:(nullable NSString *)identifier
35 | optionalDirectory:(nullable NSString *)directory {
36 | if ((self = [super initWithApplicationGroupIdentifier:identifier optionalDirectory:directory])) {
37 | // Setup transiting with the default session
38 | _session = [WCSession defaultSession];
39 |
40 | // Ensure that the MMWormholeSession's delegate is set to enable message sending
41 | NSAssert(_session.delegate != nil, @"WCSession's delegate is required to be set before you can send messages. Please initialize the MMWormholeSession sharedListeningSession object prior to creating a separate wormhole using the MMWormholeSessionTransiting classes.");
42 | }
43 |
44 | return self;
45 | }
46 |
47 |
48 | #pragma mark - MMWormholeFileTransiting Subclass Methods
49 |
50 | - (nullable NSString *)messagePassingDirectoryPath {
51 | return nil;
52 | }
53 |
54 |
55 | #pragma mark - MMWormholeTransiting Protocol Methods
56 |
57 | - (BOOL)writeMessageObject:(id)messageObject forIdentifier:(NSString *)identifier {
58 | if (identifier == nil) {
59 | return NO;
60 | }
61 |
62 | if (messageObject) {
63 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:messageObject];
64 |
65 | if (data == nil) {
66 | return NO;
67 | }
68 |
69 | if ([self.session isReachable]) {
70 | [self.session
71 | sendMessage:@{identifier : data}
72 | replyHandler:nil
73 | errorHandler:^(NSError * __nonnull error) {
74 |
75 | }];
76 | }
77 | }
78 |
79 | return NO;
80 | }
81 |
82 | - (nullable id)messageObjectForIdentifier:(nullable NSString *)identifier {
83 | return nil;
84 | }
85 |
86 | - (void)deleteContentForIdentifier:(nullable NSString *)identifier {
87 | }
88 |
89 | - (void)deleteContentForAllMessages {
90 | }
91 |
92 | @end
--------------------------------------------------------------------------------
/Source/MMWormholeTransiting.h:
--------------------------------------------------------------------------------
1 | //
2 | // MMWormholeTransiting.h
3 | //
4 | // Copyright (c) 2015 Mutual Mobile (http://www.mutualmobile.com/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import
25 |
26 | NS_ASSUME_NONNULL_BEGIN
27 |
28 | /**
29 | This protocol defines the public interface for classes wishing to support the transiting of data
30 | between two sides of the wormhole. Transiting is defined as passage between two points, and in this
31 | case it involves both the reading and writing of messages as well as the deletion of message
32 | contents.
33 | */
34 | @protocol MMWormholeTransiting
35 |
36 | /**
37 | This method is responsible for writing a given message object in a persisted format for a given
38 | identifier. The method should return YES if the message was successfully saved. The message object
39 | may be nil, in which case YES should also be returned. Returning YES from this method results in a
40 | notification being fired which will trigger the corresponding listener block for the given
41 | identifier.
42 |
43 | @param messageObject The message object to be passed.
44 | This object may be nil. In this the method should return YES.
45 | @param identifier The identifier for the message
46 | @return YES indicating that a notification should be sent and NO otherwise
47 | */
48 | - (BOOL)writeMessageObject:(nullable id)messageObject forIdentifier:(NSString *)identifier;
49 |
50 | /**
51 | This method is responsible for reading and returning the contents of a given message. It should
52 | understand the structure of messages saved by the implementation of the above writeMessageObject
53 | method and be able to read those messages and return their contents.
54 |
55 | @param identifier The identifier for the message
56 | */
57 | - (nullable id)messageObjectForIdentifier:(nullable NSString *)identifier;
58 |
59 | /**
60 | This method should clear the persisted contents of a specific message with a given identifier.
61 |
62 | @param identifier The identifier for the message
63 | */
64 | - (void)deleteContentForIdentifier:(nullable NSString *)identifier;
65 |
66 | /**
67 | This method should clear the contents of all messages passed to the wormhole.
68 | */
69 | - (void)deleteContentForAllMessages;
70 |
71 | @end
72 |
73 | @protocol MMWormholeTransitingDelegate
74 |
75 | - (void)notifyListenerForMessageWithIdentifier:(nullable NSString *)identifier message:(nullable id)message;
76 |
77 | @end
78 |
79 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------