Skip to content

Commit 6f02530

Browse files
authored
Async Await (#11)
* Bump up the target of the Example app to iOS 15 and update the example code to use the new async-await API. * Introduce async-await support. * Remove deprecated SwiftLint rules. * Update protocol requirements from `class` to `AnyObject`. * Update settings for Xcode 13.
1 parent b30d5cc commit 6f02530

File tree

10 files changed

+105
-21
lines changed

10 files changed

+105
-21
lines changed

Example/Example.xcodeproj/project.pbxproj

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@
237237
isa = PBXProject;
238238
attributes = {
239239
LastSwiftUpdateCheck = 1220;
240-
LastUpgradeCheck = 1220;
240+
LastUpgradeCheck = 1300;
241241
TargetAttributes = {
242242
4C4E42E7258ADD2B009AF14F = {
243243
CreatedOnToolsVersion = 12.2;
@@ -436,7 +436,7 @@
436436
DEVELOPMENT_TEAM = VV4VQB3X2M;
437437
ENABLE_PREVIEWS = YES;
438438
INFOPLIST_FILE = iOS/Info.plist;
439-
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
439+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
440440
LD_RUNPATH_SEARCH_PATHS = (
441441
"$(inherited)",
442442
"@executable_path/Frameworks",
@@ -458,7 +458,7 @@
458458
DEVELOPMENT_TEAM = VV4VQB3X2M;
459459
ENABLE_PREVIEWS = YES;
460460
INFOPLIST_FILE = iOS/Info.plist;
461-
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
461+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
462462
LD_RUNPATH_SEARCH_PATHS = (
463463
"$(inherited)",
464464
"@executable_path/Frameworks",
@@ -478,6 +478,7 @@
478478
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
479479
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
480480
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
481+
CODE_SIGN_IDENTITY = "-";
481482
CODE_SIGN_STYLE = Automatic;
482483
COMBINE_HIDPI_IMAGES = YES;
483484
DEVELOPMENT_TEAM = VV4VQB3X2M;
@@ -488,7 +489,7 @@
488489
"$(inherited)",
489490
"@executable_path/../Frameworks",
490491
);
491-
MACOSX_DEPLOYMENT_TARGET = 11.0;
492+
MACOSX_DEPLOYMENT_TARGET = 12.0;
492493
PRODUCT_BUNDLE_IDENTIFIER = com.alaskaair.Example;
493494
PRODUCT_NAME = Example;
494495
SDKROOT = macosx;
@@ -502,6 +503,7 @@
502503
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
503504
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
504505
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
506+
CODE_SIGN_IDENTITY = "-";
505507
CODE_SIGN_STYLE = Automatic;
506508
COMBINE_HIDPI_IMAGES = YES;
507509
DEVELOPMENT_TEAM = VV4VQB3X2M;
@@ -512,7 +514,7 @@
512514
"$(inherited)",
513515
"@executable_path/../Frameworks",
514516
);
515-
MACOSX_DEPLOYMENT_TARGET = 11.0;
517+
MACOSX_DEPLOYMENT_TARGET = 12.0;
516518
PRODUCT_BUNDLE_IDENTIFIER = com.alaskaair.Example;
517519
PRODUCT_NAME = Example;
518520
SDKROOT = macosx;

Example/Shared/Views/ContentView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ struct ContentView: View {
2424
var body: some View {
2525
content
2626
.edgesIgnoringSafeArea(.all)
27-
.onAppear { model.random() }
2827
}
2928

3029
// MARK: Content
@@ -43,7 +42,9 @@ struct ContentView: View {
4342

4443
Spacer()
4544

46-
Button(action: { model.random() }) {
45+
Button {
46+
Task { try? await model.random() }
47+
} label: {
4748
Text("REFRESH")
4849
.fontWeight(.bold)
4950
.foregroundColor(.yellow)

Example/Shared/Views/ContentViewModel.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@ import Atom
1818
import Combine
1919
import Foundation
2020

21+
@MainActor
2122
final class ContentViewModel: ObservableObject {
2223
/// The joke to display on successful fetch.
2324
@Published var joke: Joke = .default
2425

2526
/// Fetches a random joke.
26-
func random() {
27-
atom
28-
.enqueue(Joke.Endpoint.random)
29-
.resume(expecting: Joke.self)
30-
.replaceError(with: .default)
31-
.assign(to: &$joke)
27+
func random() async throws {
28+
do {
29+
joke = try await atom
30+
.enqueue(Joke.Endpoint.random)
31+
.resume(expecting: Joke.self)
32+
} catch { joke = .default }
3233
}
3334
}

Framework/.swiftlint.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ disabled_rules:
66
- nesting
77
- pattern_matching_keywords
88
- private_over_fileprivate
9-
- private_unit_testing
109
- identifier_name
1110
- vertical_whitespace
1211
- trailing_whitespace
@@ -29,7 +28,6 @@ opt_in_rules:
2928
- legacy_random
3029
- let_var_whitespace
3130
- literal_expression_end_indentation
32-
- missing_empty_line
3331
- modifier_order
3432
- object_literal
3533
- operator_usage_whitespace
@@ -43,7 +41,6 @@ opt_in_rules:
4341
- trailing_closure
4442
- unneeded_parentheses_in_closure_argument
4543
- untyped_error_in_catch
46-
- unused_private_declaration
4744
- unused_import
4845
- vertical_parameter_alignment_on_call
4946
- yoda_condition

Framework/Atom.xcodeproj/project.pbxproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
4C92FD19224E6A6100D16767 /* Result+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C92FD18224E6A6100D16767 /* Result+Additions.swift */; };
7777
4C96B7142360B6CB0021EDB8 /* Response+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C96B7132360B6CB0021EDB8 /* Response+Additions.swift */; };
7878
4C973FD3237372EC006B2736 /* AuthenticationMethod+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C973FD2237372EC006B2736 /* AuthenticationMethod+Additions.swift */; };
79+
4C98D6EB2696A5720074AC83 /* Service+AsyncAwait.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C98D6EA2696A5720074AC83 /* Service+AsyncAwait.swift */; };
7980
4CA6564D242189E000E35D9F /* URLSessionTaskMetrics+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA6564C242189E000E35D9F /* URLSessionTaskMetrics+Additions.swift */; };
8081
4CAB5A69242012E900E4FB97 /* Interceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAB5A68242012E900E4FB97 /* Interceptor.swift */; };
8182
4CAB5A6C2420217E00E4FB97 /* OSLog+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAB5A6B2420217E00E4FB97 /* OSLog+Additions.swift */; };
@@ -165,6 +166,7 @@
165166
4C92FD18224E6A6100D16767 /* Result+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Additions.swift"; sourceTree = "<group>"; };
166167
4C96B7132360B6CB0021EDB8 /* Response+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Response+Additions.swift"; sourceTree = "<group>"; };
167168
4C973FD2237372EC006B2736 /* AuthenticationMethod+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AuthenticationMethod+Additions.swift"; sourceTree = "<group>"; };
169+
4C98D6EA2696A5720074AC83 /* Service+AsyncAwait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Service+AsyncAwait.swift"; sourceTree = "<group>"; };
168170
4CA6564C242189E000E35D9F /* URLSessionTaskMetrics+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionTaskMetrics+Additions.swift"; sourceTree = "<group>"; };
169171
4CAB5A68242012E900E4FB97 /* Interceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interceptor.swift; sourceTree = "<group>"; };
170172
4CAB5A6B2420217E00E4FB97 /* OSLog+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OSLog+Additions.swift"; sourceTree = "<group>"; };
@@ -285,6 +287,7 @@
285287
isa = PBXGroup;
286288
children = (
287289
1CC3281D20D844610099644D /* Service.swift */,
290+
4C98D6EA2696A5720074AC83 /* Service+AsyncAwait.swift */,
288291
4C4E42C3258AC311009AF14F /* Service+Combine.swift */,
289292
1CC3280B20D813E70099644D /* ServiceConfiguration.swift */,
290293
4C31600C221DE0BE00E853F5 /* ServiceConfiguration+Timeout.swift */,
@@ -595,7 +598,7 @@
595598
);
596599
runOnlyForDeploymentPostprocessing = 0;
597600
shellPath = /bin/sh;
598-
shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
601+
shellScript = "if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
599602
};
600603
/* End PBXShellScriptBuildPhase section */
601604

@@ -628,6 +631,7 @@
628631
4C7B86002372361F00FA85FC /* AuthorizationEndpoint.swift in Sources */,
629632
1C241795216687100032A994 /* RequestableError.swift in Sources */,
630633
1CC3287B20D9990F0099644D /* URLRequest+Additions.swift in Sources */,
634+
4C98D6EB2696A5720074AC83 /* Service+AsyncAwait.swift in Sources */,
631635
4C316011221E0C8300E853F5 /* AtomResponse.swift in Sources */,
632636
4C316027221F0E5400E853F5 /* Data+Additions.swift in Sources */,
633637
4C6A0D3923DBC28300A64975 /* Atom+Notifications.swift in Sources */,

Framework/Atom.xcodeproj/xcshareddata/xcschemes/Atom.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1220"
3+
LastUpgradeVersion = "1300"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

Framework/Atom/Protocols/Delegates/AuthenticationManagerDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import Foundation
1818

1919
/// The `AuthenticationManagerDelegate` protocol provides an interface for responding to `AuthenticationManager` events.
20-
internal protocol AuthenticationManagerDelegate: class {
20+
internal protocol AuthenticationManagerDelegate: AnyObject {
2121
/// Notifies the delegate when the `AuthenticationManager` successfully refreshed the access token.
2222
func authenticationManagerDidRefreshAccessToken()
2323

Framework/Atom/Protocols/Types/TokenCredentialWritable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import Foundation
3434
/// ```
3535
///
3636
/// For more information see `Configuration` documentation.
37-
public protocol TokenCredentialWritable: class {
37+
public protocol TokenCredentialWritable: AnyObject {
3838
/// Returns conforming type as `TokenCredential`.
3939
var tokenCredential: TokenCredential { get set }
4040
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Atom
2+
//
3+
// Copyright (c) 2021 Alaska Airlines
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
import Foundation
18+
19+
@available(iOS 15.0, macOS 12.0, *)
20+
public extension Service {
21+
/// Creates and resumes `URLRequest` initialized from `Requestable`.
22+
///
23+
/// Use this method to make network requests where you expect data returned by the
24+
/// service and require that data to be decoded into internal representations - models.
25+
///
26+
/// Network request and decoding will be performed on a background thread after
27+
/// which the client will be notified on a queue `Atom` was configured to use.
28+
///
29+
/// A typical usage pattern for this method could look like this:
30+
///
31+
/// ```
32+
/// let user = try await atom
33+
/// .enqueue(endpoint)
34+
/// .resume(expecting: User.self)
35+
/// ```
36+
///
37+
/// In the above example, data will be decoded into a `User` instance.
38+
///
39+
/// - Parameters:
40+
/// - type: The type to decode.
41+
///
42+
/// - Throws: `AtomError` instance if an error occurred.
43+
///
44+
/// - Returns: Decoded `Model` instance.
45+
func resume<T>(expecting type: T.Type) async throws -> T where T: Model {
46+
try await withCheckedThrowingContinuation { continuation in
47+
self.resume(expecting: type) {
48+
continuation.resume(with: $0)
49+
}
50+
}
51+
}
52+
53+
/// Creates and resumes `URLRequest` initialized from `Requestable`.
54+
///
55+
/// Use this method to make network requests where you don't expect any data returned
56+
/// and are only interested in knowing if the network call succeeded or failed.
57+
///
58+
/// `Atom` framework uses a convenience computed variable on `AtomResponse` - `isSuccessful`
59+
/// to determine success or a failure of a response based on a status code returned by the service.
60+
///
61+
/// A typical usage pattern for this method could look like this:
62+
///
63+
/// ```
64+
/// let response = try await atom
65+
/// .enqueue(endpoint)
66+
/// .resume()
67+
/// ```
68+
///
69+
/// - Throws: `AtomError` instance if an error occurred.
70+
///
71+
/// - Returns: `AtomResponse` instance.
72+
func resume() async throws -> AtomResponse {
73+
try await withCheckedThrowingContinuation { continuation in
74+
self.resume {
75+
continuation.resume(with: $0)
76+
}
77+
}
78+
}
79+
}

Framework/Atom/Service/Service+Combine.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public extension Service {
6767
///
6868
/// ````
6969
/// atom
70-
/// .enqueue(Endpoint.random)
70+
/// .enqueue(endpoint)
7171
/// .resume()
7272
/// .sink {
7373
/// // Handle `AtomError`.

0 commit comments

Comments
 (0)