Skip to content

Instantly share code, notes, and snippets.

@djbe
Created September 29, 2021 23:57
Show Gist options
  • Save djbe/1ce35bbd739521787ff10fc09c4d886d to your computer and use it in GitHub Desktop.
Save djbe/1ce35bbd739521787ff10fc09c4d886d to your computer and use it in GitHub Desktop.
Migrate Alamofire5
#!/bin/bash
set -Eeuo pipefail
shopt -s extglob
update_swift_files() {
find . -name '*.swift' -print0 -o -type d \( -path .git -o -name Pods -o -name .build -o -name build -o -name Rome -o -name fastlane -o -name Generated \) -prune | xargs -0 perl -0pi -e "$@"
}
fix_dataresponse() {
echo "Fixing DataResponse..."
update_swift_files 's/DataResponse<([^,<>]+?)>/DataResponse<$1, Swift.Error>/g'
}
fix_result() {
echo "Fixing Result..."
update_swift_files 's/Result<([^,<>]+?<[^>]+?>[^,>]*?)>/Result<$1, Swift.Error>/g'
update_swift_files 's/Result<([^,<>]+?)>/Result<$1, Swift.Error>/g'
update_swift_files 's/Alamofire.Result/Result/g'
}
fix_request() {
echo "Fixing request calls..."
update_swift_files 's/requestJSONDecodable/requestDecodable/g'
update_swift_files 's/(?!func )requestInsert\((.+?),(\s+)type:(.+?),/requestInsert($1,$2of:$3,/gms'
update_swift_files 's/(?!func )requestInsert\(([^()]+?|[^()]+?\([^()]+?\)[^()]+?),(\s+db:([^()]+?|[^()]+?\([^()]+?\)[^()]+?),)?(\s+queue:([^()]+?|[^()]+?\([^()]+?\)[^()]+?),)?(\s+jsonSerializer:([^()]+?|[^()]+?\([^()]+?\)[^()]+?),)(\s+of:([^()]+?|[^()]+?\([^()]+?\)[^()]+?),)/requestInsert($1,$8$2$4\/* TODO: other params? *\//gms'
}
fix_api_client() {
echo "Fixing Client implementations..."
update_swift_files 's/\n([ \t]+)([^\n]*?(var|let)) sessionManager = SessionManager\(\)\n/\n$1$2 session = Session()\n/g'
update_swift_files 's/\n([ \t]+)([^\n]*?(var|let)) sessionManager = SessionManager\(\)\.then \{\n[^}]*?\}/\n$1private let interceptor = AuthenticationInterceptor(authenticator: OAuth2Authenticator(), credential: OAuth2Grant.grant)\n$1$2 session: Session = Session(interceptor: interceptor)/gms'
update_swift_files 's/NukeAlamofirePlugin\.AlamofireDataLoader\(manager: self\.sessionManager\)/NukeAlamofirePlugin.AlamofireDataLoader(session: self.session)/g'
update_swift_files 's/func nukeOptions.*? -> ImageLoadingOptions \{/func nukeOptions(placeholder: PlatformImage? = nil, transition: ImageLoadingOptions.Transition? = nil, failureImage: PlatformImage? = nil, failureImageTransition: ImageLoadingOptions.Transition? = nil, contentModes: ImageLoadingOptions.ContentModes? = nil) -> ImageLoadingOptions {/g'
update_swift_files 's/static func extract<T>\(from response: DataResponse<T>, error: Error\) -> Error \{/static func extract<T>(from response: DataResponse<T, Error>, error: Error) -> Error {/g'
update_swift_files 's/queue: DispatchQueue\? = nil,(\s*)jsonSerializer: DataResponseSerializer<Any> = DataRequest.jsonResponseSerializer\(\),/queue: DispatchQueue = .main,$1jsonOptions: JSONSerialization.ReadingOptions = .allowFragments,$1jsonTransformer: \@escaping (Any) throws -> Any = { \$0 },/gms'
}
fix_router() {
echo "Fixing Router implementations..."
update_swift_files 's/var headers: \[String: String\] \{/var headers: HTTPHeaders? \{/g'
}
fix_retry_handler() {
HANDLER_FILE="Application/Sources/Network/OAuth2RetryHandler.swift"
if [ ! -f "$HANDLER_FILE" ]; then return; fi
echo "Fixing retry handler..."
sed -i '' '/import p2_OAuth2/q' "$HANDLER_FILE"
cat >>"$HANDLER_FILE" <<EOL
extension OAuth2: AuthenticationCredential {
public var requiresRefresh: Bool {
!hasUnexpiredAccessToken()
}
}
final class OAuth2Authenticator: Authenticator {
func apply(_ credential: OAuth2, to urlRequest: inout URLRequest) {
try? urlRequest.sign(with: credential)
}
func refresh(_ credential: OAuth2, for session: Session, completion: @escaping (Result<OAuth2, Error>) -> Void) {
credential.authorize { _, error in
if let error = error {
completion(.failure(error))
if error.isSessionExpired {
Notification.SessionExpired().post()
}
} else {
completion(.success(credential))
}
}
}
func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool {
response.statusCode == 401
}
func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: OAuth2) -> Bool {
urlRequest.headers["Authorization"] == credential.accessToken.flatMap { "Bearer \(\$0)" }
}
}
extension OAuth2Error {
var isSessionExpired: Bool {
switch self {
case .accessDenied,
.forbidden,
.invalidGrant,
.invalidScope,
.noPasswordGrantDelegate,
.unauthorizedClient:
return true
case .generic,
.nsError,
.invalidURLComponents,
.noClientId,
.noClientSecret,
.noRedirectURL,
.noUsername,
.noPassword,
.alreadyAuthorizing,
.noAuthorizationContext,
.invalidAuthorizationContext,
.invalidRedirectURL,
.noAccessToken,
.noRefreshToken,
.noRegistrationURL,
.invalidLoginController,
.notUsingTLS,
.unableToOpenAuthorizeURL,
.invalidRequest,
.requestCancelled,
.noTokenType,
.unsupportedTokenType,
.noDataInResponse,
.prerequisiteFailed,
.missingState,
.invalidState,
.jsonParserError,
.utf8EncodeError,
.utf8DecodeError,
.wrongUsernamePassword,
.unsupportedResponseType,
.serverError,
.temporarilyUnavailable,
.responseError:
return false
}
}
}
EOL
}
fix_then() {
echo "Fixing Then..."
update_swift_files 's/extension SessionManager: Then \{\}\n//gms'
}
fix_nuke() {
echo "Fixing Nuke..."
update_swift_files 's/(\.loadImage\(\s*with:[^{]+?\{[^}]*?)\s_,\s_\sin/$1 _ in/gms'
update_swift_files 's/(\.loadImage\(\s*with:[^{]+?\{[^}]*?)\s\w+,\s\w+\sin/$1 result in/gms'
update_swift_files 's/response\?\.image/result.value?.image/gms'
update_swift_files 's/if let key = cacheKey\(for: request\) \{[^}]*imageRequest[^}]*\}/imageRequest.options.filteredURL = cacheKey(for: request)/gms'
}
echo "Resetting git..."
git checkout -- Application
echo "Fixing project..."
fix_dataresponse
fix_result
fix_request
fix_api_client
fix_router
fix_retry_handler
fix_then
fix_nuke
echo "Done!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment