Skip to content

Commit 10c83d3

Browse files
authored
Move WPAccount wordPressComRestAPI definition to Swift (#24230)
* Tidy: Remove unused `import Foundation` * Tidy: Reduce surface area of `WPAccount` `wordPressComRestApi` usages * Move `wordPressComRestAPI` definition to Swift * Use `guard` over `if` in `WPAccount+RestApi`
1 parent c701ed3 commit 10c83d3

16 files changed

+90
-76
lines changed

WordPress/Classes/Models/WPAccount+RestApi.swift

+63-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,59 @@
1-
import Foundation
2-
import WordPressShared
31
import WordPressKit
2+
import WordPressShared
43

54
extension WPAccount {
5+
6+
/// A `WordPressRestComApi` object if the account is a WordPress.com account. Otherwise, `nil`.
7+
///
8+
/// Warning: The getter has various side effects when there is no previous value set, including triggering the signup flow!
9+
///
10+
/// This used to be defined in the Objective-C layer, but we moved it here in a Swift extension in an attempt to decouple the model code from it.
11+
/// This was done in the context of https://github.com/wordpress-mobile/WordPress-iOS/pull/24165 .
12+
@objc var wordPressComRestApi: WordPressComRestApi? {
13+
get {
14+
guard let api = objc_getAssociatedObject(self, &apiAssociatedKey) as? WordPressComRestApi else {
15+
guard authToken.isEmpty else {
16+
let api = WordPressComRestApi.defaultApi(
17+
oAuthToken: authToken,
18+
userAgent: WPUserAgent.defaultUserAgent(),
19+
localeKey: WordPressComRestApi.LocaleKeyDefault
20+
)
21+
22+
api.setInvalidTokenHandler { [weak self] in
23+
guard let self else { return }
24+
25+
self.authToken = nil
26+
WordPressAuthenticationManager.showSigninForWPComFixingAuthToken()
27+
28+
guard self.isDefaultWordPressComAccount else { return }
29+
30+
// At the time of writing, there is an implicit assumption on what the object parameter value means.
31+
// For example, the WordPressAppDelegate.handleDefaultAccountChangedNotification(_:) subscriber inspects the object parameter to decide whether the notification was sent as a result of a login.
32+
// If the object is non-nil, then the method considers the source a login.
33+
//
34+
// The code path in which we are is that of an invalid token, and that's neither a login nor a logout, it's more appropriate to consider it a logout.
35+
// That's because if the token is invalid the app will soon received errors from the API and it's therefore better to force the user to login again.
36+
NotificationCenter.default.post(
37+
name: NSNotification.Name.WPAccountDefaultWordPressComAccountChanged,
38+
object: nil
39+
)
40+
}
41+
42+
return api
43+
}
44+
45+
DispatchQueue.main.async {
46+
WordPressAuthenticationManager.showSigninForWPComFixingAuthToken()
47+
}
48+
return nil
49+
}
50+
return api
51+
}
52+
set(api) {
53+
objc_setAssociatedObject(self, &apiAssociatedKey, api, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
54+
}
55+
}
56+
657
/// Returns an instance of the WPCOM REST API suitable for v2 endpoints.
758
/// If the user is not authenticated, this will be anonymous.
859
///
@@ -13,4 +64,14 @@ extension WPAccount {
1364

1465
return WordPressComRestApi.defaultApi(oAuthToken: token, userAgent: userAgent, localeKey: localeKey)
1566
}
67+
68+
/// A `WordPressRestComApi` object if a default account exists in the giveng `NSManagedObjectContext` and is a WordPress.com account.
69+
/// Otherwise, it returns `nil`
70+
static func defaultWordPressComAccountRestAPI(in context: NSManagedObjectContext) throws -> WordPressComRestApi? {
71+
let account = try WPAccount.lookupDefaultWordPressComAccount(in: context)
72+
return account?.wordPressComRestApi
73+
}
74+
1675
}
76+
77+
private var apiAssociatedKey: UInt8 = 0

WordPress/Classes/Models/WPAccount.h

+6-4
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@
3434
/// @name API Helpers
3535
///------------------
3636

37-
/**
38-
A WordPressRestComApi object if the account is a WordPress.com account. Otherwise, it returns `nil`
39-
*/
40-
@property (nonatomic, readonly) WordPressComRestApi *wordPressComRestApi;
37+
/// A WordPressRestComApi object if the account is a WordPress.com account. Otherwise, it returns `nil`.
38+
///
39+
/// Important: Do not set this directly!
40+
///
41+
/// It's reserved for Objective-C to Swift interoperability in the context of separating this model from the app target and will be removed at some point.
42+
@property (nonatomic, strong) WordPressComRestApi *wordPressComRestApi;
4143

4244
@end
4345

WordPress/Classes/Models/WPAccount.m

+5-39
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
@interface WPAccount ()
66

7-
@property (nonatomic, strong, readwrite) WordPressComRestApi *wordPressComRestApi;
87
@property (nonatomic, strong, readwrite) NSString *cachedToken;
98

109
@end
@@ -23,7 +22,7 @@ @implementation WPAccount
2322
@dynamic userID;
2423
@dynamic avatarURL;
2524
@dynamic settings;
26-
@synthesize wordPressComRestApi = _wordPressComRestApi;
25+
@synthesize wordPressComRestApi;
2726
@synthesize cachedToken;
2827

2928
#pragma mark - NSManagedObject subclass methods
@@ -35,15 +34,15 @@ - (void)prepareForDeletion
3534
return;
3635
}
3736

38-
[_wordPressComRestApi invalidateAndCancelTasks];
39-
_wordPressComRestApi = nil;
37+
[self.wordPressComRestApi invalidateAndCancelTasks];
38+
self.wordPressComRestApi = nil;
4039
self.authToken = nil;
4140
}
4241

4342
- (void)didTurnIntoFault
4443
{
4544
[super didTurnIntoFault];
46-
_wordPressComRestApi = nil;
45+
self.wordPressComRestApi = nil;
4746
self.cachedToken = nil;
4847
}
4948

@@ -115,7 +114,7 @@ - (void)setAuthToken:(NSString *)authToken
115114
}
116115

117116
// Make sure to release any RestAPI alloc'ed, since it might have an invalid token
118-
_wordPressComRestApi = nil;
117+
self.wordPressComRestApi = nil;
119118
}
120119

121120
- (BOOL)hasAtomicSite {
@@ -160,37 +159,4 @@ + (NSString *)authKeychainServiceName
160159
return [AppConstants authKeychainServiceName];
161160
}
162161

163-
#pragma mark - API Helpers
164-
165-
- (WordPressComRestApi *)wordPressComRestApi
166-
{
167-
if (!_wordPressComRestApi) {
168-
if (self.authToken.length > 0) {
169-
__weak __typeof(self) weakSelf = self;
170-
_wordPressComRestApi = [WordPressComRestApi defaultApiWithOAuthToken:self.authToken
171-
userAgent:[WPUserAgent wordPressUserAgent]
172-
localeKey:[WordPressComRestApi LocaleKeyDefault]];
173-
[_wordPressComRestApi setInvalidTokenHandler:^{
174-
[weakSelf setAuthToken:nil];
175-
[WordPressAuthenticationManager showSigninForWPComFixingAuthToken];
176-
if (weakSelf.isDefaultWordPressComAccount) {
177-
// At the time of writing, there is an implicit assumption on what the object parameter value means.
178-
// For example, the WordPressAppDelegate.handleDefaultAccountChangedNotification(_:) subscriber inspects the object parameter to decide whether the notification was sent as a result of a login.
179-
// If the object is non-nil, then the method considers the source a login.
180-
//
181-
// The code path in which we are is that of an invalid token, and that's neither a login nor a logout, it's more appropriate to consider it a logout.
182-
// That's because if the token is invalid the app will soon received errors from the API and it's therefore better to force the user to login again.
183-
[[NSNotificationCenter defaultCenter] postNotificationName:WPAccountDefaultWordPressComAccountChangedNotification object:nil];
184-
}
185-
}];
186-
} else {
187-
dispatch_async(dispatch_get_main_queue(), ^{
188-
[WordPressAuthenticationManager showSigninForWPComFixingAuthToken];
189-
});
190-
}
191-
}
192-
return _wordPressComRestApi;
193-
194-
}
195-
196162
@end

WordPress/Classes/Services/PostServiceRemoteFactory.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ import WordPressShared
3434
/// - Returns: an instance of WordPressComRestApi
3535
private func apiForRESTRequest(using context: NSManagedObjectContext) -> WordPressComRestApi? {
3636

37-
guard let account = try? WPAccount.lookupDefaultWordPressComAccount(in: context),
38-
let api = account.wordPressComRestApi else {
37+
guard let api = try? WPAccount.defaultWordPressComAccountRestAPI(in: context) else {
3938
return nil
4039
}
4140

WordPress/Classes/Services/PushAuthenticationService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class PushAuthenticationService {
4747

4848
var api: WordPressComRestApi? = nil
4949

50-
if let unwrappedRestApi = (try? WPAccount.lookupDefaultWordPressComAccount(in: context))?.wordPressComRestApi {
50+
if let unwrappedRestApi = (try? WPAccount.defaultWordPressComAccountRestAPI(in: context)) {
5151
if unwrappedRestApi.hasCredentials() {
5252
api = unwrappedRestApi
5353
}

WordPress/Classes/Services/ReaderSiteSearchService.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ typealias ReaderSiteSearchFailureBlock = (_ error: Error?) -> Void
1313

1414
private func apiRequest() -> WordPressComRestApi {
1515
let api = coreDataStack.performQuery {
16-
let defaultAccount = try? WPAccount.lookupDefaultWordPressComAccount(in: $0)
17-
return defaultAccount?.wordPressComRestApi
16+
try? WPAccount.defaultWordPressComAccountRestAPI(in: $0)
1817
}
1918

2019
if let api, api.hasCredentials() {

WordPress/Classes/Services/ReaderSiteService.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import Foundation
1010
/// - failure: Closure called when the request fails.
1111
func flagSite(withID id: NSNumber, asBlocked blocked: Bool, success: (() -> Void)? = nil, failure: ((Error?) -> Void)? = nil) {
1212
let queryResult: (NSNumber, WordPressComRestApi)? = self.coreDataStack.performQuery({
13-
guard
14-
let defaultAccount = try? WPAccount.lookupDefaultWordPressComAccount(in: $0),
13+
guard let defaultAccount = try? WPAccount.lookupDefaultWordPressComAccount(in: $0),
1514
let api = defaultAccount.wordPressComRestApi,
1615
api.hasCredentials()
1716
else {

WordPress/Classes/Services/ReaderTopicService+Subscriptions.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extension ReaderTopicService {
1414

1515
private func apiRequest() -> WordPressComRestApi {
1616
let api = coreDataStack.performQuery { context in
17-
try? WPAccount.lookupDefaultWordPressComAccount(in: context)?.wordPressComRestApi
17+
try? WPAccount.defaultWordPressComAccountRestAPI(in: context)
1818
}
1919
if let api, api.hasCredentials() {
2020
return api

WordPress/Classes/Services/SiteSuggestionService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class SiteSuggestionService {
7171
// add this blog to currently being requested list
7272
blogsCurrentlyBeingRequested.append(blogId)
7373

74-
defaultAccount()?.wordPressComRestApi.GET(suggestPath, parameters: params, success: { [weak self] responseObject, httpResponse in
74+
defaultAccount()?.wordPressComRestApi?.GET(suggestPath, parameters: params, success: { [weak self] responseObject, httpResponse in
7575
guard let `self` = self else { return }
7676

7777
let context = ContextManager.shared.mainContext

WordPress/Classes/Services/SuggestionService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class SuggestionService {
5454
// add this blog to currently being requested list
5555
blogsCurrentlyBeingRequested.append(blogId)
5656

57-
defaultAccount()?.wordPressComRestApi.GET(suggestPath, parameters: params, success: { [weak self] responseObject, httpResponse in
57+
defaultAccount()?.wordPressComRestApi?.GET(suggestPath, parameters: params, success: { [weak self] responseObject, httpResponse in
5858
guard let `self` = self else { return }
5959
guard let payload = responseObject as? [String: Any] else { return }
6060
guard let restSuggestions = payload["suggestions"] as? [[String: Any]] else { return }

WordPress/WordPressTest/MediaServiceUpdateTests.m

-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@
55
@import WordPressKit;
66
@import OCMock;
77

8-
// Redefine `WPAccount` to make `wordPressComRestApi` writable.
9-
@interface WPAccount ()
10-
@property (nonatomic, readwrite) WordPressComRestApi *wordPressComRestApi;
11-
@end
12-
138
// Re-implement `MediaService` to mock the remote service `MediaServiceRemote`.
149
@interface MediaServiceForStubbing : MediaService
1510
@property (nonatomic, strong) MediaServiceRemoteREST *remoteForStubbing;

WordPress/WordPressTest/MenusServiceTests.m

-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88

99
@import OCMock;
1010

11-
@interface WPAccount ()
12-
@property (nonatomic, readwrite) WordPressComRestApi *wordPressComRestApi;
13-
@end
14-
1511
@interface MenusServiceTests : XCTestCase
1612
@property (nonatomic, strong) id<CoreDataStack> manager;
1713
@end

WordPress/WordPressTest/PostCategoryServiceTests.m

-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
@import WordPressKit;
88
@import OCMock;
99

10-
@interface WPAccount ()
11-
@property (nonatomic, readwrite) WordPressComRestApi *wordPressComRestApi;
12-
@end
13-
1410
@interface PostCategoryServiceForStubbing : PostCategoryService
1511

1612
@property (nonatomic, strong) TaxonomyServiceRemoteREST *remoteForStubbing;

WordPress/WordPressTest/PostTagServiceTests.m

-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ @interface PostTagServiceForStubbing : PostTagService
1313

1414
@end
1515

16-
@interface WPAccount ()
17-
@property (nonatomic, readwrite) WordPressComRestApi *wordPressComRestApi;
18-
@end
19-
2016
@implementation PostTagServiceForStubbing
2117

2218
- (id <TaxonomyServiceRemote>)remoteForBlog:(Blog *)blog

WordPress/WordPressTest/ThemeServiceTests.m

-4
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@
1010

1111
#pragma mark - Support classes
1212

13-
@interface WPAccount ()
14-
@property (nonatomic, readwrite) WordPressComRestApi *wordPressComRestApi;
15-
@end
16-
1713
#pragma mark - Tests
1814

1915
@interface ThemeServiceTests : XCTestCase

WordPress/WordPressTest/WPAccount+LookupTests.swift

+9
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ class WPAccountLookupTests: CoreDataTestCase {
9696
try XCTAssertEqual(WPAccount.lookupNumberOfAccounts(in: contextManager.mainContext), 3)
9797
}
9898

99+
// MARK: - Default account WordPress.com REST API
100+
101+
func testNoAPIWhenNoDefaultAccount() throws {
102+
makeAccount()
103+
try XCTAssertNil(WPAccount.defaultWordPressComAccountRestAPI(in: contextManager.mainContext))
104+
}
105+
106+
// MARK: -
107+
99108
@discardableResult
100109
func makeAccount(_ additionalSetup: (AccountBuilder) -> (AccountBuilder) = { $0 }) -> WPAccount {
101110
additionalSetup(AccountBuilder(contextManager.mainContext)).build()

0 commit comments

Comments
 (0)